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

(cherry picked from commit 4be8334c9e)
pull/7288/head
Victor Julien 4 years ago committed by Shivani Bhardwaj
parent 5b38b97181
commit 8510031e6c

@ -35,8 +35,12 @@ pub enum SMBEvent {
ReadRequestTooLarge = 10,
/// READ response bigger than `max_read_size`
ReadResponseTooLarge = 11,
ReadResponseQueueSizeExceeded = 12,
ReadResponseQueueCntExceeded = 13,
/// WRITE request for more than `max_write_size`
WriteRequestTooLarge = 12,
WriteRequestTooLarge = 14,
WriteQueueSizeExceeded = 15,
WriteQueueCntExceeded = 16,
}
impl SMBEvent {
@ -54,7 +58,11 @@ impl SMBEvent {
9 => Some(SMBEvent::ResponseToServer),
10 => Some(SMBEvent::ReadRequestTooLarge),
11 => Some(SMBEvent::ReadResponseTooLarge),
12 => Some(SMBEvent::WriteRequestTooLarge),
12 => Some(SMBEvent::ReadResponseQueueSizeExceeded),
13 => Some(SMBEvent::ReadResponseQueueCntExceeded),
14 => Some(SMBEvent::WriteRequestTooLarge),
15 => Some(SMBEvent::WriteQueueSizeExceeded),
16 => Some(SMBEvent::WriteQueueCntExceeded),
_ => None,
}
}
@ -63,19 +71,23 @@ impl SMBEvent {
pub fn smb_str_to_event(instr: &str) -> i32 {
SCLogDebug!("checking {}", instr);
match instr {
"internal_error" => SMBEvent::InternalError as i32,
"malformed_data" => SMBEvent::MalformedData as i32,
"record_overflow" => SMBEvent::RecordOverflow as i32,
"malformed_ntlmssp_request" => SMBEvent::MalformedNtlmsspRequest as i32,
"malformed_ntlmssp_response" => SMBEvent::MalformedNtlmsspResponse as i32,
"duplicate_negotiate" => SMBEvent::DuplicateNegotiate as i32,
"negotiate_malformed_dialects" => SMBEvent::NegotiateMalformedDialects as i32,
"file_overlap" => SMBEvent::FileOverlap as i32,
"request_to_client" => SMBEvent::RequestToClient as i32,
"response_to_server" => SMBEvent::ResponseToServer as i32,
"read_request_too_large" => SMBEvent::ReadRequestTooLarge as i32,
"read_response_too_large" => SMBEvent::ReadResponseTooLarge as i32,
"write_request_too_large" => SMBEvent::WriteRequestTooLarge as i32,
"internal_error" => SMBEvent::InternalError as i32,
"malformed_data" => SMBEvent::MalformedData as i32,
"record_overflow" => SMBEvent::RecordOverflow as i32,
"malformed_ntlmssp_request" => SMBEvent::MalformedNtlmsspRequest as i32,
"malformed_ntlmssp_response" => SMBEvent::MalformedNtlmsspResponse as i32,
"duplicate_negotiate" => SMBEvent::DuplicateNegotiate as i32,
"negotiate_malformed_dialects" => SMBEvent::NegotiateMalformedDialects as i32,
"file_overlap" => SMBEvent::FileOverlap as i32,
"request_to_client" => SMBEvent::RequestToClient as i32,
"response_to_server" => SMBEvent::ResponseToServer as i32,
"read_request_too_large" => SMBEvent::ReadRequestTooLarge as i32,
"read_response_too_large" => SMBEvent::ReadResponseTooLarge as i32,
"read_response_queue_size_exceeded" => SMBEvent::ReadResponseQueueSizeExceeded as i32,
"read_response_queue_cnt_exceeded" => SMBEvent::ReadResponseQueueCntExceeded as i32,
"write_request_too_large" => SMBEvent::WriteRequestTooLarge as i32,
"write_queue_size_exceeded" => SMBEvent::WriteQueueSizeExceeded as i32,
"write_queue_cnt_exceeded" => SMBEvent::WriteQueueCntExceeded as i32,
_ => -1,
}
}

@ -52,7 +52,11 @@ use crate::smb::files::*;
use crate::smb::smb2_ioctl::*;
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;
pub static mut SURICATA_SMB_FILE_CONFIG: Option<&'static SuricataFileContext> = None;
@ -2196,7 +2200,11 @@ pub extern "C" fn rs_smb_state_get_event_info_by_id(event_id: std::os::raw::c_in
SMBEvent::ResponseToServer => { "response_to_server\0" },
SMBEvent::ReadRequestTooLarge => { "read_request_too_large\0" },
SMBEvent::ReadResponseTooLarge => { "read_response_too_large\0" },
SMBEvent::ReadResponseQueueSizeExceeded => { "read_response_queue_size_exceeded\0" },
SMBEvent::ReadResponseQueueCntExceeded => { "read_response_queue_cnt_exceeded\0" },
SMBEvent::WriteRequestTooLarge => { "write_request_too_large\0" },
SMBEvent::WriteQueueSizeExceeded => { "write_queue_size_exceeded\0" },
SMBEvent::WriteQueueCntExceeded => { "write_queue_cnt_exceeded\0" },
};
unsafe{
*event_name = estr.as_ptr() as *const std::os::raw::c_char;
@ -2235,8 +2243,13 @@ pub extern "C" fn rs_smb_state_get_event_info(event_name: *const std::os::raw::c
}
#[no_mangle]
pub unsafe extern "C" fn rs_smb_set_conf_val(max_read_size: u32, max_write_size: u32)
pub unsafe extern "C" fn rs_smb_set_conf_val(max_read_size: u32, max_write_size: u32,
max_write_queue_size: u32, max_write_queue_cnt: u32, max_read_queue_size: u32, max_read_queue_cnt: u32)
{
SMB_CFG_MAX_READ_SIZE = max_read_size;
SMB_CFG_MAX_WRITE_SIZE = max_write_size;
SMB_CFG_MAX_WRITE_QUEUE_SIZE = max_write_queue_size;
SMB_CFG_MAX_WRITE_QUEUE_CNT = max_write_queue_cnt;
SMB_CFG_MAX_READ_QUEUE_SIZE = max_read_queue_size;
SMB_CFG_MAX_READ_QUEUE_CNT = max_read_queue_cnt;
}

@ -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(STREAM_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(STREAM_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
},
@ -213,23 +224,33 @@ pub fn smb2_read_response_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
state.set_skip(STREAM_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"<unknown>".to_vec() },
Some(n) => { n.to_vec() }
None => { b"<unknown>".to_vec() }
};
let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_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(STREAM_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(STREAM_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
}
}
@ -247,6 +268,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);
@ -280,9 +304,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(STREAM_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(STREAM_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
},
@ -333,18 +365,27 @@ pub fn smb2_write_request_record<'b>(state: &mut SMBState, r: &Smb2Record<'b>)
state.set_skip(STREAM_TOSERVER, wr.wr_len, wr.data.len() as u32);
} else {
let (tx, files, flags) = state.new_file_tx(&file_guid, &file_name, STREAM_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(STREAM_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(STREAM_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
}
}

@ -237,6 +237,10 @@ void RegisterSMBParsers(void)
const char *proto_name = "smb";
uint32_t max_read_size = 0;
uint32_t max_write_size = 0;
uint32_t max_write_queue_size = 0;
uint32_t max_write_queue_cnt = 0;
uint32_t max_read_queue_size = 0;
uint32_t max_read_queue_cnt = 0;
/** SMB */
if (AppLayerProtoDetectConfProtoDetectionEnabled("tcp", proto_name)) {
AppLayerProtoDetectRegisterProtocol(ALPROTO_SMB, proto_name);
@ -339,7 +343,53 @@ void RegisterSMBParsers(void)
}
SCLogConfig("SMB max-write-size: %u", max_write_size);
rs_smb_set_conf_val(max_read_size, max_write_size);
ConfNode *wqs = ConfGetNode("app-layer.protocols.smb.max-write-queue-size");
if (wqs != NULL) {
uint32_t value;
if (ParseSizeStringU32(wqs->val, &value) < 0) {
SCLogError(
SC_ERR_SMB_CONFIG, "invalid value for max-write-queue-size %s", wqs->val);
} else {
max_write_queue_size = value;
}
}
SCLogConfig("SMB max-write-queue-size: %u", max_write_queue_size);
ConfNode *wqc = ConfGetNode("app-layer.protocols.smb.max-write-queue-cnt");
if (wqc != NULL) {
uint32_t value;
if (ParseSizeStringU32(wqc->val, &value) < 0) {
SCLogError(SC_ERR_SMB_CONFIG, "invalid value for max-write-queue-cnt %s", wqc->val);
} else {
max_write_queue_cnt = value;
}
}
SCLogConfig("SMB max-write-queue-cnt: %u", max_write_queue_cnt);
ConfNode *rqs = ConfGetNode("app-layer.protocols.smb.max-read-queue-size");
if (rqs != NULL) {
uint32_t value;
if (ParseSizeStringU32(rqs->val, &value) < 0) {
SCLogError(SC_ERR_SMB_CONFIG, "invalid value for max-read-queue-size %s", rqs->val);
} else {
max_read_queue_size = value;
}
}
SCLogConfig("SMB max-read-queue-size: %u", max_read_queue_size);
ConfNode *rqc = ConfGetNode("app-layer.protocols.smb.max-read-queue-cnt");
if (rqc != NULL) {
uint32_t value;
if (ParseSizeStringU32(rqc->val, &value) < 0) {
SCLogError(SC_ERR_SMB_CONFIG, "invalid value for max-read-queue-cnt %s", rqc->val);
} else {
max_read_queue_cnt = value;
}
}
SCLogConfig("SMB max-read-queue-cnt: %u", max_read_queue_cnt);
rs_smb_set_conf_val(max_read_size, max_write_size, max_write_queue_size,
max_write_queue_cnt, max_read_queue_size, max_read_queue_cnt);
} else {
SCLogConfig("Parsed disabled for %s protocol. Protocol detection"
"still on.", proto_name);

Loading…
Cancel
Save