]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
smb2: allow limiting in-flight data size/cnt
authorVictor Julien <vjulien@oisf.net>
Sat, 16 Apr 2022 04:58:20 +0000 (06:58 +0200)
committerVictor Julien <vjulien@oisf.net>
Tue, 19 Apr 2022 11:14:23 +0000 (13:14 +0200)
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
rust/src/smb/smb.rs
rust/src/smb/smb2.rs

index dde00c612943bd08dfa9a78b16868e1520fdce8f..650fb7d6f63262b7876a235697db9e847ff6e662 100644 (file)
@@ -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 {
index 641c47f6f0f0445aebd7dfb83c4838500481b785..f4009c108229a3fd0f2cbad08cd7228532aa3945 100644 (file)
@@ -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.");
     }
index 834a2b1ede5153e0431ebc3a2a1eae3665cc2440..0590642a3742ff6e167e93908ab1aefb245aee7d 100644 (file)
@@ -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"<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, 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
                 }
             }