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