summaryrefslogtreecommitdiffstats
path: root/rust/src/smb/smb1.rs
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/smb/smb1.rs')
-rw-r--r--rust/src/smb/smb1.rs1155
1 files changed, 1155 insertions, 0 deletions
diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs
new file mode 100644
index 0000000..9d7d47e
--- /dev/null
+++ b/rust/src/smb/smb1.rs
@@ -0,0 +1,1155 @@
+/* Copyright (C) 2018-2022 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.
+ */
+
+/* TODO
+ * - check all parsers for calls on non-SUCCESS status
+ */
+
+use crate::core::*;
+
+use crate::smb::smb::*;
+use crate::smb::dcerpc::*;
+use crate::smb::events::*;
+use crate::smb::files::*;
+
+use crate::smb::smb1_records::*;
+use crate::smb::smb1_session::*;
+
+use crate::smb::smb_status::*;
+
+use nom7::Err;
+
+// https://msdn.microsoft.com/en-us/library/ee441741.aspx
+pub const SMB1_COMMAND_CREATE_DIRECTORY: u8 = 0x00;
+pub const SMB1_COMMAND_DELETE_DIRECTORY: u8 = 0x01;
+pub const SMB1_COMMAND_OPEN: u8 = 0x02;
+pub const SMB1_COMMAND_CREATE: u8 = 0x03;
+pub const SMB1_COMMAND_CLOSE: u8 = 0x04;
+pub const SMB1_COMMAND_FLUSH: u8 = 0x05;
+pub const SMB1_COMMAND_DELETE: u8 = 0x06;
+pub const SMB1_COMMAND_RENAME: u8 = 0x07;
+pub const SMB1_COMMAND_QUERY_INFORMATION: u8 = 0x08;
+pub const SMB1_COMMAND_SET_INFORMATION: u8 = 0x09;
+pub const SMB1_COMMAND_READ: u8 = 0x0a;
+pub const SMB1_COMMAND_WRITE: u8 = 0x0b;
+pub const SMB1_COMMAND_LOCK_BYTE_RANGE: u8 = 0x0c;
+pub const SMB1_COMMAND_UNLOCK_BYTE_RANGE: u8 = 0x0d;
+pub const SMB1_COMMAND_CREATE_TEMPORARY: u8 = 0x0e;
+pub const SMB1_COMMAND_CREATE_NEW: u8 = 0x0f;
+pub const SMB1_COMMAND_CHECK_DIRECTORY: u8 = 0x10;
+pub const SMB1_COMMAND_PROCESS_EXIT: u8 = 0x11;
+pub const SMB1_COMMAND_SEEK: u8 = 0x12;
+pub const SMB1_COMMAND_LOCK_AND_READ: u8 = 0x13;
+pub const SMB1_COMMAND_WRITE_AND_UNLOCK: u8 = 0x14;
+pub const SMB1_COMMAND_LOCKING_ANDX: u8 = 0x24;
+pub const SMB1_COMMAND_TRANS: u8 = 0x25;
+pub const SMB1_COMMAND_ECHO: u8 = 0x2b;
+pub const SMB1_COMMAND_WRITE_AND_CLOSE: u8 = 0x2c;
+pub const SMB1_COMMAND_OPEN_ANDX: u8 = 0x2d;
+pub const SMB1_COMMAND_READ_ANDX: u8 = 0x2e;
+pub const SMB1_COMMAND_WRITE_ANDX: u8 = 0x2f;
+pub const SMB1_COMMAND_TRANS2: u8 = 0x32;
+pub const SMB1_COMMAND_TRANS2_SECONDARY: u8 = 0x33;
+pub const SMB1_COMMAND_FIND_CLOSE2: u8 = 0x34;
+pub const SMB1_COMMAND_TREE_DISCONNECT: u8 = 0x71;
+pub const SMB1_COMMAND_NEGOTIATE_PROTOCOL: u8 = 0x72;
+pub const SMB1_COMMAND_SESSION_SETUP_ANDX: u8 = 0x73;
+pub const SMB1_COMMAND_LOGOFF_ANDX: u8 = 0x74;
+pub const SMB1_COMMAND_TREE_CONNECT_ANDX: u8 = 0x75;
+pub const SMB1_COMMAND_QUERY_INFO_DISK: u8 = 0x80;
+pub const SMB1_COMMAND_NT_TRANS: u8 = 0xa0;
+pub const SMB1_COMMAND_NT_TRANS_SECONDARY: u8 = 0xa1;
+pub const SMB1_COMMAND_NT_CREATE_ANDX: u8 = 0xa2;
+pub const SMB1_COMMAND_NT_CANCEL: u8 = 0xa4;
+pub const SMB1_COMMAND_NONE: u8 = 0xff;
+
+pub fn smb1_command_string(c: u8) -> String {
+ match c {
+ SMB1_COMMAND_CREATE_DIRECTORY => "SMB1_COMMAND_CREATE_DIRECTORY",
+ SMB1_COMMAND_DELETE_DIRECTORY => "SMB1_COMMAND_DELETE_DIRECTORY",
+ SMB1_COMMAND_OPEN => "SMB1_COMMAND_OPEN",
+ SMB1_COMMAND_CREATE => "SMB1_COMMAND_CREATE",
+ SMB1_COMMAND_CLOSE => "SMB1_COMMAND_CLOSE",
+ SMB1_COMMAND_FLUSH => "SMB1_COMMAND_FLUSH",
+ SMB1_COMMAND_DELETE => "SMB1_COMMAND_DELETE",
+ SMB1_COMMAND_RENAME => "SMB1_COMMAND_RENAME",
+ SMB1_COMMAND_QUERY_INFORMATION => "SMB1_COMMAND_QUERY_INFORMATION",
+ SMB1_COMMAND_SET_INFORMATION => "SMB1_COMMAND_SET_INFORMATION",
+ SMB1_COMMAND_READ => "SMB1_COMMAND_READ",
+ SMB1_COMMAND_WRITE => "SMB1_COMMAND_WRITE",
+ SMB1_COMMAND_LOCK_BYTE_RANGE => "SMB1_COMMAND_LOCK_BYTE_RANGE",
+ SMB1_COMMAND_UNLOCK_BYTE_RANGE => "SMB1_COMMAND_UNLOCK_BYTE_RANGE",
+ SMB1_COMMAND_CREATE_TEMPORARY => "SMB1_COMMAND_CREATE_TEMPORARY",
+ SMB1_COMMAND_CREATE_NEW => "SMB1_COMMAND_CREATE_NEW",
+ SMB1_COMMAND_CHECK_DIRECTORY => "SMB1_COMMAND_CHECK_DIRECTORY",
+ SMB1_COMMAND_PROCESS_EXIT => "SMB1_COMMAND_PROCESS_EXIT",
+ SMB1_COMMAND_SEEK => "SMB1_COMMAND_SEEK",
+ SMB1_COMMAND_LOCK_AND_READ => "SMB1_COMMAND_LOCK_AND_READ",
+ SMB1_COMMAND_WRITE_AND_UNLOCK => "SMB1_COMMAND_WRITE_AND_UNLOCK",
+ SMB1_COMMAND_LOCKING_ANDX => "SMB1_COMMAND_LOCKING_ANDX",
+ SMB1_COMMAND_ECHO => "SMB1_COMMAND_ECHO",
+ SMB1_COMMAND_WRITE_AND_CLOSE => "SMB1_COMMAND_WRITE_AND_CLOSE",
+ SMB1_COMMAND_OPEN_ANDX => "SMB1_COMMAND_OPEN_ANDX",
+ SMB1_COMMAND_READ_ANDX => "SMB1_COMMAND_READ_ANDX",
+ SMB1_COMMAND_WRITE_ANDX => "SMB1_COMMAND_WRITE_ANDX",
+ SMB1_COMMAND_TRANS => "SMB1_COMMAND_TRANS",
+ SMB1_COMMAND_TRANS2 => "SMB1_COMMAND_TRANS2",
+ SMB1_COMMAND_TRANS2_SECONDARY => "SMB1_COMMAND_TRANS2_SECONDARY",
+ SMB1_COMMAND_FIND_CLOSE2 => "SMB1_COMMAND_FIND_CLOSE2",
+ SMB1_COMMAND_TREE_DISCONNECT => "SMB1_COMMAND_TREE_DISCONNECT",
+ SMB1_COMMAND_NEGOTIATE_PROTOCOL => "SMB1_COMMAND_NEGOTIATE_PROTOCOL",
+ SMB1_COMMAND_SESSION_SETUP_ANDX => "SMB1_COMMAND_SESSION_SETUP_ANDX",
+ SMB1_COMMAND_LOGOFF_ANDX => "SMB1_COMMAND_LOGOFF_ANDX",
+ SMB1_COMMAND_TREE_CONNECT_ANDX => "SMB1_COMMAND_TREE_CONNECT_ANDX",
+ SMB1_COMMAND_QUERY_INFO_DISK => "SMB1_COMMAND_QUERY_INFO_DISK",
+ SMB1_COMMAND_NT_TRANS => "SMB1_COMMAND_NT_TRANS",
+ SMB1_COMMAND_NT_TRANS_SECONDARY => "SMB1_COMMAND_NT_TRANS_SECONDARY",
+ SMB1_COMMAND_NT_CREATE_ANDX => "SMB1_COMMAND_NT_CREATE_ANDX",
+ SMB1_COMMAND_NT_CANCEL => "SMB1_COMMAND_NT_CANCEL",
+ _ => { return (c).to_string(); },
+ }.to_string()
+}
+
+// later we'll use this to determine if we need to
+// track a ssn per type
+pub fn smb1_create_new_tx(cmd: u8) -> bool {
+ match cmd {
+ SMB1_COMMAND_READ_ANDX |
+ SMB1_COMMAND_WRITE_ANDX |
+ SMB1_COMMAND_TRANS |
+ SMB1_COMMAND_TRANS2 => { false },
+ _ => { true },
+ }
+}
+
+// see if we're going to do a lookup for a TX.
+// related to smb1_create_new_tx(), however it
+// excludes the 'maybe' commands like TRANS2
+pub fn smb1_check_tx(cmd: u8) -> bool {
+ match cmd {
+ SMB1_COMMAND_READ_ANDX |
+ SMB1_COMMAND_WRITE_ANDX |
+ SMB1_COMMAND_TRANS => { false },
+ _ => { true },
+ }
+}
+
+fn smb1_close_file(state: &mut SMBState, fid: &[u8], direction: Direction)
+{
+ if let Some(tx) = state.get_file_tx_by_fuid(fid, direction) {
+ SCLogDebug!("found tx {}", tx.id);
+ if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+ if !tx.request_done {
+ SCLogDebug!("closing file tx {} FID {:?}", tx.id, fid);
+ filetracker_close(&mut tdf.file_tracker);
+ tx.request_done = true;
+ tx.response_done = true;
+ SCLogDebug!("tx {} is done", tx.id);
+ }
+ }
+ }
+}
+
+fn smb1_command_is_andx(c: u8) -> bool {
+ match c {
+ SMB1_COMMAND_LOCKING_ANDX |
+ SMB1_COMMAND_OPEN_ANDX |
+ SMB1_COMMAND_READ_ANDX |
+ SMB1_COMMAND_SESSION_SETUP_ANDX |
+ SMB1_COMMAND_LOGOFF_ANDX |
+ SMB1_COMMAND_TREE_CONNECT_ANDX |
+ SMB1_COMMAND_NT_CREATE_ANDX |
+ SMB1_COMMAND_WRITE_ANDX => {
+ return true;
+ }
+ _ => {
+ return false;
+ }
+ }
+}
+
+fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, andx_offset: &mut usize) {
+ let mut events : Vec<SMBEvent> = Vec::new();
+ let mut no_response_expected = false;
+
+ let have_tx = match command {
+ SMB1_COMMAND_RENAME => {
+ match parse_smb_rename_request_record(r.data) {
+ Ok((_, rd)) => {
+ SCLogDebug!("RENAME {:?}", rd);
+
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let mut newname = rd.newname;
+ newname.retain(|&i|i != 0x00);
+ let mut oldname = rd.oldname;
+ oldname.retain(|&i|i != 0x00);
+
+ let tx = state.new_rename_tx(Vec::new(), oldname, newname);
+ tx.hdr = tx_hdr;
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_RENAME);
+ true
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_TRANS2 => {
+ match parse_smb_trans2_request_record(r.data) {
+ Ok((_, rd)) => {
+ SCLogDebug!("TRANS2 DONE {:?}", rd);
+
+ if rd.subcmd == 6 {
+ SCLogDebug!("SET_PATH_INFO");
+ match parse_trans2_request_params_set_path_info(rd.setup_blob) {
+ Ok((_, pd)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS DONE {:?}", pd);
+
+ if pd.loi == 1013 { // set disposition info
+ match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) {
+ Ok((_, disp)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp);
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+
+ let tx = state.new_setpathinfo_tx(pd.oldname,
+ rd.subcmd, pd.loi, disp.delete);
+ tx.hdr = tx_hdr;
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
+ true
+
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else if pd.loi == 1010 {
+ match parse_trans2_request_data_set_path_info_rename(rd.data_blob) {
+ Ok((_, ren)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME DONE {:?}", ren);
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let mut newname = ren.newname.to_vec();
+ newname.retain(|&i|i != 0x00);
+
+ let fid : Vec<u8> = Vec::new();
+
+ let tx = state.new_rename_tx(fid, pd.oldname, newname);
+ tx.hdr = tx_hdr;
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
+ true
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO DATA RENAME ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else {
+ false
+ }
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_PATH_INFO PARAMS ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else if rd.subcmd == 8 {
+ SCLogDebug!("SET_FILE_INFO");
+ match parse_trans2_request_params_set_file_info(rd.setup_blob) {
+ Ok((_, pd)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS DONE {:?}", pd);
+
+ if pd.loi == 1013 { // set disposition info
+ match parse_trans2_request_data_set_file_info_disposition(rd.data_blob) {
+ Ok((_, disp)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION DONE {:?}", disp);
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+
+ let mut frankenfid = pd.fid.to_vec();
+ frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+
+ let filename = match state.guid2name_map.get(&frankenfid) {
+ Some(n) => n.to_vec(),
+ None => b"<unknown>".to_vec(),
+ };
+ let tx = state.new_setfileinfo_tx(filename, pd.fid.to_vec(),
+ rd.subcmd, pd.loi, disp.delete);
+ tx.hdr = tx_hdr;
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
+ true
+
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA DISPOSITION ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else if pd.loi == 1010 {
+ match parse_trans2_request_data_set_file_info_rename(rd.data_blob) {
+ Ok((_, ren)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME DONE {:?}", ren);
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let mut newname = ren.newname.to_vec();
+ newname.retain(|&i|i != 0x00);
+
+ let mut frankenfid = pd.fid.to_vec();
+ frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+
+ let oldname = match state.guid2name_map.get(&frankenfid) {
+ Some(n) => n.to_vec(),
+ None => b"<unknown>".to_vec(),
+ };
+ let tx = state.new_rename_tx(pd.fid.to_vec(), oldname, newname);
+ tx.hdr = tx_hdr;
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TRANS2);
+ true
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO DATA RENAME ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else {
+ false
+ }
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 SET_FILE_INFO PARAMS ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else {
+ false
+ }
+ },
+ Err(Err::Incomplete(_n)) => {
+ SCLogDebug!("TRANS2 INCOMPLETE {:?}", _n);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ Err(Err::Error(_e)) |
+ Err(Err::Failure(_e)) => {
+ SCLogDebug!("TRANS2 ERROR {:?}", _e);
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_READ_ANDX => {
+ match parse_smb_read_andx_request_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) {
+ Ok((_, rr)) => {
+ SCLogDebug!("rr {:?}", rr);
+
+ // store read fid,offset in map
+ let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET);
+ let mut fid = rr.fid.to_vec();
+ fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+ let fidoff = SMBFileGUIDOffset::new(fid, rr.offset);
+ state.ssn2vecoffset_map.insert(fid_key, fidoff);
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+ false
+ },
+ SMB1_COMMAND_WRITE_ANDX |
+ SMB1_COMMAND_WRITE |
+ SMB1_COMMAND_WRITE_AND_CLOSE => {
+ smb1_write_request_record(state, r, *andx_offset, command, 0);
+ true // tx handling in func
+ },
+ SMB1_COMMAND_TRANS => {
+ smb1_trans_request_record(state, r);
+ true
+ },
+ SMB1_COMMAND_NEGOTIATE_PROTOCOL => {
+ match parse_smb1_negotiate_protocol_record(r.data) {
+ Ok((_, pr)) => {
+ SCLogDebug!("SMB_COMMAND_NEGOTIATE_PROTOCOL {:?}", pr);
+
+ let mut bad_dialects = false;
+ let mut dialects : Vec<Vec<u8>> = Vec::new();
+ for d in &pr.dialects {
+ if d.is_empty() {
+ bad_dialects = true;
+ continue;
+ } else if d.len() == 1 {
+ bad_dialects = true;
+ }
+ let x = &d[1..d.len()];
+ let dvec = x.to_vec();
+ dialects.push(dvec);
+ }
+
+ let found = match state.get_negotiate_tx(1) {
+ Some(tx) => {
+ SCLogDebug!("WEIRD, should not have NEGOTIATE tx!");
+ tx.set_event(SMBEvent::DuplicateNegotiate);
+ true
+ },
+ None => { false },
+ };
+ if !found {
+ let tx = state.new_negotiate_tx(1);
+ if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
+ tdn.dialects = dialects;
+ }
+ tx.request_done = true;
+ if bad_dialects {
+ tx.set_event(SMBEvent::NegotiateMalformedDialects);
+ }
+ }
+ true
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_NT_CREATE_ANDX => {
+ match parse_smb_create_andx_request_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..], r) {
+ Ok((_, cr)) => {
+ SCLogDebug!("Create AndX {:?}", cr);
+ let del = cr.create_options & 0x0000_1000 != 0;
+ let dir = cr.create_options & 0x0000_0001 != 0;
+ SCLogDebug!("del {} dir {} options {:08x}", del, dir, cr.create_options);
+
+ let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME);
+ let name_val = cr.file_name.to_vec();
+ state.ssn2vec_map.insert(name_key, name_val);
+
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let tx = state.new_create_tx(&cr.file_name.to_vec(),
+ cr.disposition, del, dir, tx_hdr);
+ tx.vercmd.set_smb1_cmd(command);
+ SCLogDebug!("TS CREATE TX {} created", tx.id);
+ true
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_SESSION_SETUP_ANDX => {
+ SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
+ smb1_session_setup_request(state, r, *andx_offset);
+ true
+ },
+ SMB1_COMMAND_TREE_CONNECT_ANDX => {
+ SCLogDebug!("SMB1_COMMAND_TREE_CONNECT_ANDX");
+ match parse_smb_connect_tree_andx_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..], r) {
+ Ok((_, tr)) => {
+ let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
+ let mut name_val = tr.path;
+ if name_val.len() > 1 {
+ name_val = name_val[1..].to_vec();
+ }
+
+ // store hdr as SMBHDR_TYPE_TREE, so with tree id 0
+ // when the response finds this we update it
+ let tx = state.new_treeconnect_tx(name_key, name_val);
+ if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
+ tdn.req_service = Some(tr.service.to_vec());
+ }
+ tx.request_done = true;
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_TREE_CONNECT_ANDX);
+ true
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_TREE_DISCONNECT => {
+ let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
+ state.ssn2tree_map.remove(&tree_key);
+ false
+ },
+ SMB1_COMMAND_CLOSE => {
+ match parse_smb1_close_request_record(r.data) {
+ Ok((_, cd)) => {
+ let mut fid = cd.fid.to_vec();
+ fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+ state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec());
+
+ SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid);
+ smb1_close_file(state, &fid, Direction::ToServer);
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+ false
+ },
+ SMB1_COMMAND_NT_CANCEL |
+ SMB1_COMMAND_TRANS2_SECONDARY |
+ SMB1_COMMAND_LOCKING_ANDX => {
+ no_response_expected = true;
+ false
+ },
+ _ => {
+ if command == SMB1_COMMAND_LOGOFF_ANDX ||
+ command == SMB1_COMMAND_TREE_DISCONNECT ||
+ command == SMB1_COMMAND_NT_TRANS ||
+ command == SMB1_COMMAND_NT_TRANS_SECONDARY ||
+ command == SMB1_COMMAND_NT_CANCEL ||
+ command == SMB1_COMMAND_RENAME ||
+ command == SMB1_COMMAND_CHECK_DIRECTORY ||
+ command == SMB1_COMMAND_ECHO ||
+ command == SMB1_COMMAND_TRANS
+ { } else {
+ SCLogDebug!("unsupported command {}/{}",
+ command, &smb1_command_string(command));
+ }
+ false
+ },
+ };
+ if !have_tx && smb1_create_new_tx(command) {
+ let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let tx = state.new_generic_tx(1, command as u16, tx_key);
+ SCLogDebug!("tx {} created for {}/{}", tx.id, command, &smb1_command_string(command));
+ tx.set_events(events);
+ if no_response_expected {
+ tx.response_done = true;
+ }
+ }
+}
+
+pub fn smb1_request_record(state: &mut SMBState, r: &SmbRecord) -> u32 {
+ SCLogDebug!("record: command {}: record {:?}", r.command, r);
+
+ let mut andx_offset = SMB1_HEADER_SIZE;
+ let mut command = r.command;
+ loop {
+ smb1_request_record_one(state, r, command, &mut andx_offset);
+
+ // continue for next andx command if any
+ if smb1_command_is_andx(command) {
+ if let Ok((_, andx_hdr)) = smb1_parse_andx_header(&r.data[andx_offset-SMB1_HEADER_SIZE..]) {
+ if (andx_hdr.andx_offset as usize) > andx_offset &&
+ andx_hdr.andx_command != SMB1_COMMAND_NONE &&
+ (andx_hdr.andx_offset as usize) - SMB1_HEADER_SIZE < r.data.len() {
+ andx_offset = andx_hdr.andx_offset as usize;
+ command = andx_hdr.andx_command;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+
+ 0
+}
+
+fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, andx_offset: &mut usize) {
+ SCLogDebug!("record: command {} status {} -> {:?}", r.command, r.nt_status, r);
+
+ let key_ssn_id = r.ssn_id;
+ let key_tree_id = r.tree_id;
+ let key_multiplex_id = r.multiplex_id;
+ let mut tx_sync = false;
+ let mut events : Vec<SMBEvent> = Vec::new();
+
+ let have_tx = match command {
+ SMB1_COMMAND_READ_ANDX => {
+ smb1_read_response_record(state, r, *andx_offset, 0);
+ true // tx handling in func
+ },
+ SMB1_COMMAND_NEGOTIATE_PROTOCOL => {
+ SCLogDebug!("SMB1_COMMAND_NEGOTIATE_PROTOCOL response");
+ match parse_smb1_negotiate_protocol_response_record(r.data) {
+ Ok((_, pr)) => {
+ let (have_ntx, dialect) = match state.get_negotiate_tx(1) {
+ Some(tx) => {
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.response_done = true;
+ SCLogDebug!("tx {} is done", tx.id);
+ let d = match tx.type_data {
+ Some(SMBTransactionTypeData::NEGOTIATE(ref mut x)) => {
+ x.server_guid = pr.server_guid.to_vec();
+
+ let dialect_idx = pr.dialect_idx as usize;
+ if x.dialects.len() <= dialect_idx {
+ None
+ } else {
+ let d = x.dialects[dialect_idx].to_vec();
+ Some(d)
+ }
+ },
+ _ => { None },
+ };
+ if d.is_none() {
+ tx.set_event(SMBEvent::NegotiateMalformedDialects);
+ }
+ (true, d)
+ },
+ None => { (false, None) },
+ };
+ if let Some(d) = dialect {
+ SCLogDebug!("dialect {:?}", d);
+ state.dialect_vec = Some(d);
+ }
+ have_ntx
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_TREE_CONNECT_ANDX => {
+ if r.nt_status != SMB_NTSTATUS_SUCCESS {
+ let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
+ if let Some(tx) = state.get_treeconnect_tx(name_key) {
+ if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
+ tdn.tree_id = r.tree_id as u32;
+ }
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.response_done = true;
+ SCLogDebug!("tx {} is done", tx.id);
+ }
+ return;
+ }
+
+ match parse_smb_connect_tree_andx_response_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) {
+ Ok((_, tr)) => {
+ let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_TREE);
+ let is_pipe = tr.service == "IPC".as_bytes();
+ let mut share_name = Vec::new();
+ let found = match state.get_treeconnect_tx(name_key) {
+ Some(tx) => {
+ if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
+ tdn.is_pipe = is_pipe;
+ tdn.tree_id = r.tree_id as u32;
+ share_name = tdn.share_name.to_vec();
+ tdn.res_service = Some(tr.service.to_vec());
+ }
+ tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.response_done = true;
+ SCLogDebug!("tx {} is done", tx.id);
+ true
+ },
+ None => { false },
+ };
+ if found {
+ let tree = SMBTree::new(share_name.to_vec(), is_pipe);
+ let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
+ state.ssn2tree_map.insert(tree_key, tree);
+ }
+ found
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ },
+ SMB1_COMMAND_TREE_DISCONNECT => {
+ // normally removed when processing request,
+ // but in case we missed that try again here
+ let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
+ state.ssn2tree_map.remove(&tree_key);
+ false
+ },
+ SMB1_COMMAND_NT_CREATE_ANDX => {
+ SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX response {:08x}", r.nt_status);
+ if r.nt_status == SMB_NTSTATUS_SUCCESS {
+ match parse_smb_create_andx_response_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) {
+ Ok((_, cr)) => {
+ SCLogDebug!("Create AndX {:?}", cr);
+
+ let guid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME);
+ match state.ssn2vec_map.remove(&guid_key) {
+ Some(mut p) => {
+ p.retain(|&i|i != 0x00);
+
+ let mut fid = cr.fid.to_vec();
+ fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+ SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX fid {:?}", fid);
+ SCLogDebug!("fid {:?} name {:?}", fid, p);
+ state.guid2name_map.insert(fid, p);
+ },
+ _ => {
+ SCLogDebug!("SMBv1 response: GUID NOT FOUND");
+ },
+ }
+
+ let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ if let Some(tx) = state.get_generic_tx(1, command as u16, &tx_hdr) {
+ SCLogDebug!("tx {} with {}/{} marked as done",
+ tx.id, command, &smb1_command_string(command));
+ tx.set_status(r.nt_status, false);
+ tx.response_done = true;
+
+ if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data {
+ tdn.create_ts = cr.create_ts.as_unix();
+ tdn.last_access_ts = cr.last_access_ts.as_unix();
+ tdn.last_write_ts = cr.last_write_ts.as_unix();
+ tdn.last_change_ts = cr.last_change_ts.as_unix();
+ tdn.size = cr.file_size;
+ tdn.guid = cr.fid.to_vec();
+ }
+ }
+ true
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ false
+ },
+ }
+ } else {
+ false
+ }
+ },
+ SMB1_COMMAND_CLOSE => {
+ let fid = state.ssn2vec_map.remove(&SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID));
+ if let Some(fid) = fid {
+ SCLogDebug!("closing FID {:?}", fid);
+ smb1_close_file(state, &fid, Direction::ToClient);
+ }
+ false
+ },
+ SMB1_COMMAND_TRANS => {
+ smb1_trans_response_record(state, r);
+ true
+ },
+ SMB1_COMMAND_SESSION_SETUP_ANDX => {
+ smb1_session_setup_response(state, r, *andx_offset);
+ true
+ },
+ SMB1_COMMAND_LOGOFF_ANDX => {
+ tx_sync = true;
+ false
+ },
+ _ => {
+ false
+ },
+ };
+
+ if !have_tx && tx_sync {
+ if let Some(tx) = state.get_last_tx(1, command as u16) {
+ SCLogDebug!("last TX {} is CMD {}", tx.id, &smb1_command_string(command));
+ tx.response_done = true;
+ SCLogDebug!("tx {} cmd {} is done", tx.id, command);
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.set_events(events);
+ }
+ } else if !have_tx && smb1_check_tx(command) {
+ let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX,
+ key_ssn_id as u64, key_tree_id as u32, key_multiplex_id as u64);
+ let _have_tx2 = match state.get_generic_tx(1, command as u16, &tx_key) {
+ Some(tx) => {
+ tx.request_done = true;
+ tx.response_done = true;
+ SCLogDebug!("tx {} cmd {} is done", tx.id, command);
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.set_events(events);
+ true
+ },
+ None => {
+ SCLogDebug!("no TX found for key {:?}", tx_key);
+ false
+ },
+ };
+ } else {
+ SCLogDebug!("have tx for cmd {}", command);
+ }
+}
+
+pub fn smb1_response_record(state: &mut SMBState, r: &SmbRecord) -> u32 {
+ let mut andx_offset = SMB1_HEADER_SIZE;
+ let mut command = r.command;
+ loop {
+ smb1_response_record_one(state, r, command, &mut andx_offset);
+
+ // continue for next andx command if any
+ if smb1_command_is_andx(command) {
+ if let Ok((_, andx_hdr)) = smb1_parse_andx_header(&r.data[andx_offset-SMB1_HEADER_SIZE..]) {
+ if (andx_hdr.andx_offset as usize) > andx_offset &&
+ andx_hdr.andx_command != SMB1_COMMAND_NONE &&
+ (andx_hdr.andx_offset as usize) - SMB1_HEADER_SIZE < r.data.len() {
+ andx_offset = andx_hdr.andx_offset as usize;
+ command = andx_hdr.andx_command;
+ continue;
+ }
+ }
+ }
+ break;
+ }
+
+ 0
+}
+
+pub fn smb1_trans_request_record(state: &mut SMBState, r: &SmbRecord)
+{
+ let mut events : Vec<SMBEvent> = Vec::new();
+
+ match parse_smb_trans_request_record(r.data, r) {
+ Ok((_, rd)) => {
+ SCLogDebug!("TRANS request {:?}", rd);
+
+ /* if we have a fid, store it so the response can pick it up */
+ let mut pipe_dcerpc = false;
+ if rd.pipe.is_some() {
+ let pipe = rd.pipe.unwrap();
+ state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID),
+ pipe.fid.to_vec());
+
+ let mut frankenfid = pipe.fid.to_vec();
+ frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+
+ let (_filename, is_dcerpc) = state.get_service_for_guid(&frankenfid);
+
+ SCLogDebug!("smb1_trans_request_record: name {} is_dcerpc {}",
+ _filename, is_dcerpc);
+ pipe_dcerpc = is_dcerpc;
+ }
+
+ if pipe_dcerpc {
+ SCLogDebug!("SMBv1 TRANS TO PIPE");
+ let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+ let vercmd = SMBVerCmdStat::new1(r.command);
+ smb_write_dcerpc_record(state, vercmd, hdr, rd.data.data);
+ }
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+ smb1_request_record_generic(state, r, events);
+}
+
+pub fn smb1_trans_response_record(state: &mut SMBState, r: &SmbRecord)
+{
+ let mut events : Vec<SMBEvent> = Vec::new();
+
+ match parse_smb_trans_response_record(r.data) {
+ Ok((_, rd)) => {
+ SCLogDebug!("TRANS response {:?}", rd);
+
+ // see if we have a stored fid
+ let fid = match state.ssn2vec_map.remove(
+ &SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)) {
+ Some(f) => f,
+ None => Vec::new(),
+ };
+ SCLogDebug!("FID {:?}", fid);
+
+ let mut frankenfid = fid.to_vec();
+ frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+
+ let (_filename, is_dcerpc) = state.get_service_for_guid(&frankenfid);
+
+ SCLogDebug!("smb1_trans_response_record: name {} is_dcerpc {}",
+ _filename, is_dcerpc);
+
+ // if we get status 'BUFFER_OVERFLOW' this is only a part of
+ // the data. Store it in the ssn/tree for later use.
+ if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
+ let key = SMBHashKeyHdrGuid::new(SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid);
+ SCLogDebug!("SMBv1/TRANS: queueing data for len {} key {:?}", rd.data.len(), key);
+ state.ssnguid2vec_map.insert(key, rd.data.to_vec());
+ } else if is_dcerpc {
+ SCLogDebug!("SMBv1 TRANS TO PIPE");
+ let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+ let vercmd = SMBVerCmdStat::new1_with_ntstatus(r.command, r.nt_status);
+ smb_read_dcerpc_record(state, vercmd, hdr, &fid, rd.data);
+ }
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+
+ // generic tx as well. Set events if needed.
+ smb1_response_record_generic(state, r, events);
+}
+
+/// Handle WRITE, WRITE_ANDX, WRITE_AND_CLOSE request records
+pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offset: usize, command: u8, nbss_remaining: u32)
+{
+ let mut events : Vec<SMBEvent> = Vec::new();
+
+ let result = if command == SMB1_COMMAND_WRITE_ANDX {
+ parse_smb1_write_andx_request_record(&r.data[andx_offset-SMB1_HEADER_SIZE..], andx_offset)
+ } else if command == SMB1_COMMAND_WRITE {
+ parse_smb1_write_request_record(r.data)
+ } else {
+ parse_smb1_write_and_close_request_record(r.data)
+ };
+ match result {
+ Ok((_, rd)) => {
+ SCLogDebug!("SMBv1: write andx => {:?}", rd);
+ if rd.len > rd.data.len() as u32 + nbss_remaining {
+ // Record claims more bytes than are in NBSS record...
+ state.set_event(SMBEvent::WriteRequestTooLarge);
+ // Skip the remaining bytes of the record.
+ state.set_skip(Direction::ToServer, nbss_remaining);
+ return;
+ }
+ let mut file_fid = rd.fid.to_vec();
+ file_fid.extend_from_slice(&u32_as_bytes(r.ssn_id));
+ SCLogDebug!("SMBv1 WRITE: FID {:?} offset {}",
+ file_fid, rd.offset);
+
+ let file_name = match state.guid2name_map.get(&file_fid) {
+ Some(n) => n.to_vec(),
+ None => b"<unknown>".to_vec(),
+ };
+ let mut set_event_fileoverlap = false;
+ let found = match state.get_file_tx_by_fuid_with_open_file(&file_fid, Direction::ToServer) {
+ Some(tx) => {
+ let file_id : u32 = tx.id as u32;
+ if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+ if rd.offset < tdf.file_tracker.tracked {
+ set_event_fileoverlap = true;
+ }
+ filetracker_newchunk(&mut tdf.file_tracker,
+ &file_name, rd.data, rd.offset,
+ rd.len, false, &file_id);
+ SCLogDebug!("FID {:?} found at tx {} => {:?}", file_fid, tx.id, tx);
+ }
+ true
+ },
+ None => { false },
+ };
+ if !found {
+ let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
+ let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) {
+ Some(n) => (n.name.to_vec(), n.is_pipe),
+ None => (Vec::new(), false),
+ };
+ if is_pipe {
+ SCLogDebug!("SMBv1 WRITE TO PIPE");
+ let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+ let vercmd = SMBVerCmdStat::new1_with_ntstatus(command, r.nt_status);
+ smb_write_dcerpc_record(state, vercmd, hdr, rd.data);
+ } else {
+ let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToServer);
+ if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+ let file_id : u32 = tx.id as u32;
+ if rd.offset < tdf.file_tracker.tracked {
+ set_event_fileoverlap = true;
+ }
+ filetracker_newchunk(&mut tdf.file_tracker,
+ &file_name, rd.data, rd.offset,
+ rd.len, false, &file_id);
+ tdf.share_name = share_name;
+ SCLogDebug!("tdf {:?}", tdf);
+ }
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_WRITE_ANDX);
+ SCLogDebug!("FID {:?} found at tx {} => {:?}", file_fid, tx.id, tx);
+ }
+ }
+ if set_event_fileoverlap {
+ state.set_event(SMBEvent::FileOverlap);
+ }
+
+ state.set_file_left(Direction::ToServer, rd.len, rd.data.len() as u32, file_fid.to_vec());
+
+ if command == SMB1_COMMAND_WRITE_AND_CLOSE {
+ SCLogDebug!("closing FID {:?}", file_fid);
+ smb1_close_file(state, &file_fid, Direction::ToServer);
+ }
+ },
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+ smb1_request_record_generic(state, r, events);
+}
+
+pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offset: usize, nbss_remaining: u32)
+{
+ let mut events : Vec<SMBEvent> = Vec::new();
+
+ if r.nt_status == SMB_NTSTATUS_SUCCESS {
+ match parse_smb_read_andx_response_record(&r.data[andx_offset-SMB1_HEADER_SIZE..]) {
+ Ok((_, rd)) => {
+ SCLogDebug!("SMBv1: read response => {:?}", rd);
+ if rd.len > nbss_remaining + rd.data.len() as u32 {
+ // Record claims more bytes than are in NBSS record...
+ state.set_event(SMBEvent::ReadResponseTooLarge);
+ // Skip the remaining bytes of the record.
+ state.set_skip(Direction::ToClient, nbss_remaining);
+ return;
+ }
+ let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET);
+ let (offset, file_fid) = match state.ssn2vecoffset_map.remove(&fid_key) {
+ Some(o) => (o.offset, o.guid),
+ None => {
+ SCLogDebug!("SMBv1 READ response: reply to unknown request: left {} {:?}",
+ rd.len - rd.data.len() as u32, rd);
+ state.set_skip(Direction::ToClient, nbss_remaining);
+ return;
+ },
+ };
+ SCLogDebug!("SMBv1 READ: FID {:?} offset {}", file_fid, offset);
+
+ let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE);
+ let (is_pipe, share_name) = match state.ssn2tree_map.get(&tree_key) {
+ Some(n) => (n.is_pipe, n.name.to_vec()),
+ _ => { (false, Vec::new()) },
+ };
+ if !is_pipe {
+ let file_name = match state.guid2name_map.get(&file_fid) {
+ Some(n) => n.to_vec(),
+ None => Vec::new(),
+ };
+ let mut set_event_fileoverlap = false;
+ let found = match state.get_file_tx_by_fuid_with_open_file(&file_fid, Direction::ToClient) {
+ Some(tx) => {
+ if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+ let file_id : u32 = tx.id as u32;
+ SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
+ if offset < tdf.file_tracker.tracked {
+ set_event_fileoverlap = true;
+ }
+ filetracker_newchunk(&mut tdf.file_tracker,
+ &file_name, rd.data, offset,
+ rd.len, false, &file_id);
+ }
+ true
+ },
+ None => { false },
+ };
+ if !found {
+ let tx = state.new_file_tx(&file_fid, &file_name, Direction::ToClient);
+ if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
+ let file_id : u32 = tx.id as u32;
+ SCLogDebug!("FID {:?} found at tx {}", file_fid, tx.id);
+ if offset < tdf.file_tracker.tracked {
+ set_event_fileoverlap = true;
+ }
+ filetracker_newchunk(&mut tdf.file_tracker,
+ &file_name, rd.data, offset,
+ rd.len, false, &file_id);
+ tdf.share_name = share_name;
+ }
+ tx.vercmd.set_smb1_cmd(SMB1_COMMAND_READ_ANDX);
+ }
+ if set_event_fileoverlap {
+ state.set_event(SMBEvent::FileOverlap);
+ }
+ } else {
+ SCLogDebug!("SMBv1 READ response from PIPE");
+ let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+ let vercmd = SMBVerCmdStat::new1(SMB1_COMMAND_READ_ANDX);
+
+ // hack: we store fid with ssn id mixed in, but here we want the
+ // real thing instead.
+ let pure_fid = if file_fid.len() > 2 { &file_fid[0..2] } else { &[] };
+ smb_read_dcerpc_record(state, vercmd, hdr, pure_fid, rd.data);
+ }
+
+ state.set_file_left(Direction::ToClient, rd.len, rd.data.len() as u32, file_fid.to_vec());
+ }
+ _ => {
+ events.push(SMBEvent::MalformedData);
+ },
+ }
+ }
+
+ // generic tx as well. Set events if needed.
+ smb1_response_record_generic(state, r, events);
+}
+
+/// create a tx for a command / response pair if we're
+/// configured to do so, or if this is a tx especially
+/// for setting an event.
+fn smb1_request_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec<SMBEvent>) {
+ if smb1_create_new_tx(r.command) || !events.is_empty() {
+ let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ let tx = state.new_generic_tx(1, r.command as u16, tx_key);
+ tx.set_events(events);
+ }
+}
+
+/// update or create a tx for a command / response pair based
+/// on the response. We only create a tx for the response side
+/// if we didn't already update a tx, and we have to set events
+fn smb1_response_record_generic(state: &mut SMBState, r: &SmbRecord, events: Vec<SMBEvent>) {
+ let tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
+ if let Some(tx) = state.get_generic_tx(1, r.command as u16, &tx_key) {
+ tx.request_done = true;
+ tx.response_done = true;
+ SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.set_events(events);
+ return;
+ }
+ if !events.is_empty() {
+ let tx = state.new_generic_tx(1, r.command as u16, tx_key);
+ tx.request_done = true;
+ tx.response_done = true;
+ SCLogDebug!("tx {} cmd {} is done", tx.id, r.command);
+ tx.set_status(r.nt_status, r.is_dos_error);
+ tx.set_events(events);
+ }
+}