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