]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb: session setup improvements
authorVictor Julien <victor@inliniac.net>
Tue, 27 Feb 2018 17:12:07 +0000 (18:12 +0100)
committerVictor Julien <victor@inliniac.net>
Mon, 12 Mar 2018 14:34:42 +0000 (15:34 +0100)
Improve ntlmssp version extraction and logging, make its data structures
optional. Extract native os/lm from smb1 ssn setup.

Move session setup handling into their own files.

Only log auth data for the session setup tx.

rust/src/smb/auth.rs
rust/src/smb/log.rs
rust/src/smb/mod.rs
rust/src/smb/ntlmssp_records.rs
rust/src/smb/session.rs [new file with mode: 0644]
rust/src/smb/smb.rs
rust/src/smb/smb1.rs
rust/src/smb/smb1_records.rs
rust/src/smb/smb1_session.rs [new file with mode: 0644]
rust/src/smb/smb2.rs
rust/src/smb/smb2_session.rs [new file with mode: 0644]

index 6eca8609cdb071052646850decabca1abfd65a97..491818315c9beaf118273af3157c6afc5822608f 100644 (file)
@@ -306,15 +306,21 @@ fn parse_secblob_spnego_start(blob: &[u8]) -> IResult<&[u8], &[u8]>
     IResult::Done(rem, d)
 }
 
-fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
+pub struct SpnegoRequest {
+    pub krb: Option<Kerberos5Ticket>,
+    pub ntlmssp: Option<NtlmsspData>,
+}
+
+fn parse_secblob_spnego(blob: &[u8]) -> Option<SpnegoRequest>
 {
-    let mut ntlmssp = false;
-    let mut kerberos = false;
+    let mut have_ntlmssp = false;
+    let mut have_kerberos = false;
     let mut kticket : Option<Kerberos5Ticket> = None;
+    let mut ntlmssp : Option<NtlmsspData> = None;
 
     let o = match der_parser::parse_der_sequence(blob) {
         IResult::Done(_, o) => o,
-        _ => { return; },
+        _ => { return None; },
     };
     for s in o {
         SCLogDebug!("s {:?}", s);
@@ -337,11 +343,11 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
                             SCLogDebug!("OID {:?}", oid);
                             match oid.to_string().as_str() {
                                 "1.2.840.48018.1.2.2" => { SCLogDebug!("Microsoft Kerberos 5"); },
-                                "1.2.840.113554.1.2.2" => { SCLogDebug!("Kerberos 5"); kerberos = true; },
+                                "1.2.840.113554.1.2.2" => { SCLogDebug!("Kerberos 5"); have_kerberos = true; },
                                 "1.2.840.113554.1.2.2.1" => { SCLogDebug!("krb5-name"); },
                                 "1.2.840.113554.1.2.2.2" => { SCLogDebug!("krb5-principal"); },
                                 "1.2.840.113554.1.2.2.3" => { SCLogDebug!("krb5-user-to-user-mech"); },
-                                "1.3.6.1.4.1.311.2.2.10" => { SCLogDebug!("NTLMSSP"); ntlmssp = true; },
+                                "1.3.6.1.4.1.311.2.2.10" => { SCLogDebug!("NTLMSSP"); have_ntlmssp = true; },
                                 "1.3.6.1.4.1.311.2.2.30" => { SCLogDebug!("NegoEx"); },
                                 _ => { SCLogNotice!("unexpected OID {:?}", oid); },
                             }
@@ -351,7 +357,7 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
                 }
             },
             der_parser::DerObjectContent::OctetString(ref os) => {
-                if kerberos {
+                if have_kerberos {
                     match parse_kerberos5_request(os) {
                         IResult::Done(_, t) => {
                             kticket = Some(t)
@@ -360,16 +366,20 @@ fn parse_secblob_spnego(state: &mut SMBState, blob: &[u8])
                     }
                 }
 
-                if ntlmssp && kticket == None {
+                if have_ntlmssp && kticket == None {
                     SCLogDebug!("parsing expected NTLMSSP");
-                    parse_ntlmssp_blob(state, os);
+                    ntlmssp = parse_ntlmssp_blob(os);
                 }
             },
             _ => {},
         }
     }
 
-    state.krb_ticket = kticket;
+    let s = SpnegoRequest {
+        krb: kticket,
+        ntlmssp: ntlmssp,
+    };
+    Some(s)
 }
 
 #[derive(Debug,PartialEq)]
@@ -377,11 +387,14 @@ pub struct NtlmsspData {
     pub host: Vec<u8>,
     pub user: Vec<u8>,
     pub domain: Vec<u8>,
+    pub version: Option<NTLMSSPVersion>,
 }
 
 /// take in blob, search for the header and parse it
-fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
+fn parse_ntlmssp_blob(blob: &[u8]) -> Option<NtlmsspData>
 {
+    let mut ntlmssp_data : Option<NtlmsspData> = None;
+
     SCLogDebug!("NTLMSSP {:?}", blob);
     match parse_ntlmssp(blob) {
         IResult::Done(_, nd) => {
@@ -405,8 +418,9 @@ fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
                                 host: host,
                                 user: user,
                                 domain: domain,
+                                version: ad.version,
                             };
-                            state.ntlmssp = Some(d);
+                            ntlmssp_data = Some(d);
                         },
                         _ => {},
                     }
@@ -416,24 +430,43 @@ fn parse_ntlmssp_blob(state: &mut SMBState, blob: &[u8])
         },
         _ => {},
     }
+    return ntlmssp_data;
 }
 
 // if spnego parsing fails try to fall back to ntlmssp
-pub fn parse_secblob(state: &mut SMBState, blob: &[u8])
+pub fn parse_secblob(blob: &[u8]) -> Option<SpnegoRequest>
 {
     match parse_secblob_get_spnego(blob) {
         IResult::Done(_, spnego) => {
             match parse_secblob_spnego_start(spnego) {
                 IResult::Done(_, spnego_start) => {
-                    parse_secblob_spnego(state, spnego_start);
+                    parse_secblob_spnego(spnego_start)
                 },
                 _ => {
-                    parse_ntlmssp_blob(state, blob);
+                    match parse_ntlmssp_blob(blob) {
+                        Some(n) => {
+                            let s = SpnegoRequest {
+                                krb: None,
+                                ntlmssp: Some(n),
+                            };
+                            Some(s)
+                        },
+                        None => { None },
+                    }
                 },
             }
         },
         _ => {
-            parse_ntlmssp_blob(state, blob);
+            match parse_ntlmssp_blob(blob) {
+                Some(n) => {
+                    let s = SpnegoRequest {
+                        krb: None,
+                         ntlmssp: Some(n),
+                    };
+                    Some(s)
+                },
+                None => { None },
+            }
         },
     }
 }
index c22accce1374a10162cad0d7cd4e300efcf796dd..774e4a98e2cbccfb28b9c1a47b98c2d6d274320d 100644 (file)
@@ -90,55 +90,89 @@ fn smb_common_header(state: &SMBState, tx: &SMBTransaction) -> Json
 
     js.set_boolean("request_done", tx.request_done);
     js.set_boolean("response_done", tx.request_done);
