]> git.ipfire.org Git - people/ms/suricata.git/blame - rust/src/smb/smb2.rs
ike: use derive macro from app-layer events
[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
361 if let Some(ref ren) = rd.rename {
362 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
363 let mut newname = ren.name.to_vec();
364 newname.retain(|&i|i != 0x00);
365 let oldname = match state.guid2name_map.get(rd.guid) {
366 Some(n) => { n.to_vec() },
367 None => { b"<unknown>".to_vec() },
368 };
369 let tx = state.new_rename_tx(rd.guid.to_vec(), oldname, newname);
370 tx.hdr = tx_hdr;
371 tx.request_done = true;
372 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_SET_INFO);
373 true
374 } else {
375 false
376 }
377 },
13b73997 378 Err(nom::Err::Incomplete(_n)) => {
7b61f2c5
VJ
379 SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _n);
380 events.push(SMBEvent::MalformedData);
381 false
382 },
13b73997
PC
383 Err(nom::Err::Error(_e)) |
384 Err(nom::Err::Failure(_e)) => {
7b61f2c5
VJ
385 SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _e);
386 events.push(SMBEvent::MalformedData);
387 false
388 },
389 };
390 have_si_tx
391 },
75d7c9d6 392 SMB2_COMMAND_IOCTL => {
283be3ca
VJ
393 smb2_ioctl_request_record(state, r);
394 true
75d7c9d6
VJ
395 },
396 SMB2_COMMAND_TREE_DISCONNECT => {
397 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
398 state.ssn2tree_map.remove(&tree_key);
399 false
400 }
401 SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
402 match parse_smb2_request_negotiate_protocol(r.data) {
13b73997 403 Ok((_, rd)) => {
75d7c9d6
VJ
404 let mut dialects : Vec<Vec<u8>> = Vec::new();
405 for d in rd.dialects_vec {
406 SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d));
407 let dvec = smb2_dialect_string(d).as_bytes().to_vec();
408 dialects.push(dvec);
409 }
410
411 let found = match state.get_negotiate_tx(2) {
412 Some(_) => {
ea1e13cb 413 SCLogDebug!("WEIRD, should not have NEGOTIATE tx!");
75d7c9d6
VJ
414 true
415 },
416 None => { false },
417 };
418 if !found {
419 let tx = state.new_negotiate_tx(2);
420 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
421 tdn.dialects2 = dialects;
6d56edc3 422 tdn.client_guid = Some(rd.client_guid.to_vec());
75d7c9d6
VJ
423 }
424 tx.request_done = true;
425 }
426 true
427 },
428 _ => {
429 events.push(SMBEvent::MalformedData);
430 false
431 },
432 }
433 },
434 SMB2_COMMAND_SESSION_SETUP => {
8bef1208
VJ
435 smb2_session_setup_request(state, r);
436 true
75d7c9d6
VJ
437 },
438 SMB2_COMMAND_TREE_CONNECT => {
439 match parse_smb2_request_tree_connect(r.data) {
13b73997 440 Ok((_, tr)) => {
75d7c9d6
VJ
441 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
442 let mut name_val = tr.share_name.to_vec();
443 name_val.retain(|&i|i != 0x00);
444 if name_val.len() > 1 {
445 name_val = name_val[1..].to_vec();
446 }
447
448 let tx = state.new_treeconnect_tx(name_key, name_val);
449 tx.request_done = true;
450 tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT);
451 true
452 }
453 _ => {
454 events.push(SMBEvent::MalformedData);
455 false
456 },
457 }
458 },
459 SMB2_COMMAND_READ => {
460 match parse_smb2_request_read(r.data) {
13b73997 461 Ok((_, rd)) => {
75d7c9d6
VJ
462 SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}",
463 rd.guid, rd.rd_len, rd.rd_offset);
464
465 // store read guid,offset in map
466 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET);
467 let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset);
468 state.ssn2vecoffset_map.insert(guid_key, guidoff);
469 },
470 _ => {
471 events.push(SMBEvent::MalformedData);
472 },
473 }
474 false
475 },
476 SMB2_COMMAND_CREATE => {
477 match parse_smb2_request_create(r.data) {
13b73997 478 Ok((_, cr)) => {
75d7c9d6
VJ
479 let del = cr.create_options & 0x0000_1000 != 0;
480 let dir = cr.create_options & 0x0000_0001 != 0;
481
482 SCLogDebug!("create_options {:08x}", cr.create_options);
483
484 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_FILENAME);
485 state.ssn2vec_map.insert(name_key, cr.data.to_vec());
486
487 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
488 let tx = state.new_create_tx(&cr.data.to_vec(),
489 cr.disposition, del, dir, tx_hdr);
490 tx.vercmd.set_smb2_cmd(r.command);
491 SCLogDebug!("TS CREATE TX {} created", tx.id);
492 true
493 },
494 _ => {
495 events.push(SMBEvent::MalformedData);
496 false
497 },
498 }
499 },
500 SMB2_COMMAND_WRITE => {
69cf5c9e 501 smb2_write_request_record(state, r);
75d7c9d6
VJ
502 true // write handling creates both file tx and generic tx
503 },
504 SMB2_COMMAND_CLOSE => {
505 match parse_smb2_request_close(r.data) {
13b73997 506 Ok((_, cd)) => {
fb986abe 507 let found_ts = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOSERVER) {
75d7c9d6
VJ
508 Some((tx, files, flags)) => {
509 if !tx.request_done {
510 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
511 tdf.file_tracker.close(files, flags);
512 }
513 }
514 tx.request_done = true;
515 tx.response_done = true;
516 tx.set_status(SMB_NTSTATUS_SUCCESS, false);
517 true
518 },
519 None => { false },
520 };
fb986abe 521 let found_tc = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOCLIENT) {
75d7c9d6
VJ
522 Some((tx, files, flags)) => {
523 if !tx.request_done {
524 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
525 tdf.file_tracker.close(files, flags);
526 }
527 }
528 tx.request_done = true;
529 tx.response_done = true;
530 tx.set_status(SMB_NTSTATUS_SUCCESS, false);
531 true
532 },
533 None => { false },
534 };
535 if !found_ts && !found_tc {
536 SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid);
537 }
538 },
539 _ => {
540 events.push(SMBEvent::MalformedData);
541 },
542 }
543 false
544 },
545 _ => {
546 false
547 },
548 };
549 /* if we don't have a tx, create it here (maybe) */
550 if !have_tx {
551 if smb2_create_new_tx(r.command) {
283be3ca 552 let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
75d7c9d6
VJ
553 let tx = state.new_generic_tx(2, r.command, tx_key);
554 SCLogDebug!("TS TX {} command {} created with session_id {} tree_id {} message_id {}",
283be3ca 555 tx.id, r.command, r.session_id, r.tree_id, r.message_id);
75d7c9d6
VJ
556 tx.set_events(events);
557 }
558 }
559}
560
75d7c9d6
VJ
561pub fn smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
562{
563 SCLogDebug!("SMBv2 response record, command {} status {} tree {} session {} message {}",
564 &smb2_command_string(r.command), &smb_ntstatus_string(r.nt_status),
565 r.tree_id, r.session_id, r.message_id);
566
75d7c9d6
VJ
567 let mut events : Vec<SMBEvent> = Vec::new();
568
569 let have_tx = match r.command {
570 SMB2_COMMAND_IOCTL => {
283be3ca
VJ
571 smb2_ioctl_response_record(state, r);
572 true
75d7c9d6
VJ
573 },
574 SMB2_COMMAND_SESSION_SETUP => {
8bef1208 575 smb2_session_setup_response(state, r);
75d7c9d6
VJ
576 true
577 },
578 SMB2_COMMAND_WRITE => {
ecbf10da
VJ
579 if r.nt_status == SMB_NTSTATUS_SUCCESS {
580 match parse_smb2_response_write(r.data)
581 {
ef575533
PA
582 Ok((_, _wr)) => {
583 SCLogDebug!("SMBv2: Write response => {:?}", _wr);
ecbf10da
VJ
584
585 /* search key-guid map */
586 let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID,
587 r.session_id, r.tree_id, r.message_id);
ef575533 588 let _guid_vec = match state.ssn2vec_map.remove(&guid_key) {
ecbf10da
VJ
589 Some(p) => p,
590 None => {
591 SCLogDebug!("SMBv2 response: GUID NOT FOUND");
592 Vec::new()
593 },
594 };
ef575533 595 SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec);
ecbf10da
VJ
596 }
597 _ => {
598 events.push(SMBEvent::MalformedData);
599 },
75d7c9d6 600 }
75d7c9d6
VJ
601 }
602 false // the request may have created a generic tx, so handle that here
603 },
604 SMB2_COMMAND_READ => {
ac4e8885
VJ
605 if r.nt_status == SMB_NTSTATUS_SUCCESS ||
606 r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
69cf5c9e 607 smb2_read_response_record(state, r);
75d7c9d6
VJ
608 false
609
610 } else if r.nt_status == SMB_NTSTATUS_END_OF_FILE {
611 SCLogDebug!("SMBv2: read response => EOF");
612
613 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_OFFSET);
614 let file_guid = match state.ssn2vecoffset_map.remove(&guid_key) {
615 Some(o) => o.guid,
616 _ => {
ea1e13cb 617 SCLogDebug!("SMBv2 READ response: reply to unknown request");
75d7c9d6
VJ
618 Vec::new()
619 },
620 };
fb986abe 621 let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
75d7c9d6
VJ
622 Some((tx, files, flags)) => {
623 if !tx.request_done {
624 if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data {
625 tdf.file_tracker.close(files, flags);
626 }
627 }
628 tx.set_status(r.nt_status, false);
629 tx.request_done = true;
630 false
631 },
632 None => { false },
633 };
634 if !found {
635 SCLogDebug!("SMBv2 READ: no TX at GUID {:?}", file_guid);
636 }
637 false
638 } else {
ea1e13cb 639 SCLogDebug!("SMBv2 READ: status {}", &smb_ntstatus_string(r.nt_status));
75d7c9d6
VJ
640 false
641 }
642 },
643 SMB2_COMMAND_CREATE => {
644 if r.nt_status == SMB_NTSTATUS_SUCCESS {
645 match parse_smb2_response_create(r.data) {
13b73997 646 Ok((_, cr)) => {
75d7c9d6
VJ
647 SCLogDebug!("SMBv2: Create response => {:?}", cr);
648
649 let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_FILENAME);
0e05ef73
VJ
650 if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) {
651 p.retain(|&i|i != 0x00);
652 state.guid2name_map.insert(cr.guid.to_vec(), p);
653 } else {
654 SCLogDebug!("SMBv2 response: GUID NOT FOUND");
655 }
656
657 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
658 if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) {
659 SCLogDebug!("tx {} with {}/{} marked as done",
660 tx.id, r.command, &smb2_command_string(r.command));
661 tx.set_status(r.nt_status, false);
662 tx.response_done = true;
663
664 if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data {
665 tdn.create_ts = cr.create_ts.as_unix();
666 tdn.last_access_ts = cr.last_access_ts.as_unix();
667 tdn.last_write_ts = cr.last_write_ts.as_unix();
668 tdn.last_change_ts = cr.last_change_ts.as_unix();
669 tdn.size = cr.size;
fb986abe 670 tdn.guid = cr.guid.to_vec();
0e05ef73 671 }
75d7c9d6
VJ
672 }
673 }
674 _ => {
675 events.push(SMBEvent::MalformedData);
676 },
677 }
0e05ef73
VJ
678 true
679 } else {
680 false
75d7c9d6 681 }
75d7c9d6
VJ
682 },
683 SMB2_COMMAND_TREE_DISCONNECT => {
684 // normally removed when processing request,
685 // but in case we missed that try again here
686 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
687 state.ssn2tree_map.remove(&tree_key);
688 false
689 }
690 SMB2_COMMAND_TREE_CONNECT => {
691 if r.nt_status == SMB_NTSTATUS_SUCCESS {
692 match parse_smb2_response_tree_connect(r.data) {
13b73997 693 Ok((_, tr)) => {
75d7c9d6
VJ
694 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
695 let mut share_name = Vec::new();
696 let is_pipe = tr.share_type == 2;
697 let found = match state.get_treeconnect_tx(name_key) {
698 Some(tx) => {
699 if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data {
c56f5e11 700 tdn.share_type = tr.share_type;
75d7c9d6
VJ
701 tdn.is_pipe = is_pipe;
702 tdn.tree_id = r.tree_id as u32;
703 share_name = tdn.share_name.to_vec();
704 }
705 // update hdr now that we have a tree_id
706 tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
707 tx.response_done = true;
708 tx.set_status(r.nt_status, false);
709 true
710 },
711 None => { false },
712 };
713 if found {
714 let tree = SMBTree::new(share_name.to_vec(), is_pipe);
715 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
716 state.ssn2tree_map.insert(tree_key, tree);
717 }
718 true
719 }
720 _ => {
721 events.push(SMBEvent::MalformedData);
722 false
723 },
724 }
725 } else {
726 let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE);
727 let found = match state.get_treeconnect_tx(name_key) {
728 Some(tx) => {
729 tx.response_done = true;
730 tx.set_status(r.nt_status, false);
731 true
732 },
733 None => { false },
734 };
735 found
736 }
737 },
738 SMB2_COMMAND_NEGOTIATE_PROTOCOL => {
6d56edc3
VJ
739 let res = if r.nt_status == SMB_NTSTATUS_SUCCESS {
740 parse_smb2_response_negotiate_protocol(r.data)
741 } else {
742 parse_smb2_response_negotiate_protocol_error(r.data)
743 };
744 match res {
13b73997 745 Ok((_, rd)) => {
75d7c9d6
VJ
746 SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect));
747
748 state.dialect = rd.dialect;
749 let found2 = match state.get_negotiate_tx(2) {
750 Some(tx) => {
6d56edc3
VJ
751 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
752 tdn.server_guid = rd.server_guid.to_vec();
753 }
75d7c9d6
VJ
754 tx.set_status(r.nt_status, false);
755 tx.response_done = true;
756 true
757 },
758 None => { false },
759 };
760 // SMB2 response to SMB1 request?
761 let found1 = !found2 && match state.get_negotiate_tx(1) {
762 Some(tx) => {
6d56edc3
VJ
763 if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
764 tdn.server_guid = rd.server_guid.to_vec();
765 }
75d7c9d6
VJ
766 tx.set_status(r.nt_status, false);
767 tx.response_done = true;
768 true
769 },
ecbf10da 770 None => { false },
75d7c9d6
VJ
771 };
772 found1 || found2
773 },
774 _ => {
775 events.push(SMBEvent::MalformedData);
776 false
777 }
778 }
779 },
780 _ => {
781 SCLogDebug!("default case: no TX");
782 false
783 },
784 };
785 if !have_tx {
283be3ca 786 let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
75d7c9d6
VJ
787 SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
788 &smb2_command_string(r.command),
283be3ca 789 r.session_id, r.tree_id, r.message_id);
75d7c9d6
VJ
790 let _found = match state.get_generic_tx(2, r.command, &tx_hdr) {
791 Some(tx) => {
792 SCLogDebug!("tx {} with {}/{} marked as done",
793 tx.id, r.command, &smb2_command_string(r.command));
794 if r.nt_status != SMB_NTSTATUS_PENDING {
795 tx.response_done = true;
796 }
797 tx.set_status(r.nt_status, false);
798 tx.set_events(events);
799 true
800 },
801 _ => {
ea1e13cb 802 SCLogDebug!("no tx found for {:?}", r);
75d7c9d6
VJ
803 false
804 },
805 };
806 }
807}