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 use nom::IResult;
20 use nom::combinator::rest;
21 use nom::number::streaming::{le_u8, le_u16, le_u32, le_u64};
22 use crate::smb::smb::*;
23 use crate::smb::nbss_records::NBSS_MSGTYPE_SESSION_MESSAGE;
24 
25 #[derive(Debug,PartialEq)]
26 pub struct Smb2SecBlobRecord<'a> {
27     pub data: &'a[u8],
28 }
29 
30 named!(pub parse_smb2_sec_blob<Smb2SecBlobRecord>,
31     do_parse!(
32          data: rest
33          >> ( Smb2SecBlobRecord {
34                 data: data,
35             })
36 ));
37 
38 #[derive(Debug,PartialEq)]
39 pub struct Smb2RecordDir<> {
40     pub request: bool,
41 }
42 
43 named!(pub parse_smb2_record_direction<Smb2RecordDir>,
44     do_parse!(
45             _server_component: tag!(b"\xfeSMB")
46         >>  _skip: take!(12)
47         >>  flags: le_u8
48         >> (Smb2RecordDir {
49                 request: flags & 0x01 == 0,
50            })
51 ));
52 
53 #[derive(Debug,PartialEq)]
54 pub struct Smb2Record<'a> {
55     pub direction: u8,    // 0 req, 1 res
56     pub nt_status: u32,
57     pub command: u16,
58     pub message_id: u64,
59     pub tree_id: u32,
60     pub async_id: u64,
61     pub session_id: u64,
62     pub data: &'a[u8],
63 }
64 
65 impl<'a> Smb2Record<'a> {
is_async(&self) -> bool66     pub fn is_async(&self) -> bool {
67         self.async_id != 0
68     }
69 }
70 
parse_smb2_request_flags(i:&[u8]) -> IResult<&[u8],(u8,u8,u8,u32,u8,u8,u8,u8)>71 fn parse_smb2_request_flags(i:&[u8]) -> IResult<&[u8],(u8,u8,u8,u32,u8,u8,u8,u8)> {
72     bits!(i,
73         tuple!(
74             take_bits!(2u8),      // reserved / unused
75             take_bits!(1u8),      // replay op
76             take_bits!(1u8),      // dfs op
77             take_bits!(24u32),    // reserved / unused
78             take_bits!(1u8),      // signing
79             take_bits!(1u8),      // chained
80             take_bits!(1u8),      // async
81             take_bits!(1u8)       // response
82         ))
83 }
84 
85 named!(pub parse_smb2_request_record<Smb2Record>,
86     do_parse!(
87             _server_component: tag!(b"\xfeSMB")
88         >>  hlen: le_u16
89         >>  _credit_charge: le_u16
90         >>  _channel_seq: le_u16
91         >>  _reserved: take!(2)
92         >>  command: le_u16
93         >>  _credits_requested: le_u16
94         >>  flags: parse_smb2_request_flags
95         >> chain_offset: le_u32
96         >> message_id: le_u64
97         >> _process_id: le_u32
98         >> tree_id: le_u32
99         >> session_id: le_u64
100         >> _signature: take!(16)
101         // there is probably a cleaner way to do this
102         >> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
103         >> data_r: cond!(chain_offset <= hlen as u32, rest)
104         >> (Smb2Record {
105                 direction: flags.7,
106                 nt_status: 0,
107                 command:command,
108                 message_id: message_id,
109                 tree_id: tree_id,
110                 async_id: 0,
111                 session_id: session_id,
112                 data: if data_c != None { data_c.unwrap() } else { data_r.unwrap() }
113            })
114 ));
115 
116 #[derive(Debug,PartialEq)]
117 pub struct Smb2NegotiateProtocolRequestRecord<'a> {
118     pub dialects_vec: Vec<u16>,
119     pub client_guid: &'a[u8],
120 }
121 
122 named!(pub parse_smb2_request_negotiate_protocol<Smb2NegotiateProtocolRequestRecord>,
123     do_parse!(
124             _struct_size: take!(2)
125         >>  dialects_count: le_u16
126         >>  _sec_mode: le_u16
127         >>  _reserved1: le_u16
128         >>  _capabilities: le_u32
129         >>  client_guid: take!(16)
130         >>  _ctx_offset: le_u32
131         >>  _ctx_cnt: le_u16
132         >>  _reserved2: le_u16
133         >>  dia_vec: count!(le_u16, dialects_count as usize)
134         >>  (Smb2NegotiateProtocolRequestRecord {
135                 dialects_vec: dia_vec,
136                 client_guid: client_guid,
137             })
138 ));
139 
140 #[derive(Debug,PartialEq)]
141 pub struct Smb2NegotiateProtocolResponseRecord<'a> {
142     pub dialect: u16,
143     pub server_guid: &'a[u8],
144 }
145 
146 named!(pub parse_smb2_response_negotiate_protocol<Smb2NegotiateProtocolResponseRecord>,
147     do_parse!(
148             _struct_size: take!(2)
149         >>  _skip1: take!(2)
150         >>  dialect: le_u16
151         >>  _ctx_cnt: le_u16
152         >>  server_guid: take!(16)
153         >>  (Smb2NegotiateProtocolResponseRecord {
154                 dialect,
155                 server_guid
156             })
157 ));
158 
159 named!(pub parse_smb2_response_negotiate_protocol_error<Smb2NegotiateProtocolResponseRecord>,
160     do_parse!(
161             _struct_size: take!(2)
162         >>  _skip1: take!(2)
163         >>  (Smb2NegotiateProtocolResponseRecord {
164                 dialect: 0,
165                 server_guid: &[],
166             })
167 ));
168 
169 
170 #[derive(Debug,PartialEq)]
171 pub struct Smb2SessionSetupRequestRecord<'a> {
172     pub data: &'a[u8],
173 }
174 
175 named!(pub parse_smb2_request_session_setup<Smb2SessionSetupRequestRecord>,
176     do_parse!(
177             _struct_size: take!(2)
178         >>  _flags: le_u8
179         >>  _security_mode: le_u8
180         >>  _capabilities: le_u32
181         >>  _channel: le_u32
182         >>  _sec_offset: le_u16
183         >>  _sec_len: le_u16
184         >>  _prev_ssn_id: take!(8)
185         >>  data: rest
186         >>  (Smb2SessionSetupRequestRecord {
187                 data:data,
188             })
189 ));
190 
191 
192 #[derive(Debug,PartialEq)]
193 pub struct Smb2TreeConnectRequestRecord<'a> {
194     pub share_name: &'a[u8],
195 }
196 
197 named!(pub parse_smb2_request_tree_connect<Smb2TreeConnectRequestRecord>,
198     do_parse!(
199             _struct_size: take!(2)
200         >>  _offset_length: take!(4)
201         >>  data: rest
202         >>  (Smb2TreeConnectRequestRecord {
203                 share_name:data,
204             })
205 ));
206 
207 #[derive(Debug,PartialEq)]
208 pub struct Smb2TreeConnectResponseRecord<> {
209     pub share_type: u8,
210 }
211 
212 named!(pub parse_smb2_response_tree_connect<Smb2TreeConnectResponseRecord>,
213     do_parse!(
214             _struct_size: take!(2)
215         >>  share_type: le_u8
216         >>  _share_flags: le_u32
217         >>  _share_caps: le_u32
218         >>  _access_mask: le_u32
219         >>  (Smb2TreeConnectResponseRecord {
220                 share_type
221             })
222 ));
223 
224 
225 #[derive(Debug,PartialEq)]
226 pub struct Smb2CreateRequestRecord<'a> {
227     pub disposition: u32,
228     pub create_options: u32,
229     pub data: &'a[u8],
230 }
231 
232 named!(pub parse_smb2_request_create<Smb2CreateRequestRecord>,
233     do_parse!(
234             _skip1: take!(36)
235         >>  disposition: le_u32
236         >>  create_options: le_u32
237         >>  _file_name_offset: le_u16
238         >>  file_name_length: le_u16
239         >>  _skip2: take!(8)
240         >>  data: take!(file_name_length)
241         >>  _skip3: rest
242         >>  (Smb2CreateRequestRecord {
243                 disposition,
244                 create_options,
245                 data
246             })
247 ));
248 
249 #[derive(Debug,PartialEq)]
250 pub struct Smb2IOCtlRequestRecord<'a> {
251     pub is_pipe: bool,
252     pub function: u32,
253     pub guid: &'a[u8],
254     pub data: &'a[u8],
255 }
256 
257 named!(pub parse_smb2_request_ioctl<Smb2IOCtlRequestRecord>,
258     do_parse!(
259             _skip: take!(2)  // structure size
260         >>  take!(2)        // reserved
261         >>  func: le_u32
262         >>  guid: take!(16)
263         >>  _indata_offset: le_u32
264         >>  indata_len: le_u32
265         >>  take!(4)
266         >>  _outdata_offset: le_u32
267         >>  _outdata_len: le_u32
268         >>  take!(12)
269         >>  data: take!(indata_len)
270         >>  (Smb2IOCtlRequestRecord {
271                 is_pipe: (func == 0x0011c017),
272                 function: func,
273                 guid:guid,
274                 data:data,
275             })
276 ));
277 
278 #[derive(Debug,PartialEq)]
279 pub struct Smb2IOCtlResponseRecord<'a> {
280     pub is_pipe: bool,
281     pub guid: &'a[u8],
282     pub data: &'a[u8],
283     pub indata_len: u32,
284     pub outdata_len: u32,
285     pub indata_offset: u32,
286     pub outdata_offset: u32,
287 }
288 
289 named!(pub parse_smb2_response_ioctl<Smb2IOCtlResponseRecord>,
290     do_parse!(
291             _skip: take!(2)  // structure size
292         >>  take!(2)        // reserved
293         >>  func: le_u32
294         >>  guid: take!(16)
295         >>  indata_offset: le_u32
296         >>  indata_len: le_u32
297         >>  outdata_offset: le_u32
298         >>  outdata_len: le_u32
299         >>  take!(8)
300         >>  take!(indata_len)
301         >>  data: take!(outdata_len)
302         >>  (Smb2IOCtlResponseRecord {
303                 is_pipe: (func == 0x0011c017),
304                 guid:guid,
305                 data:data,
306                 indata_len:indata_len,
307                 outdata_len:outdata_len,
308                 indata_offset:indata_offset,
309                 outdata_offset:outdata_offset,
310             })
311 ));
312 
313 #[derive(Debug,PartialEq)]
314 pub struct Smb2CloseRequestRecord<'a> {
315     pub guid: &'a[u8],
316 }
317 
318 named!(pub parse_smb2_request_close<Smb2CloseRequestRecord>,
319     do_parse!(
320             _skip: take!(8)
321         >>  guid: take!(16)
322         >>  (Smb2CloseRequestRecord {
323                 guid
324             })
325 ));
326 
327 #[derive(Debug)]
328 pub struct Smb2SetInfoRequestRenameRecord<'a> {
329     pub name: &'a[u8],
330 }
331 
332 named!(pub parse_smb2_request_setinfo_rename<Smb2SetInfoRequestRenameRecord>,
333     do_parse!(
334             _replace: le_u8
335         >>  _reserved: take!(7)
336         >>  _root_handle: take!(8)
337         >>  name_len: le_u32
338         >>  name: take!(name_len)
339         >> (Smb2SetInfoRequestRenameRecord {
340                 name
341             })
342 ));
343 
344 #[derive(Debug)]
345 pub struct Smb2SetInfoRequestRecord<'a> {
346     pub guid: &'a[u8],
347     pub class: u8,
348     pub infolvl: u8,
349     pub rename: Option<Smb2SetInfoRequestRenameRecord<'a>>,
350 }
351 
352 named!(pub parse_smb2_request_setinfo<Smb2SetInfoRequestRecord>,
353     do_parse!(
354             _struct_size: le_u16
355         >>  class: le_u8
356         >>  infolvl: le_u8
357         >>  setinfo_size: le_u32
358         >>  _setinfo_offset: le_u16
359         >>  _reserved: take!(2)
360         >>  _additional_info: le_u32
361         >>  guid: take!(16)
362         >>  rename: cond!(class == 1 && infolvl == 10, flat_map!(take!(setinfo_size),parse_smb2_request_setinfo_rename))
363         >> (Smb2SetInfoRequestRecord {
364                 guid: guid,
365                 class: class,
366                 infolvl: infolvl,
367                 rename: rename,
368             })
369 ));
370 
371 #[derive(Debug,PartialEq)]
372 pub struct Smb2WriteRequestRecord<'a> {
373     pub wr_len: u32,
374     pub wr_offset: u64,
375     pub guid: &'a[u8],
376     pub data: &'a[u8],
377 }
378 
379 // can be called on incomplete records
380 named!(pub parse_smb2_request_write<Smb2WriteRequestRecord>,
381     do_parse!(
382             _skip1: take!(4)
383         >>  wr_len: le_u32
384         >>  wr_offset: le_u64
385         >>  guid: take!(16)
386         >>  _channel: le_u32
387         >>  _remaining_bytes: le_u32
388         >>  _write_flags: le_u32
389         >>  _skip2: take!(4)
390         >>  data: call!(parse_smb2_data, wr_len)
391         >>  (Smb2WriteRequestRecord {
392                 wr_len:wr_len,
393                 wr_offset:wr_offset,
394                 guid:guid,
395                 data:data,
396             })
397 ));
398 
399 #[derive(Debug,PartialEq)]
400 pub struct Smb2ReadRequestRecord<'a> {
401     pub rd_len: u32,
402     pub rd_offset: u64,
403     pub guid: &'a[u8],
404 }
405 
406 named!(pub parse_smb2_request_read<Smb2ReadRequestRecord>,
407     do_parse!(
408             _skip1: take!(4)
409         >>  rd_len: le_u32
410         >>  rd_offset: le_u64
411         >>  guid: take!(16)
412         >>  _min_count: le_u32
413         >>  _channel: le_u32
414         >>  _remaining_bytes: le_u32
415         >>  _skip2: take!(4)
416         >>  (Smb2ReadRequestRecord {
417                 rd_len:rd_len,
418                 rd_offset:rd_offset,
419                 guid:guid,
420             })
421 ));
422 
423 #[derive(Debug,PartialEq)]
424 pub struct Smb2ReadResponseRecord<'a> {
425     pub len: u32,
426     pub data: &'a[u8],
427 }
428 
429 // parse read/write data. If all is available, 'take' it.
430 // otherwise just return what we have. So this may return
431 // partial data.
parse_smb2_data<'a>(i: &'a[u8], len: u32) -> IResult<&'a[u8], &'a[u8]>432 fn parse_smb2_data<'a>(i: &'a[u8], len: u32)
433     -> IResult<&'a[u8], &'a[u8]>
434 {
435     if len as usize > i.len() {
436         rest(i)
437     } else {
438         take!(i, len)
439     }
440 }
441 
442 // can be called on incomplete records
443 named!(pub parse_smb2_response_read<Smb2ReadResponseRecord>,
444     do_parse!(
445             _struct_size: le_u16
446         >>  _data_offset: le_u16
447         >>  rd_len: le_u32
448         >>  _rd_rem: le_u32
449         >>  _padding: take!(4)
450         >>  data: call!(parse_smb2_data, rd_len)
451         >>  (Smb2ReadResponseRecord {
452                 len : rd_len,
453                 data : data,
454             })
455 ));
456 
457 #[derive(Debug,PartialEq)]
458 pub struct Smb2CreateResponseRecord<'a> {
459     pub guid: &'a[u8],
460     pub create_ts: SMBFiletime,
461     pub last_access_ts: SMBFiletime,
462     pub last_write_ts: SMBFiletime,
463     pub last_change_ts: SMBFiletime,
464     pub size: u64,
465 }
466 
467 named!(pub parse_smb2_response_create<Smb2CreateResponseRecord>,
468     do_parse!(
469             _ssize: le_u16
470         >>  _oplock: le_u8
471         >>  _resp_flags: le_u8
472         >>  _create_action: le_u32
473         >>  create_ts: le_u64
474         >>  last_access_ts: le_u64
475         >>  last_write_ts: le_u64
476         >>  last_change_ts: le_u64
477         >>  _alloc_size: le_u64
478         >>  eof: le_u64
479         >>  _attrs: le_u32
480         >>  _padding: take!(4)
481         >>  guid: take!(16)
482         >>  _skip2: take!(8)
483         >>  (Smb2CreateResponseRecord {
484                 guid : guid,
485                 create_ts: SMBFiletime::new(create_ts),
486                 last_access_ts: SMBFiletime::new(last_access_ts),
487                 last_write_ts: SMBFiletime::new(last_write_ts),
488                 last_change_ts: SMBFiletime::new(last_change_ts),
489                 size: eof,
490             })
491 ));
492 
493 #[derive(Debug,PartialEq)]
494 pub struct Smb2WriteResponseRecord<> {
495     pub wr_cnt: u32,
496 }
497 
498 named!(pub parse_smb2_response_write<Smb2WriteResponseRecord>,
499     do_parse!(
500             _skip1: take!(4)
501         >>  wr_cnt: le_u32
502         >>  _skip2: take!(6)
503         >>  (Smb2WriteResponseRecord {
504                 wr_cnt : wr_cnt,
505             })
506 ));
507 
508 named!(pub parse_smb2_response_record<Smb2Record>,
509     do_parse!(
510             tag!(b"\xfeSMB")
511         >>  hlen: le_u16
512         >>  _credit_charge: le_u16
513         >>  nt_status: le_u32
514         >>  command: le_u16
515         >>  _credit_granted: le_u16
516         >>  flags: parse_smb2_request_flags
517         >> chain_offset: le_u32
518         >> message_id: le_u64
519         >> _process_id: cond!(flags.6==0, le_u32)
520         >> tree_id: cond!(flags.6==0, le_u32)
521         >> async_id: cond!(flags.6==1, le_u64)
522         >> session_id: le_u64
523         >> _signature: take!(16)
524         // there is probably a cleaner way to do this
525         >> data_c: cond!(chain_offset > hlen as u32, take!(chain_offset - hlen as u32))
526         >> data_r: cond!(chain_offset <= hlen as u32, rest)
527         >> (Smb2Record {
528                 direction: flags.7,
529                 nt_status: nt_status,
530                 message_id: message_id,
531                 tree_id: tree_id.unwrap_or(0),
532                 async_id: async_id.unwrap_or(0),
533                 session_id: session_id,
534                 command:command,
535                 data: data_c.or(data_r).unwrap()
536            })
537 ));
538 
smb_basic_search(d: &[u8]) -> usize539 fn smb_basic_search(d: &[u8]) -> usize {
540     let needle = b"SMB";
541     let mut r = 0 as usize;
542     // this could be replaced by aho-corasick
543     let iter = d.windows(needle.len());
544     for window in iter {
545         if window == needle {
546             return r;
547         }
548         r = r + 1;
549     }
550     return 0;
551 }
552 
search_smb_record<'a>(i: &'a [u8]) -> nom::IResult<&'a [u8], &'a [u8]>553 pub fn search_smb_record<'a>(i: &'a [u8]) -> nom::IResult<&'a [u8], &'a [u8]> {
554     let mut d = i;
555     while d.len() >= 4 {
556         let index = smb_basic_search(d);
557         if index == 0 {
558             return Err(nom::Err::Error((d, nom::error::ErrorKind::Eof)));
559         }
560         if d[index - 1] == 0xfe || d[index - 1] == 0xff || d[index - 1] == 0xfd {
561             // if we have enough data, check nbss
562             if index < 5 || d[index-5] == NBSS_MSGTYPE_SESSION_MESSAGE {
563                 return Ok((&d[index + 3..], &d[index - 1..]));
564             }
565         }
566         d = &d[index + 3..];
567     }
568     Err(nom::Err::Incomplete(nom::Needed::Size(4 as usize - d.len())))
569 }
570