+    match tx.type_data {
+        Some(SMBTransactionTypeData::SESSIONSETUP(ref x)) => {
+            if let Some(ref ntlmssp) = x.ntlmssp {
+                let jsd = Json::object();
+                let domain = match str::from_utf8(&ntlmssp.domain) {
+                    Ok(v) => v,
+                    Err(_) => "UTF8_ERROR",
+                };
+                jsd.set_string("domain", &domain);
 
-    match state.ntlmssp {
-        Some(ref ntlmssp) => {
-            let jsd = Json::object();
-            let domain = match str::from_utf8(&ntlmssp.domain) {
-                Ok(v) => v,
-                Err(_) => "UTF8_ERROR",
-            };
-            jsd.set_string("domain", &domain);
+                let user = match str::from_utf8(&ntlmssp.user) {
+                    Ok(v) => v,
+                    Err(_) => "UTF8_ERROR",
+                };
+                jsd.set_string("user", &user);
 
-            let user = match str::from_utf8(&ntlmssp.user) {
-                Ok(v) => v,
-                Err(_) => "UTF8_ERROR",
-            };
-            jsd.set_string("user", &user);
+                let host = match str::from_utf8(&ntlmssp.host) {
+                    Ok(v) => v,
+                    Err(_) => "UTF8_ERROR",
+                };
+                jsd.set_string("host", &host);
 
-            let host = match str::from_utf8(&ntlmssp.host) {
-                Ok(v) => v,
-                Err(_) => "UTF8_ERROR",
-            };
-            jsd.set_string("host", &host);
-            js.set("ntlmssp", jsd);
-        }
-        None => {},
-    }
+                if let Some(ref v) = ntlmssp.version {
+                    jsd.set_string("version", v.to_string().as_str());
+                }
 
-    match state.krb_ticket {
-        Some(ref ticket) => {
-            let jsd = Json::object();
-            let realm = match str::from_utf8(&ticket.realm) {
-                Ok(v) => v,
-                Err(_) => "UTF8_ERROR",
-            };
-            jsd.set_string("realm", &realm);
-            let jsa = Json::array();
-            for sname in &ticket.snames {
-                let name = match str::from_utf8(&sname) {
+                js.set("ntlmssp", jsd);
+            }
+
+            if let Some(ref ticket) = x.krb_ticket {
+                let jsd = Json::object();
+                let realm = match str::from_utf8(&ticket.realm) {
                     Ok(v) => v,
                     Err(_) => "UTF8_ERROR",
                 };
-                jsa.array_append_string(&name);
+                jsd.set_string("realm", &realm);
+                let jsa = Json::array();
+                for sname in &ticket.snames {
+                    let name = match str::from_utf8(&sname) {
+                        Ok(v) => v,
+                        Err(_) => "UTF8_ERROR",
+                    };
+                    jsa.array_append_string(&name);
+                }
+                jsd.set("snames", jsa);
+                js.set("kerberos", jsd);
             }
-            jsd.set("snames", jsa);
-            js.set("kerberos", jsd);
-        },
-        None => { },
-    }
 
-    match tx.type_data {
+            match x.request_host {
+                Some(ref r) => {
+                    let jsd = Json::object();
+                    let os = match str::from_utf8(&r.native_os) {
+                        Ok(v) => v,
+                            Err(_) => "UTF8_ERROR",
+                    };
+                    jsd.set_string("native_os", &os);
+                    let lm = match str::from_utf8(&r.native_lm) {
+                        Ok(v) => v,
+                            Err(_) => "UTF8_ERROR",
+                    };
+                    jsd.set_string("native_lm", &lm);
+                    js.set("request", jsd);
+                },
+                None => { },
+            }
+            match x.response_host {
+                Some(ref r) => {
+                    let jsd = Json::object();
+                    let os = match str::from_utf8(&r.native_os) {
+                        Ok(v) => v,
+                            Err(_) => "UTF8_ERROR",
+                    };
+                    jsd.set_string("native_os", &os);
+                    let lm = match str::from_utf8(&r.native_lm) {
+                        Ok(v) => v,
+                            Err(_) => "UTF8_ERROR",
+                    };
+                    jsd.set_string("native_lm", &lm);
+                    js.set("response", jsd);
+                },
+                None => { },
+            }
+        },
         Some(SMBTransactionTypeData::CREATE(ref x)) => {
             let mut name_raw = x.filename.to_vec();
             name_raw.retain(|&i|i != 0x00);
index 31761dcd6f80f5595d357310bf88b376cf507c60..0019da92b5b49f51994780e2ff73f6b1531fafba 100644 (file)
@@ -23,8 +23,11 @@ pub mod ntlmssp_records;
 
 pub mod smb;
 pub mod smb1;
+pub mod smb1_session;
 pub mod smb2;
+pub mod smb2_session;
 pub mod dcerpc;
+pub mod session;
 pub mod log;
 pub mod detect;
 pub mod debug;
index c47fa1125c790ca6ee19c2d657a082df4e342b46..2f6343fad02aa08db1f31da39f99df43305919e2 100644 (file)
  * 02110-1301, USA.
  */
 
-use nom::{rest, le_u16, le_u32};
+use nom::{rest, le_u8, le_u16, le_u32};
+
+#[derive(Debug,PartialEq)]
+pub struct NTLMSSPVersion {
+    pub ver_major: u8,
+    pub ver_minor: u8,
+    pub ver_build: u16,
+    pub ver_ntlm_rev: u8,
+}
+
+impl NTLMSSPVersion {
+    pub fn to_string(&self) -> String {
+        format!("{}.{} build {} rev {}",
+                self.ver_major, self.ver_minor,
+                self.ver_build, self.ver_ntlm_rev)
+    }
+}
+
+named!(parse_ntlm_auth_version<NTLMSSPVersion>,
+    do_parse!(
+            ver_major: le_u8
+         >> ver_minor: le_u8
+         >> ver_build: le_u16
+         >> take!(3)
+         >> ver_ntlm_rev: le_u8
+         >> ( NTLMSSPVersion {
+                ver_major: ver_major,
+                ver_minor: ver_minor,
+                ver_build: ver_build,
+                ver_ntlm_rev: ver_ntlm_rev,
+             })
+));
 
 #[derive(Debug,PartialEq)]
 pub struct NTLMSSPAuthRecord<'a> {
     pub domain: &'a[u8],
     pub user: &'a[u8],
     pub host: &'a[u8],
+    pub version: Option<NTLMSSPVersion>,
 }
 
 named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
-    dbg_dmp!(do_parse!(
+    do_parse!(
             lm_blob_len: le_u16
          >> lm_blob_maxlen: le_u16
          >> lm_blob_offset: le_u32
@@ -50,12 +82,15 @@ named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
          >> ssnkey_blob_maxlen: le_u16
          >> ssnkey_blob_offset: le_u32
 
+         >> nego_flags: bits!(tuple!(take_bits!(u8, 6),take_bits!(u8,1),take_bits!(u32,25)))
+         >> version: cond!(nego_flags.1==1, parse_ntlm_auth_version)
+
          // subtrack 12 as idenfier (8) and type (4) are cut before we are called
-         // subtract 48 for the len/offset/maxlen fields above
-         >> take!(domain_blob_offset - (12 + 48))
+         // subtract 60 for the len/offset/maxlen fields above
+         >> cond!(nego_flags.1==1, take!(domain_blob_offset - (12 + 60)))
+         // or 52 if we have no version
+         >> cond!(nego_flags.1==0, take!(domain_blob_offset - (12 + 52)))
 
-         //>> lm_blob: take!(lm_blob_len)
-         //>> ntlmresp_blob: take!(ntlmresp_blob_len)
          >> domain_blob: take!(domain_blob_len)
          >> user_blob: take!(user_blob_len)
          >> host_blob: take!(host_blob_len)
@@ -64,8 +99,10 @@ named!(pub parse_ntlm_auth_record<NTLMSSPAuthRecord>,
                 domain: domain_blob,
                 user: user_blob,
                 host: host_blob,
+
+                version: version,
             })
-)));
+));
 
 #[derive(Debug,PartialEq)]
 pub struct NTLMSSPRecord<'a> {
diff --git a/rust/src/smb/session.rs b/rust/src/smb/session.rs
new file mode 100644 (file)
index 0000000..ed57c6c
--- /dev/null
@@ -0,0 +1,75 @@
+/* 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 log::*;
+
+use smb::smb::*;
+use smb::smb1_session::*;
+use smb::auth::*;
+
+#[derive(Debug)]
+pub struct SMBTransactionSessionSetup {
+    pub request_host: Option<SessionSetupRequest>,
+    pub response_host: Option<SessionSetupResponse>,
+    pub ntlmssp: Option<NtlmsspData>,
+    pub krb_ticket: Option<Kerberos5Ticket>,
+}
+
+impl SMBTransactionSessionSetup {
+    pub fn new() -> SMBTransactionSessionSetup {
+        return SMBTransactionSessionSetup {
+            request_host: None,
+            response_host: None,
+            ntlmssp: None,
+            krb_ticket: None,
+        }
+    }
+}
+
+impl SMBState {
+    pub fn new_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
+        -> (&mut SMBTransaction)
+    {
+        let mut tx = self.new_tx();
+
+        tx.hdr = hdr;
+        tx.type_data = Some(SMBTransactionTypeData::SESSIONSETUP(
+                    SMBTransactionSessionSetup::new()));
+        tx.request_done = true;
+        tx.response_done = self.tc_trunc; // no response expected if tc is truncated
+
+        SCLogDebug!("SMB: TX SESSIONSETUP created: ID {}", tx.id);
+        self.transactions.push(tx);
+        let tx_ref = self.transactions.last_mut();
+        return tx_ref.unwrap();
+    }
+
+    pub fn get_sessionsetup_tx(&mut self, hdr: SMBCommonHdr)
+        -> Option<&mut SMBTransaction>
+    {
+        for tx in &mut self.transactions {
+            let hit = tx.hdr == hdr && match tx.type_data {
+                Some(SMBTransactionTypeData::SESSIONSETUP(_)) => { true },
+                _ => { false },
+            };
+            if hit {
+                return Some(tx);
+            }
+        }
+        return None;
+    }
+}
index b99e013234ea8a38fbb87e1f7bd69aa6a65bd171..f2c4615738697783798616c0ec3ddec9ae5cf42c 100644 (file)
@@ -45,8 +45,8 @@ use smb::smb2_records::*;
 use smb::smb1::*;
 use smb::smb2::*;
 use smb::dcerpc::*;
+use smb::session::*;
 use smb::events::*;
-use smb::auth::*;
 use smb::files::*;
 
 pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
@@ -311,6 +311,7 @@ pub enum SMBTransactionTypeData {
     NEGOTIATE(SMBTransactionNegotiate),
     DCERPC(SMBTransactionDCERPC),
     CREATE(SMBTransactionCreate),
+    SESSIONSETUP(SMBTransactionSessionSetup),
 }
 
 #[derive(Debug)]
@@ -600,8 +601,8 @@ pub struct SMBState<> {
     pub ts_gap: bool, // last TS update was gap
     pub tc_gap: bool, // last TC update was gap
 
-    ts_trunc: bool, // no more data for TOSERVER
-    tc_trunc: bool, // no more data for TOCLIENT
+    pub ts_trunc: bool, // no more data for TOSERVER
+    pub tc_trunc: bool, // no more data for TOCLIENT
 
     /// transactions list
     pub transactions: Vec<SMBTransaction>,
@@ -616,9 +617,6 @@ pub struct SMBState<> {
     /// dcerpc interfaces, stored here to be able to match
     /// them while inspecting DCERPC REQUEST txs
     pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
-
-    pub ntlmssp: Option<NtlmsspData>,
-    pub krb_ticket: Option<Kerberos5Ticket>,
 }
 
 impl SMBState {
@@ -652,8 +650,6 @@ impl SMBState {
             dialect_vec: None,
             dialects: None,
             dcerpc_ifaces: None,
-            ntlmssp: None,
-            krb_ticket: None,
         }
     }
 
index c75832199b5732b62a79ba0b326058cccf5c27c6..560f439e77a5a9add8890b63e9ea9233c92d19b5 100644 (file)
@@ -27,13 +27,14 @@ use nom::{IResult};
 use core::*;
 use log::*;
 
-use smb::smb1_records::*;
 use smb::smb::*;
 use smb::dcerpc::*;
 use smb::events::*;
-use smb::auth::*;
 use smb::files::*;
 
+use smb::smb1_records::*;
+use smb::smb1_session::*;
+
 // 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;
@@ -129,10 +130,6 @@ pub fn smb1_create_new_tx(_cmd: u8) -> bool {
 pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
     SCLogDebug!("record: {:?} command {}", r.greeter, r.command);
 
-    // init default tx keys
-    let mut key_ssn_id = r.ssn_id;
-    let key_tree_id = r.tree_id;
-    let key_multiplex_id = r.multiplex_id;
     let mut events : Vec<SMBEvent> = Vec::new();
     let mut no_response_expected = false;
 
@@ -229,21 +226,8 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
         },
         SMB1_COMMAND_SESSION_SETUP_ANDX => {
             SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
-            match parse_smb_setup_andx_record(r.data) {
-                IResult::Done(_, setup) => {
-                    parse_secblob(state, setup.sec_blob);
-/*
-                        _ => {
-                            events.push(SMBEvent::MalformedNtlmsspRequest);
-                        },
-*/
-                },
-                _ => {
-                    events.push(SMBEvent::MalformedData);
-                },
-            }
-            key_ssn_id = 0;
-            false
+            smb1_session_setup_request(state, r);
+            true
         },
         SMB1_COMMAND_TREE_CONNECT_ANDX => {
             SCLogDebug!("SMB1_COMMAND_TREE_CONNECT_ANDX");
@@ -353,8 +337,7 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 {
     };
     if !have_tx {
         if smb1_create_new_tx(r.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 tx_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX);
             let tx = state.new_generic_tx(1, r.command as u16, tx_key);
             SCLogDebug!("tx {} created for {}/{}", tx.id, r.command, &smb1_command_string(r.command));
             tx.set_events(events);
@@ -510,8 +493,20 @@ pub fn smb1_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32
             true
         },
         SMB1_COMMAND_SESSION_SETUP_ANDX => {
+/*
+            SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
+            match parse_smb_response_setup_andx_record(r.data) {
+                IResult::Done(rem, _setup) => {
+                    //parse_secblob(state, setup.sec_blob);
+                    state.response_host = Some(smb1_session_setup_response_host_info(r, rem));
+                },
+                _ => {},
+            }
             tx_sync = true;
             false
+*/
+            smb1_session_setup_response(state, r);
+            true
         },
         SMB1_COMMAND_LOGOFF_ANDX => {
             tx_sync = true;
index b65fa0af0c5c8949993c9f1c983503fe0ef02cc0..81864c75d58e4e4590aa73c501a882965d93530a 100644 (file)
@@ -400,12 +400,29 @@ named!(pub parse_smb_setup_andx_record<SmbRecordSetupAndX>,
        >> skip2: take!(8)
        >> bcc: le_u16
        >> sec_blob: take!(sec_blob_len)
-       >> skip3: rest
+       //>> skip3: rest
        >> (SmbRecordSetupAndX {
                 sec_blob:sec_blob,
            }))
 );
 
+#[derive(Debug,PartialEq)]
+pub struct SmbResponseRecordSetupAndX<'a> {
+    pub sec_blob: &'a[u8],
+}
+
+named!(pub parse_smb_response_setup_andx_record<SmbResponseRecordSetupAndX>,
+    do_parse!(
+       skip1: take!(7)
+       >> sec_blob_len: le_u16
+       >> bcc: le_u16
+       >> sec_blob: take!(sec_blob_len)
+       //>> skip3: rest
+       >> (SmbResponseRecordSetupAndX {
+                sec_blob:sec_blob,
+           }))
+);
+
 #[derive(Debug,PartialEq)]
 pub struct SmbRequestReadAndXRecord<'a> {
     pub fid: &'a[u8],
