]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb.rs
smb1: implement WRITE_AND_CLOSE
[people/ms/suricata.git] / rust / src / smb / smb.rs
CommitLineData
75d7c9d6
VJ
1/* Copyright (C) 2017 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
27extern crate libc;
28use std;
29use std::mem::transmute;
30use std::str;
31use std::ffi::CStr;
32
33use nom::IResult;
34use std::collections::HashMap;
35
36use core::*;
37use log::*;
38use applayer;
39use applayer::LoggerFlags;
40
41use smb::nbss_records::*;
42use smb::smb1_records::*;
43use smb::smb2_records::*;
44
45use smb::smb1::*;
46use smb::smb2::*;
47use smb::dcerpc::*;
8bef1208 48use smb::session::*;
75d7c9d6 49use smb::events::*;
75d7c9d6
VJ
50use smb::files::*;
51
52pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
53
54#[no_mangle]
55pub extern "C" fn rs_smb_init(context: &'static mut SuricataFileContext)
56{
57 unsafe {
58 SURICATA_SMB_FILE_CONFIG = Some(context);
59 }
60}
61
75d7c9d6
VJ
62pub const SMB_NTSTATUS_SUCCESS: u32 = 0;
63pub const SMB_NTSTATUS_PENDING: u32 = 0x00000103;
64pub const SMB_NTSTATUS_BUFFER_OVERFLOW: u32 = 0x80000005;
65pub const SMB_NTSTATUS_NO_MORE_FILES: u32 = 0x80000006;
66pub const SMB_NTSTATUS_NO_MORE_ENTRIES: u32 = 0x8000001a;
67pub const SMB_NTSTATUS_INVALID_HANDLE: u32 = 0xc0000008;
68pub const SMB_NTSTATUS_INVALID_PARAMETER: u32 = 0xc000000d;
69pub const SMB_NTSTATUS_NO_SUCH_DEVICE: u32 = 0xc000000e;
70pub const SMB_NTSTATUS_NO_SUCH_FILE: u32 = 0xc000000f;
71pub const SMB_NTSTATUS_INVALID_DEVICE_REQUEST: u32 = 0xc0000010;
72pub const SMB_NTSTATUS_END_OF_FILE: u32 = 0xc0000011;
73pub const SMB_NTSTATUS_MORE_PROCESSING_REQUIRED: u32 = 0xc0000016;
74pub const SMB_NTSTATUS_ACCESS_DENIED: u32 = 0xc0000022;
75pub const SMB_NTSTATUS_OBJECT_NAME_INVALID: u32 = 0xc0000033;
76pub const SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND: u32 = 0xc0000034;
77pub const SMB_NTSTATUS_OBJECT_NAME_COLLISION: u32 = 0xc0000035;
78pub const SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND: u32 = 0xc000003a;
79pub const SMB_NTSTATUS_SHARING_VIOLATION: u32 = 0xc0000043;
80pub const SMB_NTSTATUS_LOCK_CONFLICT: u32 = 0xc0000054;
81pub const SMB_NTSTATUS_LOCK_NOT_GRANTED: u32 = 0xc0000055;
82pub const SMB_NTSTATUS_PRIVILEGE_NOT_HELD: u32 = 0xc0000061;
83pub const SMB_NTSTATUS_LOGON_FAILURE: u32 = 0xc000006d;
84pub const SMB_NTSTATUS_PIPE_DISCONNECTED: u32 = 0xc00000b0;
85pub const SMB_NTSTATUS_FILE_IS_A_DIRECTORY: u32 = 0xc00000ba;
86pub const SMB_NTSTATUS_NOT_SUPPORTED: u32 = 0xc00000bb;
87pub const SMB_NTSTATUS_BAD_NETWORK_NAME: u32 = 0xc00000cc;
88pub const SMB_NTSTATUS_OPLOCK_NOT_GRANTED: u32 = 0xc00000e2;
89pub const SMB_NTSTATUS_CANCELLED: u32 = 0xc0000120;
90pub const SMB_NTSTATUS_FILE_CLOSED: u32 = 0xc0000128;
91pub const SMB_NTSTATUS_FS_DRIVER_REQUIRED: u32 = 0xc000019c;
92pub const SMB_NTSTATUS_INSUFF_SERVER_RESOURCES: u32 = 0xc0000205;
93pub const SMB_NTSTATUS_NOT_FOUND: u32 = 0xc0000225;
94pub const SMB_NTSTATUS_PIPE_BROKEN: u32 = 0xc000014b;
95pub const SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE: u32 = 0xc000018d;
96pub const SMB_NTSTATUS_NOT_A_REPARSE_POINT: u32 = 0xc0000275;
97pub const SMB_NTSTATUS_NETWORK_SESSION_EXPIRED: u32 = 0xc000035c;
98
99pub fn smb_ntstatus_string(c: u32) -> String {
100 match c {
101 SMB_NTSTATUS_SUCCESS => "STATUS_SUCCESS",
102 SMB_NTSTATUS_BUFFER_OVERFLOW => "STATUS_BUFFER_OVERFLOW",
103 SMB_NTSTATUS_PENDING => "STATUS_PENDING",
104 SMB_NTSTATUS_NO_MORE_FILES => "STATUS_NO_MORE_FILES",
105 SMB_NTSTATUS_NO_MORE_ENTRIES => "STATUS_NO_MORE_ENTRIES",
106 SMB_NTSTATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
107 SMB_NTSTATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
108 SMB_NTSTATUS_NO_SUCH_DEVICE => "STATUS_NO_SUCH_DEVICE",
109 SMB_NTSTATUS_NO_SUCH_FILE => "STATUS_NO_SUCH_FILE",
110 SMB_NTSTATUS_INVALID_DEVICE_REQUEST => "STATUS_INVALID_DEVICE_REQUEST",
111 SMB_NTSTATUS_END_OF_FILE => "STATUS_END_OF_FILE",
112 SMB_NTSTATUS_MORE_PROCESSING_REQUIRED => "STATUS_MORE_PROCESSING_REQUIRED",
113 SMB_NTSTATUS_ACCESS_DENIED => "STATUS_ACCESS_DENIED",
114 SMB_NTSTATUS_OBJECT_NAME_INVALID => "STATUS_OBJECT_NAME_INVALID",
115 SMB_NTSTATUS_OBJECT_NAME_NOT_FOUND => "STATUS_OBJECT_NAME_NOT_FOUND",
116 SMB_NTSTATUS_OBJECT_NAME_COLLISION => "STATUS_OBJECT_NAME_COLLISION",
117 SMB_NTSTATUS_OBJECT_PATH_NOT_FOUND => "STATUS_OBJECT_PATH_NOT_FOUND",
118 SMB_NTSTATUS_SHARING_VIOLATION => "STATUS_SHARING_VIOLATION",
119 SMB_NTSTATUS_LOCK_CONFLICT => "STATUS_LOCK_CONFLICT",
120 SMB_NTSTATUS_LOCK_NOT_GRANTED => "STATUS_LOCK_NOT_GRANTED",
121 SMB_NTSTATUS_PRIVILEGE_NOT_HELD => "STATUS_PRIVILEGE_NOT_HELD",
122 SMB_NTSTATUS_LOGON_FAILURE => "STATUS_LOGON_FAILURE",
123 SMB_NTSTATUS_PIPE_DISCONNECTED => "STATUS_PIPE_DISCONNECTED",
124 SMB_NTSTATUS_FILE_IS_A_DIRECTORY => "STATUS_FILE_IS_A_DIRECTORY",
125 SMB_NTSTATUS_NOT_SUPPORTED => "STATUS_NOT_SUPPORTED",
126 SMB_NTSTATUS_BAD_NETWORK_NAME => "STATUS_BAD_NETWORK_NAME",
127 SMB_NTSTATUS_OPLOCK_NOT_GRANTED => "STATUS_OPLOCK_NOT_GRANTED",
128 SMB_NTSTATUS_CANCELLED => "STATUS_CANCELLED",
129 SMB_NTSTATUS_FILE_CLOSED => "STATUS_FILE_CLOSED",
130 SMB_NTSTATUS_FS_DRIVER_REQUIRED => "STATUS_FS_DRIVER_REQUIRED",
131 SMB_NTSTATUS_INSUFF_SERVER_RESOURCES => "STATUS_INSUFF_SERVER_RESOURCES",
132 SMB_NTSTATUS_NOT_FOUND => "STATUS_NOT_FOUND",
133 SMB_NTSTATUS_PIPE_BROKEN => "STATUS_PIPE_BROKEN",
134 SMB_NTSTATUS_TRUSTED_RELATIONSHIP_FAILURE => "STATUS_TRUSTED_RELATIONSHIP_FAILURE",
135 SMB_NTSTATUS_NOT_A_REPARSE_POINT => "STATUS_NOT_A_REPARSE_POINT",
136 SMB_NTSTATUS_NETWORK_SESSION_EXPIRED => "STATUS_NETWORK_SESSION_EXPIRED",
137 _ => { return (c).to_string(); },
138 }.to_string()
139}
140
141
142pub const SMB_DOS_SUCCESS: u16 = 0;
143pub const SMB_DOS_BAD_FUNC: u16 = 1;
144pub const SMB_DOS_BAD_FILE: u16 = 2;
145pub const SMB_DOS_BAD_PATH: u16 = 3;
146pub const SMB_DOS_TOO_MANY_OPEN_FILES: u16 = 4;
147pub const SMB_DOS_ACCESS_DENIED: u16 = 5;
148
149pub fn smb_dos_error_string(c: u16) -> String {
150 match c {
151 SMB_DOS_SUCCESS => "DOS_SUCCESS",
152 SMB_DOS_BAD_FUNC => "DOS_BAD_FUNC",
153 SMB_DOS_BAD_FILE => "DOS_BAD_FILE",
154 SMB_DOS_BAD_PATH => "DOS_BAD_PATH",
155 SMB_DOS_TOO_MANY_OPEN_FILES => "DOS_TOO_MANY_OPEN_FILES",
156 SMB_DOS_ACCESS_DENIED => "DOS_ACCESS_DENIED",
157 _ => { return (c).to_string(); },
158 }.to_string()
159}
160
161pub const NTLMSSP_NEGOTIATE: u32 = 1;
162pub const NTLMSSP_CHALLENGE: u32 = 2;
163pub const NTLMSSP_AUTH: u32 = 3;
164
165pub fn ntlmssp_type_string(c: u32) -> String {
166 match c {
167 NTLMSSP_NEGOTIATE => "NTLMSSP_NEGOTIATE",
168 NTLMSSP_CHALLENGE => "NTLMSSP_CHALLENGE",
169 NTLMSSP_AUTH => "NTLMSSP_AUTH",
170 _ => { return (c).to_string(); },
171 }.to_string()
172}
173
174#[derive(Eq, PartialEq, Debug, Clone)]
175pub struct SMBVerCmdStat {
176 smb_ver: u8,
177 smb1_cmd: u8,
178 smb2_cmd: u16,
179
180 status_set: bool,
181 status_is_dos_error: bool,
182 status: u32,
183}
184
185impl SMBVerCmdStat {
186 pub fn new() -> SMBVerCmdStat {
187 return SMBVerCmdStat {
188 smb_ver: 0,
189 smb1_cmd: 0,
190 smb2_cmd: 0,
191 status_set: false,
192 status_is_dos_error: false,
193 status: 0,
194 }
195 }
196 pub fn new1(cmd: u8) -> SMBVerCmdStat {
197 return SMBVerCmdStat {
198 smb_ver: 1,
199 smb1_cmd: cmd,
200 smb2_cmd: 0,
201 status_set: false,
202 status_is_dos_error: false,
203 status: 0,
204 }
205 }
206 pub fn new1_with_ntstatus(cmd: u8, status: u32) -> SMBVerCmdStat {
207 return SMBVerCmdStat {
208 smb_ver: 1,
209 smb1_cmd: cmd,
210 smb2_cmd: 0,
211 status_set: true,
212 status_is_dos_error: false,
213 status: status,
214 }
215 }
216 pub fn new2(cmd: u16) -> SMBVerCmdStat {
217 return SMBVerCmdStat {
218 smb_ver: 2,
219 smb1_cmd: 0,
220 smb2_cmd: cmd,
221 status_set: false,
222 status_is_dos_error: false,
223 status: 0,
224 }
225 }
226
227 pub fn new2_with_ntstatus(cmd: u16, status: u32) -> SMBVerCmdStat {
228 return SMBVerCmdStat {
229 smb_ver: 2,
230 smb1_cmd: 0,
231 smb2_cmd: cmd,
232 status_set: true,
233 status_is_dos_error: false,
234 status: status,
235 }
236 }
237
238 pub fn set_smb1_cmd(&mut self, cmd: u8) -> bool {
239 if self.smb_ver != 0 {
240 return false;
241 }
242 self.smb_ver = 1;
243 self.smb1_cmd = cmd;
244 return true;
245 }
246
247 pub fn set_smb2_cmd(&mut self, cmd: u16) -> bool {
248 if self.smb_ver != 0 {
249 return false;
250 }
251 self.smb_ver = 2;
252 self.smb2_cmd = cmd;
253 return true;
254 }
255
256 pub fn get_version(&self) -> u8 {
257 self.smb_ver
258 }
259
260 pub fn get_smb1_cmd(&self) -> (bool, u8) {
261 if self.smb_ver != 1 {
262 return (false, 0);
263 }
264 return (true, self.smb1_cmd);
265 }
266
267 pub fn get_smb2_cmd(&self) -> (bool, u16) {
268 if self.smb_ver != 2 {
269 return (false, 0);
270 }
271 return (true, self.smb2_cmd);
272 }
273
274 pub fn get_ntstatus(&self) -> (bool, u32) {
275 (self.status_set && !self.status_is_dos_error, self.status)
276 }
277
278 pub fn get_dos_error(&self) -> (bool, u16) {
279 (self.status_set && self.status_is_dos_error, self.status as u16)
280 }
281
282 fn set_status(&mut self, status: u32, is_dos_error: bool)
283 {
284 if is_dos_error {
285 self.status_is_dos_error = true;
286 self.status = (status & 0xffff_0000) >> 16;
287 } else {
288 self.status = status;
289 }
290 self.status_set = true;
291 }
292
293 pub fn set_ntstatus(&mut self, status: u32)
294 {
295 self.set_status(status, false)
296 }
297
298 pub fn set_status_dos_error(&mut self, status: u32)
299 {
300 self.set_status(status, true)
301 }
302}
303
304#[derive(Debug)]
305pub enum SMBTransactionTypeData {
306 FILE(SMBTransactionFile),
307 TREECONNECT(SMBTransactionTreeConnect),
308 NEGOTIATE(SMBTransactionNegotiate),
309 DCERPC(SMBTransactionDCERPC),
310 CREATE(SMBTransactionCreate),
8bef1208 311 SESSIONSETUP(SMBTransactionSessionSetup),
75d7c9d6
VJ
312}
313
314#[derive(Debug)]
315pub struct SMBTransactionCreate {
316 pub disposition: u32,
317 pub delete_on_close: bool,
318 pub directory: bool,
319 pub filename: Vec<u8>,
320}
321
322impl SMBTransactionCreate {
323 pub fn new(filename: Vec<u8>, disp: u32, del: bool, dir: bool) -> SMBTransactionCreate {
324 return SMBTransactionCreate {
325 disposition: disp,
326 delete_on_close: del,
327 directory: dir,
328 filename: filename,
329 }
330 }
331}
332
333#[derive(Debug)]
334pub struct SMBTransactionNegotiate {
335 pub smb_ver: u8,
336 pub dialects: Vec<Vec<u8>>,
337 pub dialects2: Vec<Vec<u8>>,
338}
339
340impl SMBTransactionNegotiate {
341 pub fn new(smb_ver: u8) -> SMBTransactionNegotiate {
342 return SMBTransactionNegotiate {
343 smb_ver: smb_ver,
344 dialects: Vec::new(),
345 dialects2: Vec::new(),
346 }
347 }
348}
349
350#[derive(Debug)]
351pub struct SMBTransactionTreeConnect {
352 pub is_pipe: bool,
353 pub tree_id: u32,
354 pub share_name: Vec<u8>,
355}
356
357impl SMBTransactionTreeConnect {
358 pub fn new(share_name: Vec<u8>) -> SMBTransactionTreeConnect {
359 return SMBTransactionTreeConnect {
360 is_pipe:false,
361 tree_id:0,
362 share_name:share_name,
363 }
364 }
365}
366
367#[derive(Debug)]
368pub struct SMBTransaction {
369 pub id: u64, /// internal id
370
371 /// version, command and status
372 pub vercmd: SMBVerCmdStat,
373 /// session id, tree id, etc.
374 pub hdr: SMBCommonHdr,
375
376 /// for state tracking. false means this side is in progress, true
377 /// that it's complete.
378 pub request_done: bool,
379 pub response_done: bool,
380
381 /// Command specific data
382 pub type_data: Option<SMBTransactionTypeData>,
383
384 /// detection engine flags for use by detection engine
385 detect_flags_ts: u64,
386 detect_flags_tc: u64,
387 pub logged: LoggerFlags,
388 pub de_state: Option<*mut DetectEngineState>,
389 pub events: *mut AppLayerDecoderEvents,
390}
391
392impl SMBTransaction {
393 pub fn new() -> SMBTransaction {
394 return SMBTransaction{
395 id: 0,
396 vercmd: SMBVerCmdStat::new(),
397 hdr: SMBCommonHdr::init(),
398 request_done: false,
399 response_done: false,
400 type_data: None,
401 detect_flags_ts: 0,
402 detect_flags_tc: 0,
403 logged: LoggerFlags::new(),
404 de_state: None,
405 events: std::ptr::null_mut(),
406 }
407 }
408
409 pub fn set_status(&mut self, status: u32, is_dos_error: bool)
410 {
411 if is_dos_error {
412 self.vercmd.set_status_dos_error(status);
413 } else {
414 self.vercmd.set_ntstatus(status);
415 }
416 }
417
418 pub fn free(&mut self) {
419 if self.events != std::ptr::null_mut() {
420 sc_app_layer_decoder_events_free_events(&mut self.events);
421 }
422 match self.de_state {
423 Some(state) => {
424 sc_detect_engine_state_free(state);
425 }
426 _ => {}
427 }
428 }
429}
430
431impl Drop for SMBTransaction {
432 fn drop(&mut self) {
433 self.free();
434 }
435}
436
437#[derive(Hash, Eq, PartialEq, Debug, Clone)]
438pub struct SMBFileGUIDOffset {
439 pub guid: Vec<u8>,
440 pub offset: u64,
441}
442
443impl SMBFileGUIDOffset {
444 pub fn new(guid: Vec<u8>, offset: u64) -> SMBFileGUIDOffset {
445 SMBFileGUIDOffset {
446 guid:guid,
447 offset:offset,
448 }
449 }
450}
451
452/// type values to make sure we're not mixing things
453/// up in hashmap lookups
454pub const SMBHDR_TYPE_GUID: u32 = 1;
455pub const SMBHDR_TYPE_SHARE: u32 = 2;
456pub const SMBHDR_TYPE_FILENAME: u32 = 3;
457pub const SMBHDR_TYPE_OFFSET: u32 = 4;
458pub const SMBHDR_TYPE_GENERICTX: u32 = 5;
459pub const SMBHDR_TYPE_HEADER: u32 = 6;
460pub const SMBHDR_TYPE_MAX_SIZE: u32 = 7; // max resp size for SMB1_COMMAND_TRANS
461pub const SMBHDR_TYPE_TXNAME: u32 = 8; // SMB1_COMMAND_TRANS tx_name
462pub const SMBHDR_TYPE_TRANS_FRAG: u32 = 9;
463pub const SMBHDR_TYPE_TREE: u32 = 10;
464pub const SMBHDR_TYPE_DCERPCTX: u32 = 11;
465
466#[derive(Hash, Eq, PartialEq, Debug)]
467pub struct SMBCommonHdr {
468 pub ssn_id: u64,
469 pub tree_id: u32,
470 pub rec_type: u32,
471 pub msg_id: u64,
472}
473
474impl SMBCommonHdr {
475 pub fn init() -> SMBCommonHdr {
476 SMBCommonHdr {
477 rec_type : 0,
478 ssn_id : 0,
479 tree_id : 0,
480 msg_id : 0,
481 }
482 }
483 pub fn new(rec_type: u32, ssn_id: u64, tree_id: u32, msg_id: u64) -> SMBCommonHdr {
484 SMBCommonHdr {
485 rec_type : rec_type,
486 ssn_id : ssn_id,
487 tree_id : tree_id,
488 msg_id : msg_id,
489 }
490 }
491 pub fn from2(r: &Smb2Record, rec_type: u32) -> SMBCommonHdr {
492 let tree_id = match rec_type {
493 SMBHDR_TYPE_TREE => { 0 },
494 _ => r.tree_id,
495 };
496 let msg_id = match rec_type {
497 SMBHDR_TYPE_TRANS_FRAG => { 0 },
498 SMBHDR_TYPE_SHARE => { 0 },
499 _ => { r.message_id as u64 },
500 };
501
502 SMBCommonHdr {
503 rec_type : rec_type,
504 ssn_id : r.session_id,
505 tree_id : tree_id,
506 msg_id : msg_id,
507 }
508
509 }
510 pub fn from1(r: &SmbRecord, rec_type: u32) -> SMBCommonHdr {
511 let tree_id = match rec_type {
512 SMBHDR_TYPE_TREE => { 0 },
513 _ => r.tree_id as u32,
514 };
515 let msg_id = match rec_type {
516 SMBHDR_TYPE_TRANS_FRAG => { 0 },
517 SMBHDR_TYPE_SHARE => { 0 },
518 _ => { r.multiplex_id as u64 },
519 };
520
521 SMBCommonHdr {
522 rec_type : rec_type,
523 ssn_id : r.ssn_id as u64,
524 tree_id : tree_id,
525 msg_id : msg_id,
526 }
527 }
528}
529
530#[derive(Hash, Eq, PartialEq, Debug)]
531pub struct SMBHashKeyHdrGuid {
532 hdr: SMBCommonHdr,
533 guid: Vec<u8>,
534}
535
536impl SMBHashKeyHdrGuid {
537 pub fn new(hdr: SMBCommonHdr, guid: Vec<u8>) -> SMBHashKeyHdrGuid {
538 SMBHashKeyHdrGuid {
539 hdr: hdr, guid: guid,
540 }
541 }
542}
543
544#[derive(Hash, Eq, PartialEq, Debug)]
545pub struct SMBTree {
546 pub name: Vec<u8>,
547 pub is_pipe: bool,
548}
549
550impl SMBTree {
551 pub fn new(name: Vec<u8>, is_pipe: bool) -> SMBTree {
552 SMBTree {
553 name:name,
554 is_pipe:is_pipe,
555 }
556 }
557}
558
559pub fn u32_as_bytes(i: u32) -> [u8;4] {
560 let o1: u8 = ((i >> 24) & 0xff) as u8;
561 let o2: u8 = ((i >> 16) & 0xff) as u8;
562 let o3: u8 = ((i >> 8) & 0xff) as u8;
563 let o4: u8 = (i & 0xff) as u8;
564 return [o1, o2, o3, o4]
565}
566
567pub struct SMBState<> {
568 /// map ssn/tree/msgid to vec (guid/name/share)
569 pub ssn2vec_map: HashMap<SMBCommonHdr, Vec<u8>>,
570 /// map guid to filename
571 pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
572 /// map ssn key to read offset
573 pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
574
575 pub ssn2tree_map: HashMap<SMBCommonHdr, SMBTree>,
576
577 // track the max size we expect for TRANS responses
578 pub ssn2maxsize_map: HashMap<SMBCommonHdr, u16>,
579 pub ssnguid2vec_map: HashMap<SMBHashKeyHdrGuid, Vec<u8>>,
580
581 /// TCP segments defragmentation buffer
582 pub tcp_buffer_ts: Vec<u8>,
583 pub tcp_buffer_tc: Vec<u8>,
584
585 pub files: SMBFiles,
586
587 pub skip_ts: u32,
588 pub skip_tc: u32,
589
590 pub file_ts_left : u32,
591 pub file_tc_left : u32,
592 pub file_ts_guid : Vec<u8>,
593 pub file_tc_guid : Vec<u8>,
594
595 pub ts_ssn_gap: bool,
596 pub tc_ssn_gap: bool,
597
598 pub ts_gap: bool, // last TS update was gap
599 pub tc_gap: bool, // last TC update was gap
600
8bef1208
VJ
601 pub ts_trunc: bool, // no more data for TOSERVER
602 pub tc_trunc: bool, // no more data for TOCLIENT
75d7c9d6
VJ
603
604 /// transactions list
605 pub transactions: Vec<SMBTransaction>,
606
607 /// tx counter for assigning incrementing id's to tx's
608 tx_id: u64,
609
610 pub dialect: u16,
611 pub dialect_vec: Option<Vec<u8>>, // used if dialect == 0
612 pub dialects: Option<Vec<Vec<u8>>>,
613
614 /// dcerpc interfaces, stored here to be able to match
615 /// them while inspecting DCERPC REQUEST txs
616 pub dcerpc_ifaces: Option<Vec<DCERPCIface>>,
75d7c9d6
VJ
617}
618
619impl SMBState {
620 /// Allocation function for a new TLS parser instance
621 pub fn new() -> SMBState {
622 SMBState {
623 ssn2vec_map:HashMap::new(),
624 guid2name_map:HashMap::new(),
625 ssn2vecoffset_map:HashMap::new(),
626 ssn2tree_map:HashMap::new(),
627 ssn2maxsize_map:HashMap::new(),
628 ssnguid2vec_map:HashMap::new(),
629 tcp_buffer_ts:Vec::new(),
630 tcp_buffer_tc:Vec::new(),
631 files: SMBFiles::new(),
632 skip_ts:0,
633 skip_tc:0,
634 file_ts_left:0,
635 file_tc_left:0,
636 file_ts_guid:Vec::new(),
637 file_tc_guid:Vec::new(),
638 ts_ssn_gap: false,
639 tc_ssn_gap: false,
640 ts_gap: false,
641 tc_gap: false,
642 ts_trunc: false,
643 tc_trunc: false,
644 transactions: Vec::new(),
645 tx_id:0,
646 dialect:0,
647 dialect_vec: None,
648 dialects: None,
649 dcerpc_ifaces: None,
75d7c9d6
VJ
650 }
651 }
652
653 pub fn free(&mut self) {
654 //self._debug_state_stats();
655 self._debug_tx_stats();
656 self.files.free();
657 }
658
659 pub fn new_tx(&mut self) -> SMBTransaction {
660 let mut tx = SMBTransaction::new();
661 self.tx_id += 1;
662 tx.id = self.tx_id;
663 SCLogDebug!("TX {} created", tx.id);
664 return tx;
665 }
666
667 pub fn free_tx(&mut self, tx_id: u64) {
668 SCLogDebug!("Freeing TX with ID {} TX.ID {}", tx_id, tx_id+1);
669 let len = self.transactions.len();
670 let mut found = false;
671 let mut index = 0;
672 for i in 0..len {
673 let tx = &self.transactions[i];
674 if tx.id == tx_id + 1 {
675 found = true;
676 index = i;
677 SCLogDebug!("tx {} progress {}/{}", tx.id, tx.request_done, tx.response_done);
678 break;
679 }
680 }
681 if found {
682 SCLogDebug!("freeing TX with ID {} TX.ID {} at index {} left: {} max id: {}",
683 tx_id, tx_id+1, index, self.transactions.len(), self.tx_id);
684 self.transactions.remove(index);
685 }
686 }
687
688 // for use with the C API call StateGetTxIterator
689 pub fn get_tx_iterator(&mut self, min_tx_id: u64, state: &mut u64) ->
690 Option<(&SMBTransaction, u64, bool)>
691 {
692 let mut index = *state as usize;
693 let len = self.transactions.len();
694
695 // find tx that is >= min_tx_id
696 while index < len {
697 let tx = &self.transactions[index];
698 if tx.id < min_tx_id + 1 {
699 index += 1;
700 continue;
701 }
702 *state = index as u64 + 1;
703 //SCLogDebug!("returning tx_id {} has_next? {} (len {} index {}), tx {:?}",
704 // tx.id - 1, (len - index) > 1, len, index, tx);
705 return Some((tx, tx.id - 1, (len - index) > 1));
706 }
707 return None;
708 }
709
710 pub fn get_tx_by_id(&mut self, tx_id: u64) -> Option<&SMBTransaction> {
711/*
712 if self.transactions.len() > 100 {
713 SCLogNotice!("get_tx_by_id: tx_id={} in list={}", tx_id, self.transactions.len());
714 self._dump_txs();
715 panic!("txs exploded");
716 }
717*/
718 for tx in &mut self.transactions {
719 if tx.id == tx_id + 1 {
720 let ver = tx.vercmd.get_version();
721 let mut _smbcmd;
722 if ver == 2 {
723 let (_, cmd) = tx.vercmd.get_smb2_cmd();
724 _smbcmd = cmd;
725 } else {
726 let (_, cmd) = tx.vercmd.get_smb1_cmd();
727 _smbcmd = cmd as u16;
728 }
729 SCLogDebug!("Found SMB TX: id {} ver:{} cmd:{} progress {}/{} type_data {:?}",
730 tx.id, ver, _smbcmd, tx.request_done, tx.response_done, tx.type_data);
731 return Some(tx);
732 }
733 }
734 SCLogDebug!("Failed to find SMB TX with ID {}", tx_id);
735 return None;
736 }
737
738 /* generic TX has no type_data and is only used to
739 * track a single cmd request/reply pair. */
740
741 pub fn new_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: SMBCommonHdr)
742 -> (&mut SMBTransaction)
743 {
744 let mut tx = self.new_tx();
745 if smb_ver == 1 && smb_cmd <= 255 {
746 tx.vercmd.set_smb1_cmd(smb_cmd as u8);
747 } else if smb_ver == 2 {
748 tx.vercmd.set_smb2_cmd(smb_cmd);
749 }
750
751 tx.type_data = None;
752 tx.request_done = true;
753 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
754 tx.hdr = key;
755
756 SCLogDebug!("SMB: TX GENERIC created: ID {} tx list {} {:?}",
757 tx.id, self.transactions.len(), &tx);
758 self.transactions.push(tx);
759 let tx_ref = self.transactions.last_mut();
760 return tx_ref.unwrap();
761 }
762
763 pub fn get_last_tx(&mut self, smb_ver: u8, smb_cmd: u16)
764 -> Option<&mut SMBTransaction>
765 {
766 let tx_ref = self.transactions.last_mut();
767 match tx_ref {
768 Some(tx) => {
769 let found = if tx.vercmd.get_version() == smb_ver {
770 if smb_ver == 1 {
771 let (_, cmd) = tx.vercmd.get_smb1_cmd();
772 cmd as u16 == smb_cmd
773 } else if smb_ver == 2 {
774 let (_, cmd) = tx.vercmd.get_smb2_cmd();
775 cmd == smb_cmd
776 } else {
777 false
778 }
779 } else {
780 false
781 };
782 if found {
783 return Some(tx);
784 }
785 },
786 None => { },
787 }
788 return None;
789 }
790
791 pub fn get_generic_tx(&mut self, smb_ver: u8, smb_cmd: u16, key: &SMBCommonHdr)
792 -> Option<&mut SMBTransaction>
793 {
794 for tx in &mut self.transactions {
795 let found = if tx.vercmd.get_version() == smb_ver {
796 if smb_ver == 1 {
797 let (_, cmd) = tx.vercmd.get_smb1_cmd();
798 cmd as u16 == smb_cmd && tx.hdr == *key
799 } else if smb_ver == 2 {
800 let (_, cmd) = tx.vercmd.get_smb2_cmd();
801 cmd == smb_cmd && tx.hdr == *key
802 } else {
803 false
804 }
805 } else {
806 false
807 };
808 if found {
809 return Some(tx);
810 }
811 }
812 return None;
813 }
814
815 pub fn new_negotiate_tx(&mut self, smb_ver: u8)
816 -> (&mut SMBTransaction)
817 {
818 let mut tx = self.new_tx();
819 if smb_ver == 1 {
820 tx.vercmd.set_smb1_cmd(SMB1_COMMAND_NEGOTIATE_PROTOCOL);
821 } else if smb_ver == 2 {
822 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_NEGOTIATE_PROTOCOL);
823 }
824
825 tx.type_data = Some(SMBTransactionTypeData::NEGOTIATE(
826 SMBTransactionNegotiate::new(smb_ver)));
827 tx.request_done = true;
828 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
829
830 SCLogDebug!("SMB: TX NEGOTIATE created: ID {} SMB ver {}", tx.id, smb_ver);
831 self.transactions.push(tx);
832 let tx_ref = self.transactions.last_mut();
833 return tx_ref.unwrap();
834 }
835
836 pub fn get_negotiate_tx(&mut self, smb_ver: u8)
837 -> Option<&mut SMBTransaction>
838 {
839 for tx in &mut self.transactions {
840 let found = match tx.type_data {
841 Some(SMBTransactionTypeData::NEGOTIATE(ref x)) => {
842 if x.smb_ver == smb_ver {
843 true
844 } else {
845 false
846 }
847 },
848 _ => { false },
849 };
850 if found {
851 return Some(tx);
852 }
853 }
854 return None;
855 }
856
857 pub fn new_treeconnect_tx(&mut self, hdr: SMBCommonHdr, name: Vec<u8>)
858 -> (&mut SMBTransaction)
859 {
860 let mut tx = self.new_tx();
861
862 tx.hdr = hdr;
863 tx.type_data = Some(SMBTransactionTypeData::TREECONNECT(
864 SMBTransactionTreeConnect::new(name.to_vec())));
865 tx.request_done = true;
866 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
867
868 SCLogDebug!("SMB: TX TREECONNECT created: ID {} NAME {}",
869 tx.id, String::from_utf8_lossy(&name));
870 self.transactions.push(tx);
871 let tx_ref = self.transactions.last_mut();
872 return tx_ref.unwrap();
873 }
874
875 pub fn get_treeconnect_tx(&mut self, hdr: SMBCommonHdr)
876 -> Option<&mut SMBTransaction>
877 {
878 for tx in &mut self.transactions {
879 let hit = tx.hdr == hdr && match tx.type_data {
880 Some(SMBTransactionTypeData::TREECONNECT(_)) => { true },
881 _ => { false },
882 };
883 if hit {
884 return Some(tx);
885 }
886 }
887 return None;
888 }
889
890 pub fn new_create_tx(&mut self, file_name: &Vec<u8>,
891 disposition: u32, del: bool, dir: bool,
892 hdr: SMBCommonHdr)
893 -> &mut SMBTransaction
894 {
895 let mut tx = self.new_tx();
896 tx.hdr = hdr;
897 tx.type_data = Some(SMBTransactionTypeData::CREATE(
898 SMBTransactionCreate::new(
899 file_name.to_vec(), disposition,
900 del, dir)));
901 tx.request_done = true;
902 tx.response_done = self.tc_trunc; // no response expected if tc is truncated
903
904 self.transactions.push(tx);
905 let tx_ref = self.transactions.last_mut();
906 return tx_ref.unwrap();
907 }
908
909 pub fn get_create_tx_by_hdr(&mut self, hdr: &SMBCommonHdr)
910 -> Option<&mut SMBTransaction>
911 {
912 for tx in &mut self.transactions {
913 let found = match tx.type_data {
914 Some(SMBTransactionTypeData::CREATE(ref _d)) => {
915 *hdr == tx.hdr
916 },
917 _ => { false },
918 };
919
920 if found {
921 SCLogDebug!("SMB: Found SMB create TX with ID {}", tx.id);
922 return Some(tx);
923 }
924 }
925 SCLogDebug!("SMB: Failed to find SMB create TX with key {:?}", hdr);
926 return None;
927 }
928
929 pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool)
930 {
931 let (name, is_dcerpc) = match self.guid2name_map.get(&guid.to_vec()) {
932 Some(n) => {
933 match str::from_utf8(&n) {
934 Ok("PSEXESVC") => ("PSEXESVC", false),
935 Ok("svcctl") => ("svcctl", true),
936 Ok("srvsvc") => ("srvsvc", true),
937 Ok("atsvc") => ("atsvc", true),
938 Ok("lsarpc") => ("lsarpc", true),
939 Ok("samr") => ("samr", true),
940 Err(_) => ("MALFORMED", false),
941 Ok(&_) => {
942 SCLogNotice!("don't know {}", String::from_utf8_lossy(&n));
943 ("UNKNOWN", false)
944 },
945 }
946 },
947 _ => { ("UNKNOWN", false) },
948 };
949 SCLogDebug!("service {} is_dcerpc {}", name, is_dcerpc);
950 (&name, is_dcerpc)
951 }
952
953 /* if we have marked the ssn as 'gapped' we check to see if
954 * we've caught up. The check is to see if we have the last
955 * tx in our list is smaller than the max id. This means we've
956 * seen a tx that has been fully processed and removed. */
957 pub fn check_gap_resync(&mut self)
958 {
959 if self.ts_ssn_gap || self.tc_ssn_gap {
960 let max_id = self.tx_id;
961 let over = match self.transactions.last() {
962 Some(tx) => {
963 SCLogDebug!("tx.id {} max_id {}", tx.id, max_id);
964 tx.id < max_id
965 },
966 None => { false },
967 };
968 if over {
969 SCLogDebug!("post-GAP resync confirmed");
970 self.ts_ssn_gap = false;
971 self.tc_ssn_gap = false;
972 self.close_non_file_txs();
973 }
974 }
975 }
976
977 /* close all txs execpt file xfers. */
978 fn close_non_file_txs(&mut self) {
979 SCLogDebug!("checking for non-file txs to wrap up");
980 for tx in &mut self.transactions {
981 match tx.type_data {
982 None => {
983 SCLogDebug!("tx {} marked as done", tx.id);
984 tx.request_done = true;
985 tx.response_done = true;
986 },
987 _ => { },
988 }
989 }
990 }
991
992 // return how much data we consumed
993 fn handle_skip(&mut self, direction: u8, input_size: u32) -> u32 {
994 let mut skip_left = if direction == STREAM_TOSERVER {
995 self.skip_ts
996 } else {
997 self.skip_tc
998 };
999 if skip_left == 0 {
1000 return 0
1001 }
1002 SCLogDebug!("skip_left {} input_size {}", skip_left, input_size);
1003
1004 let consumed = if skip_left >= input_size {
1005 input_size
1006 } else {
1007 skip_left
1008 };
1009
1010 if skip_left <= input_size {
1011 skip_left = 0;
1012 } else {
1013 skip_left -= input_size;
1014 }
1015
1016 if direction == STREAM_TOSERVER {
1017 self.skip_ts = skip_left;
1018 } else {
1019 self.skip_tc = skip_left;
1020 }
1021 return consumed;
1022 }
1023
1024 /// return bytes consumed
1025 pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize
1026 {
1027 SCLogDebug!("incomplete of size {}", input.len());
1028 if input.len() < 512 {
7dff9b99
VJ
1029 // check for malformed data. Wireshark reports as
1030 // 'NBSS continuation data'. If it's invalid we're
1031 // lost so we give up.
1032 if input.len() > 8 {
1033 match parse_nbss_record_partial(input) {
1034 IResult::Done(_, ref hdr) => {
1035 if !hdr.is_smb() {
1036 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1037 self.trunc_ts();
1038 return 0;
1039 }
1040 },
1041 _ => {},
1042 }
1043 }
75d7c9d6
VJ
1044 return 0;
1045 }
1046
1047 match parse_nbss_record_partial(input) {
1048 IResult::Done(output, ref nbss_part_hdr) => {
1049 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1050 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1051 match parse_smb_version(&nbss_part_hdr.data) {
1052 IResult::Done(_, ref smb) => {
1053 SCLogDebug!("SMB {:?}", smb);
1054 if smb.version == 255u8 { // SMB1
1055 SCLogDebug!("SMBv1 record");
1056 match parse_smb_record(&nbss_part_hdr.data) {
1057 IResult::Done(_, ref r) => {
1058 if r.command == SMB1_COMMAND_WRITE_ANDX {
1059 // see if it's a write to a pipe. We only handle those
1060 // if complete.
1061 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1062 r.ssn_id as u64, r.tree_id as u32, 0);
1063 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1064 Some(n) => n.is_pipe,
1065 None => false,
1066 };
1067 if is_pipe {
1068 return 0;
1069 }
1070 smb1_write_request_record(self, r);
1071 let consumed = input.len() - output.len();
1072 return consumed;
1073 }
1074 },
1075 _ => { },
1076
1077 }
1078 } else if smb.version == 254u8 { // SMB2
1079 SCLogDebug!("SMBv2 record");
1080 match parse_smb2_request_record(&nbss_part_hdr.data) {
1081 IResult::Done(_, ref smb_record) => {
1082 SCLogDebug!("SMB2: partial record {}",
1083 &smb2_command_string(smb_record.command));
1084 if smb_record.command == SMB2_COMMAND_WRITE {
1085 smb2_write_request_record(self, smb_record);
1086 let consumed = input.len() - output.len();
1087 return consumed;
1088 }
1089 },
1090 _ => { },
1091 }
1092 }
1093 },
1094 _ => { },
1095 }
1096 }
1097 },
1098 _ => { },
1099 }
1100
1101 return 0;
1102 }
1103
1104 /// Parsing function, handling TCP chunks fragmentation
1105 pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> u32
1106 {
1107 self.check_gap_resync();
1108
1109 let mut v : Vec<u8>;
1110 //println!("parse_tcp_data_ts ({})",i.len());
1111 //println!("{:?}",i);
1112 // Check if TCP data is being defragmented
1113 let tcp_buffer = match self.tcp_buffer_ts.len() {
1114 0 => i,
1115 _ => {
1116 v = self.tcp_buffer_ts.split_off(0);
1117 if self.tcp_buffer_ts.len() + i.len() > 100000 {
1118 self.set_event(SMBEvent::RecordOverflow);
1119 return 1;
1120 };
1121 v.extend_from_slice(i);
1122 v.as_slice()
1123 },
1124 };
1125 //println!("tcp_buffer ({})",tcp_buffer.len());
1126 let mut cur_i = tcp_buffer;
1127 if cur_i.len() > 1000000 {
1128 self.set_event(SMBEvent::RecordOverflow);
1129 return 1;
1130 }
1131 let consumed = self.handle_skip(STREAM_TOSERVER, cur_i.len() as u32);
1132 if consumed > 0 {
1133 if consumed > cur_i.len() as u32 {
1134 self.set_event(SMBEvent::InternalError);
1135 return 1;
1136 }
1137 cur_i = &cur_i[consumed as usize..];
1138 }
1139 // take care of in progress file chunk transfers
1140 // and skip buffer beyond it
1141 let consumed = self.filetracker_update(STREAM_TOSERVER, cur_i, 0);
1142 if consumed > 0 {
1143 if consumed > cur_i.len() as u32 {
1144 self.set_event(SMBEvent::InternalError);
1145 return 1;
1146 }
1147 cur_i = &cur_i[consumed as usize..];
1148 }
1149 // gap
1150 if self.ts_gap {
1151 SCLogDebug!("TODO TS trying to catch up after GAP (input {})", cur_i.len());
1152 match search_smb2_record(cur_i) {
1153 IResult::Done(_, pg) => {
1154 SCLogDebug!("smb record found");
1155 let smb2_offset = cur_i.len() - pg.data.len();
1156 if smb2_offset < 4 {
1157 return 0;
1158 }
1159 let nbss_offset = smb2_offset - 4;
1160 cur_i = &cur_i[nbss_offset..];
1161
1162 self.ts_gap = false;
1163 },
1164 _ => {
1165 SCLogDebug!("smb record NOT found");
1166 self.tcp_buffer_ts.extend_from_slice(cur_i);
1167 return 0;
1168 },
1169 }
1170 }
1171 while cur_i.len() > 0 { // min record size
1172 match parse_nbss_record(cur_i) {
1173 IResult::Done(rem, ref nbss_hdr) => {
1174 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1175 // we have the full records size worth of data,
1176 // let's parse it
1177 match parse_smb_version(&nbss_hdr.data) {
1178 IResult::Done(_, ref smb) => {
1179 SCLogDebug!("SMB {:?}", smb);
1180 if smb.version == 255u8 { // SMB1
1181 SCLogDebug!("SMBv1 record");
1182 match parse_smb_record(&nbss_hdr.data) {
1183 IResult::Done(_, ref smb_record) => {
1184 smb1_request_record(self, smb_record);
1185 },
1186 _ => {
1187 self.set_event(SMBEvent::MalformedData);
1188 return 1;
1189 },
1190 }
1191 } else if smb.version == 254u8 { // SMB2
1192 let mut nbss_data = nbss_hdr.data;
1193 while nbss_data.len() > 0 {
1194 SCLogDebug!("SMBv2 record");
1195 match parse_smb2_request_record(&nbss_data) {
1196 IResult::Done(nbss_data_rem, ref smb_record) => {
1197 SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len());
1198
1199 smb2_request_record(self, smb_record);
1200 nbss_data = nbss_data_rem;
1201 },
1202 _ => {
1203 self.set_event(SMBEvent::MalformedData);
1204 return 1;
1205 },
1206 }
1207 }
1208 }
1209 },
1210 _ => {
1211 self.set_event(SMBEvent::MalformedData);
1212 return 1;
1213 },
1214 }
1215 } else {
1216 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1217 }
1218 cur_i = rem;
1219 },
1220 IResult::Incomplete(_) => {
1221 let consumed = self.parse_tcp_data_ts_partial(cur_i);
1222 cur_i = &cur_i[consumed ..];
1223
1224 self.tcp_buffer_ts.extend_from_slice(cur_i);
1225 break;
1226 },
1227 IResult::Error(_) => {
1228 self.set_event(SMBEvent::MalformedData);
1229 return 1;
1230 },
1231 }
1232 };
1233
1234 //self.debug_tx_stats();
1235 0
1236 }
1237
1238 /// return bytes consumed
1239 pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize
1240 {
1241 SCLogDebug!("incomplete of size {}", input.len());
1242 if input.len() < 512 {
7dff9b99
VJ
1243 // check for malformed data. Wireshark reports as
1244 // 'NBSS continuation data'. If it's invalid we're
1245 // lost so we give up.
1246 if input.len() > 8 {
1247 match parse_nbss_record_partial(input) {
1248 IResult::Done(_, ref hdr) => {
1249 if !hdr.is_smb() {
1250 SCLogDebug!("partial NBSS, not SMB and no known msg type {}", hdr.message_type);
1251 self.trunc_tc();
1252 return 0;
1253 }
1254 },
1255 _ => {},
1256 }
1257 }
75d7c9d6
VJ
1258 return 0;
1259 }
1260
1261 match parse_nbss_record_partial(input) {
1262 IResult::Done(output, ref nbss_part_hdr) => {
1263 SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len());
1264 if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1265 match parse_smb_version(&nbss_part_hdr.data) {
1266 IResult::Done(_, ref smb) => {
1267 SCLogDebug!("SMB {:?}", smb);
1268 if smb.version == 255u8 { // SMB1
1269 SCLogDebug!("SMBv1 record");
1270 match parse_smb_record(&nbss_part_hdr.data) {
1271 IResult::Done(_, ref r) => {
1272 SCLogDebug!("SMB1: partial record {}",
1273 r.command);
1274 if r.command == SMB1_COMMAND_READ_ANDX {
1275 let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE,
1276 r.ssn_id as u64, r.tree_id as u32, 0);
1277 let is_pipe = match self.ssn2tree_map.get(&tree_key) {
1278 Some(n) => n.is_pipe,
1279 None => false,
1280 };
1281 if is_pipe {
1282 return 0;
1283 }
1284 smb1_read_response_record(self, r);
1285 let consumed = input.len() - output.len();
1286 return consumed;
1287 }
1288 },
1289 _ => { },
1290 }
1291 } else if smb.version == 254u8 { // SMB2
1292 SCLogDebug!("SMBv2 record");
1293 match parse_smb2_response_record(&nbss_part_hdr.data) {
1294 IResult::Done(_, ref smb_record) => {
1295 SCLogDebug!("SMB2: partial record {}",
1296 &smb2_command_string(smb_record.command));
1297 if smb_record.command == SMB2_COMMAND_READ {
1298 smb2_read_response_record(self, smb_record);
1299 let consumed = input.len() - output.len();
1300 return consumed;
1301 }
1302 },
1303 _ => { },
1304 }
1305 }
1306 },
1307 _ => { },
1308 }
1309 }
1310 },
1311 _ => { },
1312 }
1313
1314 return 0;
1315 }
1316
1317 /// Parsing function, handling TCP chunks fragmentation
1318 pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> u32
1319 {
1320 self.check_gap_resync();
1321
1322 let mut v : Vec<u8>;
1323 // Check if TCP data is being defragmented
1324 let tcp_buffer = match self.tcp_buffer_tc.len() {
1325 0 => i,
1326 _ => {
1327 v = self.tcp_buffer_tc.split_off(0);
1328 if self.tcp_buffer_tc.len() + i.len() > 100000 {
1329 self.set_event(SMBEvent::RecordOverflow);
1330 return 1;
1331 };
1332 v.extend_from_slice(i);
1333 v.as_slice()
1334 },
1335 };
1336 let mut cur_i = tcp_buffer;
1337 SCLogDebug!("cur_i.len {}", cur_i.len());
1338 if cur_i.len() > 100000 {
1339 self.set_event(SMBEvent::RecordOverflow);
1340 return 1;
1341 }
1342 let consumed = self.handle_skip(STREAM_TOCLIENT, cur_i.len() as u32);
1343 if consumed > 0 {
1344 if consumed > cur_i.len() as u32 {
1345 self.set_event(SMBEvent::InternalError);
1346 return 1;
1347 }
1348 cur_i = &cur_i[consumed as usize..];
1349 }
1350 // take care of in progress file chunk transfers
1351 // and skip buffer beyond it
1352 let consumed = self.filetracker_update(STREAM_TOCLIENT, cur_i, 0);
1353 if consumed > 0 {
1354 if consumed > cur_i.len() as u32 {
1355 self.set_event(SMBEvent::InternalError);
1356 return 1;
1357 }
1358 cur_i = &cur_i[consumed as usize..];
1359 }
1360 // gap
1361 if self.tc_gap {
1362 SCLogDebug!("TODO TC trying to catch up after GAP (input {})", cur_i.len());
1363 match search_smb2_record(cur_i) {
1364 IResult::Done(_, pg) => {
1365 SCLogDebug!("smb record found");
1366 let smb2_offset = cur_i.len() - pg.data.len();
1367 if smb2_offset < 4 {
1368 return 0;
1369 }
1370 let nbss_offset = smb2_offset - 4;
1371 cur_i = &cur_i[nbss_offset..];
1372
1373 self.tc_gap = false;
1374 },
1375 _ => {
1376 SCLogDebug!("smb record NOT found");
1377 self.tcp_buffer_tc.extend_from_slice(cur_i);
1378 return 0;
1379 },
1380 }
1381 }
1382 while cur_i.len() > 0 { // min record size
1383 match parse_nbss_record(cur_i) {
1384 IResult::Done(rem, ref nbss_hdr) => {
1385 if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE {
1386 // we have the full records size worth of data,
1387 // let's parse it
1388 match parse_smb_version(&nbss_hdr.data) {
1389 IResult::Done(_, ref smb) => {
1390 SCLogDebug!("SMB {:?}", smb);
1391 if smb.version == 255u8 { // SMB1
1392 SCLogDebug!("SMBv1 record");
1393 match parse_smb_record(&nbss_hdr.data) {
1394 IResult::Done(_, ref smb_record) => {
1395 smb1_response_record(self, smb_record);
1396 },
1397 _ => {
1398 self.set_event(SMBEvent::MalformedData);
1399 return 1;
1400 },
1401 }
1402 } else if smb.version == 254u8 { // SMB2
1403 let mut nbss_data = nbss_hdr.data;
1404 while nbss_data.len() > 0 {
1405 SCLogDebug!("SMBv2 record");
1406 match parse_smb2_response_record(&nbss_data) {
1407 IResult::Done(nbss_data_rem, ref smb_record) => {
1408 smb2_response_record(self, smb_record);
1409 nbss_data = nbss_data_rem;
1410 },
1411 _ => {
1412 self.set_event(SMBEvent::MalformedData);
1413 return 1;
1414 },
1415 }
1416 }
1417 }
1418 },
1419 IResult::Incomplete(_) => {
1420 // not enough data to contain basic SMB hdr
1421 // TODO event: empty NBSS_MSGTYPE_SESSION_MESSAGE
1422 },
1423 IResult::Error(_) => {
1424 self.set_event(SMBEvent::MalformedData);
1425 return 1;
1426 },
1427 }
1428 } else {
1429 SCLogDebug!("NBSS message {:X}", nbss_hdr.message_type);
1430 }
1431 cur_i = rem;
1432 },
7dff9b99
VJ
1433 IResult::Incomplete(needed) => {
1434 SCLogDebug!("INCOMPLETE have {} needed {:?}", cur_i.len(), needed);
75d7c9d6
VJ
1435 let consumed = self.parse_tcp_data_tc_partial(cur_i);
1436 cur_i = &cur_i[consumed ..];
1437
1438 SCLogDebug!("INCOMPLETE have {}", cur_i.len());
1439 self.tcp_buffer_tc.extend_from_slice(cur_i);
1440 break;
1441 },
1442 IResult::Error(_) => {
1443 self.set_event(SMBEvent::MalformedData);
1444 return 1;
1445 },
1446 }
1447 };
1448 0
1449 }
1450
1451 /// handle a gap in the TOSERVER direction
1452 /// returns: 0 ok, 1 unrecoverable error
1453 pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> u32 {
1454 if self.tcp_buffer_ts.len() > 0 {
1455 self.tcp_buffer_ts.clear();
1456 }
1457 let consumed = self.handle_skip(STREAM_TOSERVER, gap_size);
1458 if consumed < gap_size {
1459 let new_gap_size = gap_size - consumed;
1460 let gap = vec![0; new_gap_size as usize];
1461
1462 let consumed2 = self.filetracker_update(STREAM_TOSERVER, &gap, new_gap_size);
1463 if consumed2 > new_gap_size {
1464 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1465 self.set_event(SMBEvent::InternalError);
1466 return 1;
1467 }
1468 }
1469 SCLogDebug!("GAP of size {} in toserver direction", gap_size);
1470 self.ts_ssn_gap = true;
1471 self.ts_gap = true;
1472 return 0
1473 }
1474
1475 /// handle a gap in the TOCLIENT direction
1476 /// returns: 0 ok, 1 unrecoverable error
1477 pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> u32 {
1478 if self.tcp_buffer_tc.len() > 0 {
1479 self.tcp_buffer_tc.clear();
1480 }
1481 let consumed = self.handle_skip(STREAM_TOCLIENT, gap_size);
1482 if consumed < gap_size {
1483 let new_gap_size = gap_size - consumed;
1484 let gap = vec![0; new_gap_size as usize];
1485
1486 let consumed2 = self.filetracker_update(STREAM_TOCLIENT, &gap, new_gap_size);
1487 if consumed2 > new_gap_size {
1488 SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size);
1489 self.set_event(SMBEvent::InternalError);
1490 return 1;
1491 }
1492 }
1493 SCLogDebug!("GAP of size {} in toclient direction", gap_size);
1494 self.tc_ssn_gap = true;
1495 self.tc_gap = true;
1496 return 0
1497 }
1498
1499 pub fn trunc_ts(&mut self) {
1500 SCLogDebug!("TRUNC TS");
1501 self.ts_trunc = true;
7dff9b99 1502 self.tcp_buffer_ts.clear();
75d7c9d6
VJ
1503
1504 for tx in &mut self.transactions {
1505 if !tx.request_done {
1506 SCLogDebug!("TRUNCING TX {} in TOSERVER direction", tx.id);
1507 tx.request_done = true;
1508 }
1509 }
1510 }
1511 pub fn trunc_tc(&mut self) {
1512 SCLogDebug!("TRUNC TC");
1513 self.tc_trunc = true;
7dff9b99 1514 self.tcp_buffer_tc.clear();
75d7c9d6
VJ
1515
1516 for tx in &mut self.transactions {
1517 if !tx.response_done {
1518 SCLogDebug!("TRUNCING TX {} in TOCLIENT direction", tx.id);
1519 tx.response_done = true;
1520 }
1521 }
1522 }
1523}
1524
1525/// Returns *mut SMBState
1526#[no_mangle]
1527pub extern "C" fn rs_smb_state_new() -> *mut libc::c_void {
1528 let state = SMBState::new();
1529 let boxed = Box::new(state);
1530 SCLogDebug!("allocating state");
1531 return unsafe{transmute(boxed)};
1532}
1533
1534/// Params:
1535/// - state: *mut SMBState as void pointer
1536#[no_mangle]
1537pub extern "C" fn rs_smb_state_free(state: *mut libc::c_void) {
1538 // Just unbox...
1539 SCLogDebug!("freeing state");
1540 let mut smb_state: Box<SMBState> = unsafe{transmute(state)};
1541 smb_state.free();
1542}
1543
1544/// C binding parse a SMB request. Returns 1 on success, -1 on failure.
1545#[no_mangle]
1546pub extern "C" fn rs_smb_parse_request_tcp(_flow: *mut Flow,
1547 state: &mut SMBState,
1548 _pstate: *mut libc::c_void,
1549 input: *mut libc::uint8_t,
1550 input_len: libc::uint32_t,
1551 _data: *mut libc::c_void)
1552 -> libc::int8_t
1553{
1554 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1555 SCLogDebug!("parsing {} bytes of request data", input_len);
1556
1557 if state.parse_tcp_data_ts(buf) == 0 {
1558 return 1;
1559 } else {
1560 return -1;
1561 }
1562}
1563
1564#[no_mangle]
1565pub extern "C" fn rs_smb_parse_request_tcp_gap(
1566 state: &mut SMBState,
1567 input_len: libc::uint32_t)
1568 -> libc::int8_t
1569{
1570 if state.parse_tcp_data_ts_gap(input_len as u32) == 0 {
1571 return 1;
1572 }
1573 return -1;
1574}
1575
1576
1577#[no_mangle]
1578pub extern "C" fn rs_smb_parse_response_tcp(_flow: *mut Flow,
1579 state: &mut SMBState,
1580 _pstate: *mut libc::c_void,
1581 input: *mut libc::uint8_t,
1582 input_len: libc::uint32_t,
1583 _data: *mut libc::c_void)
1584 -> libc::int8_t
1585{
1586 SCLogDebug!("parsing {} bytes of response data", input_len);
1587 let buf = unsafe{std::slice::from_raw_parts(input, input_len as usize)};
1588
1589 if state.parse_tcp_data_tc(buf) == 0 {
1590 return 1;
1591 } else {
1592 return -1;
1593 }
1594}
1595
1596#[no_mangle]
1597pub extern "C" fn rs_smb_parse_response_tcp_gap(
1598 state: &mut SMBState,
1599 input_len: libc::uint32_t)
1600 -> libc::int8_t
1601{
1602 if state.parse_tcp_data_tc_gap(input_len as u32) == 0 {
1603 return 1;
1604 }
1605 return -1;
1606}
1607
1608/// TOSERVER probe function
1609#[no_mangle]
1610pub extern "C" fn rs_smb_probe_tcp_ts(_input: *const libc::uint8_t, _len: libc::uint32_t)
1611 -> libc::int8_t
1612{
1613// let slice: &[u8] = unsafe {
1614// std::slice::from_raw_parts(input as *mut u8, len as usize)
1615// };
1616 //return smb3_probe(slice, STREAM_TOSERVER);
1617 return 1
1618}
1619/// TOCLIENT probe function
1620#[no_mangle]
1621pub extern "C" fn rs_smb_probe_tcp_tc(_input: *const libc::uint8_t, _len: libc::uint32_t)
1622 -> libc::int8_t
1623{
1624// let slice: &[u8] = unsafe {
1625// std::slice::from_raw_parts(input as *mut u8, len as usize)
1626// };
1627 //return smb3_probe(slice, STREAM_TOCLIENT);
1628 return 1
1629}
1630
1631#[no_mangle]
1632pub extern "C" fn rs_smb_state_get_tx_count(state: &mut SMBState)
1633 -> libc::uint64_t
1634{
1635 SCLogDebug!("rs_smb_state_get_tx_count: returning {}", state.tx_id);
1636 return state.tx_id;
1637}
1638
1639#[no_mangle]
1640pub extern "C" fn rs_smb_state_get_tx(state: &mut SMBState,
1641 tx_id: libc::uint64_t)
1642 -> *mut SMBTransaction
1643{
1644 match state.get_tx_by_id(tx_id) {
1645 Some(tx) => {
1646 return unsafe{transmute(tx)};
1647 }
1648 None => {
1649 return std::ptr::null_mut();
1650 }
1651 }
1652}
1653
1654// for use with the C API call StateGetTxIterator
1655#[no_mangle]
1656pub extern "C" fn rs_smb_state_get_tx_iterator(
1657 state: &mut SMBState,
1658 min_tx_id: libc::uint64_t,
1659 istate: &mut libc::uint64_t)
1660 -> applayer::AppLayerGetTxIterTuple
1661{
1662 match state.get_tx_iterator(min_tx_id, istate) {
1663 Some((tx, out_tx_id, has_next)) => {
1664 let c_tx = unsafe { transmute(tx) };
1665 let ires = applayer::AppLayerGetTxIterTuple::with_values(c_tx, out_tx_id, has_next);
1666 return ires;
1667 }
1668 None => {
1669 return applayer::AppLayerGetTxIterTuple::not_found();
1670 }
1671 }
1672}
1673
1674#[no_mangle]
1675pub extern "C" fn rs_smb_state_tx_free(state: &mut SMBState,
1676 tx_id: libc::uint64_t)
1677{
1678 SCLogDebug!("freeing tx {}", tx_id as u64);
1679 state.free_tx(tx_id);
1680}
1681
1682#[no_mangle]
1683pub extern "C" fn rs_smb_state_progress_completion_status(
1684 _direction: libc::uint8_t)
1685 -> libc::c_int
1686{
1687 return 1;
1688}
1689
1690#[no_mangle]
1691pub extern "C" fn rs_smb_tx_get_alstate_progress(tx: &mut SMBTransaction,
1692 direction: libc::uint8_t)
1693 -> libc::uint8_t
1694{
1695 if direction == STREAM_TOSERVER && tx.request_done {
1696 SCLogDebug!("tx {} TOSERVER progress 1 => {:?}", tx.id, tx);
1697 return 1;
1698 } else if direction == STREAM_TOCLIENT && tx.response_done {
1699 SCLogDebug!("tx {} TOCLIENT progress 1 => {:?}", tx.id, tx);
1700 return 1;
1701 } else {
1702 SCLogDebug!("tx {} direction {} progress 0", tx.id, direction);
1703 return 0;
1704 }
1705}
1706
1707#[no_mangle]
1708pub extern "C" fn rs_smb_tx_set_logged(_state: &mut SMBState,
1709 tx: &mut SMBTransaction,
1710 bits: libc::uint32_t)
1711{
1712 tx.logged.set(bits);
1713}
1714
1715#[no_mangle]
1716pub extern "C" fn rs_smb_tx_get_logged(_state: &mut SMBState,
1717 tx: &mut SMBTransaction)
1718 -> u32
1719{
1720 return tx.logged.get();
1721}
1722
1723#[no_mangle]
1724pub extern "C" fn rs_smb_tx_set_detect_flags(
1725 tx: &mut SMBTransaction,
1726 direction: libc::uint8_t,
1727 flags: libc::uint64_t)
1728{
1729 if (direction & STREAM_TOSERVER) != 0 {
1730 tx.detect_flags_ts = flags as u64;
1731 } else {
1732 tx.detect_flags_tc = flags as u64;
1733 }
1734}
1735
1736#[no_mangle]
1737pub extern "C" fn rs_smb_tx_get_detect_flags(
1738 tx: &mut SMBTransaction,
1739 direction: libc::uint8_t)
1740 -> libc::uint64_t
1741{
1742 if (direction & STREAM_TOSERVER) != 0 {
1743 return tx.detect_flags_ts as libc::uint64_t;
1744 } else {
1745 return tx.detect_flags_tc as libc::uint64_t;
1746 }
1747}
1748
1749#[no_mangle]
1750pub extern "C" fn rs_smb_state_set_tx_detect_state(
1751 tx: &mut SMBTransaction,
1752 de_state: &mut DetectEngineState)
1753{
1754 tx.de_state = Some(de_state);
1755}
1756
1757#[no_mangle]
1758pub extern "C" fn rs_smb_state_get_tx_detect_state(
1759 tx: &mut SMBTransaction)
1760 -> *mut DetectEngineState
1761{
1762 match tx.de_state {
1763 Some(ds) => {
1764 return ds;
1765 },
1766 None => {
1767 return std::ptr::null_mut();
1768 }
1769 }
1770}
1771
1772#[no_mangle]
1773pub extern "C" fn rs_smb_state_truncate(
1774 state: &mut SMBState,
1775 direction: libc::uint8_t)
1776{
1777 if (direction & STREAM_TOSERVER) != 0 {
1778 state.trunc_ts();
1779 } else {
1780 state.trunc_tc();
1781 }
1782}
1783
1784#[no_mangle]
1785pub extern "C" fn rs_smb_state_get_events(state: &mut SMBState,
1786 tx_id: libc::uint64_t)
1787 -> *mut AppLayerDecoderEvents
1788{
1789 match state.get_tx_by_id(tx_id) {
1790 Some(tx) => {
1791 return tx.events;
1792 }
1793 _ => {
1794 return std::ptr::null_mut();
1795 }
1796 }
1797}
1798
1799#[no_mangle]
1800pub extern "C" fn rs_smb_state_get_event_info(event_name: *const libc::c_char,
1801 event_id: *mut libc::c_int,
1802 event_type: *mut AppLayerEventType)
1803 -> i8
1804{
1805 if event_name == std::ptr::null() {
1806 return -1;
1807 }
1808 let c_event_name: &CStr = unsafe { CStr::from_ptr(event_name) };
1809 let event = match c_event_name.to_str() {
1810 Ok(s) => {
1811 smb_str_to_event(s)
1812 },
1813 Err(_) => -1, // UTF-8 conversion failed
1814 };
1815 unsafe {
1816 *event_type = APP_LAYER_EVENT_TYPE_TRANSACTION;
1817 *event_id = event as libc::c_int;
1818 };
1819 if event == -1 {
1820 return -1;
1821 }
1822 0
1823}