1 /* Copyright (C) 2017-2020 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /* TODO
19  * - check all parsers for calls on non-SUCCESS status
20  */
21 
22 /*  GAP processing:
23  *  - if post-gap we've seen a succesful tx req/res: we consider "re-sync'd"
24  */
25 
26 // written by Victor Julien
27 
28 use std;
29 use std::mem::transmute;
30 use std::str;
31 use std::ffi::CStr;
32 
33 use std::collections::HashMap;
34 
35 use nom;
36 
37 use crate::core::*;
38 use crate::applayer;
39 use crate::applayer::{AppLayerResult, AppLayerTxData};
40 
41 use crate::smb::nbss_records::*;
42 use crate::smb::smb1_records::*;
43 use crate::smb::smb2_records::*;
44 
45 use crate::smb::smb1::*;
46 use crate::smb::smb2::*;
47 use crate::smb::smb3::*;
48 use crate::smb::dcerpc::*;
49 use crate::smb::session::*;
50 use crate::smb::events::*;
51 use crate::smb::files::*;
52 use crate::smb::smb2_ioctl::*;
53 
54 pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
55 
56 #[no_mangle]
rs_smb_init(context: &'static mut SuricataFileContext)57 pub extern "C" fn rs_smb_init(context: &'static mut SuricataFileContext)
58 {
59     unsafe {
60         SURICATA_SMB_FILE_CONFIG = Some(context);
61     }
62 }
63 
64 pub const SMB_NTSTATUS_SUCCESS:                    u32 = 0;
65 pub const SMB_NTSTATUS_PENDING:                    u32 = 0x00000103;
66 pub const SMB_NTSTATUS_BUFFER_OVERFLOW:            u32 = 0x80000005;
67 pub const SMB_NTSTATUS_NO_MORE_FILES:              u32 = 0x80000006;
68 pub const SMB_NTSTATUS_NO_MORE_ENTRIES:            u32 = 0x8000001a;
69 pub const SMB_NTSTATUS_INVALID_HANDLE:             u32 = 0xc0000008;
70 pub const SMB_NTSTATUS_INVALID_PARAMETER:          u32 = 0xc000000d;
71 pub const SMB_NTSTATUS_NO_SUCH_DEVICE:             u32 = 0xc000000e;
72 pub const SMB_NTSTATUS_NO_SUCH_FILE:               u32 = 0xc000000f;
73 pub const SMB_NTSTATUS_INVALID_DEVICE_REQUEST:     u32 = 0xc0000010;
74 pub const SMB_NTSTATUS_END_OF_FILE:                u32 = 0xc0000011;
75 pub const SMB_NTSTATUS_MORE_PROCESSING_REQUIRED:   u32 = 0xc0000016;
76 pub const SMB_NTSTATUS_ACCESS_DENIED:              u32 = 0xc0000022;
77 pub const SMB_NTSTATUS_OBJECT_NAME_INVALID:        u32 = 0xc0000033;
78 pub const SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND:      u32 = 0xc0000034;
79 pub const SMB_NTSTATUS_OBJECT_NAME_COLLISION:      u32 = 0xc0000035;
80 pub const SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND:      u32 = 0xc000003a;
81 pub const SMB_NTSTATUS_SHARING_VIOLATION:          u32 = 0xc0000043;
82 pub const SMB_NTSTATUS_LOCK_CONFLICT:              u32 = 0xc0000054;
83 pub const SMB_NTSTATUS_LOCK_NOT_GRANTED:           u32 = 0xc0000055;
84 pub const SMB_NTSTATUS_PRIVILEGE_NOT_HELD:         u32 = 0xc0000061;
85 pub const SMB_NTSTATUS_LOGON_FAILURE:              u32 = 0xc000006d;
86 pub const SMB_NTSTATUS_PIPE_DISCONNECTED:          u32 = 0xc00000b0;
87 pub const SMB_NTSTATUS_FILE_IS_A_DIRECTORY:        u32 = 0xc00000ba;
88 pub const SMB_NTSTATUS_NOT_SUPPORTED:              u32 = 0xc00000bb;
89 pub const SMB_NTSTATUS_BAD_NETWORK_NAME:           u32 = 0xc00000cc;
90 pub const SMB_NTSTATUS_REQUEST_NOT_ACCEPTED:       u32 = 0xc00000d0;
91 pub const SMB_NTSTATUS_OPLOCK_NOT_GRANTED:         u32 = 0xc00000e2;
92 pub const SMB_NTSTATUS_CANCELLED:                  u32 = 0xc0000120;
93 pub const SMB_NTSTATUS_FILE_CLOSED:                u32 = 0xc0000128;
94 pub const SMB_NTSTATUS_FS_DRIVER_REQUIRED:         u32 = 0xc000019c;
95 pub const SMB_NTSTATUS_INSUFF_SERVER_RESOURCES:    u32 = 0xc0000205;
96 pub const SMB_NTSTATUS_NOT_FOUND:                  u32 = 0xc0000225;
97 pub const SMB_NTSTATUS_PIPE_BROKEN:                u32 = 0xc000014b;
98 pub const SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE:    u32 = 0xc000018d;
99 pub const SMB_NTSTATUS_NOT_A_REPARSE_POINT:        u32 = 0xc0000275;
100 pub const SMB_NTSTATUS_NETWORK_SESSION_EXPIRED:    u32 = 0xc000035c;
101 
smb_ntstatus_string(c: u32) -> String102 pub fn smb_ntstatus_string(c: u32) -> String {
103     match c {
104         SMB_NTSTATUS_SUCCESS                   => "STATUS_SUCCESS",
105         SMB_NTSTATUS_BUFFER_OVERFLOW           => "STATUS_BUFFER_OVERFLOW",
106         SMB_NTSTATUS_PENDING                   => "STATUS_PENDING",
107         SMB_NTSTATUS_NO_MORE_FILES             => "STATUS_NO_MORE_FILES",
108         SMB_NTSTATUS_NO_MORE_ENTRIES           => "STATUS_NO_MORE_ENTRIES",
109         SMB_NTSTATUS_INVALID_HANDLE            => "STATUS_INVALID_HANDLE",
110         SMB_NTSTATUS_INVALID_PARAMETER         => "STATUS_INVALID_PARAMETER",
111         SMB_NTSTATUS_NO_SUCH_DEVICE            => "STATUS_NO_SUCH_DEVICE",
112         SMB_NTSTATUS_NO_SUCH_FILE              => "STATUS_NO_SUCH_FILE",
113         SMB_NTSTATUS_INVALID_DEVICE_REQUEST    => "STATUS_INVALID_DEVICE_REQUEST",
114         SMB_NTSTATUS_END_OF_FILE               => "STATUS_END_OF_FILE",
115         SMB_NTSTATUS_MORE_PROCESSING_REQUIRED  => "STATUS_MORE_PROCESSING_REQUIRED",
116         SMB_NTSTATUS_ACCESS_DENIED             => "STATUS_ACCESS_DENIED",
117         SMB_NTSTATUS_OBJECT_NAME_INVALID       => "STATUS_OBJECT_NAME_INVALID",
118         SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND     => "STATUS_OBJECT_NAME_NOT_FOUND",
119         SMB_NTSTATUS_OBJECT_NAME_COLLISION     => "STATUS_OBJECT_NAME_COLLISION",
120         SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND     => "STATUS_OBJECT_PATH_NOT_FOUND",
121         SMB_NTSTATUS_SHARING_VIOLATION         => "STATUS_SHARING_VIOLATION",
122         SMB_NTSTATUS_LOCK_CONFLICT             => "STATUS_LOCK_CONFLICT",
123         SMB_NTSTATUS_LOCK_NOT_GRANTED          => "STATUS_LOCK_NOT_GRANTED",
124         SMB_NTSTATUS_PRIVILEGE_NOT_HELD        => "STATUS_PRIVILEGE_NOT_HELD",
125         SMB_NTSTATUS_LOGON_FAILURE             => "STATUS_LOGON_FAILURE",
126         SMB_NTSTATUS_PIPE_DISCONNECTED         => "STATUS_PIPE_DISCONNECTED",
127         SMB_NTSTATUS_FILE_IS_A_DIRECTORY       => "STATUS_FILE_IS_A_DIRECTORY",
128         SMB_NTSTATUS_NOT_SUPPORTED             => "STATUS_NOT_SUPPORTED",
129         SMB_NTSTATUS_BAD_NETWORK_NAME          => "STATUS_BAD_NETWORK_NAME",
130         SMB_NTSTATUS_REQUEST_NOT_ACCEPTED      => "STATUS_REQUEST_NOT_ACCEPTED",
131         SMB_NTSTATUS_OPLOCK_NOT_GRANTED        => "STATUS_OPLOCK_NOT_GRANTED",
132         SMB_NTSTATUS_CANCELLED                 => "STATUS_CANCELLED",
133         SMB_NTSTATUS_FILE_CLOSED               => "STATUS_FILE_CLOSED",
134         SMB_NTSTATUS_FS_DRIVER_REQUIRED        => "STATUS_FS_DRIVER_REQUIRED",
135         SMB_NTSTATUS_INSUFF_SERVER_RESOURCES   => "STATUS_INSUFF_SERVER_RESOURCES",
136         SMB_NTSTATUS_NOT_FOUND                 => "STATUS_NOT_FOUND",
137         SMB_NTSTATUS_PIPE_BROKEN               => "STATUS_PIPE_BROKEN",
138         SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE   => "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
139         SMB_NTSTATUS_NOT_A_REPARSE_POINT       => "STATUS_NOT_A_REPARSE_POINT",
140         SMB_NTSTATUS_NETWORK_SESSION_EXPIRED   => "STATUS_NETWORK_SESSION_EXPIRED",
141         _ => { return (c).to_string(); },
142     }.to_string()
143 }
144 
145 pub const SMB_SRV_ERROR:                u16 = 1;
146 pub const SMB_SRV_BADPW:                u16 = 2;
147 pub const SMB_SRV_BADTYPE:              u16 = 3;
148 pub const SMB_SRV_ACCESS:               u16 = 4;
149 pub const SMB_SRV_BADUID:               u16 = 91;
150 
smb_srv_error_string(c: u16) -> String151 pub fn smb_srv_error_string(c: u16) -> String {
152     match c {
153         SMB_SRV_ERROR           => "SRV_ERROR",
154         SMB_SRV_BADPW           => "SRV_BADPW",
155         SMB_SRV_BADTYPE         => "SRV_BADTYPE",
156         SMB_SRV_ACCESS          => "SRV_ACCESS",
157         SMB_SRV_BADUID          => "SRV_BADUID",
158         _ => { return (c).to_string(); },
159     }.to_string()
160 }
161 
162 pub const SMB_DOS_SUCCESS:                u16 = 0;
163 pub const SMB_DOS_BAD_FUNC:               u16 = 1;
164 pub const SMB_DOS_BAD_FILE:               u16 = 2;
165 pub const SMB_DOS_BAD_PATH:               u16 = 3;
166 pub const SMB_DOS_TOO_MANY_OPEN_FILES:    u16 = 4;
167 pub const SMB_DOS_ACCESS_DENIED:          u16 = 5;
168 
smb_dos_error_string(c: u16) -> String169 pub fn smb_dos_error_string(c: u16) -> String {
170     match c {
171         SMB_DOS_SUCCESS           => "DOS_SUCCESS",
172         SMB_DOS_BAD_FUNC          => "DOS_BAD_FUNC",
173         SMB_DOS_BAD_FILE          => "DOS_BAD_FILE",
174         SMB_DOS_BAD_PATH          => "DOS_BAD_PATH",
175         SMB_DOS_TOO_MANY_OPEN_FILES => "DOS_TOO_MANY_OPEN_FILES",
176         SMB_DOS_ACCESS_DENIED     => "DOS_ACCESS_DENIED",
177         _ => { return (c).to_string(); },
178     }.to_string()
179 }
180 
181 pub const NTLMSSP_NEGOTIATE:               u32 = 1;
182 pub const NTLMSSP_CHALLENGE:               u32 = 2;
183 pub const NTLMSSP_AUTH:                    u32 = 3;
184 
ntlmssp_type_string(c: u32) -> String185 pub fn ntlmssp_type_string(c: u32) -> String {
186     match c {
187         NTLMSSP_NEGOTIATE   => "NTLMSSP_NEGOTIATE",
188         NTLMSSP_CHALLENGE   => "NTLMSSP_CHALLENGE",
189         NTLMSSP_AUTH        => "NTLMSSP_AUTH",
190         _ => { return (c).to_string(); },
191     }.to_string()
192 }
193 
194 #[derive(Eq, PartialEq, Debug, Clone)]
195 pub struct SMBVerCmdStat {
196     smb_ver: u8,
197     smb1_cmd: u8,
198     smb2_cmd: u16,
199 
200     status_set: bool,
201     status_is_dos_error: bool,
202     status_error_class: u8,
203     status: u32,
204 }
205 
206 impl SMBVerCmdStat {
new() -> SMBVerCmdStat207     pub fn new() -> SMBVerCmdStat {
208         return SMBVerCmdStat {
209             smb_ver: 0,
210             smb1_cmd: 0,
211             smb2_cmd: 0,
212             status_set: false,
213             status_is_dos_error: false,
214             status_error_class: 0,
215             status: 0,
216         }
217     }
new1(cmd: u8) -> SMBVerCmdStat218     pub fn new1(cmd: u8) -> SMBVerCmdStat {
219         return SMBVerCmdStat {
220             smb_ver: 1,
221             smb1_cmd: cmd,
222             smb2_cmd: 0,
223             status_set: false,
224             status_is_dos_error: false,
225             status_error_class: 0,
226             status: 0,
227         }
228     }
new1_with_ntstatus(cmd: u8, status: u32) -> SMBVerCmdStat229     pub fn new1_with_ntstatus(cmd: u8, status: u32) -> SMBVerCmdStat {
230         return SMBVerCmdStat {
231             smb_ver: 1,
232             smb1_cmd: cmd,
233             smb2_cmd: 0,
234             status_set: true,
235             status_is_dos_error: false,
236             status_error_class: 0,
237             status: status,
238         }
239     }
new2(cmd: u16) -> SMBVerCmdStat240     pub fn new2(cmd: u16) -> SMBVerCmdStat {
241         return SMBVerCmdStat {
242             smb_ver: 2,
243             smb1_cmd: 0,
244             smb2_cmd: cmd,
245             status_set: false,
246             status_is_dos_error: false,
247             status_error_class: 0,
248             status: 0,
249         }
250     }
251 
new2_with_ntstatus(cmd: u16, status: u32) -> SMBVerCmdStat252     pub fn new2_with_ntstatus(cmd: u16, status: u32) -> SMBVerCmdStat {
253         return SMBVerCmdStat {
254             smb_ver: 2,
255             smb1_cmd: 0,
256             smb2_cmd: cmd,
257             status_set: true,
258             status_is_dos_error: false,
259             status_error_class: 0,
260             status: status,
261         }
262     }
263 
set_smb1_cmd(&mut self, cmd: u8) -> bool264     pub fn set_smb1_cmd(&mut self, cmd: u8) -> bool {
265         if self.smb_ver != 0 {
266             return false;
267         }
268         self.smb_ver = 1;
269         self.smb1_cmd = cmd;
270         return true;
271     }
272 
set_smb2_cmd(&mut self, cmd: u16) -> bool273     pub fn set_smb2_cmd(&mut self, cmd: u16) -> bool {
274         if self.smb_ver != 0 {
275             return false;
276         }
277         self.smb_ver = 2;
278         self.smb2_cmd = cmd;
279         return true;
280     }
281 
get_version(&self) -> u8282     pub fn get_version(&self) -> u8 {
283         self.smb_ver
284     }
285 
get_smb1_cmd(&self) -> (bool, u8)286     pub fn get_smb1_cmd(&self) -> (bool, u8) {
287         if self.smb_ver != 1 {
288             return (false, 0);
289         }
290         return (true, self.smb1_cmd);
291     }
292 
get_smb2_cmd(&self) -> (bool, u16)293     pub fn get_smb2_cmd(&self) -> (bool, u16) {
294         if self.smb_ver != 2 {
295             return (false, 0);
296         }
297         return (true, self.smb2_cmd);
298     }
299 
get_ntstatus(&self) -> (bool, u32)300     pub fn get_ntstatus(&self) -> (bool, u32) {
301         (self.status_set && !self.status_is_dos_error, self.status)
302     }
303 
get_dos_error(&self) -> (bool, u8, u16)304     pub fn get_dos_error(&self) -> (bool, u8, u16) {
305         (self.status_set && self.status_is_dos_error, self.status_error_class, self.status as u16)
306     }
307 
set_status(&mut self, status: u32, is_dos_error: bool)308     fn set_status(&mut self, status: u32, is_dos_error: bool)
309     {
310         if is_dos_error {
311             self.status_is_dos_error = true;
312             self.status_error_class = (status & 0x0000_00ff) as u8;
313             self.status = (status & 0xffff_0000) >> 16;
314         } else {
315             self.status = status;
316         }
317         self.status_set = true;
318     }
319 
set_ntstatus(&mut self, status: u32)320     pub fn set_ntstatus(&mut self, status: u32)
321     {
322         self.set_status(status, false)
323     }
324 
set_status_dos_error(&mut self, status: u32)325     pub fn set_status_dos_error(&mut self, status: u32)
326     {
327         self.set_status(status, true)
328     }
329 }
330 
331 /// "The FILETIME structure is a 64-bit value that represents the number of
332 /// 100-nanosecond intervals that have elapsed since January 1, 1601,
333 /// Coordinated Universal Time (UTC)."
334 #[derive(Eq, PartialEq, Debug, Clone)]
335 pub struct SMBFiletime {
336     ts: u64,
337 }
338 
339 impl SMBFiletime {
new(raw: u64) -> SMBFiletime340     pub fn new(raw: u64) -> SMBFiletime {
341         SMBFiletime {
342             ts: raw,
343         }
344     }
345 
346     /// inspired by Bro, convert FILETIME to secs since unix epoch
as_unix(&self) -> u32347     pub fn as_unix(&self) -> u32 {
348         if self.ts > 116_444_736_000_000_000_u64 {
349             let ts = self.ts / 10000000 - 11644473600;
350             ts as u32
351         } else {
352             0
353         }
354     }
355 }
356 
357 #[derive(Debug)]
358 pub enum SMBTransactionTypeData {
359     FILE(SMBTransactionFile),
360     TREECONNECT(SMBTransactionTreeConnect),
361     NEGOTIATE(SMBTransactionNegotiate),
362     DCERPC(SMBTransactionDCERPC),
363     CREATE(SMBTransactionCreate),
364     SESSIONSETUP(SMBTransactionSessionSetup),
365     IOCTL(SMBTransactionIoctl),
366     RENAME(SMBTransactionRename),
367     SETFILEPATHINFO(SMBTransactionSetFilePathInfo),
368 }
369 
370 // Used for Trans2 SET_PATH_INFO and SET_FILE_INFO
371 #[derive(Debug)]
372 pub struct SMBTransactionSetFilePathInfo {
373     pub subcmd: u16,
374     pub loi: u16,
375     pub delete_on_close: bool,
376     pub filename: Vec<u8>,
377     pub fid: Vec<u8>,
378 }
379 
380 impl SMBTransactionSetFilePathInfo {
new(filename: Vec<u8>, fid: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool) -> SMBTransactionSetFilePathInfo381     pub fn new(filename: Vec<u8>, fid: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool)
382         -> SMBTransactionSetFilePathInfo
383     {
384         return SMBTransactionSetFilePathInfo {
385             filename: filename, fid: fid,
386             subcmd: subcmd,
387             loi: loi,
388             delete_on_close: delete_on_close,
389         }
390     }
391 }
392 
393 impl SMBState {
new_setfileinfo_tx(&mut self, filename: Vec<u8>, fid: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool) -> &mut SMBTransaction394     pub fn new_setfileinfo_tx(&mut self, filename: Vec<u8>, fid: Vec<u8>,
395             subcmd: u16, loi: u16, delete_on_close: bool)
396         -> &mut SMBTransaction
397     {
398         let mut tx = self.new_tx();
399 
400         tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
401                     SMBTransactionSetFilePathInfo::new(
402                         filename, fid, subcmd, loi, delete_on_close)));
403         tx.request_done = true;
404         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
405 
406         SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id);
407         self.transactions.push(tx);
408         let tx_ref = self.transactions.last_mut();
409         return tx_ref.unwrap();
410     }
411 
new_setpathinfo_tx(&mut self, filename: Vec<u8>, subcmd: u16, loi: u16, delete_on_close: bool) -> &mut SMBTransaction412     pub fn new_setpathinfo_tx(&mut self, filename: Vec<u8>,
413             subcmd: u16, loi: u16, delete_on_close: bool)
414         -> &mut SMBTransaction
415     {
416         let mut tx = self.new_tx();
417 
418         let fid : Vec<u8> = Vec::new();
419         tx.type_data = Some(SMBTransactionTypeData::SETFILEPATHINFO(
420                     SMBTransactionSetFilePathInfo::new(filename, fid,
421                         subcmd, loi, delete_on_close)));
422         tx.request_done = true;
423         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
424 
425         SCLogDebug!("SMB: TX SETFILEPATHINFO created: ID {}", tx.id);
426         self.transactions.push(tx);
427         let tx_ref = self.transactions.last_mut();
428         return tx_ref.unwrap();
429     }
430 }
431 
432 #[derive(Debug)]
433 pub struct SMBTransactionRename {
434     pub oldname: Vec<u8>,
435     pub newname: Vec<u8>,
436     pub fuid: Vec<u8>,
437 }
438 
439 impl SMBTransactionRename {
new(fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>) -> SMBTransactionRename440     pub fn new(fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>) -> SMBTransactionRename {
441         return SMBTransactionRename {
442             fuid: fuid, oldname: oldname, newname: newname,
443         }
444     }
445 }
446 
447 impl SMBState {
new_rename_tx(&mut self, fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>) -> &mut SMBTransaction448     pub fn new_rename_tx(&mut self, fuid: Vec<u8>, oldname: Vec<u8>, newname: Vec<u8>)
449         -> &mut SMBTransaction
450     {
451         let mut tx = self.new_tx();
452 
453         tx.type_data = Some(SMBTransactionTypeData::RENAME(
454                     SMBTransactionRename::new(fuid, oldname, newname)));
455         tx.request_done = true;
456         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
457 
458         SCLogDebug!("SMB: TX RENAME created: ID {}", tx.id);
459         self.transactions.push(tx);
460         let tx_ref = self.transactions.last_mut();
461         return tx_ref.unwrap();
462     }
463 }
464 
465 #[derive(Debug)]
466 pub struct SMBTransactionCreate {
467     pub disposition: u32,
468     pub delete_on_close: bool,
469     pub directory: bool,
470     pub filename: Vec<u8>,
471     pub guid: Vec<u8>,
472 
473     pub create_ts: u32,
474     pub last_access_ts: u32,
475     pub last_write_ts: u32,
476     pub last_change_ts: u32,
477 
478     pub size: u64,
479 }
480 
481 impl SMBTransactionCreate {
new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> SMBTransactionCreate482     pub fn new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> SMBTransactionCreate {
483         return SMBTransactionCreate {
484             disposition: disp,
485             delete_on_close: del,
486             directory: dir,
487             filename: filename,
488             guid: Vec::new(),
489             create_ts: 0,
490             last_access_ts: 0,
491             last_write_ts: 0,
492             last_change_ts: 0,
493             size: 0,
494         }
495     }
496 }
497 
498 #[derive(Debug)]
499 pub struct SMBTransactionNegotiate {
500     pub smb_ver: u8,
501     pub dialects: Vec<Vec<u8>>,
502     pub dialects2: Vec<Vec<u8>>,
503 
504     // SMB1 doesn't have the client GUID
505     pub client_guid: Option<Vec<u8>>,
506     pub server_guid: Vec<u8>,
507 }
508 
509 impl SMBTransactionNegotiate {
new(smb_ver: u8) -> SMBTransactionNegotiate510     pub fn new(smb_ver: u8) -> SMBTransactionNegotiate {
511         return SMBTransactionNegotiate {
512             smb_ver: smb_ver,
513             dialects: Vec::new(),
514             dialects2: Vec::new(),
515             client_guid: None,
516             server_guid: Vec::with_capacity(16),
517         }
518     }
519 }
520 
521 #[derive(Debug)]
522 pub struct SMBTransactionTreeConnect {
523     pub is_pipe: bool,
524     pub share_type: u8,
525     pub tree_id: u32,
526     pub share_name: Vec<u8>,
527 
528     /// SMB1 service strings
529     pub req_service: Option<Vec<u8>>,
530     pub res_service: Option<Vec<u8>>,
531 }
532 
533 impl SMBTransactionTreeConnect {
new(share_name: Vec<u8>) -> SMBTransactionTreeConnect534     pub fn new(share_name: Vec<u8>) -> SMBTransactionTreeConnect {
535         return SMBTransactionTreeConnect {
536             is_pipe:false,
537             share_type: 0,
538             tree_id:0,
539             share_name:share_name,
540             req_service: None,
541             res_service: None,
542         }
543     }
544 }
545 
546 #[derive(Debug)]
547 pub struct SMBTransaction {
548     pub id: u64,    /// internal id
549 
550     /// version, command and status
551     pub vercmd: SMBVerCmdStat,
552     /// session id, tree id, etc.
553     pub hdr: SMBCommonHdr,
554 
555     /// for state tracking. false means this side is in progress, true
556     /// that it's complete.
557     pub request_done: bool,
558     pub response_done: bool,
559 
560     /// Command specific data
561     pub type_data: Option<SMBTransactionTypeData>,
562 
563     pub de_state: Option<*mut DetectEngineState>,
564     pub events: *mut AppLayerDecoderEvents,
565     pub tx_data: AppLayerTxData,
566 }
567 
568 impl SMBTransaction {
new() -> SMBTransaction569     pub fn new() -> SMBTransaction {
570         return SMBTransaction{
571             id: 0,
572             vercmd: SMBVerCmdStat::new(),
573             hdr: SMBCommonHdr::init(),
574             request_done: false,
575             response_done: false,
576             type_data: None,
577             de_state: None,
578             events: std::ptr::null_mut(),
579             tx_data: AppLayerTxData::new(),
580         }
581     }
582 
set_status(&mut self, status: u32, is_dos_error: bool)583     pub fn set_status(&mut self, status: u32, is_dos_error: bool)
584     {
585         if is_dos_error {
586             self.vercmd.set_status_dos_error(status);
587         } else {
588             self.vercmd.set_ntstatus(status);
589         }
590     }
591 
free(&mut self)592     pub fn free(&mut self) {
593         debug_validate_bug_on!(self.tx_data.files_opened > 1);
594         debug_validate_bug_on!(self.tx_data.files_logged > 1);
595         if self.events != std::ptr::null_mut() {
596             sc_app_layer_decoder_events_free_events(&mut self.events);
597         }
598         match self.de_state {
599             Some(state) => {
600                 sc_detect_engine_state_free(state);
601             }
602             _ => {}
603         }
604     }
605 }
606 
607 impl Drop for SMBTransaction {
drop(&mut self)608     fn drop(&mut self) {
609         self.free();
610     }
611 }
612 
613 #[derive(Hash, Eq, PartialEq, Debug, Clone)]
614 pub struct SMBFileGUIDOffset {
615     pub guid: Vec<u8>,
616     pub offset: u64,
617 }
618 
619 impl SMBFileGUIDOffset {
new(guid: Vec<u8>, offset: u64) -> SMBFileGUIDOffset620     pub fn new(guid: Vec<u8>, offset: u64) -> SMBFileGUIDOffset {
621         SMBFileGUIDOffset {
622             guid:guid,
623             offset:offset,
624         }
625     }
626 }
627 
628 /// type values to make sure we're not mixing things
629 /// up in hashmap lookups
630 pub const SMBHDR_TYPE_GUID:        u32 = 1;
631 pub const SMBHDR_TYPE_SHARE:       u32 = 2;
632 pub const SMBHDR_TYPE_FILENAME:    u32 = 3;
633 pub const SMBHDR_TYPE_OFFSET:      u32 = 4;
634 pub const SMBHDR_TYPE_GENERICTX:   u32 = 5;
635 pub const SMBHDR_TYPE_HEADER:      u32 = 6;
636 pub const SMBHDR_TYPE_MAX_SIZE:    u32 = 7; // max resp size for SMB1_COMMAND_TRANS
637 pub const SMBHDR_TYPE_TRANS_FRAG:  u32 = 8;
638 pub const SMBHDR_TYPE_TREE:        u32 = 9;
639 pub const SMBHDR_TYPE_DCERPCTX:    u32 = 10;
640 
641 #[derive(Hash, Eq, PartialEq, Debug)]
642 pub struct SMBCommonHdr {
643     pub ssn_id: u64,
644     pub tree_id: u32,
645     pub rec_type: u32,
646     pub msg_id: u64,
647 }
648 
649 impl SMBCommonHdr {
init() -> SMBCommonHdr650     pub fn init() -> SMBCommonHdr {
651         SMBCommonHdr {
652             rec_type : 0,
653             ssn_id : 0,
654             tree_id : 0,
655             msg_id : 0,
656         }
657     }
new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> SMBCommonHdr658     pub fn new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> SMBCommonHdr {
659         SMBCommonHdr {
660             rec_type : rec_type,
661             ssn_id : ssn_id,
662             tree_id : tree_id,
663             msg_id : msg_id,
664         }
665     }
from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr666     pub fn from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr {
667         let tree_id = match rec_type {
668             SMBHDR_TYPE_TREE => { 0 },
669             _ => r.tree_id,
670         };
671         let msg_id = match rec_type {
672             SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
673             _ => { r.message_id as u64 },
674         };
675 
676         SMBCommonHdr {
677             rec_type : rec_type,
678             ssn_id : r.session_id,
679             tree_id : tree_id,
680             msg_id : msg_id,
681         }
682 
683     }
from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr684     pub fn from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr {
685         let tree_id = match rec_type {
686             SMBHDR_TYPE_TREE => { 0 },
687             _ => r.tree_id as u32,
688         };
689         let msg_id = match rec_type {
690             SMBHDR_TYPE_TRANS_FRAG | SMBHDR_TYPE_SHARE => { 0 },
691             _ => { r.multiplex_id as u64 },
692         };
693 
694         SMBCommonHdr {
695             rec_type : rec_type,
696             ssn_id : r.ssn_id as u64,
697             tree_id : tree_id,
698             msg_id : msg_id,
699         }
700     }
701 
702     // don't include tree id
compare(&self, hdr: &SMBCommonHdr) -> bool703     pub fn compare(&self, hdr: &SMBCommonHdr) -> bool {
704         self.rec_type == hdr.rec_type && self.ssn_id == hdr.ssn_id &&
705             self.msg_id == hdr.msg_id
706     }
707 }
708 
709 #[derive(Hash, Eq, PartialEq, Debug)]
710 pub struct SMBHashKeyHdrGuid {
711     hdr: SMBCommonHdr,
712     guid: Vec<u8>,
713 }
714 
715 impl SMBHashKeyHdrGuid {
new(hdr: SMBCommonHdr, guid: Vec<u8>) -> SMBHashKeyHdrGuid716     pub fn new(hdr: SMBCommonHdr, guid: Vec<u8>) -> SMBHashKeyHdrGuid {
717         SMBHashKeyHdrGuid {
718             hdr: hdr, guid: guid,
719         }
720     }
721 }
722 
723 #[derive(Hash, Eq, PartialEq, Debug)]
724 pub struct SMBTree {
725     pub name: Vec<u8>,
726     pub is_pipe: bool,
727 }
728 
729 impl SMBTree {
new(name: Vec<u8>, is_pipe: bool) -> SMBTree730     pub fn new(name: Vec<u8>, is_pipe: bool) -> SMBTree {
731         SMBTree {
732             name:name,
733             is_pipe:is_pipe,
734         }
735     }
736 }
737 
u32_as_bytes(i: u32) -> [u8;4]738 pub fn u32_as_bytes(i: u32) -> [u8;4] {
739     let o1: u8 = ((i >> 24) & 0xff) as u8;
740     let o2: u8 = ((i >> 16) & 0xff) as u8;
741     let o3: u8 = ((i >> 8)  & 0xff) as u8;
742     let o4: u8 =  (i        & 0xff) as u8;
743     return [o1, o2, o3, o4]
744 }
745 
746 pub struct SMBState<> {
747     /// map ssn/tree/msgid to vec (guid/name/share)
748     pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
749     /// map guid to filename
750     pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
751     /// map ssn key to read offset
752     pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
753 
754     pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
755 
756     // store partial data records that are transfered in multiple
757     // requests for DCERPC.
758     pub ssnguid2vec_map: HashMap<SMBHashKeyHdrGuid, Vec<u8>>,
759 
760     pub files: SMBFiles,
761 
762     skip_ts: u32,
763     skip_tc: u32,
764 
765     pub file_ts_left : u32,
766     pub file_tc_left : u32,
767     pub file_ts_guid : Vec<u8>,
768     pub file_tc_guid : Vec<u8>,
769 
770     pub ts_ssn_gap: bool,
771     pub tc_ssn_gap: bool,
772 
773     pub ts_gap: bool, // last TS update was gap
774     pub tc_gap: bool, // last TC update was gap
775 
776     pub ts_trunc: bool, // no more data for TOSERVER
777     pub tc_trunc: bool, // no more data for TOCLIENT
778 
779     /// true as long as we have file txs that are in a post-gap
780     /// state. It means we'll do extra house keeping for those.
781     check_post_gap_file_txs: bool,
782     post_gap_files_checked: bool,
783 
784     /// transactions list
785     pub transactions: Vec<SMBTransaction>,
786 
787     /// tx counter for assigning incrementing id's to tx's
788     tx_id: u64,
789 
790     /// SMB2 dialect or 0 if not set or SMB1
791     pub dialect: u16,
792     /// contains name of SMB1 dialect
793     pub dialect_vec: Option<Vec<u8>>, // used if dialect == 0
794 
795     /// dcerpc interfaces, stored here to be able to match
796     /// them while inspecting DCERPC REQUEST txs
797     pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
798 
799     /// Timestamp in seconds of last update. This is packet time,
800     /// potentially coming from pcaps.
801     ts: u64,
802 }
803 
804 impl SMBState {
805     /// Allocation function for a new TLS parser instance
new() -> SMBState806     pub fn new() -> SMBState {
807         SMBState {
808             ssn2vec_map:HashMap::new(),
809             guid2name_map:HashMap::new(),
810             ssn2vecoffset_map:HashMap::new(),
811             ssn2tree_map:HashMap::new(),
812             ssnguid2vec_map:HashMap::new(),
813             files: SMBFiles::new(),
814             skip_ts:0,
815             skip_tc:0,
816             file_ts_left:0,
817             file_tc_left:0,
818             file_ts_guid:Vec::new(),
819             file_tc_guid:Vec::new(),
820             ts_ssn_gap: false,
821             tc_ssn_gap: false,
822             ts_gap: false,
823             tc_gap: false,
824             ts_trunc: false,
825             tc_trunc: false,
826             check_post_gap_file_txs: false,
827             post_gap_files_checked: false,
828             transactions: Vec::new(),
829             tx_id:0,
830             dialect:0,
831             dialect_vec: None,
832             dcerpc_ifaces: None,
833             ts: 0,
834         }
835     }
836 
free(&mut self)837     pub fn free(&mut self) {
838         //self._debug_state_stats();
839         self._debug_tx_stats();
840         self.files.free();
841     }
842 
new_tx(&mut self) -> SMBTransaction843     pub fn new_tx(&mut self) -> SMBTransaction {
844         let mut tx = SMBTransaction::new();
845         self.tx_id += 1;
846         tx.id = self.tx_id;
847         SCLogDebug!("TX {} created", tx.id);
848         return tx;
849     }
850 
free_tx(&mut self, tx_id: u64)851     pub fn free_tx(&mut self, tx_id: u64) {
852         SCLogDebug!("Freeing TX with ID {} TX.ID {}", tx_id, tx_id+1);
853         let len = self.transactions.len();
854         let mut found = false;
855         let mut index = 0;
856         for i in 0..len {
857             let tx = &self.transactions[i];
858             if tx.id == tx_id + 1 {
859                 found = true;
860                 index = i;
861                 SCLogDebug!("tx {} progress {}/{}", tx.id, tx.request_done, tx.response_done);
862                 break;
863             }
864         }
865         if found {
866             SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}",
867                     tx_id, tx_id+1, index, self.transactions.len(), self.tx_id);
868             self.transactions.remove(index);
869         }
870     }
871 
872     // for use with the C API call StateGetTxIterator
get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) -> Option<(&SMBTransaction, u64, bool)>873     pub fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) ->
874         Option<(&SMBTransaction, u64, bool)>
875     {
876         let mut index = *state as usize;
877         let len = self.transactions.len();
878 
879         // find tx that is >= min_tx_id
880         while index < len {
881             let tx = &self.transactions[index];
882             if tx.id < min_tx_id + 1 {
883                 index += 1;
884                 continue;
885             }
886             // store current index in the state and not the next
887             // as transactions might be freed between now and the
888             // next time we are called.
889             *state = index as u64;
890             //SCLogDebug!("returning tx_id {} has_next? {} (len {} index {}), tx {:?}",
891             //        tx.id - 1, (len - index) > 1, len, index, tx);
892             return Some((tx, tx.id - 1, (len - index) > 1));
893         }
894         return None;
895     }
896 
get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction>897     pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction> {
898 /*
899         if self.transactions.len() > 100 {
900             SCLogNotice!("get_tx_by_id: tx_id={} in list={}", tx_id, self.transactions.len());
901             self._dump_txs();
902             panic!("txs exploded");
903         }
904 */
905         for tx in &mut self.transactions {
906             if tx.id == tx_id + 1 {
907                 let ver = tx.vercmd.get_version();
908                 let mut _smbcmd;
909                 if ver == 2 {
910                     let (_, cmd) = tx.vercmd.get_smb2_cmd();
911                     _smbcmd = cmd;
912                 } else {
913                     let (_, cmd) = tx.vercmd.get_smb1_cmd();
914                     _smbcmd = cmd as u16;
915                 }
916                 SCLogDebug!("Found SMB TX: id {} ver:{} cmd:{} progress {}/{} type_data {:?}",
917                         tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data);
918                 return Some(tx);
919             }
920         }
921         SCLogDebug!("Failed to find SMB TX with ID {}", tx_id);
922         return None;
923     }
924 
update_ts(&mut self, ts: u64)925     fn update_ts(&mut self, ts: u64) {
926         if ts != self.ts {
927             self.ts = ts;
928             self.post_gap_files_checked = false;
929         }
930     }
931 
932     /* generic TX has no type_data and is only used to
933      * track a single cmd request/reply pair. */
934 
new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr) -> &mut SMBTransaction935     pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr)
936         -> &mut SMBTransaction
937     {
938         let mut tx = self.new_tx();
939         if smb_ver == 1 && smb_cmd <= 255 {
940             tx.vercmd.set_smb1_cmd(smb_cmd as u8);
941         } else if smb_ver == 2 {
942             tx.vercmd.set_smb2_cmd(smb_cmd);
943         }
944 
945         tx.type_data = None;
946         tx.request_done = true;
947         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
948         tx.hdr = key;
949 
950         SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}",
951                 tx.id, self.transactions.len(), &tx);
952         self.transactions.push(tx);
953         let tx_ref = self.transactions.last_mut();
954         return tx_ref.unwrap();
955     }
956 
get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16) -> Option<&mut SMBTransaction>957     pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16)
958         -> Option<&mut SMBTransaction>
959     {
960         let tx_ref = self.transactions.last_mut();
961         match tx_ref {
962             Some(tx) => {
963                 let found = if tx.vercmd.get_version() == smb_ver {
964                     if smb_ver == 1 {
965                         let (_, cmd) = tx.vercmd.get_smb1_cmd();
966                         cmd as u16 == smb_cmd
967                     } else if smb_ver == 2 {
968                         let (_, cmd) = tx.vercmd.get_smb2_cmd();
969                         cmd == smb_cmd
970                     } else {
971                         false
972                     }
973                 } else {
974                     false
975                 };
976                 if found {
977                     return Some(tx);
978                 }
979             },
980             None => { },
981         }
982         return None;
983     }
984 
get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr) -> Option<&mut SMBTransaction>985     pub fn get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr)
986         -> Option<&mut SMBTransaction>
987     {
988         for tx in &mut self.transactions {
989             let found = if tx.vercmd.get_version() == smb_ver {
990                 if smb_ver == 1 {
991                     let (_, cmd) = tx.vercmd.get_smb1_cmd();
992                     cmd as u16 == smb_cmd && tx.hdr.compare(key)
993                 } else if smb_ver == 2 {
994                     let (_, cmd) = tx.vercmd.get_smb2_cmd();
995                     cmd == smb_cmd && tx.hdr.compare(key)
996                 } else {
997                     false
998                 }
999             } else {
1000                 false
1001             };
1002             if found {
1003                 return Some(tx);
1004             }
1005         }
1006         return None;
1007     }
1008 
new_negotiate_tx(&mut self, smb_ver: u8) -> &mut SMBTransaction1009     pub fn new_negotiate_tx(&mut self, smb_ver: u8)
1010         -> &mut SMBTransaction
1011     {
1012         let mut tx = self.new_tx();
1013         if smb_ver == 1 {
1014             tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
1015         } else if smb_ver == 2 {
1016             tx.vercmd.set_smb2_cmd(SMB2_COMMAND_NEGOTIATE_PROTOCOL);
1017         }
1018 
1019         tx.type_data = Some(SMBTransactionTypeData::NEGOTIATE(
1020                     SMBTransactionNegotiate::new(smb_ver)));
1021         tx.request_done = true;
1022         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1023 
1024         SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver);
1025         self.transactions.push(tx);
1026         let tx_ref = self.transactions.last_mut();
1027         return tx_ref.unwrap();
1028     }
1029 
get_negotiate_tx(&mut self, smb_ver: u8) -> Option<&mut SMBTransaction>1030     pub fn get_negotiate_tx(&mut self, smb_ver: u8)
1031         -> Option<&mut SMBTransaction>
1032     {
1033         for tx in &mut self.transactions {
1034             let found = match tx.type_data {
1035                 Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
1036                     if x.smb_ver == smb_ver {
1037                         true
1038                     } else {
1039                         false
1040                     }
1041                 },
1042                 _ => { false },
1043             };
1044             if found {
1045                 return Some(tx);
1046             }
1047         }
1048         return None;
1049     }
1050 
new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>) -> &mut SMBTransaction1051     pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>)
1052         -> &mut SMBTransaction
1053     {
1054         let mut tx = self.new_tx();
1055 
1056         tx.hdr = hdr;
1057         tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
1058                     SMBTransactionTreeConnect::new(name.to_vec())));
1059         tx.request_done = true;
1060         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1061 
1062         SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}",
1063                 tx.id, String::from_utf8_lossy(&name));
1064         self.transactions.push(tx);
1065         let tx_ref = self.transactions.last_mut();
1066         return tx_ref.unwrap();
1067     }
1068 
get_treeconnect_tx(&mut self, hdr: SMBCommonHdr) -> Option<&mut SMBTransaction>1069     pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr)
1070         -> Option<&mut SMBTransaction>
1071     {
1072         for tx in &mut self.transactions {
1073             let hit = tx.hdr.compare(&hdr) && match tx.type_data {
1074                 Some(SMBTransactionTypeData::TREECONNECT(_)) => { true },
1075                 _ => { false },
1076             };
1077             if hit {
1078                 return Some(tx);
1079             }
1080         }
1081         return None;
1082     }
1083 
new_create_tx(&mut self, file_name: &Vec<u8>, disposition: u32, del: bool, dir: bool, hdr: SMBCommonHdr) -> &mut SMBTransaction1084     pub fn new_create_tx(&mut self, file_name: &Vec<u8>,
1085             disposition: u32, del: bool, dir: bool,
1086             hdr: SMBCommonHdr)
1087         -> &mut SMBTransaction
1088     {
1089         let mut tx = self.new_tx();
1090         tx.hdr = hdr;
1091         tx.type_data = Some(SMBTransactionTypeData::CREATE(
1092                             SMBTransactionCreate::new(
1093                                 file_name.to_vec(), disposition,
1094                                 del, dir)));
1095         tx.request_done = true;
1096         tx.response_done = self.tc_trunc; // no response expected if tc is truncated
1097 
1098         self.transactions.push(tx);
1099         let tx_ref = self.transactions.last_mut();
1100         return tx_ref.unwrap();
1101     }
1102 
get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr) -> Option<&mut SMBTransaction>1103     pub fn get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr)
1104         -> Option<&mut SMBTransaction>
1105     {
1106         for tx in &mut self.transactions {
1107             let found = match tx.type_data {
1108                 Some(SMBTransactionTypeData::CREATE(ref _d)) => {
1109                     tx.hdr.compare(&hdr)
1110                 },
1111                 _ => { false },
1112             };
1113 
1114             if found {
1115                 SCLogDebug!("SMB: Found SMB create TX with ID {}", tx.id);
1116                 return Some(tx);
1117             }
1118         }
1119         SCLogDebug!("SMB: Failed to find SMB create TX with key {:?}", hdr);
1120         return None;
1121     }
1122 
get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)1123     pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
1124     {
1125         let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) {
1126             Some(n) => {
1127                 let mut s = n.as_slice();
1128                 // skip leading \ if we have it
1129                 if s.len() > 1 && s[0] == 0x5c_u8 {
1130                     s = &s[1..];
1131                 }
1132                 match str::from_utf8(s) {
1133                     Ok("PSEXESVC") => ("PSEXESVC", false),
1134                     Ok("svcctl") => ("svcctl", true),
1135                     Ok("srvsvc") => ("srvsvc", true),
1136                     Ok("atsvc") => ("atsvc", true),
1137                     Ok("lsarpc") => ("lsarpc", true),
1138                     Ok("samr") => ("samr", true),
1139                     Ok("spoolss") => ("spoolss", true),
1140                     Ok("winreg") => ("winreg", true),
1141                     Ok("suricata::dcerpc") => ("unknown", true),
1142                     Err(_) => ("MALFORMED", false),
1143                     Ok(&_) => {
1144                         SCLogDebug!("don't know {}", String::from_utf8_lossy(&n));
1145                         ("UNKNOWN", false)
1146                     },
1147                 }
1148             },
1149             _ => { ("UNKNOWN", false) },
1150         };
1151         SCLogDebug!("service {} is_dcerpc {}", name, is_dcerpc);
1152         (&name, is_dcerpc)
1153     }
1154 
post_gap_housekeeping_for_files(&mut self)1155     fn post_gap_housekeeping_for_files(&mut self)
1156     {
1157         let mut post_gap_txs = false;
1158         for tx in &mut self.transactions {
1159             if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
1160                 if f.post_gap_ts > 0 {
1161                     if self.ts > f.post_gap_ts {
1162                         tx.request_done = true;
1163                         tx.response_done = true;
1164                         let (files, flags) = self.files.get(f.direction);
1165                         f.file_tracker.trunc(files, flags);
1166                     } else {
1167                         post_gap_txs = true;
1168                     }
1169                 }
1170             }
1171         }
1172         self.check_post_gap_file_txs = post_gap_txs;
1173     }
1174 
1175     /* after a gap we will consider all transactions complete for our
1176      * direction. File transfer transactions are an exception. Those
1177      * can handle gaps. For the file transactions we set the current
1178      * (flow) time and prune them in 60 seconds if no update for them
1179      * was received. */
post_gap_housekeeping(&mut self, dir: u8)1180     fn post_gap_housekeeping(&mut self, dir: u8)
1181     {
1182         if self.ts_ssn_gap && dir == STREAM_TOSERVER {
1183             for tx in &mut self.transactions {
1184                 if tx.id >= self.tx_id {
1185                     SCLogDebug!("post_gap_housekeeping: done");
1186                     break;
1187                 }
1188                 if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
1189                     // leaving FILE txs open as they can deal with gaps. We
1190                     // remove them after 60 seconds of no activity though.
1191                     if f.post_gap_ts == 0 {
1192                         f.post_gap_ts = self.ts + 60;
1193                         self.check_post_gap_file_txs = true;
1194                     }
1195                 } else {
1196                     SCLogDebug!("post_gap_housekeeping: tx {} marked as done TS", tx.id);
1197                     tx.request_done = true;
1198                 }
1199             }
1200         } else if self.tc_ssn_gap && dir == STREAM_TOCLIENT {
1201             for tx in &mut self.transactions {
1202                 if tx.id >= self.tx_id {
1203                     SCLogDebug!("post_gap_housekeeping: done");
1204                     break;
1205                 }
1206                 if let Some(SMBTransactionTypeData::FILE(ref mut f)) = tx.type_data {
1207                     // leaving FILE txs open as they can deal with gaps. We
1208                     // remove them after 60 seconds of no activity though.
1209                     if f.post_gap_ts == 0 {
1210                         f.post_gap_ts = self.ts + 60;
1211                         self.check_post_gap_file_txs = true;
1212                     }
1213                 } else {
1214                     SCLogDebug!("post_gap_housekeeping: tx {} marked as done TC", tx.id);
1215                     tx.request_done = true;
1216                     tx.response_done = true;
1217                 }
1218             }
1219 
1220         }
1221     }
1222 
set_file_left(&mut self, direction: u8, rec_size: u32, data_size: u32, fuid: Vec<u8>)1223     pub fn set_file_left(&mut self, direction: u8, rec_size: u32, data_size: u32, fuid: Vec<u8>)
1224     {
1225         let left = rec_size.saturating_sub(data_size);
1226         if direction == STREAM_TOSERVER {
1227             self.file_ts_left = left;
1228             self.file_ts_guid = fuid;
1229         } else {
1230             self.file_tc_left = left;
1231             self.file_tc_guid = fuid;
1232         }
1233     }
1234 
set_skip(&mut self, direction: u8, rec_size: u32, data_size: u32)1235     pub fn set_skip(&mut self, direction: u8, rec_size: u32, data_size: u32)
1236     {
1237         let skip = rec_size.saturating_sub(data_size);
1238         if direction == STREAM_TOSERVER {
1239             self.skip_ts = skip;
1240         } else {
1241             self.skip_tc = skip;
1242         }
1243     }
1244 
1245     // return how much data we consumed
handle_skip(&mut self, direction: u8, input_size: u32) -> u321246     fn handle_skip(&mut self, direction: u8, input_size: u32) -> u32 {
1247         let mut skip_left = if direction == STREAM_TOSERVER {
1248             self.skip_ts
1249         } else {
1250             self.skip_tc
1251         };
1252         if skip_left == 0 {
1253             return 0
1254         }
1255         SCLogDebug!("skip_left {} input_size {}", skip_left, input_size);
1256 
1257         let consumed = if skip_left >= input_size {
1258             input_size
1259         } else {
1260             skip_left
1261         };
1262 
1263         if skip_left <= input_size {
1264             skip_left = 0;
1265         } else {
1266             skip_left -= input_size;
1267         }
1268 
1269         if direction == STREAM_TOSERVER {
1270             self.skip_ts = skip_left;
1271         } else {
1272             self.skip_tc = skip_left;
1273         }
1274         return consumed;
1275     }
1276 
1277     /// return bytes consumed
parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize1278     pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize
1279     {
1280         SCLogDebug!("incomplete of size {}", input.len());
1281         if input.len() < 512 {
1282             // check for malformed data. Wireshark reports as
1283             // 'NBSS continuation data'. If it's invalid we're
1284             // lost so we give up.
1285             if input.len() > 8 {
1286                 match parse_nbss_record_partial(input) {
1287                     Ok((_, ref hdr)) => {
1288                         if !hdr.is_smb() {
1289                             SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1290                             self.trunc_ts();
1291                             return 0;
1292                         }
1293                     },
1294                     _ => {},
1295                 }
1296             }
1297             return 0;
1298         }
1299 
1300         match parse_nbss_record_partial(input) {
1301             Ok((output, ref nbss_part_hdr)) => {
1302                 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1303                 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1304                     match parse_smb_version(&nbss_part_hdr.data) {
1305                         Ok((_, ref smb)) => {
1306                             SCLogDebug!("SMB {:?}", smb);
1307                             if smb.version == 0xff_u8 { // SMB1
1308                                 SCLogDebug!("SMBv1 record");
1309                                 match parse_smb_record(&nbss_part_hdr.data) {
1310                                     Ok((_, ref r)) => {
1311                                         if r.command == SMB1_COMMAND_WRITE_ANDX {
1312                                             // see if it's a write to a pipe. We only handle those
1313                                             // if complete.
1314                                             let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1315                                                     r.ssn_id as u64, r.tree_id as u32, 0);
1316                                             let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1317                                                 Some(n) => n.is_pipe,
1318                                                 None => false,
1319                                             };
1320                                             if is_pipe {
1321                                                 return 0;
1322                                             }
1323                                             smb1_write_request_record(self, r);
1324                                             let consumed = input.len() - output.len();
1325                                             return consumed;
1326                                         }
1327                                     },
1328                                     _ => { },
1329 
1330                                 }
1331                             } else if smb.version == 0xfe_u8 { // SMB2
1332                                 SCLogDebug!("NBSS record {:?}", nbss_part_hdr);
1333                                 SCLogDebug!("SMBv2 record");
1334                                 match parse_smb2_request_record(&nbss_part_hdr.data) {
1335                                     Ok((_, ref smb_record)) => {
1336                                         SCLogDebug!("SMB2: partial record {}",
1337                                                 &smb2_command_string(smb_record.command));
1338                                         if smb_record.command == SMB2_COMMAND_WRITE {
1339                                             smb2_write_request_record(self, smb_record);
1340                                             let consumed = input.len() - output.len();
1341                                             SCLogDebug!("consumed {}", consumed);
1342                                             return consumed;
1343                                         }
1344                                     },
1345                                     _ => { },
1346                                 }
1347                             }
1348                             // no SMB3 here yet, will buffer full records
1349                         },
1350                         _ => { },
1351                     }
1352                 }
1353             },
1354             _ => { },
1355         }
1356 
1357         return 0;
1358     }
1359 
1360     /// Parsing function, handling TCP chunks fragmentation
parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> AppLayerResult1361     pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> AppLayerResult
1362     {
1363         let mut cur_i = i;
1364         let consumed = self.handle_skip(STREAM_TOSERVER, cur_i.len() as u32);
1365         if consumed > 0 {
1366             if consumed > cur_i.len() as u32 {
1367                 self.set_event(SMBEvent::InternalError);
1368                 return AppLayerResult::err();
1369             }
1370             cur_i = &cur_i[consumed as usize..];
1371         }
1372         // take care of in progress file chunk transfers
1373         // and skip buffer beyond it
1374         let consumed = self.filetracker_update(STREAM_TOSERVER, cur_i, 0);
1375         if consumed > 0 {
1376             if consumed > cur_i.len() as u32 {
1377                 self.set_event(SMBEvent::InternalError);
1378                 return AppLayerResult::err();
1379             }
1380             cur_i = &cur_i[consumed as usize..];
1381         }
1382         if cur_i.len() == 0 {
1383             return AppLayerResult::ok();
1384         }
1385         // gap
1386         if self.ts_gap {
1387             SCLogDebug!("TS trying to catch up after GAP (input {})", cur_i.len());
1388             while cur_i.len() > 0 { // min record size
1389                 match search_smb_record(cur_i) {
1390                     Ok((_, pg)) => {
1391                         SCLogDebug!("smb record found");
1392                         let smb2_offset = cur_i.len() - pg.len();
1393                         if smb2_offset < 4 {
1394                             cur_i = &cur_i[smb2_offset+4..];
1395                             continue; // see if we have another record in our data
1396                         }
1397                         let nbss_offset = smb2_offset - 4;
1398                         cur_i = &cur_i[nbss_offset..];
1399 
1400                         self.ts_gap = false;
1401                         break;
1402                     },
1403                     _ => {
1404                         let mut consumed = i.len();
1405                         if consumed < 4 {
1406                             consumed = 0;
1407                         } else {
1408                             consumed = consumed - 3;
1409                         }
1410                         SCLogDebug!("smb record NOT found");
1411                         return AppLayerResult::incomplete(consumed as u32, 8);
1412                     },
1413                 }
1414             }
1415         }
1416         while cur_i.len() > 0 { // min record size
1417             match parse_nbss_record(cur_i) {
1418                 Ok((rem, ref nbss_hdr)) => {
1419                     if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1420                         // we have the full records size worth of data,
1421                         // let's parse it
1422                         match parse_smb_version(&nbss_hdr.data) {
1423                             Ok((_, ref smb)) => {
1424                                 SCLogDebug!("SMB {:?}", smb);
1425                                 if smb.version == 0xff_u8 { // SMB1
1426                                     SCLogDebug!("SMBv1 record");
1427                                     match parse_smb_record(&nbss_hdr.data) {
1428                                         Ok((_, ref smb_record)) => {
1429                                             smb1_request_record(self, smb_record);
1430                                         },
1431                                         _ => {
1432                                             self.set_event(SMBEvent::MalformedData);
1433                                             return AppLayerResult::err();
1434                                         },
1435                                     }
1436                                 } else if smb.version == 0xfe_u8 { // SMB2
1437                                     let mut nbss_data = nbss_hdr.data;
1438                                     while nbss_data.len() > 0 {
1439                                         SCLogDebug!("SMBv2 record");
1440                                         match parse_smb2_request_record(&nbss_data) {
1441                                             Ok((nbss_data_rem, ref smb_record)) => {
1442                                                 SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len());
1443 
1444                                                 smb2_request_record(self, smb_record);
1445                                                 nbss_data = nbss_data_rem;
1446                                             },
1447                                             _ => {
1448                                                 self.set_event(SMBEvent::MalformedData);
1449                                                 return AppLayerResult::err();
1450                                             },
1451                                         }
1452                                     }
1453                                 } else if smb.version == 0xfd_u8 { // SMB3 transform
1454                                     let mut nbss_data = nbss_hdr.data;
1455                                     while nbss_data.len() > 0 {
1456                                         SCLogDebug!("SMBv3 transform record");
1457                                         match parse_smb3_transform_record(&nbss_data) {
1458                                             Ok((nbss_data_rem, ref _smb3_record)) => {
1459                                                 nbss_data = nbss_data_rem;
1460                                             },
1461                                             _ => {
1462                                                 self.set_event(SMBEvent::MalformedData);
1463                                                 return AppLayerResult::err();
1464                                             },
1465                                         }
1466                                     }
1467                                 }
1468                             },
1469                             _ => {
1470                                 self.set_event(SMBEvent::MalformedData);
1471                                 return AppLayerResult::err();
1472                             },
1473                         }
1474                     } else {
1475                         SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1476                     }
1477                     cur_i = rem;
1478                 },
1479                 Err(nom::Err::Incomplete(needed)) => {
1480                     if let nom::Needed::Size(n) = needed {
1481                         // 512 is the minimum for parse_tcp_data_ts_partial
1482                         if n >= 512 && cur_i.len() < 512 {
1483                             let total_consumed = i.len() - cur_i.len();
1484                             return AppLayerResult::incomplete(total_consumed as u32, 512);
1485                         }
1486                         let consumed = self.parse_tcp_data_ts_partial(cur_i);
1487                         if consumed == 0 {
1488                             // if we consumed none we will buffer the entire record
1489                             let total_consumed = i.len() - cur_i.len();
1490                             SCLogDebug!("setting consumed {} need {} needed {:?} total input {}",
1491                                     total_consumed, n, needed, i.len());
1492                             let need = n + 4; // Incomplete returns size of data minus NBSS header
1493                             return AppLayerResult::incomplete(total_consumed as u32, need as u32);
1494                         }
1495                         // tracking a write record, which we don't need to
1496                         // queue up at the stream level, but can feed to us
1497                         // in small chunks
1498                         return AppLayerResult::ok();
1499                     } else {
1500                         self.set_event(SMBEvent::InternalError);
1501                         return AppLayerResult::err();
1502                     }
1503                 },
1504                 Err(_) => {
1505                     self.set_event(SMBEvent::MalformedData);
1506                     return AppLayerResult::err();
1507                 },
1508             }
1509         };
1510 
1511         self.post_gap_housekeeping(STREAM_TOSERVER);
1512         if self.check_post_gap_file_txs && !self.post_gap_files_checked {
1513             self.post_gap_housekeeping_for_files();
1514             self.post_gap_files_checked = true;
1515         }
1516         AppLayerResult::ok()
1517     }
1518 
1519     /// return bytes consumed
parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize1520     pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize
1521     {
1522         SCLogDebug!("incomplete of size {}", input.len());
1523         if input.len() < 512 {
1524             // check for malformed data. Wireshark reports as
1525             // 'NBSS continuation data'. If it's invalid we're
1526             // lost so we give up.
1527             if input.len() > 8 {
1528                 match parse_nbss_record_partial(input) {
1529                     Ok((_, ref hdr)) => {
1530                         if !hdr.is_smb() {
1531                             SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1532                             self.trunc_tc();
1533                             return 0;
1534                         }
1535                     },
1536                     _ => {},
1537                 }
1538             }
1539             return 0;
1540         }
1541 
1542         match parse_nbss_record_partial(input) {
1543             Ok((output, ref nbss_part_hdr)) => {
1544                 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1545                 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1546                     match parse_smb_version(&nbss_part_hdr.data) {
1547                         Ok((_, ref smb)) => {
1548                             SCLogDebug!("SMB {:?}", smb);
1549                             if smb.version == 255u8 { // SMB1
1550                                 SCLogDebug!("SMBv1 record");
1551                                 match parse_smb_record(&nbss_part_hdr.data) {
1552                                     Ok((_, ref r)) => {
1553                                         SCLogDebug!("SMB1: partial record {}",
1554                                                 r.command);
1555                                         if r.command == SMB1_COMMAND_READ_ANDX {
1556                                             let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1557                                                     r.ssn_id as u64, r.tree_id as u32, 0);
1558                                             let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1559                                                 Some(n) => n.is_pipe,
1560                                                 None => false,
1561                                             };
1562                                             if is_pipe {
1563                                                 return 0;
1564                                             }
1565                                             smb1_read_response_record(self, r);
1566                                             let consumed = input.len() - output.len();
1567                                             return consumed;
1568                                         }
1569                                     },
1570                                     _ => { },
1571                                 }
1572                             } else if smb.version == 254u8 { // SMB2
1573                                 SCLogDebug!("SMBv2 record");
1574                                 match parse_smb2_response_record(&nbss_part_hdr.data) {
1575                                     Ok((_, ref smb_record)) => {
1576                                         SCLogDebug!("SMB2: partial record {}",
1577                                                 &smb2_command_string(smb_record.command));
1578                                         if smb_record.command == SMB2_COMMAND_READ {
1579                                             smb2_read_response_record(self, smb_record);
1580                                             let consumed = input.len() - output.len();
1581                                             return consumed;
1582                                         }
1583                                     },
1584                                     _ => { },
1585                                 }
1586                             }
1587                             // no SMB3 here yet, will buffer full records
1588                         },
1589                         _ => { },
1590                     }
1591                 }
1592             },
1593             _ => { },
1594         }
1595 
1596         return 0;
1597     }
1598 
1599     /// Parsing function, handling TCP chunks fragmentation
parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> AppLayerResult1600     pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> AppLayerResult
1601     {
1602         let mut cur_i = i;
1603         let consumed = self.handle_skip(STREAM_TOCLIENT, cur_i.len() as u32);
1604         if consumed > 0 {
1605             if consumed > cur_i.len() as u32 {
1606                 self.set_event(SMBEvent::InternalError);
1607                 return AppLayerResult::err();
1608             }
1609             cur_i = &cur_i[consumed as usize..];
1610         }
1611         // take care of in progress file chunk transfers
1612         // and skip buffer beyond it
1613         let consumed = self.filetracker_update(STREAM_TOCLIENT, cur_i, 0);
1614         if consumed > 0 {
1615             if consumed > cur_i.len() as u32 {
1616                 self.set_event(SMBEvent::InternalError);
1617                 return AppLayerResult::err();
1618             }
1619             cur_i = &cur_i[consumed as usize..];
1620         }
1621         if cur_i.len() == 0 {
1622             return AppLayerResult::ok();
1623         }
1624         // gap
1625         if self.tc_gap {
1626             SCLogDebug!("TC trying to catch up after GAP (input {})", cur_i.len());
1627             while cur_i.len() > 0 { // min record size
1628                 match search_smb_record(cur_i) {
1629                     Ok((_, pg)) => {
1630                         SCLogDebug!("smb record found");
1631                         let smb2_offset = cur_i.len() - pg.len();
1632                         if smb2_offset < 4 {
1633                             cur_i = &cur_i[smb2_offset+4..];
1634                             continue; // see if we have another record in our data
1635                         }
1636                         let nbss_offset = smb2_offset - 4;
1637                         cur_i = &cur_i[nbss_offset..];
1638 
1639                         self.tc_gap = false;
1640                         break;
1641                     },
1642                     _ => {
1643                         let mut consumed = i.len();
1644                         if consumed < 4 {
1645                             consumed = 0;
1646                         } else {
1647                             consumed = consumed - 3;
1648                         }
1649                         SCLogDebug!("smb record NOT found");
1650                         return AppLayerResult::incomplete(consumed as u32, 8);
1651                     },
1652                 }
1653             }
1654         }
1655         while cur_i.len() > 0 { // min record size
1656             match parse_nbss_record(cur_i) {
1657                 Ok((rem, ref nbss_hdr)) => {
1658                     if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1659                         // we have the full records size worth of data,
1660                         // let's parse it
1661                         match parse_smb_version(&nbss_hdr.data) {
1662                             Ok((_, ref smb)) => {
1663                                 SCLogDebug!("SMB {:?}", smb);
1664                                 if smb.version == 0xff_u8 { // SMB1
1665                                     SCLogDebug!("SMBv1 record");
1666                                     match parse_smb_record(&nbss_hdr.data) {
1667                                         Ok((_, ref smb_record)) => {
1668                                             smb1_response_record(self, smb_record);
1669                                         },
1670                                         _ => {
1671                                             self.set_event(SMBEvent::MalformedData);
1672                                             return AppLayerResult::err();
1673                                         },
1674                                     }
1675                                 } else if smb.version == 0xfe_u8 { // SMB2
1676                                     let mut nbss_data = nbss_hdr.data;
1677                                     while nbss_data.len() > 0 {
1678                                         SCLogDebug!("SMBv2 record");
1679                                         match parse_smb2_response_record(&nbss_data) {
1680                                             Ok((nbss_data_rem, ref smb_record)) => {
1681                                                 smb2_response_record(self, smb_record);
1682                                                 nbss_data = nbss_data_rem;
1683                                             },
1684                                             _ => {
1685                                                 self.set_event(SMBEvent::MalformedData);
1686                                                 return AppLayerResult::err();
1687                                             },
1688                                         }
1689                                     }
1690                                 } else if smb.version == 0xfd_u8 { // SMB3 transform
1691                                     let mut nbss_data = nbss_hdr.data;
1692                                     while nbss_data.len() > 0 {
1693                                         SCLogDebug!("SMBv3 transform record");
1694                                         match parse_smb3_transform_record(&nbss_data) {
1695                                             Ok((nbss_data_rem, ref _smb3_record)) => {
1696                                                 nbss_data = nbss_data_rem;
1697                                             },
1698                                             _ => {
1699                                                 self.set_event(SMBEvent::MalformedData);
1700                                                 return AppLayerResult::err();
1701                                             },
1702                                         }
1703                                     }
1704                                 }
1705                             },
1706                             Err(nom::Err::Incomplete(_)) => {
1707                                 // not enough data to contain basic SMB hdr
1708                                 // TODO event: empty NBSS_MSGTYPE_SESSION_MESSAGE
1709                             },
1710                             Err(_) => {
1711                                 self.set_event(SMBEvent::MalformedData);
1712                                 return AppLayerResult::err();
1713                             },
1714                         }
1715                     } else {
1716                         SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1717                     }
1718                     cur_i = rem;
1719                 },
1720                 Err(nom::Err::Incomplete(needed)) => {
1721                     SCLogDebug!("INCOMPLETE have {} needed {:?}", cur_i.len(), needed);
1722                     if let nom::Needed::Size(n) = needed {
1723                         // 512 is the minimum for parse_tcp_data_tc_partial
1724                         if n >= 512 && cur_i.len() < 512 {
1725                             let total_consumed = i.len() - cur_i.len();
1726                             return AppLayerResult::incomplete(total_consumed as u32, 512);
1727                         }
1728                         let consumed = self.parse_tcp_data_tc_partial(cur_i);
1729                         if consumed == 0 {
1730                             // if we consumed none we will buffer the entire record
1731                             let total_consumed = i.len() - cur_i.len();
1732                             SCLogDebug!("setting consumed {} need {} needed {:?} total input {}",
1733                                     total_consumed, n, needed, i.len());
1734                             let need = n + 4; // Incomplete returns size of data minus NBSS header
1735                             return AppLayerResult::incomplete(total_consumed as u32, need as u32);
1736                         }
1737                         // tracking a read record, which we don't need to
1738                         // queue up at the stream level, but can feed to us
1739                         // in small chunks
1740                         return AppLayerResult::ok();
1741                     } else {
1742                         self.set_event(SMBEvent::InternalError);
1743                         return AppLayerResult::err();
1744                     }
1745                 },
1746                 Err(_) => {
1747                     self.set_event(SMBEvent::MalformedData);
1748                     return AppLayerResult::err();
1749                 },
1750             }
1751         };
1752         self.post_gap_housekeeping(STREAM_TOCLIENT);
1753         if self.check_post_gap_file_txs && !self.post_gap_files_checked {
1754             self.post_gap_housekeeping_for_files();
1755             self.post_gap_files_checked = true;
1756         }
1757         self._debug_tx_stats();
1758         AppLayerResult::ok()
1759     }
1760 
1761     /// handle a gap in the TOSERVER direction
1762     /// returns: 0 ok, 1 unrecoverable error
parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult1763     pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult {
1764         let consumed = self.handle_skip(STREAM_TOSERVER, gap_size);
1765         if consumed < gap_size {
1766             let new_gap_size = gap_size - consumed;
1767             let gap = vec![0; new_gap_size as usize];
1768 
1769             let consumed2 = self.filetracker_update(STREAM_TOSERVER, &gap, new_gap_size);
1770             if consumed2 > new_gap_size {
1771                 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1772                 self.set_event(SMBEvent::InternalError);
1773                 return AppLayerResult::err();
1774             }
1775         }
1776         SCLogDebug!("GAP of size {} in toserver direction", gap_size);
1777         self.ts_ssn_gap = true;
1778         self.ts_gap = true;
1779         return AppLayerResult::ok();
1780     }
1781 
1782     /// handle a gap in the TOCLIENT direction
1783     /// returns: 0 ok, 1 unrecoverable error
parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult1784     pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult {
1785         let consumed = self.handle_skip(STREAM_TOCLIENT, gap_size);
1786         if consumed < gap_size {
1787             let new_gap_size = gap_size - consumed;
1788             let gap = vec![0; new_gap_size as usize];
1789 
1790             let consumed2 = self.filetracker_update(STREAM_TOCLIENT, &gap, new_gap_size);
1791             if consumed2 > new_gap_size {
1792                 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1793                 self.set_event(SMBEvent::InternalError);
1794                 return AppLayerResult::err();
1795             }
1796         }
1797         SCLogDebug!("GAP of size {} in toclient direction", gap_size);
1798         self.tc_ssn_gap = true;
1799         self.tc_gap = true;
1800         return AppLayerResult::ok();
1801     }
1802 
trunc_ts(&mut self)1803     pub fn trunc_ts(&mut self) {
1804         SCLogDebug!("TRUNC TS");
1805         self.ts_trunc = true;
1806 
1807         for tx in &mut self.transactions {
1808             if !tx.request_done {
1809                 SCLogDebug!("TRUNCING TX {} in TOSERVER direction", tx.id);
1810                 tx.request_done = true;
1811             }
1812        }
1813     }
trunc_tc(&mut self)1814     pub fn trunc_tc(&mut self) {
1815         SCLogDebug!("TRUNC TC");
1816         self.tc_trunc = true;
1817 
1818         for tx in &mut self.transactions {
1819             if !tx.response_done {
1820                 SCLogDebug!("TRUNCING TX {} in TOCLIENT direction", tx.id);
1821                 tx.response_done = true;
1822             }
1823         }
1824     }
1825 }
1826 
1827 /// Returns *mut SMBState
1828 #[no_mangle]
rs_smb_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void1829 pub extern "C" fn rs_smb_state_new(_orig_state: *mut std::os::raw::c_void, _orig_proto: AppProto) -> *mut std::os::raw::c_void {
1830     let state = SMBState::new();
1831     let boxed = Box::new(state);
1832     SCLogDebug!("allocating state");
1833     return unsafe{transmute(boxed)};
1834 }
1835 
1836 /// Params:
1837 /// - state: *mut SMBState as void pointer
1838 #[no_mangle]
rs_smb_state_free(state: *mut std::os::raw::c_void)1839 pub extern "C" fn rs_smb_state_free(state: *mut std::os::raw::c_void) {
1840     // Just unbox...
1841     SCLogDebug!("freeing state");
1842     let mut smb_state: Box<SMBState> = unsafe{transmute(state)};
1843     smb_state.free();
1844 }
1845 
1846 /// C binding parse a SMB request. Returns 1 on success, -1 on failure.
1847 #[no_mangle]
rs_smb_parse_request_tcp(flow: &mut Flow, state: &mut SMBState, _pstate: *mut std::os::raw::c_void, input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8) -> AppLayerResult1848 pub extern "C" fn rs_smb_parse_request_tcp(flow: &mut Flow,
1849                                        state: &mut SMBState,
1850                                        _pstate: *mut std::os::raw::c_void,
1851                                        input: *const u8,
1852                                        input_len: u32,
1853                                        _data: *mut std::os::raw::c_void,
1854                                        flags: u8)
1855                                        -> AppLayerResult
1856 {
1857     let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1858     SCLogDebug!("parsing {} bytes of request data", input_len);
1859 
1860     /* START with MISTREAM set: record might be starting the middle. */
1861     if flags & (STREAM_START|STREAM_MIDSTREAM) == (STREAM_START|STREAM_MIDSTREAM) {
1862         state.ts_gap = true;
1863     }
1864 
1865     state.update_ts(flow.get_last_time().as_secs());
1866     state.parse_tcp_data_ts(buf)
1867 }
1868 
1869 #[no_mangle]
rs_smb_parse_request_tcp_gap( state: &mut SMBState, input_len: u32) -> AppLayerResult1870 pub extern "C" fn rs_smb_parse_request_tcp_gap(
1871                                         state: &mut SMBState,
1872                                         input_len: u32)
1873                                         -> AppLayerResult
1874 {
1875     state.parse_tcp_data_ts_gap(input_len as u32)
1876 }
1877 
1878 
1879 #[no_mangle]
rs_smb_parse_response_tcp(flow: &mut Flow, state: &mut SMBState, _pstate: *mut std::os::raw::c_void, input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, flags: u8) -> AppLayerResult1880 pub extern "C" fn rs_smb_parse_response_tcp(flow: &mut Flow,
1881                                         state: &mut SMBState,
1882                                         _pstate: *mut std::os::raw::c_void,
1883                                         input: *const u8,
1884                                         input_len: u32,
1885                                         _data: *mut std::os::raw::c_void,
1886                                         flags: u8)
1887                                         -> AppLayerResult
1888 {
1889     SCLogDebug!("parsing {} bytes of response data", input_len);
1890     let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1891 
1892     /* START with MISTREAM set: record might be starting the middle. */
1893     if flags & (STREAM_START|STREAM_MIDSTREAM) == (STREAM_START|STREAM_MIDSTREAM) {
1894         state.tc_gap = true;
1895     }
1896 
1897     state.update_ts(flow.get_last_time().as_secs());
1898     state.parse_tcp_data_tc(buf)
1899 }
1900 
1901 #[no_mangle]
rs_smb_parse_response_tcp_gap( state: &mut SMBState, input_len: u32) -> AppLayerResult1902 pub extern "C" fn rs_smb_parse_response_tcp_gap(
1903                                         state: &mut SMBState,
1904                                         input_len: u32)
1905                                         -> AppLayerResult
1906 {
1907     state.parse_tcp_data_tc_gap(input_len as u32)
1908 }
1909 
rs_smb_probe_tcp_midstream(direction: u8, slice: &[u8], rdir: *mut u8) -> i81910 fn rs_smb_probe_tcp_midstream(direction: u8, slice: &[u8], rdir: *mut u8) -> i8
1911 {
1912     match search_smb_record(slice) {
1913         Ok((_, ref data)) => {
1914             SCLogDebug!("smb found");
1915             match parse_smb_version(data) {
1916                 Ok((_, ref smb)) => {
1917                     SCLogDebug!("SMB {:?}", smb);
1918                     if smb.version == 0xff_u8 { // SMB1
1919                         SCLogDebug!("SMBv1 record");
1920                         match parse_smb_record(data) {
1921                             Ok((_, ref smb_record)) => {
1922                                 if smb_record.flags & 0x80 != 0 {
1923                                     SCLogDebug!("RESPONSE {:02x}", smb_record.flags);
1924                                     if direction & STREAM_TOSERVER != 0 {
1925                                         unsafe { *rdir = STREAM_TOCLIENT; }
1926                                     }
1927                                 } else {
1928                                     SCLogDebug!("REQUEST {:02x}", smb_record.flags);
1929                                     if direction & STREAM_TOCLIENT != 0 {
1930                                         unsafe { *rdir = STREAM_TOSERVER; }
1931                                     }
1932                                 }
1933                                 return 1;
1934                             },
1935                             _ => { },
1936                         }
1937                     } else if smb.version == 0xfe_u8 { // SMB2
1938                         SCLogDebug!("SMB2 record");
1939                         match parse_smb2_record_direction(data) {
1940                             Ok((_, ref smb_record)) => {
1941                                 if direction & STREAM_TOSERVER != 0 {
1942                                     SCLogDebug!("direction STREAM_TOSERVER smb_record {:?}", smb_record);
1943                                     if !smb_record.request {
1944                                         unsafe { *rdir = STREAM_TOCLIENT; }
1945                                     }
1946                                 } else {
1947                                     SCLogDebug!("direction STREAM_TOCLIENT smb_record {:?}", smb_record);
1948                                     if smb_record.request {
1949                                         unsafe { *rdir = STREAM_TOSERVER; }
1950                                     }
1951                                 }
1952                             },
1953                             _ => {},
1954                         }
1955                     }
1956                     else if smb.version == 0xfd_u8 { // SMB3 transform
1957                         SCLogDebug!("SMB3 record");
1958                     }
1959                     return 1;
1960                 },
1961                     _ => {
1962                         SCLogDebug!("smb not found in {:?}", slice);
1963                     },
1964             }
1965         },
1966         _ => {
1967             SCLogDebug!("no dice");
1968         },
1969     }
1970     return 0;
1971 }
1972 
1973 // probing parser
1974 // return 1 if found, 0 is not found
1975 #[no_mangle]
rs_smb_probe_tcp(flags: u8, input: *const u8, len: u32, rdir: *mut u8) -> i81976 pub extern "C" fn rs_smb_probe_tcp(flags: u8,
1977         input: *const u8, len: u32,
1978         rdir: *mut u8)
1979     -> i8
1980 {
1981     let slice = build_slice!(input, len as usize);
1982     if flags & STREAM_MIDSTREAM == STREAM_MIDSTREAM {
1983         if rs_smb_probe_tcp_midstream(flags, slice, rdir) == 1 {
1984             return 1;
1985         }
1986     }
1987     match parse_nbss_record_partial(slice) {
1988         Ok((_, ref hdr)) => {
1989             if hdr.is_smb() {
1990                 SCLogDebug!("smb found");
1991                 return 1;
1992             } else if hdr.needs_more(){
1993                 return 0;
1994             } else if hdr.is_valid() &&
1995                 hdr.message_type != NBSS_MSGTYPE_SESSION_MESSAGE {
1996                 //we accept a first small netbios message before real SMB
1997                 let hl = hdr.length as usize;
1998                 if hdr.data.len() >= hl + 8 {
1999                     // 8 is 4 bytes NBSS + 4 bytes SMB0xFX magic
2000                     match parse_nbss_record_partial(&hdr.data[hl..]) {
2001                         Ok((_, ref hdr2)) => {
2002                             if hdr2.is_smb() {
2003                                 SCLogDebug!("smb found");
2004                                 return 1;
2005                             }
2006                         }
2007                         _ => {}
2008                     }
2009                 } else if hdr.length < 256 {
2010                     // we want more data, 256 is some random value
2011                     return 0;
2012                 }
2013                 // default is failure
2014             }
2015         },
2016         _ => { },
2017     }
2018     SCLogDebug!("no smb");
2019     return -1
2020 }
2021 
2022 #[no_mangle]
rs_smb_state_get_tx_count(state: &mut SMBState) -> u642023 pub extern "C" fn rs_smb_state_get_tx_count(state: &mut SMBState)
2024                                             -> u64
2025 {
2026     SCLogDebug!("rs_smb_state_get_tx_count: returning {}", state.tx_id);
2027     return state.tx_id;
2028 }
2029 
2030 #[no_mangle]
rs_smb_state_get_tx(state: &mut SMBState, tx_id: u64) -> *mut SMBTransaction2031 pub extern "C" fn rs_smb_state_get_tx(state: &mut SMBState,
2032                                       tx_id: u64)
2033                                       -> *mut SMBTransaction
2034 {
2035     match state.get_tx_by_id(tx_id) {
2036         Some(tx) => {
2037             return unsafe{transmute(tx)};
2038         }
2039         None => {
2040             return std::ptr::null_mut();
2041         }
2042     }
2043 }
2044 
2045 // for use with the C API call StateGetTxIterator
2046 #[no_mangle]
rs_smb_state_get_tx_iterator( state: &mut SMBState, min_tx_id: u64, istate: &mut u64) -> applayer::AppLayerGetTxIterTuple2047 pub extern "C" fn rs_smb_state_get_tx_iterator(
2048                                       state: &mut SMBState,
2049                                       min_tx_id: u64,
2050                                       istate: &mut u64)
2051                                       -> applayer::AppLayerGetTxIterTuple
2052 {
2053     match state.get_tx_iterator(min_tx_id, istate) {
2054         Some((tx, out_tx_id, has_next)) => {
2055             let c_tx = unsafe { transmute(tx) };
2056             let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
2057             return ires;
2058         }
2059         None => {
2060             return applayer::AppLayerGetTxIterTuple::not_found();
2061         }
2062     }
2063 }
2064 
2065 #[no_mangle]
rs_smb_state_tx_free(state: &mut SMBState, tx_id: u64)2066 pub extern "C" fn rs_smb_state_tx_free(state: &mut SMBState,
2067                                        tx_id: u64)
2068 {
2069     SCLogDebug!("freeing tx {}", tx_id as u64);
2070     state.free_tx(tx_id);
2071 }
2072 
2073 #[no_mangle]
rs_smb_state_progress_completion_status( _direction: u8) -> std::os::raw::c_int2074 pub extern "C" fn rs_smb_state_progress_completion_status(
2075     _direction: u8)
2076     -> std::os::raw::c_int
2077 {
2078     return 1;
2079 }
2080 
2081 #[no_mangle]
rs_smb_tx_get_alstate_progress(tx: &mut SMBTransaction, direction: u8) -> u82082 pub extern "C" fn rs_smb_tx_get_alstate_progress(tx: &mut SMBTransaction,
2083                                                   direction: u8)
2084                                                   -> u8
2085 {
2086     if direction == STREAM_TOSERVER && tx.request_done {
2087         SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.id, tx);
2088         return 1;
2089     } else if direction == STREAM_TOCLIENT && tx.response_done {
2090         SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.id, tx);
2091         return 1;
2092     } else {
2093         SCLogDebug!("tx {} direction {} progress 0", tx.id, direction);
2094         return 0;
2095     }
2096 }
2097 
2098 #[no_mangle]
rs_smb_get_tx_data( tx: *mut std::os::raw::c_void) -> *mut AppLayerTxData2099 pub extern "C" fn rs_smb_get_tx_data(
2100     tx: *mut std::os::raw::c_void)
2101     -> *mut AppLayerTxData
2102 {
2103     let tx = cast_pointer!(tx, SMBTransaction);
2104     return &mut tx.tx_data;
2105 }
2106 
2107 #[no_mangle]
rs_smb_state_set_tx_detect_state( tx: &mut SMBTransaction, de_state: &mut DetectEngineState)2108 pub extern "C" fn rs_smb_state_set_tx_detect_state(
2109     tx: &mut SMBTransaction,
2110     de_state: &mut DetectEngineState)
2111 {
2112     tx.de_state = Some(de_state);
2113 }
2114 
2115 #[no_mangle]
rs_smb_state_get_tx_detect_state( tx: &mut SMBTransaction) -> *mut DetectEngineState2116 pub extern "C" fn rs_smb_state_get_tx_detect_state(
2117     tx: &mut SMBTransaction)
2118     -> *mut DetectEngineState
2119 {
2120     match tx.de_state {
2121         Some(ds) => {
2122             return ds;
2123         },
2124         None => {
2125             return std::ptr::null_mut();
2126         }
2127     }
2128 }
2129 
2130 #[no_mangle]
rs_smb_state_truncate( state: &mut SMBState, direction: u8)2131 pub extern "C" fn rs_smb_state_truncate(
2132         state: &mut SMBState,
2133         direction: u8)
2134 {
2135     if (direction & STREAM_TOSERVER) != 0 {
2136         state.trunc_ts();
2137     } else {
2138         state.trunc_tc();
2139     }
2140 }
2141 
2142 #[no_mangle]
rs_smb_state_get_events(tx: *mut std::os::raw::c_void) -> *mut AppLayerDecoderEvents2143 pub extern "C" fn rs_smb_state_get_events(tx: *mut std::os::raw::c_void)
2144                                           -> *mut AppLayerDecoderEvents
2145 {
2146     let tx = cast_pointer!(tx, SMBTransaction);
2147     return tx.events;
2148 }
2149 
2150 #[no_mangle]
rs_smb_state_get_event_info_by_id(event_id: std::os::raw::c_int, event_name: *mut *const std::os::raw::c_char, event_type: *mut AppLayerEventType) -> i82151 pub extern "C" fn rs_smb_state_get_event_info_by_id(event_id: std::os::raw::c_int,
2152                                               event_name: *mut *const std::os::raw::c_char,
2153                                               event_type: *mut AppLayerEventType)
2154                                               -> i8
2155 {
2156     if let Some(e) = SMBEvent::from_i32(event_id as i32) {
2157         let estr = match e {
2158             SMBEvent::InternalError => { "internal_error\0" },
2159             SMBEvent::MalformedData => { "malformed_data\0" },
2160             SMBEvent::RecordOverflow => { "record_overflow\0" },
2161             SMBEvent::MalformedNtlmsspRequest => { "malformed_ntlmssp_request\0" },
2162             SMBEvent::MalformedNtlmsspResponse => { "malformed_ntlmssp_response\0" },
2163             SMBEvent::DuplicateNegotiate => { "duplicate_negotiate\0" },
2164             SMBEvent::NegotiateMalformedDialects => { "netogiate_malformed_dialects\0" },
2165             SMBEvent::FileOverlap => { "file_overlap\0" },
2166         };
2167         unsafe{
2168             *event_name = estr.as_ptr() as *const std::os::raw::c_char;
2169             *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
2170         };
2171         0
2172     } else {
2173         -1
2174     }
2175 }
2176 
2177 #[no_mangle]
rs_smb_state_get_event_info(event_name: *const std::os::raw::c_char, event_id: *mut std::os::raw::c_int, event_type: *mut AppLayerEventType) -> i82178 pub extern "C" fn rs_smb_state_get_event_info(event_name: *const std::os::raw::c_char,
2179                                               event_id: *mut std::os::raw::c_int,
2180                                               event_type: *mut AppLayerEventType)
2181                                               -> i8
2182 {
2183     if event_name == std::ptr::null() {
2184         return -1;
2185     }
2186     let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
2187     let event = match c_event_name.to_str() {
2188         Ok(s) => {
2189             smb_str_to_event(s)
2190         },
2191         Err(_) => -1, // UTF-8 conversion failed
2192     };
2193     unsafe {
2194         *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
2195         *event_id = event as std::os::raw::c_int;
2196     };
2197     if event == -1 {
2198         return -1;
2199     }
2200     0
2201 }
2202