diff --git a/rust/src/smb/smb1_session.rs b/rust/src/smb/smb1_session.rs
new file mode 100644 (file)
index 0000000..ebbcfc9
--- /dev/null
@@ -0,0 +1,234 @@
+/* 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, ErrorKind};
+
+use log::*;
+
+use smb::smb1_records::*;
+use smb::smb::*;
+use smb::events::*;
+use smb::auth::*;
+
+#[derive(Debug)]
+pub struct SessionSetupRequest {
+    pub native_os: Vec<u8>,
+    pub native_lm: Vec<u8>,
+    pub primary_domain: Vec<u8>,
+}
+
+#[derive(Debug)]
+pub struct SessionSetupResponse {
+    pub native_os: Vec<u8>,
+    pub native_lm: Vec<u8>,
+}
+
+fn get_unicode_string(blob: &[u8]) -> IResult<&[u8], Vec<u8>>
+{
+    SCLogDebug!("get_unicode_string: blob {} {:?}", blob.len(), blob);
+    let mut name : Vec<u8> = Vec::new();
+    let mut c = blob;
+    while c.len() >= 1 {
+        if c.len() == 1 && c[0] == 0 {
+            let rem = &c[1..];
+            SCLogDebug!("get_unicode_string: name {:?}", name);
+            return IResult::Done(rem, name)
+        } else if c.len() == 1 {
+            break;
+        } else if c[0] == 0 && c[1] == 0 {
+            let rem = &c[2..];
+            SCLogDebug!("get_unicode_string: name {:?}", name);
+            return IResult::Done(rem, name)
+        }
+        name.push(c[0]);
+        c = &c[2..];
+        //SCLogNotice!("get_unicode_string: c {:?}", c);
+    }
+    IResult::Error(error_code!(ErrorKind::Custom(130)))
+}
+
+named!(pub get_nullterm_string<Vec<u8>>,
+    do_parse!(
+            s: take_until_and_consume!("\x00")
+        >> ( s.to_vec() )
+));
+
+pub fn smb1_session_setup_request_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupRequest
+{
+    if blob.len() > 1 && r.flags2 & 0x8000_u16 != 0 {
+        let offset = r.data.len() - blob.len();
+        let blob = if offset % 2 == 1 { &blob[1..] } else { blob };
+        let (native_os, native_lm, primary_domain) = match get_unicode_string(blob) {
+            IResult::Done(rem, n1) => {
+                match get_unicode_string(rem) {
+                    IResult::Done(rem, n2) => {
+                        match get_unicode_string(rem) {
+                            IResult::Done(_, n3) => { (n1, n2, n3) },
+                                _ => { (n1, n2, Vec::new()) },
+                        }
+                    },
+                        _ => { (n1, Vec::new(), Vec::new()) },
+                }
+            },
+                _ => { (Vec::new(), Vec::new(), Vec::new()) },
+        };
+
+        SCLogDebug!("name1 {:?} name2 {:?} name3 {:?}", native_os,native_lm,primary_domain);
+        SessionSetupRequest {
+            native_os:native_os,
+            native_lm:native_lm,
+            primary_domain:primary_domain,
+        }
+    } else {
+        let (native_os, native_lm, primary_domain) = match get_nullterm_string(blob) {
+            IResult::Done(rem, n1) => {
+                match get_nullterm_string(rem) {
+                    IResult::Done(rem, n2) => {
+                        match get_nullterm_string(rem) {
+                            IResult::Done(_, n3) => { (n1, n2, n3) },
+                                _ => { (n1, n2, Vec::new()) },
+                        }
+                    },
+                        _ => { (n1, Vec::new(), Vec::new()) },
+                }
+            },
+                _ => { (Vec::new(), Vec::new(), Vec::new()) },
+        };
+
+        SCLogDebug!("session_setup_request_host_info: not unicode");
+        SessionSetupRequest {
+            native_os: native_os,
+            native_lm: native_lm,
+            primary_domain: primary_domain,
+        }
+    }
+}
+
+pub fn smb1_session_setup_response_host_info(r: &SmbRecord, blob: &[u8]) -> SessionSetupResponse
+{
+    if blob.len() > 1 && r.flags2 & 0x8000_u16 != 0 {
+        let offset = r.data.len() - blob.len();
+        let blob = if offset % 2 == 1 { &blob[1..] } else { blob };
+        let (native_os, native_lm) = match get_unicode_string(blob) {
+            IResult::Done(rem, n1) => {
+                match get_unicode_string(rem) {
+                    IResult::Done(_, n2) => {
+                        (n1, n2)
+                    },
+                    _ => { (n1, Vec::new()) },
+                }
+            },
+            _ => { (Vec::new(), Vec::new()) },
+        };
+
+        SCLogDebug!("name1 {:?} name2 {:?}", native_os,native_lm);
+        SessionSetupResponse {
+            native_os:native_os,
+            native_lm:native_lm,
+        }
+    } else {
+        SCLogDebug!("session_setup_response_host_info: not unicode");
+        let (native_os, native_lm) = match get_nullterm_string(blob) {
+            IResult::Done(rem, n1) => {
+                match get_nullterm_string(rem) {
+                    IResult::Done(_, n2) => {
+                        (n1, n2)
+                    },
+                    _ => { (n1, Vec::new()) },
+                }
+            },
+            _ => { (Vec::new(), Vec::new()) },
+        };
+        SessionSetupResponse {
+            native_os: native_os,
+            native_lm: native_lm,
+        }
+    }
+}
+
+pub fn smb1_session_setup_request(state: &mut SMBState, r: &SmbRecord)
+{
+    SCLogDebug!("SMB1_COMMAND_SESSION_SETUP_ANDX user_id {}", r.user_id);
+    match parse_smb_setup_andx_record(r.data) {
+        IResult::Done(rem, setup) => {
+            let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER);
+            let tx = state.new_sessionsetup_tx(hdr);
+            tx.vercmd.set_smb1_cmd(r.command);
+
+            if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
+                match parse_secblob(setup.sec_blob) {
+                    Some(s) => {
+                        td.ntlmssp = s.ntlmssp;
+                        td.krb_ticket = s.krb;
+                    },
+                    None => { },
+                }
+                td.request_host = Some(smb1_session_setup_request_host_info(r, rem));
+            }
+        },
+            _ => {
+//                events.push(SMBEvent::MalformedData);
+        },
+    }
+}
+
+fn smb1_session_setup_update_tx(tx: &mut SMBTransaction, r: &SmbRecord)
+{
+    match parse_smb_response_setup_andx_record(r.data) {
+        IResult::Done(rem, _setup) => {
+            if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
+                td.response_host = Some(smb1_session_setup_response_host_info(r, rem));
+            }
+        },
+        _ => {
+            tx.set_event(SMBEvent::MalformedData);
+        },
+    }
+    // update tx even if we can't parse the response
+    tx.hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
+    tx.set_status(r.nt_status, r.is_dos_error);
+    tx.response_done = true;
+}
+
+pub fn smb1_session_setup_response(state: &mut SMBState, r: &SmbRecord)
+{
+    // try exact match with session id already set (e.g. NTLMSSP AUTH phase)
+    let found = r.ssn_id != 0 && match state.get_sessionsetup_tx(
+                SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER))
+    {
+        Some(tx) => {
+            smb1_session_setup_update_tx(tx, r);
+            SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
+            true
+        },
+        None => { false },
+    };
+    // otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
+    if !found {
+        match state.get_sessionsetup_tx(
+                SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.multiplex_id as u64))
+        {
+            Some(tx) => {
+                smb1_session_setup_update_tx(tx, r);
+                SCLogDebug!("smb1_session_setup_response: tx {:?}", tx);
+            },
+            None => {
+                SCLogNotice!("smb1_session_setup_response: tx not found for {:?}", r);
+            },
+        }
+    }
+}
index 0a7acf1f2e0153286c73b08c7dd539d1d5448445..a2b9623e5393500347dda5e652162e6974bb8cb0 100644 (file)
@@ -21,9 +21,9 @@ use nom::IResult;
 
 use smb::smb::*;
 use smb::smb2_records::*;
+use smb::smb2_session::*;
 use smb::dcerpc::*;
 use smb::events::*;
-use smb::auth::*;
 use smb::files::*;
 
 pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL:      u16 = 0;
@@ -324,47 +324,8 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             }
         },
         SMB2_COMMAND_SESSION_SETUP => {
-            SCLogDebug!("SMB2_COMMAND_SESSION_SETUP: r.data.len() {}", r.data.len());
-            match parse_smb2_request_session_setup(r.data) {
-                IResult::Done(_, setup) => {
-                    SCLogDebug!("SMB2_COMMAND_SESSION_SETUP parsed. rd.data.len() {}", setup.data.len());
-                    parse_secblob(state, setup.data);
-/*
-                    match parse_ntlmssp(rd.data) {
-                        IResult::Done(_, nd) => {
-                            SCLogDebug!("NTLMSSP TYPE {}/{} nd {:?}",
-                                    nd.msg_type, &ntlmssp_type_string(nd.msg_type), nd);
-                            match nd.msg_type {
-                                NTLMSSP_NEGOTIATE => {
-                                    key_session_id = 0;
-                                },
-                                NTLMSSP_AUTH => {
-                                    match parse_ntlm_auth_record(nd.data) {
-                                        IResult::Done(_, ad) => {
-                                            SCLogDebug!("auth data {:?}", ad);
-                                            state.ntlmssp_host = ad.host.to_vec();
-                                            state.ntlmssp_user = ad.user.to_vec();
-                                            state.ntlmssp_domain = ad.domain.to_vec();
-                                        },
-                                        _ => {
-                                            events.push(SMBEvent::MalformedData);
-                                        },
-                                    }
-                                },
-                                _ => { },
-                            }
-                        },
-                        _ => {
-                            SCLogNotice!("error parsing ntlmssp");
-                        },
-                    }
-*/
-                },
-                _ => {
-                    events.push(SMBEvent::MalformedData);
-                },
-            }
-            false
+            smb2_session_setup_request(state, r);
+            true
         },
         SMB2_COMMAND_TREE_CONNECT => {
             match parse_smb2_request_tree_connect(r.data) {
@@ -490,34 +451,6 @@ pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
     }
 }
 
