]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb2.rs
smb: log file FID/GUID as fuid
[people/ms/suricata.git] / rust / src / smb / smb2.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
18use core::*;
19use log::*;
20use nom::IResult;
21
22use smb::smb::*;
23use smb::smb2_records::*;
8bef1208 24use smb::smb2_session::*;
283be3ca 25use smb::smb2_ioctl::*;
75d7c9d6
VJ
26use smb::dcerpc::*;
27use smb::events::*;
75d7c9d6
VJ
28use smb::files::*;
29
30pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL: u16 = 0;
31pub const SMB2_COMMAND_SESSION_SETUP: u16 = 1;
32pub const SMB2_COMMAND_SESSION_LOGOFF: u16 = 2;
33pub const SMB2_COMMAND_TREE_CONNECT: u16 = 3;
34pub const SMB2_COMMAND_TREE_DISCONNECT: u16 = 4;
35pub const SMB2_COMMAND_CREATE: u16 = 5;
36pub const SMB2_COMMAND_CLOSE: u16 = 6;
894a73ee 37pub const SMB2_COMMAND_FLUSH: u16 = 7;
75d7c9d6
VJ
38pub const SMB2_COMMAND_READ: u16 = 8;
39pub const SMB2_COMMAND_WRITE: u16 = 9;
894a73ee 40pub const SMB2_COMMAND_LOCK: u16 = 10;
75d7c9d6 41pub const SMB2_COMMAND_IOCTL: u16 = 11;
894a73ee 42pub const SMB2_COMMAND_CANCEL: u16 = 12;
75d7c9d6
VJ
43pub const SMB2_COMMAND_KEEPALIVE: u16 = 13;
44pub const SMB2_COMMAND_FIND: u16 = 14;
894a73ee 45pub const SMB2_COMMAND_CHANGE_NOTIFY: u16 = 15;
75d7c9d6
VJ
46pub const SMB2_COMMAND_GET_INFO: u16 = 16;
47pub const SMB2_COMMAND_SET_INFO: u16 = 17;
894a73ee 48pub const SMB2_COMMAND_OPLOCK_BREAK: u16 = 18;
75d7c9d6
VJ
49
50pub fn smb2_command_string(c: u16) -> String {
51 match c {
52 SMB2_COMMAND_NEGOTIATE_PROTOCOL => "SMB2_COMMAND_NEGOTIATE_PROTOCOL",
53 SMB2_COMMAND_SESSION_SETUP => "SMB2_COMMAND_SESSION_SETUP",
54 SMB2_COMMAND_SESSION_LOGOFF => "SMB2_COMMAND_SESSION_LOGOFF",
55 SMB2_COMMAND_TREE_CONNECT => "SMB2_COMMAND_TREE_CONNECT",
56 SMB2_COMMAND_TREE_DISCONNECT => "SMB2_COMMAND_TREE_DISCONNECT",
57 SMB2_COMMAND_CREATE => "SMB2_COMMAND_CREATE",
58 SMB2_COMMAND_CLOSE => "SMB2_COMMAND_CLOSE",
59 SMB2_COMMAND_READ => "SMB2_COMMAND_READ",
894a73ee 60 SMB2_COMMAND_FLUSH => "SMB2_COMMAND_FLUSH",
75d7c9d6 61 SMB2_COMMAND_WRITE => "SMB2_COMMAND_WRITE",
894a73ee 62 SMB2_COMMAND_LOCK => "SMB2_COMMAND_LOCK",
75d7c9d6 63 SMB2_COMMAND_IOCTL => "SMB2_COMMAND_IOCTL",
894a73ee 64 SMB2_COMMAND_CANCEL => "SMB2_COMMAND_CANCEL",
75d7c9d6
VJ
65 SMB2_COMMAND_KEEPALIVE => "SMB2_COMMAND_KEEPALIVE",
66 SMB2_COMMAND_FIND => "SMB2_COMMAND_FIND",
894a73ee 67 SMB2_COMMAND_CHANGE_NOTIFY => "SMB2_COMMAND_CHANGE_NOTIFY",
75d7c9d6
VJ
68 SMB2_COMMAND_GET_INFO => "SMB2_COMMAND_GET_INFO",
69 SMB2_COMMAND_SET_INFO => "SMB2_COMMAND_SET_INFO",
894a73ee 70 SMB2_COMMAND_OPLOCK_BREAK => "SMB2_COMMAND_OPLOCK_BREAK",
75d7c9d6
VJ
71 _ => { return (c).to_string(); },
72 }.to_string()
73
74}
75
76pub fn smb2_dialect_string(d: u16) -> String {
77 match d {
78 0x0202 => "2.02",
79 0x0210 => "2.10",
80 0x0222 => "2.22",
81 0x0224 => "2.24",
82 0x02ff => "2.??",
83 0x0300 => "3.00",
84 0x0302 => "3.02",
85 0x0310 => "3.10",
86 0x0311 => "3.11",
87 _ => { return (d).to_string(); },
88 }.to_string()
89}
90
91// later we'll use this to determine if we need to
92// track a ssn per type
93fn smb2_create_new_tx(_cmd: u16) -> bool {
94 true
95}
96
97fn smb2_read_response_record_generic<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
98{
99 if smb2_create_new_tx(r.command) {
100 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
101 let tx = state.get_generic_tx(2, r.command as u16, &tx_hdr);
102 if let Some(tx) = tx {
103 tx.set_status(r.nt_status, false);
104 tx.response_done = true;
105 }
106 }
107}
108
109pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
110{
111 smb2_read_response_record_generic(state, r);
112
113 if r.nt_status != SMB_NTSTATUS_SUCCESS {
114 return;
115 }
116
117 match parse_smb2_response_read(r.data) {
118 IResult::Done(_, rd) => {
119 SCLogDebug!("SMBv2: read response => {:?}", rd);
120
121 // get the request info. If we don't have it, there is nothing
122 // we can do except skip this record.
123 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET);
124 let (offset, file_guid) = match state.ssn2vecoffset_map.remove(&guid_key) {
125 Some(o) => (o.offset, o.guid),
126 None => {
127 SCLogDebug!("SMBv2 READ response: reply to unknown request");
128 state.skip_tc = rd.len - rd.data.len() as u32;
129 return;
130 },
131 };
132 SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset);
133
134 // look up existing tracker and if we have it update it
fb986abe 135 let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
75d7c9d6
VJ
136 Some((tx, files, flags)) => {
137 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
138 let file_id : u32 = tx.id as u32;
139 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
140 &tdf.file_name, rd.data, offset,
141 rd.len, 0, false, &file_id);
142 }
143 true
144 },
145 None => { false },
146 };
147 if !found {
148 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
149 let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) {
150 Some(n) => (n.name.to_vec(), n.is_pipe),
151 _ => { (Vec::new(), false) },
152 };
153 let is_dcerpc = is_pipe && match state.get_service_for_guid(&file_guid) {
154 (_, x) => x,
155 };
156 if is_pipe && is_dcerpc {
157 SCLogDebug!("SMBv2 DCERPC read");
158 let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
159 let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_READ);
160 smb_read_dcerpc_record(state, vercmd, hdr, &file_guid, rd.data);
161 } else if is_pipe {
162 SCLogDebug!("non-DCERPC pipe");
163 state.skip_tc = rd.len - rd.data.len() as u32;
164 } else {
165 let file_name = match state.guid2name_map.get(&file_guid) {
166 Some(n) => { n.to_vec() },
167 None => { Vec::new() },
168 };
169 let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_TOCLIENT);
170 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
171 let file_id : u32 = tx.id as u32;
172 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
173 &file_name, rd.data, offset,
174 rd.len, 0, false, &file_id);
175 tdf.share_name = share_name;
176 }
177 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ);
178 tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
179 r.session_id, r.tree_id, 0); // TODO move into new_file_tx
180 }
181 }
182
183 state.file_tc_left = rd.len - rd.data.len() as u32;
184 state.file_tc_guid = file_guid.to_vec();
185 SCLogDebug!("SMBv2 READ RESPONSE: {} bytes left", state.file_tc_left);
186 }
187 _ => {
188 state.set_event(SMBEvent::MalformedData);
189 }
190 }
191}
192
193pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
194{
195 if smb2_create_new_tx(r.command) {
196 let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
197 let tx = state.new_generic_tx(2, r.command, tx_key);
198 tx.request_done = true;
199 }
200 match parse_smb2_request_write(r.data) {
201 IResult::Done(_, wr) => {
202 /* update key-guid map */
203 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GUID);
204 state.ssn2vec_map.insert(guid_key, wr.guid.to_vec());
205
206 let file_guid = wr.guid.to_vec();
207 let file_name = match state.guid2name_map.get(&file_guid) {
208 Some(n) => n.to_vec(),
209 None => Vec::new(),
210 };
211
fb986abe 212 let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOSERVER) {
75d7c9d6
VJ
213 Some((tx, files, flags)) => {
214 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
215 let file_id : u32 = tx.id as u32;
216 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
217 &file_name, wr.data, wr.wr_offset,
218 wr.wr_len, 0, false, &file_id);
219 }
220 true
221 },
222 None => { false },
223 };
224 if !found {
225 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
226 let is_pipe = match state.ssn2tree_map.get(&tree_key) {
227 Some(n) => { n.is_pipe },
228 _ => { false },
229 };
230 let is_dcerpc = is_pipe && match state.get_service_for_guid(&wr.guid) {
231 (_, x) => x,
232 };
233 if is_pipe && is_dcerpc {
234 SCLogDebug!("SMBv2 DCERPC write");
235 let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
236 let vercmd = SMBVerCmdStat::new2(SMB2_COMMAND_WRITE);
237 smb_write_dcerpc_record(state, vercmd, hdr, wr.data);
238 } else if is_pipe {
239 SCLogDebug!("non-DCERPC pipe: skip rest of the record");
240 state.skip_ts = wr.wr_len - wr.data.len() as u32;
241 } else {
242 let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_TOSERVER);
243 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
244 let file_id : u32 = tx.id as u32;
245 filetracker_newchunk(&mut tdf.file_tracker, files, flags,
246 &file_name, wr.data, wr.wr_offset,
247 wr.wr_len, 0, false, &file_id);
248 }
249 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE);
250 tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER,
251 r.session_id, r.tree_id, 0); // TODO move into new_file_tx
252 }
253 }
254 state.file_ts_left = wr.wr_len - wr.data.len() as u32;
255 state.file_ts_guid = file_guid.to_vec();
256 SCLogDebug!("SMBv2 WRITE REQUEST: {} bytes left", state.file_ts_left);
257
258 },
259 _ => {
260 state.set_event(SMBEvent::MalformedData);
261 },
262 }
263}
264
265pub fn smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
266{
267 SCLogDebug!("SMBv2 request record, command {} tree {} session {}",
268 &smb2_command_string(r.command), r.tree_id, r.session_id);
269
75d7c9d6
VJ
270 let mut events : Vec<SMBEvent> = Vec::new();
271
272 let have_tx = match r.command {
273 SMB2_COMMAND_IOCTL => {
283be3ca
VJ
274 smb2_ioctl_request_record(state, r);
275 true
75d7c9d6
VJ
276 },
277 SMB2_COMMAND_TREE_DISCONNECT => {
278 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
279 state.ssn2tree_map.remove(&tree_key);
280 false
281 }
282 SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
283 match parse_smb2_request_negotiate_protocol(r.data) {
284 IResult::Done(_, rd) => {
285 let mut dialects : Vec<Vec<u8>> = Vec::new();
286 for d in rd.dialects_vec {
287 SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d));
288 let dvec = smb2_dialect_string(d).as_bytes().to_vec();
289 dialects.push(dvec);
290 }
291
292 let found = match state.get_negotiate_tx(2) {
293 Some(_) => {
294 SCLogNotice!("WEIRD, should not have NEGOTIATE tx!");
295 true
296 },
297 None => { false },
298 };
299 if !found {
300 let tx = state.new_negotiate_tx(2);
301 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
302 tdn.dialects2 = dialects;
6d56edc3 303 tdn.client_guid = Some(rd.client_guid.to_vec());
75d7c9d6
VJ
304 }
305 tx.request_done = true;
306 }
307 true
308 },
309 _ => {
310 events.push(SMBEvent::MalformedData);
311 false
312 },
313 }
314 },
315 SMB2_COMMAND_SESSION_SETUP => {
8bef1208
VJ
316 smb2_session_setup_request(state, r);
317 true
75d7c9d6
VJ
318 },
319 SMB2_COMMAND_TREE_CONNECT => {
320 match parse_smb2_request_tree_connect(r.data) {
321 IResult::Done(_, tr) => {
322 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
323 let mut name_val = tr.share_name.to_vec();
324 name_val.retain(|&i|i != 0x00);
325 if name_val.len() > 1 {
326 name_val = name_val[1..].to_vec();
327 }
328
329 let tx = state.new_treeconnect_tx(name_key, name_val);
330 tx.request_done = true;
331 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT);
332 true
333 }
334 _ => {
335 events.push(SMBEvent::MalformedData);
336 false
337 },
338 }
339 },
340 SMB2_COMMAND_READ => {
341 match parse_smb2_request_read(r.data) {
342 IResult::Done(_, rd) => {
343 SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}",
344 rd.guid, rd.rd_len, rd.rd_offset);
345
346 // store read guid,offset in map
347 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET);
348 let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset);
349 state.ssn2vecoffset_map.insert(guid_key, guidoff);
350 },
351 _ => {
352 events.push(SMBEvent::MalformedData);
353 },
354 }
355 false
356 },
357 SMB2_COMMAND_CREATE => {
358 match parse_smb2_request_create(r.data) {
359 IResult::Done(_, cr) => {
360 let del = cr.create_options & 0x0000_1000 != 0;
361 let dir = cr.create_options & 0x0000_0001 != 0;
362
363 SCLogDebug!("create_options {:08x}", cr.create_options);
364
365 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_FILENAME);
366 state.ssn2vec_map.insert(name_key, cr.data.to_vec());
367
368 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
369 let tx = state.new_create_tx(&cr.data.to_vec(),
370 cr.disposition, del, dir, tx_hdr);
371 tx.vercmd.set_smb2_cmd(r.command);
372 SCLogDebug!("TS CREATE TX {} created", tx.id);
373 true
374 },
375 _ => {
376 events.push(SMBEvent::MalformedData);
377 false
378 },
379 }
380 },
381 SMB2_COMMAND_WRITE => {
382 smb2_write_request_record(state, &r);
383 true // write handling creates both file tx and generic tx
384 },
385 SMB2_COMMAND_CLOSE => {
386 match parse_smb2_request_close(r.data) {
387 IResult::Done(_, cd) => {
fb986abe 388 let found_ts = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOSERVER) {
75d7c9d6
VJ
389 Some((tx, files, flags)) => {
390 if !tx.request_done {
391 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
392 tdf.file_tracker.close(files, flags);
393 }
394 }
395 tx.request_done = true;
396 tx.response_done = true;
397 tx.set_status(SMB_NTSTATUS_SUCCESS, false);
398 true
399 },
400 None => { false },
401 };
fb986abe 402 let found_tc = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOCLIENT) {
75d7c9d6
VJ
403 Some((tx, files, flags)) => {
404 if !tx.request_done {
405 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
406 tdf.file_tracker.close(files, flags);
407 }
408 }
409 tx.request_done = true;
410 tx.response_done = true;
411 tx.set_status(SMB_NTSTATUS_SUCCESS, false);
412 true
413 },
414 None => { false },
415 };
416 if !found_ts && !found_tc {
417 SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid);
418 }
419 },
420 _ => {
421 events.push(SMBEvent::MalformedData);
422 },
423 }
424 false
425 },
426 _ => {
427 false
428 },
429 };
430 /* if we don't have a tx, create it here (maybe) */
431 if !have_tx {
432 if smb2_create_new_tx(r.command) {
283be3ca 433 let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
75d7c9d6
VJ
434 let tx = state.new_generic_tx(2, r.command, tx_key);
435 SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}",
283be3ca 436 tx.id, r.command, r.session_id, r.tree_id, r.message_id);
75d7c9d6
VJ
437 tx.set_events(events);
438 }
439 }
440}
441
75d7c9d6
VJ
442pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
443{
444 SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
445 &smb2_command_string(r.command), &smb_ntstatus_string(r.nt_status),
446 r.tree_id, r.session_id, r.message_id);
447
75d7c9d6
VJ
448 let mut events : Vec<SMBEvent> = Vec::new();
449
450 let have_tx = match r.command {
451 SMB2_COMMAND_IOCTL => {
283be3ca
VJ
452 smb2_ioctl_response_record(state, r);
453 true
75d7c9d6
VJ
454 },
455 SMB2_COMMAND_SESSION_SETUP => {
8bef1208 456 smb2_session_setup_response(state, r);
75d7c9d6
VJ
457 true
458 },
459 SMB2_COMMAND_WRITE => {
ecbf10da
VJ
460 if r.nt_status == SMB_NTSTATUS_SUCCESS {
461 match parse_smb2_response_write(r.data)
462 {
463 IResult::Done(_, wr) => {
464 SCLogDebug!("SMBv2: Write response => {:?}", wr);
465
466 /* search key-guid map */
467 let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID,
468 r.session_id, r.tree_id, r.message_id);
469 let guid_vec = match state.ssn2vec_map.remove(&guid_key) {
470 Some(p) => p,
471 None => {
472 SCLogDebug!("SMBv2 response: GUID NOT FOUND");
473 Vec::new()
474 },
475 };
476 SCLogDebug!("SMBv2 write response for GUID {:?}", guid_vec);
477 }
478 _ => {
479 events.push(SMBEvent::MalformedData);
480 },
75d7c9d6 481 }
75d7c9d6
VJ
482 }
483 false // the request may have created a generic tx, so handle that here
484 },
485 SMB2_COMMAND_READ => {
486 if r.nt_status == SMB_NTSTATUS_SUCCESS {
487 smb2_read_response_record(state, &r);
488 false
489
490 } else if r.nt_status == SMB_NTSTATUS_END_OF_FILE {
491 SCLogDebug!("SMBv2: read response => EOF");
492
493 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET);
494 let file_guid = match state.ssn2vecoffset_map.remove(&guid_key) {
495 Some(o) => o.guid,
496 _ => {
497 SCLogNotice!("SMBv2 READ response: reply to unknown request");
498 Vec::new()
499 },
500 };
fb986abe 501 let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
75d7c9d6
VJ
502 Some((tx, files, flags)) => {
503 if !tx.request_done {
504 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
505 tdf.file_tracker.close(files, flags);
506 }
507 }
508 tx.set_status(r.nt_status, false);
509 tx.request_done = true;
510 false
511 },
512 None => { false },
513 };
514 if !found {
515 SCLogDebug!("SMBv2 READ: no TX at GUID {:?}", file_guid);
516 }
517 false
518 } else {
519 SCLogNotice!("SMBv2 READ: status {}", &smb_ntstatus_string(r.nt_status));
520 false
521 }
522 },
523 SMB2_COMMAND_CREATE => {
524 if r.nt_status == SMB_NTSTATUS_SUCCESS {
525 match parse_smb2_response_create(r.data) {
526 IResult::Done(_, cr) => {
527 SCLogDebug!("SMBv2: Create response => {:?}", cr);
528
529 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_FILENAME);
0e05ef73
VJ
530 if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) {
531 p.retain(|&i|i != 0x00);
532 state.guid2name_map.insert(cr.guid.to_vec(), p);
533 } else {
534 SCLogDebug!("SMBv2 response: GUID NOT FOUND");
535 }
536
537 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
538 if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) {
539 SCLogDebug!("tx {} with {}/{} marked as done",
540 tx.id, r.command, &smb2_command_string(r.command));
541 tx.set_status(r.nt_status, false);
542 tx.response_done = true;
543
544 if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data {
545 tdn.create_ts = cr.create_ts.as_unix();
546 tdn.last_access_ts = cr.last_access_ts.as_unix();
547 tdn.last_write_ts = cr.last_write_ts.as_unix();
548 tdn.last_change_ts = cr.last_change_ts.as_unix();
549 tdn.size = cr.size;
fb986abe 550 tdn.guid = cr.guid.to_vec();
0e05ef73 551 }
75d7c9d6
VJ
552 }
553 }
554 _ => {
555 events.push(SMBEvent::MalformedData);
556 },
557 }
0e05ef73
VJ
558 true
559 } else {
560 false
75d7c9d6 561 }
75d7c9d6
VJ
562 },
563 SMB2_COMMAND_TREE_DISCONNECT => {
564 // normally removed when processing request,
565 // but in case we missed that try again here
566 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
567 state.ssn2tree_map.remove(&tree_key);
568 false
569 }
570 SMB2_COMMAND_TREE_CONNECT => {
571 if r.nt_status == SMB_NTSTATUS_SUCCESS {
572 match parse_smb2_response_tree_connect(r.data) {
573 IResult::Done(_, tr) => {
574 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
575 let mut share_name = Vec::new();
576 let is_pipe = tr.share_type == 2;
577 let found = match state.get_treeconnect_tx(name_key) {
578 Some(tx) => {
579 if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
c56f5e11 580 tdn.share_type = tr.share_type;
75d7c9d6
VJ
581 tdn.is_pipe = is_pipe;
582 tdn.tree_id = r.tree_id as u32;
583 share_name = tdn.share_name.to_vec();
584 }
585 // update hdr now that we have a tree_id
586 tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
587 tx.response_done = true;
588 tx.set_status(r.nt_status, false);
589 true
590 },
591 None => { false },
592 };
593 if found {
594 let tree = SMBTree::new(share_name.to_vec(), is_pipe);
595 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
596 state.ssn2tree_map.insert(tree_key, tree);
597 }
598 true
599 }
600 _ => {
601 events.push(SMBEvent::MalformedData);
602 false
603 },
604 }
605 } else {
606 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
607 let found = match state.get_treeconnect_tx(name_key) {
608 Some(tx) => {
609 tx.response_done = true;
610 tx.set_status(r.nt_status, false);
611 true
612 },
613 None => { false },
614 };
615 found
616 }
617 },
618 SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
6d56edc3
VJ
619 let res = if r.nt_status == SMB_NTSTATUS_SUCCESS {
620 parse_smb2_response_negotiate_protocol(r.data)
621 } else {
622 parse_smb2_response_negotiate_protocol_error(r.data)
623 };
624 match res {
75d7c9d6
VJ
625 IResult::Done(_, rd) => {
626 SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect));
627
628 state.dialect = rd.dialect;
629 let found2 = match state.get_negotiate_tx(2) {
630 Some(tx) => {
6d56edc3
VJ
631 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
632 tdn.server_guid = rd.server_guid.to_vec();
633 }
75d7c9d6
VJ
634 tx.set_status(r.nt_status, false);
635 tx.response_done = true;
636 true
637 },
638 None => { false },
639 };
640 // SMB2 response to SMB1 request?
641 let found1 = !found2 && match state.get_negotiate_tx(1) {
642 Some(tx) => {
6d56edc3
VJ
643 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
644 tdn.server_guid = rd.server_guid.to_vec();
645 }
75d7c9d6
VJ
646 tx.set_status(r.nt_status, false);
647 tx.response_done = true;
648 true
649 },
ecbf10da 650 None => { false },
75d7c9d6
VJ
651 };
652 found1 || found2
653 },
654 _ => {
655 events.push(SMBEvent::MalformedData);
656 false
657 }
658 }
659 },
660 _ => {
661 SCLogDebug!("default case: no TX");
662 false
663 },
664 };
665 if !have_tx {
283be3ca 666 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
75d7c9d6
VJ
667 SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
668 &smb2_command_string(r.command),
283be3ca 669 r.session_id, r.tree_id, r.message_id);
75d7c9d6
VJ
670 let _found = match state.get_generic_tx(2, r.command, &tx_hdr) {
671 Some(tx) => {
672 SCLogDebug!("tx {} with {}/{} marked as done",
673 tx.id, r.command, &smb2_command_string(r.command));
674 if r.nt_status != SMB_NTSTATUS_PENDING {
675 tx.response_done = true;
676 }
677 tx.set_status(r.nt_status, false);
678 tx.set_events(events);
679 true
680 },
681 _ => {
682 SCLogNotice!("no tx found for {:?}", r);
683 false
684 },
685 };
686 }
687}