From 0c9fdf8f4fe9c4d86468a75313b95abf2fe89436 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Mon, 6 Dec 2021 11:35:23 +0100 Subject: [PATCH] smb: implement frames SMB1 record parsing code simplification. Frames: nbss.pdu nbss.hdr nbss.data smb1.pdu smb1.hdr smb1.data smb2.pdu smb2.hdr smb2.data smb3.pdu smb3.hdr smb3.data The smb* frames are created for valid SMB records. --- rust/src/smb/smb.rs | 381 ++++++++++++++++++++++++++++------- rust/src/smb/smb1.rs | 20 +- rust/src/smb/smb2_records.rs | 3 + 3 files changed, 317 insertions(+), 87 deletions(-) diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 6336ce4384..9a9a59823a 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -36,6 +36,7 @@ use nom7::{Err, Needed}; use crate::core::*; use crate::applayer; use crate::applayer::*; +use crate::frames::*; use crate::conf::*; use crate::filecontainer::*; use crate::applayer::{AppLayerResult, AppLayerTxData, AppLayerEvent}; @@ -53,6 +54,82 @@ use crate::smb::events::*; use crate::smb::files::*; use crate::smb::smb2_ioctl::*; +#[repr(C)] +pub enum SMBFrameType { + NBSSPdu = 0, + NBSSHdr = 1, + NBSSData = 2, + SMB1Pdu = 3, + SMB1Hdr = 4, + SMB1Data = 5, + SMB2Pdu = 6, + SMB2Hdr = 7, + SMB2Data = 8, + SMB3Pdu = 9, + SMB3Hdr = 10, + SMB3Data = 11, +} + +impl SMBFrameType { + fn from_u8(value: u8) -> Option { + match value { + 0 => Some(SMBFrameType::NBSSPdu), + 1 => Some(SMBFrameType::NBSSHdr), + 2 => Some(SMBFrameType::NBSSData), + 3 => Some(SMBFrameType::SMB1Pdu), + 4 => Some(SMBFrameType::SMB1Hdr), + 5 => Some(SMBFrameType::SMB1Data), + 6 => Some(SMBFrameType::SMB2Pdu), + 7 => Some(SMBFrameType::SMB2Hdr), + 8 => Some(SMBFrameType::SMB2Data), + 9 => Some(SMBFrameType::SMB3Pdu), + 10 => Some(SMBFrameType::SMB3Hdr), + 11 => Some(SMBFrameType::SMB3Data), + _ => None, + } + } +} + +fn smb_frame_type_string(s: &str) -> i32 { + match s { + "nbss.pdu" => SMBFrameType::NBSSPdu as i32, + "nbss.hdr" => SMBFrameType::NBSSHdr as i32, + "nbss.data" => SMBFrameType::NBSSData as i32, + "smb1.pdu" => SMBFrameType::SMB1Pdu as i32, + "smb1.hdr" => SMBFrameType::SMB1Hdr as i32, + "smb1.data" => SMBFrameType::SMB1Data as i32, + "smb2.pdu" => SMBFrameType::SMB2Pdu as i32, + "smb2.hdr" => SMBFrameType::SMB2Hdr as i32, + "smb2.data" => SMBFrameType::SMB2Data as i32, + "smb3.pdu" => SMBFrameType::SMB3Pdu as i32, + "smb3.hdr" => SMBFrameType::SMB3Hdr as i32, + "smb3.data" => SMBFrameType::SMB3Data as i32, + _ => -1, + } +} + +fn smb_frame_string_type(id: u8) -> *const std::os::raw::c_char { + if let Some(s) = SMBFrameType::from_u8(id) { + let estr = match s { + SMBFrameType::NBSSPdu => "nbss.pdu\0", + SMBFrameType::NBSSHdr => "nbss.hdr\0", + SMBFrameType::NBSSData => "nbss.data\0", + SMBFrameType::SMB1Pdu => "smb1.pdu\0", + SMBFrameType::SMB1Hdr => "smb1.hdr\0", + SMBFrameType::SMB1Data => "smb1.data\0", + SMBFrameType::SMB2Pdu => "smb2.pdu\0", + SMBFrameType::SMB2Hdr => "smb2.hdr\0", + SMBFrameType::SMB2Data => "smb2.data\0", + SMBFrameType::SMB3Pdu => "smb3.pdu\0", + SMBFrameType::SMB3Hdr => "smb3.hdr\0", + SMBFrameType::SMB3Data => "smb3.data\0", + }; + + return estr.as_ptr() as *const std::os::raw::c_char; + } + return std::ptr::null(); +} + pub const MIN_REC_SIZE: u16 = 32 + 4; // SMB hdr + nbss hdr pub const SMB_CONFIG_DEFAULT_STREAM_DEPTH: u32 = 0; @@ -1218,8 +1295,60 @@ impl SMBState { return consumed; } + fn add_nbss_ts_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) -> (Option, Option, Option) { + let nbss_pdu = Frame::new_ts(flow, stream_slice, input, nbss_len + 4, SMBFrameType::NBSSPdu as u8); + SCLogDebug!("NBSS PDU frame {:?}", nbss_pdu); + let nbss_hdr_frame = Frame::new_ts(flow, stream_slice, input, 4 as i64, SMBFrameType::NBSSHdr as u8); + SCLogDebug!("NBSS HDR frame {:?}", nbss_hdr_frame); + let nbss_data_frame = Frame::new_ts(flow, stream_slice, &input[4..], nbss_len, SMBFrameType::NBSSData as u8); + SCLogDebug!("NBSS DATA frame {:?}", nbss_data_frame); + (nbss_pdu, nbss_hdr_frame, nbss_data_frame) + } + + fn add_smb1_ts_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) -> Option { + let smb_pdu = Frame::new_ts(flow, stream_slice, input, nbss_len, SMBFrameType::SMB1Pdu as u8); + SCLogDebug!("SMB PDU frame {:?}", smb_pdu); + smb_pdu + } + fn add_smb1_ts_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb1_hdr = Frame::new_ts(flow, stream_slice, input, 32 as i64, SMBFrameType::SMB1Hdr as u8); + SCLogDebug!("SMBv1 HDR frame {:?}", _smb1_hdr); + if input.len() > 32 { + let _smb1_data = Frame::new_ts(flow, stream_slice, &input[32..], (nbss_len - 32) as i64, SMBFrameType::SMB1Data as u8); + SCLogDebug!("SMBv1 DATA frame {:?}", _smb1_data); + } + } + + fn add_smb2_ts_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) -> Option { + let smb_pdu = Frame::new_ts(flow, stream_slice, input, nbss_len, SMBFrameType::SMB2Pdu as u8); + SCLogDebug!("SMBv2 PDU frame {:?}", smb_pdu); + smb_pdu + } + fn add_smb2_ts_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64, hdr_len: i64) { + let _smb2_hdr = Frame::new_ts(flow, stream_slice, input, hdr_len, SMBFrameType::SMB2Hdr as u8); + SCLogDebug!("SMBv2 HDR frame {:?}", _smb2_hdr); + if input.len() > hdr_len as usize { + let _smb2_data = Frame::new_ts(flow, stream_slice, &input[hdr_len as usize..], nbss_len - hdr_len, SMBFrameType::SMB2Data as u8); + SCLogDebug!("SMBv2 DATA frame {:?}", _smb2_data); + } + } + + fn add_smb3_ts_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) -> Option { + let smb_pdu = Frame::new_ts(flow, stream_slice, input, nbss_len, SMBFrameType::SMB3Pdu as u8); + SCLogDebug!("SMBv3 PDU frame {:?}", smb_pdu); + smb_pdu + } + fn add_smb3_ts_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb3_hdr = Frame::new_ts(flow, stream_slice, input, 52 as i64, SMBFrameType::SMB3Hdr as u8); + SCLogDebug!("SMBv3 HDR frame {:?}", _smb3_hdr); + if input.len() > 52 { + let _smb3_data = Frame::new_ts(flow, stream_slice, &input[52..], (nbss_len - 52) as i64, SMBFrameType::SMB3Data as u8); + SCLogDebug!("SMBv3 DATA frame {:?}", _smb3_data); + } + } + /// return bytes consumed - pub fn parse_tcp_data_ts_partial<'b>(&mut self, input: &'b[u8]) -> usize + pub fn parse_tcp_data_ts_partial<'b>(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &'b[u8]) -> usize { SCLogDebug!("incomplete of size {}", input.len()); if input.len() < 512 { @@ -1265,6 +1394,11 @@ impl SMBState { return 0; } smb1_write_request_record(self, r, SMB1_HEADER_SIZE, SMB1_COMMAND_WRITE_ANDX); + + self.add_nbss_ts_frames(flow, stream_slice, input, nbss_part_hdr.length as i64); + self.add_smb1_ts_pdu_frame(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + self.add_smb1_ts_hdr_data_frames(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + let consumed = input.len() - output.len(); return consumed; } @@ -1273,7 +1407,6 @@ impl SMBState { } } else if smb.version == 0xfe_u8 { // SMB2 - SCLogDebug!("NBSS record {:?}", nbss_part_hdr); SCLogDebug!("SMBv2 record"); match parse_smb2_request_record(nbss_part_hdr.data) { Ok((_, ref smb_record)) => { @@ -1281,6 +1414,11 @@ impl SMBState { &smb2_command_string(smb_record.command)); if smb_record.command == SMB2_COMMAND_WRITE { smb2_write_request_record(self, smb_record); + + self.add_nbss_ts_frames(flow, stream_slice, input, nbss_part_hdr.length as i64); + self.add_smb2_ts_pdu_frame(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + self.add_smb2_ts_hdr_data_frames(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64, smb_record.header_len as i64); + let consumed = input.len() - output.len(); SCLogDebug!("consumed {}", consumed); return consumed; @@ -1302,9 +1440,9 @@ impl SMBState { } /// Parsing function, handling TCP chunks fragmentation - pub fn parse_tcp_data_ts<'b>(&mut self, i: &'b[u8]) -> AppLayerResult + pub fn parse_tcp_data_ts<'b>(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult { - let mut cur_i = i; + let mut cur_i = stream_slice.as_slice(); let consumed = self.handle_skip(Direction::ToServer, cur_i.len() as u32); if consumed > 0 { if consumed > cur_i.len() as u32 { @@ -1345,7 +1483,7 @@ impl SMBState { break; }, _ => { - let mut consumed = i.len(); + let mut consumed = stream_slice.len(); if consumed < 4 { consumed = 0; } else { @@ -1360,19 +1498,29 @@ impl SMBState { while cur_i.len() > 0 { // min record size match parse_nbss_record(cur_i) { Ok((rem, ref nbss_hdr)) => { + SCLogDebug!("nbss frame offset {} len {}", stream_slice.offset_from(cur_i), cur_i.len() - rem.len()); + let (_, _, nbss_data_frame) = self.add_nbss_ts_frames(flow, stream_slice, cur_i, nbss_hdr.length as i64); + if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE { // we have the full records size worth of data, // let's parse it match parse_smb_version(nbss_hdr.data) { Ok((_, ref smb)) => { + SCLogDebug!("SMB {:?}", smb); if smb.version == 0xff_u8 { // SMB1 + SCLogDebug!("SMBv1 record"); match parse_smb_record(nbss_hdr.data) { Ok((_, ref smb_record)) => { + self.add_smb1_ts_pdu_frame(flow, stream_slice, nbss_hdr.data, nbss_hdr.length as i64); + self.add_smb1_ts_hdr_data_frames(flow, stream_slice, nbss_hdr.data, nbss_hdr.length as i64); smb1_request_record(self, smb_record); }, _ => { + if let Some(frame) = nbss_data_frame { + frame.add_event(flow, 0, SMBEvent::MalformedData as u8); + } self.set_event(SMBEvent::MalformedData); return AppLayerResult::err(); }, @@ -1383,26 +1531,38 @@ impl SMBState { SCLogDebug!("SMBv2 record"); match parse_smb2_request_record(nbss_data) { Ok((nbss_data_rem, ref smb_record)) => { + let record_len = (nbss_data.len() - nbss_data_rem.len()) as i64; + self.add_smb2_ts_pdu_frame(flow, stream_slice, nbss_data, record_len); + self.add_smb2_ts_hdr_data_frames(flow, stream_slice, nbss_data, record_len, smb_record.header_len as i64); SCLogDebug!("nbss_data_rem {}", nbss_data_rem.len()); - smb2_request_record(self, smb_record); nbss_data = nbss_data_rem; }, _ => { + if let Some(frame) = nbss_data_frame { + frame.add_event(flow, 0, SMBEvent::MalformedData as u8); + } self.set_event(SMBEvent::MalformedData); return AppLayerResult::err(); }, } } } else if smb.version == 0xfd_u8 { // SMB3 transform + let mut nbss_data = nbss_hdr.data; while nbss_data.len() > 0 { SCLogDebug!("SMBv3 transform record"); match parse_smb3_transform_record(nbss_data) { Ok((nbss_data_rem, ref _smb3_record)) => { + let record_len = (nbss_data.len() - nbss_data_rem.len()) as i64; + self.add_smb3_ts_pdu_frame(flow, stream_slice, nbss_data, record_len); + self.add_smb3_ts_hdr_data_frames(flow, stream_slice, nbss_data, record_len); nbss_data = nbss_data_rem; }, _ => { + if let Some(frame) = nbss_data_frame { + frame.add_event(flow, 0, SMBEvent::MalformedData as u8); + } self.set_event(SMBEvent::MalformedData); return AppLayerResult::err(); }, @@ -1425,15 +1585,15 @@ impl SMBState { let n = usize::from(n) + cur_i.len(); // 512 is the minimum for parse_tcp_data_ts_partial if n >= 512 && cur_i.len() < 512 { - let total_consumed = i.len() - cur_i.len(); - return AppLayerResult::incomplete(total_consumed as u32, 512); + let total_consumed = stream_slice.offset_from(cur_i); + return AppLayerResult::incomplete(total_consumed, 512); } - let consumed = self.parse_tcp_data_ts_partial(cur_i); + let consumed = self.parse_tcp_data_ts_partial(flow, stream_slice, cur_i); if consumed == 0 { // if we consumed none we will buffer the entire record - let total_consumed = i.len() - cur_i.len(); + let total_consumed = stream_slice.offset_from(cur_i); SCLogDebug!("setting consumed {} need {} needed {:?} total input {}", - total_consumed, n, needed, i.len()); + total_consumed, n, needed, stream_slice.len()); let need = n; return AppLayerResult::incomplete(total_consumed as u32, need as u32); } @@ -1461,8 +1621,58 @@ impl SMBState { AppLayerResult::ok() } + fn add_nbss_tc_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) -> (Option, Option, Option) { + let nbss_pdu = Frame::new_tc(flow, stream_slice, input, nbss_len + 4, SMBFrameType::NBSSPdu as u8); + SCLogDebug!("NBSS PDU frame {:?}", nbss_pdu); + let nbss_hdr_frame = Frame::new_tc(flow, stream_slice, input, 4 as i64, SMBFrameType::NBSSHdr as u8); + SCLogDebug!("NBSS HDR frame {:?}", nbss_hdr_frame); + let nbss_data_frame = Frame::new_tc(flow, stream_slice, &input[4..], nbss_len, SMBFrameType::NBSSData as u8); + SCLogDebug!("NBSS DATA frame {:?}", nbss_data_frame); + (nbss_pdu, nbss_hdr_frame, nbss_data_frame) + } + + fn add_smb1_tc_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb_pdu = Frame::new_tc(flow, stream_slice, input, nbss_len, SMBFrameType::SMB1Pdu as u8); + SCLogDebug!("SMB PDU frame {:?}", _smb_pdu); + } + fn add_smb1_tc_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb1_hdr = Frame::new_tc(flow, stream_slice, input, SMB1_HEADER_SIZE as i64, SMBFrameType::SMB1Hdr as u8); + SCLogDebug!("SMBv1 HDR frame {:?}", _smb1_hdr); + if input.len() > SMB1_HEADER_SIZE { + let _smb1_data = Frame::new_tc(flow, stream_slice, &input[SMB1_HEADER_SIZE..], (nbss_len - SMB1_HEADER_SIZE as i64) as i64, + SMBFrameType::SMB1Data as u8); + SCLogDebug!("SMBv1 DATA frame {:?}", _smb1_data); + } + } + + fn add_smb2_tc_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb_pdu = Frame::new_tc(flow, stream_slice, input, nbss_len, SMBFrameType::SMB2Pdu as u8); + SCLogDebug!("SMBv2 PDU frame {:?}", _smb_pdu); + } + fn add_smb2_tc_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64, hdr_len: i64) { + let _smb2_hdr = Frame::new_tc(flow, stream_slice, input, hdr_len, SMBFrameType::SMB2Hdr as u8); + SCLogDebug!("SMBv2 HDR frame {:?}", _smb2_hdr); + if input.len() > hdr_len as usize { + let _smb2_data = Frame::new_tc(flow, stream_slice, &input[hdr_len as usize ..], nbss_len - hdr_len, SMBFrameType::SMB2Data as u8); + SCLogDebug!("SMBv2 DATA frame {:?}", _smb2_data); + } + } + + fn add_smb3_tc_pdu_frame(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb_pdu = Frame::new_tc(flow, stream_slice, input, nbss_len, SMBFrameType::SMB3Pdu as u8); + SCLogDebug!("SMBv3 PDU frame {:?}", _smb_pdu); + } + fn add_smb3_tc_hdr_data_frames(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &[u8], nbss_len: i64) { + let _smb3_hdr = Frame::new_tc(flow, stream_slice, input, 52 as i64, SMBFrameType::SMB3Hdr as u8); + SCLogDebug!("SMBv3 HDR frame {:?}", _smb3_hdr); + if input.len() > 52 { + let _smb3_data = Frame::new_tc(flow, stream_slice, &input[52..], (nbss_len - 52) as i64, SMBFrameType::SMB3Data as u8); + SCLogDebug!("SMBv3 DATA frame {:?}", _smb3_data); + } + } + /// return bytes consumed - pub fn parse_tcp_data_tc_partial<'b>(&mut self, input: &'b[u8]) -> usize + pub fn parse_tcp_data_tc_partial<'b>(&mut self, flow: *const Flow, stream_slice: &StreamSlice, input: &'b[u8]) -> usize { SCLogDebug!("incomplete of size {}", input.len()); if input.len() < 512 { @@ -1484,67 +1694,67 @@ impl SMBState { return 0; } - match parse_nbss_record_partial(input) { - Ok((output, ref nbss_part_hdr)) => { - SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len()); - if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE { - match parse_smb_version(nbss_part_hdr.data) { - Ok((_, ref smb)) => { - SCLogDebug!("SMB {:?}", smb); - if smb.version == 255u8 { // SMB1 - SCLogDebug!("SMBv1 record"); - match parse_smb_record(nbss_part_hdr.data) { - Ok((_, ref r)) => { - SCLogDebug!("SMB1: partial record {}", - r.command); - if r.command == SMB1_COMMAND_READ_ANDX { - let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE, - r.ssn_id as u64, r.tree_id as u32, 0); - let is_pipe = match self.ssn2tree_map.get(&tree_key) { - Some(n) => n.is_pipe, - None => false, - }; - if is_pipe { - return 0; - } - smb1_read_response_record(self, r, SMB1_HEADER_SIZE); - let consumed = input.len() - output.len(); - return consumed; - } - }, - _ => { }, - } - } else if smb.version == 254u8 { // SMB2 - SCLogDebug!("SMBv2 record"); - match parse_smb2_response_record(nbss_part_hdr.data) { - Ok((_, ref smb_record)) => { - SCLogDebug!("SMB2: partial record {}", - &smb2_command_string(smb_record.command)); - if smb_record.command == SMB2_COMMAND_READ { - smb2_read_response_record(self, smb_record); - let consumed = input.len() - output.len(); - return consumed; - } - }, - _ => { }, + if let Ok((output, ref nbss_part_hdr)) = parse_nbss_record_partial(input) { + SCLogDebug!("parse_nbss_record_partial ok, output len {}", output.len()); + if nbss_part_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE { + if let Ok((_, ref smb)) = parse_smb_version(nbss_part_hdr.data) { + SCLogDebug!("SMB {:?}", smb); + if smb.version == 255u8 { // SMB1 + SCLogDebug!("SMBv1 record"); + if let Ok((_, ref r)) = parse_smb_record(nbss_part_hdr.data) { + SCLogDebug!("SMB1: partial record {}", + r.command); + if r.command == SMB1_COMMAND_READ_ANDX { + let tree_key = SMBCommonHdr::new(SMBHDR_TYPE_SHARE, + r.ssn_id as u64, r.tree_id as u32, 0); + let is_pipe = match self.ssn2tree_map.get(&tree_key) { + Some(n) => n.is_pipe, + None => false, + }; + if is_pipe { + return 0; } + + // create NBSS frames here so we don't get double frames + // when we don't consume the data now. + self.add_nbss_tc_frames(flow, stream_slice, input, nbss_part_hdr.length as i64); + self.add_smb1_tc_pdu_frame(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + self.add_smb1_tc_hdr_data_frames(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + + smb1_read_response_record(self, r, SMB1_HEADER_SIZE); + let consumed = input.len() - output.len(); + return consumed; } - // no SMB3 here yet, will buffer full records - }, - _ => { }, + } + } else if smb.version == 254u8 { // SMB2 + SCLogDebug!("SMBv2 record"); + if let Ok((_, ref smb_record)) = parse_smb2_response_record(nbss_part_hdr.data) { + SCLogDebug!("SMB2: partial record {}", + &smb2_command_string(smb_record.command)); + if smb_record.command == SMB2_COMMAND_READ { + // create NBSS frames here so we don't get double frames + // when we don't consume the data now. + self.add_nbss_tc_frames(flow, stream_slice, input, nbss_part_hdr.length as i64); + self.add_smb2_tc_pdu_frame(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64); + self.add_smb2_tc_hdr_data_frames(flow, stream_slice, nbss_part_hdr.data, nbss_part_hdr.length as i64, smb_record.header_len as i64); + + smb2_read_response_record(self, smb_record); + let consumed = input.len() - output.len(); + return consumed; + } + } } + // no SMB3 here yet, will buffer full records } - }, - _ => { }, + } } - return 0; } /// Parsing function, handling TCP chunks fragmentation - pub fn parse_tcp_data_tc<'b>(&mut self, i: &'b[u8]) -> AppLayerResult + pub fn parse_tcp_data_tc<'b>(&mut self, flow: *const Flow, stream_slice: &StreamSlice) -> AppLayerResult { - let mut cur_i = i; + let mut cur_i = stream_slice.as_slice(); let consumed = self.handle_skip(Direction::ToClient, cur_i.len() as u32); if consumed > 0 { if consumed > cur_i.len() as u32 { @@ -1585,7 +1795,7 @@ impl SMBState { break; }, _ => { - let mut consumed = i.len(); + let mut consumed = stream_slice.len(); if consumed < 4 { consumed = 0; } else { @@ -1600,6 +1810,10 @@ impl SMBState { while cur_i.len() > 0 { // min record size match parse_nbss_record(cur_i) { Ok((rem, ref nbss_hdr)) => { + SCLogDebug!("nbss record offset {} len {}", stream_slice.offset_from(cur_i), cur_i.len() - rem.len()); + self.add_nbss_tc_frames(flow, stream_slice, cur_i, nbss_hdr.length as i64); + SCLogDebug!("nbss frames added"); + if nbss_hdr.message_type == NBSS_MSGTYPE_SESSION_MESSAGE { // we have the full records size worth of data, // let's parse it @@ -1610,6 +1824,8 @@ impl SMBState { SCLogDebug!("SMBv1 record"); match parse_smb_record(nbss_hdr.data) { Ok((_, ref smb_record)) => { + self.add_smb1_tc_pdu_frame(flow, stream_slice, nbss_hdr.data, nbss_hdr.length as i64); + self.add_smb1_tc_hdr_data_frames(flow, stream_slice, nbss_hdr.data, nbss_hdr.length as i64); smb1_response_record(self, smb_record); }, _ => { @@ -1623,6 +1839,9 @@ impl SMBState { SCLogDebug!("SMBv2 record"); match parse_smb2_response_record(nbss_data) { Ok((nbss_data_rem, ref smb_record)) => { + let record_len = (nbss_data.len() - nbss_data_rem.len()) as i64; + self.add_smb2_tc_pdu_frame(flow, stream_slice, nbss_data, record_len); + self.add_smb2_tc_hdr_data_frames(flow, stream_slice, nbss_data, record_len, smb_record.header_len as i64); smb2_response_record(self, smb_record); nbss_data = nbss_data_rem; }, @@ -1638,6 +1857,9 @@ impl SMBState { SCLogDebug!("SMBv3 transform record"); match parse_smb3_transform_record(nbss_data) { Ok((nbss_data_rem, ref _smb3_record)) => { + let record_len = (nbss_data.len() - nbss_data_rem.len()) as i64; + self.add_smb3_tc_pdu_frame(flow, stream_slice, nbss_data, record_len); + self.add_smb3_tc_hdr_data_frames(flow, stream_slice, nbss_data, record_len); nbss_data = nbss_data_rem; }, _ => { @@ -1668,15 +1890,15 @@ impl SMBState { let n = usize::from(n) + cur_i.len(); // 512 is the minimum for parse_tcp_data_tc_partial if n >= 512 && cur_i.len() < 512 { - let total_consumed = i.len() - cur_i.len(); - return AppLayerResult::incomplete(total_consumed as u32, 512); + let total_consumed = stream_slice.offset_from(cur_i); + return AppLayerResult::incomplete(total_consumed, 512); } - let consumed = self.parse_tcp_data_tc_partial(cur_i); + let consumed = self.parse_tcp_data_tc_partial(flow, stream_slice, cur_i); if consumed == 0 { // if we consumed none we will buffer the entire record - let total_consumed = i.len() - cur_i.len(); + let total_consumed = stream_slice.offset_from(cur_i); SCLogDebug!("setting consumed {} need {} needed {:?} total input {}", - total_consumed, n, needed, i.len()); + total_consumed, n, needed, stream_slice.len()); let need = n; return AppLayerResult::incomplete(total_consumed as u32, need as u32); } @@ -1815,7 +2037,7 @@ pub unsafe extern "C" fn rs_smb_parse_request_tcp(flow: *const Flow, } state.update_ts(flow.get_last_time().as_secs()); - state.parse_tcp_data_ts(stream_slice.as_slice()) + state.parse_tcp_data_ts(flow, &stream_slice) } #[no_mangle] @@ -1845,7 +2067,6 @@ pub unsafe extern "C" fn rs_smb_parse_response_tcp(flow: *const Flow, if stream_slice.is_gap() { return rs_smb_parse_response_tcp_gap(state, stream_slice.gap_size()); } - SCLogDebug!("parsing {} bytes of response data", stream_slice.len()); /* START with MISTREAM set: record might be starting the middle. */ if stream_slice.flags() & (STREAM_START|STREAM_MIDSTREAM) == (STREAM_START|STREAM_MIDSTREAM) { @@ -1853,7 +2074,7 @@ pub unsafe extern "C" fn rs_smb_parse_response_tcp(flow: *const Flow, } state.update_ts(flow.get_last_time().as_secs()); - state.parse_tcp_data_tc(stream_slice.as_slice()) + state.parse_tcp_data_tc(flow, &stream_slice) } #[no_mangle] @@ -2100,6 +2321,18 @@ pub unsafe extern "C" fn smb3_probe_tcp(f: *const Flow, dir: u8, input: *const u return ALPROTO_SMB; } +pub unsafe extern "C" fn smb_frames_get_frame_id_by_name(name: *const std::os::raw::c_char) -> std::os::raw::c_int { + if let Ok(s) = std::ffi::CStr::from_ptr(name).to_str() { + smb_frame_type_string(s) as std::os::raw::c_int + } else { + -1 + } +} + +pub unsafe extern "C" fn smb_frames_get_frame_by_id(id: u8) -> *const std::os::raw::c_char { + smb_frame_string_type(id) +} + fn register_pattern_probe() -> i8 { let mut r = 0; unsafe { @@ -2168,8 +2401,8 @@ pub unsafe extern "C" fn rs_smb_register_parser() { apply_tx_config: None, flags: APP_LAYER_PARSER_OPT_ACCEPT_GAPS, truncate: Some(rs_smb_state_truncate), - get_frame_id_by_name: None, - get_frame_name_by_id: None, + get_frame_id_by_name: Some(smb_frames_get_frame_id_by_name), + get_frame_name_by_id: Some(smb_frames_get_frame_by_id), }; let ip_proto_str = CString::new("tcp").unwrap(); diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index ec4149d3b2..f503401009 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -1,4 +1,4 @@ -/* Copyright (C) 2018 Open Information Security Foundation +/* Copyright (C) 2018-2021 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -198,7 +198,7 @@ fn smb1_command_is_andx(c: u8) -> bool { } } -fn smb1_request_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: u8, andx_offset: &mut usize) -> u32 { +fn smb1_request_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: u8, andx_offset: &mut usize) { let mut events : Vec = Vec::new(); let mut no_response_expected = false; @@ -593,7 +593,6 @@ fn smb1_request_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: } } } - return 0; } pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 { @@ -602,9 +601,8 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 { let mut andx_offset = SMB1_HEADER_SIZE; let mut command = r.command; loop { - if smb1_request_record_one(state, r, command, &mut andx_offset) != 0 { - break; - } + smb1_request_record_one(state, r, command, &mut andx_offset); + // continue for next andx command if any if smb1_command_is_andx(command) { if let Ok((_, andx_hdr)) = smb1_parse_andx_header(&r.data[andx_offset-SMB1_HEADER_SIZE..]) { @@ -623,7 +621,7 @@ pub fn smb1_request_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 { 0 } -fn smb1_response_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: u8, andx_offset: &mut usize) -> u32 { +fn smb1_response_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command: u8, andx_offset: &mut usize) { SCLogDebug!("record: command {} status {} -> {:?}", r.command, r.nt_status, r); let key_ssn_id = r.ssn_id; @@ -693,7 +691,7 @@ fn smb1_response_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command }, None => { }, } - return 0; + return; } match parse_smb_connect_tree_andx_response_record(&r.data[*andx_offset-SMB1_HEADER_SIZE..]) { @@ -835,17 +833,13 @@ fn smb1_response_record_one<'b>(state: &mut SMBState, r: &SmbRecord<'b>, command } else { SCLogDebug!("have tx for cmd {}", command); } - - return 0; } pub fn smb1_response_record<'b>(state: &mut SMBState, r: &SmbRecord<'b>) -> u32 { let mut andx_offset = SMB1_HEADER_SIZE; let mut command = r.command; loop { - if smb1_response_record_one(state, r, command, &mut andx_offset) != 0 { - break; - } + smb1_response_record_one(state, r, command, &mut andx_offset); // continue for next andx command if any if smb1_command_is_andx(command) { diff --git a/rust/src/smb/smb2_records.rs b/rust/src/smb/smb2_records.rs index c892faad47..5e5f1da448 100644 --- a/rust/src/smb/smb2_records.rs +++ b/rust/src/smb/smb2_records.rs @@ -54,6 +54,7 @@ pub fn parse_smb2_record_direction(i: &[u8]) -> IResult<&[u8], Smb2RecordDir> { #[derive(Debug,PartialEq)] pub struct Smb2Record<'a> { pub direction: u8, // 0 req, 1 res + pub header_len: u16, pub nt_status: u32, pub command: u16, pub message_id: u64, @@ -104,6 +105,7 @@ pub fn parse_smb2_request_record(i: &[u8]) -> IResult<&[u8], Smb2Record> { }; let record = Smb2Record { direction: flags.7, + header_len: hlen, nt_status: 0, command, message_id, @@ -558,6 +560,7 @@ pub fn parse_smb2_response_record(i: &[u8]) -> IResult<&[u8], Smb2Record> { }; let record = Smb2Record { direction: flags.7, + header_len: hlen, nt_status, message_id, tree_id: tree_id.unwrap_or(0),