-fn smb2_response_record_session_setup<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
-{
-    // first try exact match
-    let found = match state.get_generic_tx(2, r.command, &SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX)) {
-        Some(tx) => {
-            if r.nt_status != SMB_NTSTATUS_PENDING {
-                tx.response_done = true;
-            }
-            tx.set_status(r.nt_status, false);
-            true
-        },
-        None => false,
-    };
-    // if not found try with ssn id 0.
-    if !found {
-        let tx_key = SMBCommonHdr::new(SMBHDR_TYPE_GENERICTX, 0, 0, r.message_id);
-        match state.get_generic_tx(2, r.command, &tx_key) {
-            Some(tx) => {
-                if r.nt_status != SMB_NTSTATUS_PENDING {
-                    tx.response_done = true;
-                }
-                tx.set_status(r.nt_status, false);
-            },
-            None => { },
-        }
-    }
-}
-
 pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
 {
     SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
@@ -578,7 +511,7 @@ pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
             have_ioctl_tx
         },
         SMB2_COMMAND_SESSION_SETUP => {
-            smb2_response_record_session_setup(state, r);
+            smb2_session_setup_response(state, r);
             true
         },
         SMB2_COMMAND_WRITE => {
diff --git a/rust/src/smb/smb2_session.rs b/rust/src/smb/smb2_session.rs
new file mode 100644 (file)
index 0000000..5c4b26f
--- /dev/null
@@ -0,0 +1,83 @@
+/* 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::smb2_records::*;
+use smb::smb::*;
+//use smb::events::*;
+use smb::auth::*;
+
+pub fn smb2_session_setup_request(state: &mut SMBState, r: &Smb2Record)
+{
+    SCLogDebug!("SMB2_COMMAND_SESSION_SETUP: r.data.len() {}", r.data.len());
+    match parse_smb2_request_session_setup(r.data) {
+        IResult::Done(_, setup) => {
+            let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
+            let tx = state.new_sessionsetup_tx(hdr);
+            tx.vercmd.set_smb2_cmd(r.command);
+
+            if let Some(SMBTransactionTypeData::SESSIONSETUP(ref mut td)) = tx.type_data {
+                if let Some(s) = parse_secblob(setup.data) {
+                    td.ntlmssp = s.ntlmssp;
+                    td.krb_ticket = s.krb;
+                }
+            }
+        },
+            _ => {
+//                events.push(SMBEvent::MalformedData);
+        },
+    }
+}
+
+fn smb2_session_setup_update_tx(tx: &mut SMBTransaction, r: &Smb2Record)
+{
+    tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); // to overwrite ssn_id 0
+    tx.set_status(r.nt_status, false);
+    tx.response_done = true;
+}
+
+pub fn smb2_session_setup_response(state: &mut SMBState, r: &Smb2Record)
+{
+    // try exact match with session id already set (e.g. NTLMSSP AUTH phase)
+    let found = r.session_id != 0 && match state.get_sessionsetup_tx(
+                SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER))
+    {
+        Some(tx) => {
+            smb2_session_setup_update_tx(tx, r);
+            SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
+            true
+        },
+        None => { false },
+    };
+    // otherwise try match with ssn id 0 (e.g. NTLMSSP_NEGOTIATE)
+    if !found {
+        match state.get_sessionsetup_tx(
+                SMBCommonHdr::new(SMBHDR_TYPE_HEADER, 0, 0, r.message_id))
+        {
+            Some(tx) => {
+                smb2_session_setup_update_tx(tx, r);
+                SCLogDebug!("smb2_session_setup_response: tx {:?}", tx);
+            },
+            None => {
+                SCLogNotice!("smb2_session_setup_response: tx not found for {:?}", r);
+            },
+        }
+    }
+}