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