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 
18 use nom;
19 
20 use crate::core::*;
21 
22 use crate::smb::smb::*;
23 use crate::smb::smb2_records::*;
24 use crate::smb::smb2_session::*;
25 use crate::smb::smb2_ioctl::*;
26 use crate::smb::dcerpc::*;
27 use crate::smb::events::*;
28 use crate::smb::files::*;
29 
30 pub const SMB2_COMMAND_NEGOTIATE_PROTOCOL:      u16 = 0;
31 pub const SMB2_COMMAND_SESSION_SETUP:           u16 = 1;
32 pub const SMB2_COMMAND_SESSION_LOGOFF:          u16 = 2;
33 pub const SMB2_COMMAND_TREE_CONNECT:            u16 = 3;
34 pub const SMB2_COMMAND_TREE_DISCONNECT:         u16 = 4;
35 pub const SMB2_COMMAND_CREATE:                  u16 = 5;
36 pub const SMB2_COMMAND_CLOSE:                   u16 = 6;
37 pub const SMB2_COMMAND_FLUSH:                   u16 = 7;
38 pub const SMB2_COMMAND_READ:                    u16 = 8;
39 pub const SMB2_COMMAND_WRITE:                   u16 = 9;
40 pub const SMB2_COMMAND_LOCK:                    u16 = 10;
41 pub const SMB2_COMMAND_IOCTL:                   u16 = 11;
42 pub const SMB2_COMMAND_CANCEL:                  u16 = 12;
43 pub const SMB2_COMMAND_KEEPALIVE:               u16 = 13;
44 pub const SMB2_COMMAND_FIND:                    u16 = 14;
45 pub const SMB2_COMMAND_CHANGE_NOTIFY:           u16 = 15;
46 pub const SMB2_COMMAND_GET_INFO:                u16 = 16;
47 pub const SMB2_COMMAND_SET_INFO:                u16 = 17;
48 pub const SMB2_COMMAND_OPLOCK_BREAK:            u16 = 18;
49 
smb2_command_string(c: u16) -> String50 pub 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",
60         SMB2_COMMAND_FLUSH                  => "SMB2_COMMAND_FLUSH",
61         SMB2_COMMAND_WRITE                  => "SMB2_COMMAND_WRITE",
62         SMB2_COMMAND_LOCK                   => "SMB2_COMMAND_LOCK",
63         SMB2_COMMAND_IOCTL                  => "SMB2_COMMAND_IOCTL",
64         SMB2_COMMAND_CANCEL                 => "SMB2_COMMAND_CANCEL",
65         SMB2_COMMAND_KEEPALIVE              => "SMB2_COMMAND_KEEPALIVE",
66         SMB2_COMMAND_FIND                   => "SMB2_COMMAND_FIND",
67         SMB2_COMMAND_CHANGE_NOTIFY          => "SMB2_COMMAND_CHANGE_NOTIFY",
68         SMB2_COMMAND_GET_INFO               => "SMB2_COMMAND_GET_INFO",
69         SMB2_COMMAND_SET_INFO               => "SMB2_COMMAND_SET_INFO",
70         SMB2_COMMAND_OPLOCK_BREAK           => "SMB2_COMMAND_OPLOCK_BREAK",
71         _ => { return (c).to_string(); },
72     }.to_string()
73 
74 }
75 
smb2_dialect_string(d: u16) -> String76 pub 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
smb2_create_new_tx(cmd: u16) -> bool93 fn 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     }
101 }
102 
smb2_read_response_record_generic<'b>(state: &mut SMBState, r: &Smb2Record<'b>)103 fn 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 
smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)115 pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
116 {
117     smb2_read_response_record_generic(state, r);
118 
119     match parse_smb2_response_read(r.data) {
120         Ok((_, rd)) => {
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 {
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 
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 => {
139                     SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}",rd);
140                     state.set_skip(STREAM_TOCLIENT, rd.len, rd.data.len() as u32);
141                     return;
142                 },
143             };
144             SCLogDebug!("SMBv2 READ: GUID {:?} offset {}", file_guid, offset);
145 
146             let mut set_event_fileoverlap = false;
147             // look up existing tracker and if we have it update it
148             let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
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;
152                         if offset < tdf.file_tracker.tracked {
153                             set_event_fileoverlap = true;
154                         }
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             };
163             SCLogDebug!("existing file tx? {}", found);
164             if !found {
165                 let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE);
166                 let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) {
167                     Some(n) => (n.name.to_vec(), n.is_pipe),
168                     _ => { (Vec::new(), false) },
169                 };
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
176                 };
177                 SCLogDebug!("SMBv2/READ: share_name {:?} is_pipe {} is_dcerpc {}",
178                         share_name, is_pipe, is_dcerpc);
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 
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");
195                     }
196                 }
197 
198                 if is_pipe && is_dcerpc {
199                     SCLogDebug!("SMBv2 DCERPC read");
200                     let hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER);
201                     let vercmd = SMBVerCmdStat::new2_with_ntstatus(SMB2_COMMAND_READ, r.nt_status);
202                     smb_read_dcerpc_record(state, vercmd, hdr, &file_guid, rd.data);
203                 } else if is_pipe {
204                     SCLogDebug!("non-DCERPC pipe");
205                     state.set_skip(STREAM_TOCLIENT, rd.len, rd.data.len() as u32);
206                 } else {
207                     let file_name = match state.guid2name_map.get(&file_guid) {
208                         Some(n) => { n.to_vec() },
209                         None => { b"<unknown>".to_vec() },
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;
214                         if offset < tdf.file_tracker.tracked {
215                             set_event_fileoverlap = true;
216                         }
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 
228             if set_event_fileoverlap {
229                 state.set_event(SMBEvent::FileOverlap);
230             }
231             state.set_file_left(STREAM_TOCLIENT, rd.len, rd.data.len() as u32, file_guid.to_vec());
232         }
233         _ => {
234             SCLogDebug!("SMBv2: failed to parse read response");
235             state.set_event(SMBEvent::MalformedData);
236         }
237     }
238 }
239 
smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)240 pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
241 {
242     SCLogDebug!("SMBv2/WRITE: request record");
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) {
249         Ok((_, wr)) => {
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 
260             let mut set_event_fileoverlap = false;
261             let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOSERVER) {
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;
265                         if wr.wr_offset < tdf.file_tracker.tracked {
266                             set_event_fileoverlap = true;
267                         }
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);
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) },
281                 };
282                 let mut is_dcerpc = if is_pipe || (share_name.len() == 0 && !is_pipe) {
283                     match state.get_service_for_guid(&wr.guid) {
284                         (_, x) => x,
285                     }
286                 } else {
287                     false
288                 };
289                 SCLogDebug!("SMBv2/WRITE: share_name {:?} is_pipe {} is_dcerpc {}",
290                         share_name, is_pipe, is_dcerpc);
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 
296                     if smb_dcerpc_probe(wr.data) == true {
297                         SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc");
298 
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");
309                     }
310                 }
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");
318                     state.set_skip(STREAM_TOSERVER, wr.wr_len, wr.data.len() as u32);
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;
323                         if wr.wr_offset < tdf.file_tracker.tracked {
324                             set_event_fileoverlap = true;
325                         }
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             }
335 
336             if set_event_fileoverlap {
337                 state.set_event(SMBEvent::FileOverlap);
338             }
339             state.set_file_left(STREAM_TOSERVER, wr.wr_len, wr.data.len() as u32, file_guid.to_vec());
340         },
341         _ => {
342             state.set_event(SMBEvent::MalformedData);
343         },
344     }
345 }
346 
smb2_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)347 pub 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 
352     let mut events : Vec<SMBEvent> = Vec::new();
353 
354     let have_tx = match r.command {
355         SMB2_COMMAND_SET_INFO => {
356             SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", r);
357             let have_si_tx = match parse_smb2_request_setinfo(r.data) {
358                 Ok((_, rd)) => {
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                 },
378                 Err(nom::Err::Incomplete(_n)) => {
379                     SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _n);
380                     events.push(SMBEvent::MalformedData);
381                     false
382                 },
383                 Err(nom::Err::Error(_e)) |
384                 Err(nom::Err::Failure(_e)) => {
385                     SCLogDebug!("SMB2_COMMAND_SET_INFO: {:?}", _e);
386                     events.push(SMBEvent::MalformedData);
387                     false
388                 },
389             };
390             have_si_tx
391         },
392         SMB2_COMMAND_IOCTL => {
393             smb2_ioctl_request_record(state, r);
394             true
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) {
403                 Ok((_, rd)) => {
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(_) => {
413                             SCLogDebug!("WEIRD, should not have NEGOTIATE tx!");
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;
422                             tdn.client_guid = Some(rd.client_guid.to_vec());
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 => {
435             smb2_session_setup_request(state, r);
436             true
437         },
438         SMB2_COMMAND_TREE_CONNECT => {
439             match parse_smb2_request_tree_connect(r.data) {
440                 Ok((_, tr)) => {
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) {
461                 Ok((_, rd)) => {
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) {
478                 Ok((_, cr)) => {
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 => {
501             smb2_write_request_record(state, &r);
502             true // write handling creates both file tx and generic tx
503         },
504         SMB2_COMMAND_CLOSE => {
505             match parse_smb2_request_close(r.data) {
506                 Ok((_, cd)) => {
507                     let found_ts = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOSERVER) {
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                     };
521                     let found_tc = match state.get_file_tx_by_fuid(&cd.guid.to_vec(), STREAM_TOCLIENT) {
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) {
552             let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
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 {}",
555                     tx.id, r.command, r.session_id, r.tree_id, r.message_id);
556             tx.set_events(events);
557         }
558     }
559 }
560 
smb2_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)561 pub 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 
567     let mut events : Vec<SMBEvent> = Vec::new();
568 
569     let have_tx = match r.command {
570         SMB2_COMMAND_IOCTL => {
571             smb2_ioctl_response_record(state, r);
572             true
573         },
574         SMB2_COMMAND_SESSION_SETUP => {
575             smb2_session_setup_response(state, r);
576             true
577         },
578         SMB2_COMMAND_WRITE => {
579             if r.nt_status == SMB_NTSTATUS_SUCCESS {
580                 match parse_smb2_response_write(r.data)
581                 {
582                     Ok((_, _wr)) => {
583                         SCLogDebug!("SMBv2: Write response => {:?}", _wr);
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);
588                         let _guid_vec = match state.ssn2vec_map.remove(&guid_key) {
589                             Some(p) => p,
590                             None => {
591                                 SCLogDebug!("SMBv2 response: GUID NOT FOUND");
592                                 Vec::new()
593                             },
594                         };
595                         SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec);
596                     }
597                     _ => {
598                         events.push(SMBEvent::MalformedData);
599                     },
600                 }
601             }
602             false // the request may have created a generic tx, so handle that here
603         },
604         SMB2_COMMAND_READ => {
605             if r.nt_status == SMB_NTSTATUS_SUCCESS ||
606                r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW {
607                 smb2_read_response_record(state, &r);
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                     _ => {
617                         SCLogDebug!("SMBv2 READ response: reply to unknown request");
618                         Vec::new()
619                     },
620                 };
621                 let found = match state.get_file_tx_by_fuid(&file_guid, STREAM_TOCLIENT) {
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 {
639                 SCLogDebug!("SMBv2 READ: status {}", &smb_ntstatus_string(r.nt_status));
640                 false
641             }
642         },
643         SMB2_COMMAND_CREATE => {
644             if r.nt_status == SMB_NTSTATUS_SUCCESS {
645                 match parse_smb2_response_create(r.data) {
646                     Ok((_, cr)) => {
647                         SCLogDebug!("SMBv2: Create response => {:?}", cr);
648 
649                         let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_FILENAME);
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;
670                                 tdn.guid = cr.guid.to_vec();
671                             }
672                         }
673                     }
674                     _ => {
675                         events.push(SMBEvent::MalformedData);
676                     },
677                 }
678                 true
679             } else {
680                 false
681             }
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) {
693                     Ok((_, tr)) => {
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 {
700                                     tdn.share_type = tr.share_type;
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 => {
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 {
745                 Ok((_, rd)) => {
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) => {
751                             if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
752                                 tdn.server_guid = rd.server_guid.to_vec();
753                             }
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) => {
763                             if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data {
764                                 tdn.server_guid = rd.server_guid.to_vec();
765                             }
766                             tx.set_status(r.nt_status, false);
767                             tx.response_done = true;
768                             true
769                         },
770                         None => { false },
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 {
786         let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX);
787         SCLogDebug!("looking for TX {} with session_id {} tree_id {} message_id {}",
788                 &smb2_command_string(r.command),
789                 r.session_id, r.tree_id, r.message_id);
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             _ => {
802                 SCLogDebug!("no tx found for {:?}", r);
803                 false
804             },
805         };
806     }
807 }
808