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