From 4be8334c9e95cc520c9c6a63a98d6b160915d07f Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Sat, 16 Apr 2022 06:58:20 +0200 Subject: [PATCH] smb2: allow limiting in-flight data size/cnt Allow limiting in-flight out or order data chunks per size or count. Implemented for read and writes separately: app-layer.protocols.smb.max-write-queue-size app-layer.protocols.smb.max-write-queue-cnt app-layer.protocols.smb.max-read-queue-size app-layer.protocols.smb.max-read-queue-cnt --- rust/src/smb/events.rs | 4 ++ rust/src/smb/smb.rs | 32 ++++++++++++++++ rust/src/smb/smb2.rs | 83 +++++++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 21 deletions(-) diff --git a/rust/src/smb/events.rs b/rust/src/smb/events.rs index dde00c6129..650fb7d6f6 100644 --- a/rust/src/smb/events.rs +++ b/rust/src/smb/events.rs @@ -35,8 +35,12 @@ pub enum SMBEvent { ReadRequestTooLarge, /// READ response bigger than `max_read_size` ReadResponseTooLarge, + ReadResponseQueueSizeExceeded, + ReadResponseQueueCntExceeded, /// WRITE request for more than `max_write_size` WriteRequestTooLarge, + WriteQueueSizeExceeded, + WriteQueueCntExceeded, } impl SMBTransaction { diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 641c47f6f0..f4009c1082 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -74,7 +74,11 @@ pub const MIN_REC_SIZE: u16 = 32 + 4; // SMB hdr + nbss hdr pub const SMB_CONFIG_DEFAULT_STREAM_DEPTH: u32 = 0; pub static mut SMB_CFG_MAX_READ_SIZE: u32 = 0; +pub static mut SMB_CFG_MAX_READ_QUEUE_SIZE: u32 = 0; +pub static mut SMB_CFG_MAX_READ_QUEUE_CNT: u32 = 0; pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 0; +pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 0; +pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 0; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -2427,6 +2431,34 @@ pub unsafe extern "C" fn rs_smb_register_parser() { Err(_) => { SCLogError!("Invalid max-write-size value"); } } } + let retval = conf_get("app-layer.protocols.smb.max-write-queue-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { SMB_CFG_MAX_WRITE_QUEUE_SIZE = retval as u32; } + Err(_) => { SCLogError!("Invalid max-write-queue-size value"); } + } + } + let retval = conf_get("app-layer.protocols.smb.max-write-queue-cnt"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { SMB_CFG_MAX_WRITE_QUEUE_CNT = retval as u32; } + Err(_) => { SCLogError!("Invalid max-write-queue-cnt value"); } + } + } + let retval = conf_get("app-layer.protocols.smb.max-read-queue-size"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { SMB_CFG_MAX_READ_QUEUE_SIZE = retval as u32; } + Err(_) => { SCLogError!("Invalid max-read-queue-size value"); } + } + } + let retval = conf_get("app-layer.protocols.smb.max-read-queue-cnt"); + if let Some(val) = retval { + match get_memval(val) { + Ok(retval) => { SMB_CFG_MAX_READ_QUEUE_CNT = retval as u32; } + Err(_) => { SCLogError!("Invalid max-read-queue-cnt value"); } + } + } } else { SCLogDebug!("Protocol detector and parser disabled for SMB."); } diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 834a2b1ede..0590642a37 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -114,6 +114,9 @@ fn smb2_read_response_record_generic<'b>(state: &mut SMBState, r: &Smb2Record<'b pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) { + let max_queue_size = unsafe { SMB_CFG_MAX_READ_QUEUE_SIZE }; + let max_queue_cnt = unsafe { SMB_CFG_MAX_READ_QUEUE_CNT }; + smb2_read_response_record_generic(state, r); match parse_smb2_response_read(r.data) { @@ -160,9 +163,17 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) if offset < tdf.file_tracker.tracked { set_event_fileoverlap = true; } - filetracker_newchunk(&mut tdf.file_tracker, files, flags, - &tdf.file_name, rd.data, offset, - rd.len, false, &file_id); + if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { + state.set_event(SMBEvent::ReadResponseQueueSizeExceeded); + state.set_skip(Direction::ToClient, rd.len, rd.data.len() as u32); + } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { + state.set_event(SMBEvent::ReadResponseQueueCntExceeded); + state.set_skip(Direction::ToClient, rd.len, rd.data.len() as u32); + } else { + filetracker_newchunk(&mut tdf.file_tracker, files, flags, + &tdf.file_name, rd.data, offset, + rd.len, false, &file_id); + } } true }, @@ -211,23 +222,33 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) state.set_skip(Direction::ToClient, rd.len, rd.data.len() as u32); } else { let file_name = match state.guid2name_map.get(&file_guid) { - Some(n) => { n.to_vec() }, - None => { b"".to_vec() }, + Some(n) => { n.to_vec() } + None => { b"".to_vec() } }; let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, Direction::ToClient); + + tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ); + tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, + r.session_id, r.tree_id, 0); // TODO move into new_file_tx + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + tdf.share_name = share_name; let file_id : u32 = tx.id as u32; if offset < tdf.file_tracker.tracked { set_event_fileoverlap = true; } - filetracker_newchunk(&mut tdf.file_tracker, files, flags, - &file_name, rd.data, offset, - rd.len, false, &file_id); - tdf.share_name = share_name; + if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { + state.set_event(SMBEvent::ReadResponseQueueSizeExceeded); + state.set_skip(Direction::ToClient, rd.len, rd.data.len() as u32); + } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { + state.set_event(SMBEvent::ReadResponseQueueCntExceeded); + state.set_skip(Direction::ToClient, rd.len, rd.data.len() as u32); + } else { + filetracker_newchunk(&mut tdf.file_tracker, files, flags, + &file_name, rd.data, offset, + rd.len, false, &file_id); + } } - tx.vercmd.set_smb2_cmd(SMB2_COMMAND_READ); - tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, - r.session_id, r.tree_id, 0); // TODO move into new_file_tx } } @@ -245,6 +266,9 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) { + let max_queue_size = unsafe { SMB_CFG_MAX_WRITE_QUEUE_SIZE }; + let max_queue_cnt = unsafe { SMB_CFG_MAX_WRITE_QUEUE_CNT }; + SCLogDebug!("SMBv2/WRITE: request record"); if smb2_create_new_tx(r.command) { let tx_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); @@ -278,9 +302,17 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) if wr.wr_offset < tdf.file_tracker.tracked { set_event_fileoverlap = true; } - filetracker_newchunk(&mut tdf.file_tracker, files, flags, - &file_name, wr.data, wr.wr_offset, - wr.wr_len, false, &file_id); + if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() { + state.set_event(SMBEvent::WriteQueueSizeExceeded); + state.set_skip(Direction::ToServer, wr.wr_len, wr.data.len() as u32); + } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { + state.set_event(SMBEvent::WriteQueueCntExceeded); + state.set_skip(Direction::ToServer, wr.wr_len, wr.data.len() as u32); + } else { + filetracker_newchunk(&mut tdf.file_tracker, files, flags, + &file_name, wr.data, wr.wr_offset, + wr.wr_len, false, &file_id); + } } true }, @@ -329,18 +361,27 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>) state.set_skip(Direction::ToServer, wr.wr_len, wr.data.len() as u32); } else { let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, Direction::ToServer); + tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE); + tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, + r.session_id, r.tree_id, 0); // TODO move into new_file_tx if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { let file_id : u32 = tx.id as u32; if wr.wr_offset < tdf.file_tracker.tracked { set_event_fileoverlap = true; } - filetracker_newchunk(&mut tdf.file_tracker, files, flags, - &file_name, wr.data, wr.wr_offset, - wr.wr_len, false, &file_id); + + if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + wr.wr_len as u64 > max_queue_size.into() { + state.set_event(SMBEvent::WriteQueueSizeExceeded); + state.set_skip(Direction::ToServer, wr.wr_len, wr.data.len() as u32); + } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { + state.set_event(SMBEvent::WriteQueueCntExceeded); + state.set_skip(Direction::ToServer, wr.wr_len, wr.data.len() as u32); + } else { + filetracker_newchunk(&mut tdf.file_tracker, files, flags, + &file_name, wr.data, wr.wr_offset, + wr.wr_len, false, &file_id); + } } - tx.vercmd.set_smb2_cmd(SMB2_COMMAND_WRITE); - tx.hdr = SMBCommonHdr::new(SMBHDR_TYPE_HEADER, - r.session_id, r.tree_id, 0); // TODO move into new_file_tx } }