From 927e1b3af86d604a9b61f77c057489850da9b509 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 25 Oct 2024 16:29:05 +0200 Subject: [PATCH 01/11] rust: update crates --- rust/Cargo.lock.in | 90 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/rust/Cargo.lock.in b/rust/Cargo.lock.in index df3228dc0f82..311a98eff9bf 100644 --- a/rust/Cargo.lock.in +++ b/rust/Cargo.lock.in @@ -89,9 +89,9 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", "synstructure 0.13.1", ] @@ -101,9 +101,9 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -282,9 +282,9 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -311,7 +311,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", "synstructure 0.12.6", @@ -434,9 +434,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.159" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "lzma-rs" @@ -506,7 +506,7 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd0b9a93a84b0d3ec3e70e02d332dc33ac6dfac9cde63e17fcb77172dededa62" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -649,7 +649,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -755,9 +755,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.87" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3e4daa0dcf6feba26f985457cdf104d4b4256fc5a09547140f3631bb076b19a" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] @@ -777,7 +777,7 @@ version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", ] [[package]] @@ -838,9 +838,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "sawp" @@ -867,7 +867,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a585d3c22887d23bb06dd602b8ce96c2a716e1fa89beec8bfb49e466f2d643" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] @@ -886,22 +886,22 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.210" +version = "1.0.213" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1002,16 +1002,16 @@ name = "suricata-derive" version = "8.0.0-dev" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", ] [[package]] name = "suricata-lua-sys" -version = "0.1.0-alpha.1" +version = "0.1.0-alpha.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27aba92b2bc53955440218aed3fa6e6f4cf6cba443deb8fed7e216e6b1d96af2" +checksum = "aed3f46aa1b92feb15880f722bbce121e769bda1770f0090121cd6c920d89450" dependencies = [ "fs_extra", ] @@ -1033,18 +1033,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.79" +version = "2.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "unicode-ident", ] @@ -1055,7 +1055,7 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", "syn 1.0.109", "unicode-xid 0.2.6", @@ -1067,9 +1067,9 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1088,9 +1088,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adcb7fd841cd518e279be3d5a3eb0636409487998a4aff22f3de87b81e88384f" dependencies = [ "cfg-if", - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1099,30 +1099,30 @@ version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", "test-case-core", ] [[package]] name = "thiserror" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "1.0.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] [[package]] @@ -1270,7 +1270,7 @@ version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "proc-macro2 1.0.87", + "proc-macro2 1.0.89", "quote 1.0.37", - "syn 2.0.79", + "syn 2.0.85", ] From 4f4726faf6ea1b54cc43cd15e02720e09bde2aa8 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 30 Jul 2024 10:56:28 +0200 Subject: [PATCH 02/11] smb2: use if let for read/write parsing --- rust/src/smb/smb2.rs | 552 +++++++++++++++++++------------------------ 1 file changed, 249 insertions(+), 303 deletions(-) diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 98ed30da1cb5..180c15b61b51 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -164,28 +164,27 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema let mut set_event_fileoverlap = false; // look up existing tracker and if we have it update it - let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient) { - Some(tx) => { - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - let file_id : u32 = tx.id as u32; - if offset < tdf.file_tracker.tracked { - set_event_fileoverlap = true; - } - if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { - state.set_event(SMBEvent::ReadQueueSizeExceeded); - state.set_skip(Direction::ToClient, nbss_remaining); - } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { - state.set_event(SMBEvent::ReadQueueCntExceeded); - state.set_skip(Direction::ToClient, nbss_remaining); - } else { - filetracker_newchunk(&mut tdf.file_tracker, - &tdf.file_name, rd.data, offset, - rd.len, false, &file_id); - } + let found = if let Some(tx) = state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToClient) { + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + let file_id : u32 = tx.id as u32; + if offset < tdf.file_tracker.tracked { + set_event_fileoverlap = true; } - true - }, - None => { false }, + if max_queue_size != 0 && tdf.file_tracker.get_inflight_size() + rd.len as u64 > max_queue_size.into() { + state.set_event(SMBEvent::ReadQueueSizeExceeded); + state.set_skip(Direction::ToClient, nbss_remaining); + } else if max_queue_cnt != 0 && tdf.file_tracker.get_inflight_cnt() >= max_queue_cnt as usize { + state.set_event(SMBEvent::ReadQueueCntExceeded); + state.set_skip(Direction::ToClient, nbss_remaining); + } else { + filetracker_newchunk(&mut tdf.file_tracker, + &tdf.file_name, rd.data, offset, + rd.len, false, &file_id); + } + } + true + } else { + false }; SCLogDebug!("existing file tx? {}", found); if !found { @@ -309,28 +308,27 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema }; let mut set_event_fileoverlap = false; - let found = match state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer) { - Some(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; - } - 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, nbss_remaining); - } 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, nbss_remaining); - } else { - filetracker_newchunk(&mut tdf.file_tracker, - &file_name, wr.data, wr.wr_offset, - wr.wr_len, false, &file_id); - } + let found = if let Some(tx) = state.get_file_tx_by_fuid_with_open_file(&file_guid, Direction::ToServer) { + 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; } - true - }, - None => { false }, + 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, nbss_remaining); + } 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, nbss_remaining); + } else { + filetracker_newchunk(&mut tdf.file_tracker, + &file_name, wr.data, wr.wr_offset, + wr.wr_len, false, &file_id); + } + } + true + } else { + false }; if !found { let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); @@ -490,36 +488,26 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) false } SMB2_COMMAND_NEGOTIATE_PROTOCOL => { - match parse_smb2_request_negotiate_protocol(r.data) { - Ok((_, rd)) => { - let mut dialects : Vec> = Vec::new(); - for d in rd.dialects_vec { - SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d)); - let dvec = smb2_dialect_string(d).as_bytes().to_vec(); - dialects.push(dvec); - } + if let Ok((_, rd)) = parse_smb2_request_negotiate_protocol(r.data) { + let mut dialects : Vec> = Vec::new(); + for d in rd.dialects_vec { + SCLogDebug!("dialect {:x} => {}", d, &smb2_dialect_string(d)); + let dvec = smb2_dialect_string(d).as_bytes().to_vec(); + dialects.push(dvec); + } - let found = match state.get_negotiate_tx(2) { - Some(_) => { - SCLogDebug!("WEIRD, should not have NEGOTIATE tx!"); - true - }, - None => { false }, - }; - if !found { - let tx = state.new_negotiate_tx(2); - if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { - tdn.dialects2 = dialects; - tdn.client_guid = Some(rd.client_guid.to_vec()); - } - tx.request_done = true; + if state.get_negotiate_tx(2).is_none() { + let tx = state.new_negotiate_tx(2); + if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { + tdn.dialects2 = dialects; + tdn.client_guid = Some(rd.client_guid.to_vec()); } - true - }, - _ => { - events.push(SMBEvent::MalformedData); - false - }, + tx.request_done = true; + } + true + } else { + events.push(SMBEvent::MalformedData); + false } }, SMB2_COMMAND_SESSION_SETUP => { @@ -527,70 +515,59 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) true }, SMB2_COMMAND_TREE_CONNECT => { - match parse_smb2_request_tree_connect(r.data) { - Ok((_, tr)) => { - let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); - let mut name_val = tr.share_name.to_vec(); - name_val.retain(|&i|i != 0x00); - if name_val.len() > 1 { - name_val = name_val[1..].to_vec(); - } - - let tx = state.new_treeconnect_tx(name_key, name_val); - tx.request_done = true; - tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT); - true + if let Ok((_, tr)) = parse_smb2_request_tree_connect(r.data) { + let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); + let mut name_val = tr.share_name.to_vec(); + name_val.retain(|&i|i != 0x00); + if name_val.len() > 1 { + name_val = name_val[1..].to_vec(); } - _ => { - events.push(SMBEvent::MalformedData); - false - }, + + let tx = state.new_treeconnect_tx(name_key, name_val); + tx.request_done = true; + tx.vercmd.set_smb2_cmd(SMB2_COMMAND_TREE_CONNECT); + true + } else { + events.push(SMBEvent::MalformedData); + false } }, SMB2_COMMAND_READ => { - match parse_smb2_request_read(r.data) { - Ok((_, rd)) => { - if (state.max_read_size != 0 && rd.rd_len > state.max_read_size) || - (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len }) { + if let Ok((_, rd)) = parse_smb2_request_read(r.data) { + if (state.max_read_size != 0 && rd.rd_len > state.max_read_size) || + (unsafe { SMB_CFG_MAX_READ_SIZE != 0 && SMB_CFG_MAX_READ_SIZE < rd.rd_len }) { events.push(SMBEvent::ReadRequestTooLarge); } else { SCLogDebug!("SMBv2 READ: GUID {:?} requesting {} bytes at offset {}", - rd.guid, rd.rd_len, rd.rd_offset); + rd.guid, rd.rd_len, rd.rd_offset); // store read guid,offset in map let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset); state.ssn2vecoffset_map.insert(guid_key, guidoff); - } - }, - _ => { - events.push(SMBEvent::MalformedData); - }, + } + } else { + events.push(SMBEvent::MalformedData); } false }, SMB2_COMMAND_CREATE => { - match parse_smb2_request_create(r.data) { - Ok((_, cr)) => { - let del = cr.create_options & 0x0000_1000 != 0; - let dir = cr.create_options & 0x0000_0001 != 0; - - SCLogDebug!("create_options {:08x}", cr.create_options); - - let name_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); - state.ssn2vec_map.insert(name_key, cr.data.to_vec()); - - let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - let tx = state.new_create_tx(cr.data, - cr.disposition, del, dir, tx_hdr); - tx.vercmd.set_smb2_cmd(r.command); - SCLogDebug!("TS CREATE TX {} created", tx.id); - true - }, - _ => { - events.push(SMBEvent::MalformedData); - false - }, + if let Ok((_, cr)) = parse_smb2_request_create(r.data) { + let del = cr.create_options & 0x0000_1000 != 0; + let dir = cr.create_options & 0x0000_0001 != 0; + SCLogDebug!("create_options {:08x}", cr.create_options); + + let name_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); + state.ssn2vec_map.insert(name_key, cr.data.to_vec()); + + let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); + let tx = state.new_create_tx(cr.data, cr.disposition, del, dir, tx_hdr); + tx.vercmd.set_smb2_cmd(r.command); + SCLogDebug!("TS CREATE TX {} created", tx.id); + true + } else { + events.push(SMBEvent::MalformedData); + false } }, SMB2_COMMAND_WRITE => { @@ -598,43 +575,38 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) true // write handling creates both file tx and generic tx }, SMB2_COMMAND_CLOSE => { - match parse_smb2_request_close(r.data) { - Ok((_, cd)) => { - let found_ts = match state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) { - Some(tx) => { - if !tx.request_done { - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - filetracker_close(&mut tdf.file_tracker); - } - } - tx.request_done = true; - tx.response_done = true; - tx.set_status(SMB_NTSTATUS_SUCCESS, false); - true - }, - None => { false }, - }; - let found_tc = match state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) { - Some(tx) => { - if !tx.request_done { - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - filetracker_close(&mut tdf.file_tracker); - } - } - tx.request_done = true; - tx.response_done = true; - tx.set_status(SMB_NTSTATUS_SUCCESS, false); - true - }, - None => { false }, - }; - if !found_ts && !found_tc { - SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid); + if let Ok((_, cd)) = parse_smb2_request_close(r.data) { + let found_ts = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) { + if !tx.request_done { + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + filetracker_close(&mut tdf.file_tracker); + } } - }, - _ => { - events.push(SMBEvent::MalformedData); - }, + tx.request_done = true; + tx.response_done = true; + tx.set_status(SMB_NTSTATUS_SUCCESS, false); + true + } else { + false + }; + let found_tc = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToClient) { + if !tx.request_done { + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + filetracker_close(&mut tdf.file_tracker); + } + } + tx.request_done = true; + tx.response_done = true; + tx.set_status(SMB_NTSTATUS_SUCCESS, false); + true + } else { + false + }; + if !found_ts && !found_tc { + SCLogDebug!("SMBv2: CLOSE(TS): no TX at GUID {:?}", cd.guid); + } + } else { + events.push(SMBEvent::MalformedData); } false }, @@ -671,20 +643,16 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) }, SMB2_COMMAND_WRITE => { if r.nt_status == SMB_NTSTATUS_SUCCESS { - match parse_smb2_response_write(r.data) - { - Ok((_, _wr)) => { - SCLogDebug!("SMBv2: Write response => {:?}", _wr); - - /* search key-guid map */ - let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID, - r.session_id, r.tree_id, r.message_id); - let _guid_vec = state.ssn2vec_map.remove(&guid_key).unwrap_or_default(); - SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec); - } - _ => { - events.push(SMBEvent::MalformedData); - }, + if let Ok((_, _wr)) = parse_smb2_response_write(r.data) { + SCLogDebug!("SMBv2: Write response => {:?}", _wr); + + /* search key-guid map */ + let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID, + r.session_id, r.tree_id, r.message_id); + let _guid_vec = state.ssn2vec_map.remove(&guid_key).unwrap_or_default(); + SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec); + } else { + events.push(SMBEvent::MalformedData); } } false // the request may have created a generic tx, so handle that here @@ -693,75 +661,61 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) if r.nt_status == SMB_NTSTATUS_SUCCESS || r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW { smb2_read_response_record(state, r, 0); - false - } else if r.nt_status == SMB_NTSTATUS_END_OF_FILE { SCLogDebug!("SMBv2: read response => EOF"); let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); - let file_guid = match state.ssn2vecoffset_map.remove(&guid_key) { - Some(o) => o.guid, - _ => { - SCLogDebug!("SMBv2 READ response: reply to unknown request"); - Vec::new() - }, + let file_guid = if let Some(o) = state.ssn2vecoffset_map.remove(&guid_key) { + o.guid + } else { + SCLogDebug!("SMBv2 READ response: reply to unknown request"); + Vec::new() }; - let found = match state.get_file_tx_by_fuid(&file_guid, Direction::ToClient) { - Some(tx) => { - if !tx.request_done { - if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { - filetracker_close(&mut tdf.file_tracker); - } + if let Some(tx) = state.get_file_tx_by_fuid(&file_guid, Direction::ToClient) { + if !tx.request_done { + if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { + filetracker_close(&mut tdf.file_tracker); } - tx.set_status(r.nt_status, false); - tx.request_done = true; - false - }, - None => { false }, - }; - if !found { - SCLogDebug!("SMBv2 READ: no TX at GUID {:?}", file_guid); + } + tx.set_status(r.nt_status, false); + tx.request_done = true; } - false } else { SCLogDebug!("SMBv2 READ: status {}", r.nt_status); - false } + false }, SMB2_COMMAND_CREATE => { if r.nt_status == SMB_NTSTATUS_SUCCESS { - match parse_smb2_response_create(r.data) { - Ok((_, cr)) => { - SCLogDebug!("SMBv2: Create response => {:?}", cr); - - let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); - if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { - p.retain(|&i|i != 0x00); - state.guid2name_map.insert(cr.guid.to_vec(), p); - } else { - SCLogDebug!("SMBv2 response: GUID NOT FOUND"); - } + if let Ok((_, cr)) = parse_smb2_response_create(r.data) { + SCLogDebug!("SMBv2: Create response => {:?}", cr); - let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) { - SCLogDebug!("tx {} with {}/{} marked as done", - tx.id, r.command, &smb2_command_string(r.command)); - tx.set_status(r.nt_status, false); - tx.response_done = true; + let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); + if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { + p.retain(|&i|i != 0x00); + state.guid2name_map.insert(cr.guid.to_vec(), p); + } else { + SCLogDebug!("SMBv2 response: GUID NOT FOUND"); + } - if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data { - tdn.create_ts = cr.create_ts.as_unix(); - tdn.last_access_ts = cr.last_access_ts.as_unix(); - tdn.last_write_ts = cr.last_write_ts.as_unix(); - tdn.last_change_ts = cr.last_change_ts.as_unix(); - tdn.size = cr.size; - tdn.guid = cr.guid.to_vec(); - } + let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); + if let Some(tx) = state.get_generic_tx(2, r.command, &tx_hdr) { + SCLogDebug!("tx {} with {}/{} marked as done", + tx.id, r.command, &smb2_command_string(r.command)); + tx.set_status(r.nt_status, false); + tx.response_done = true; + + if let Some(SMBTransactionTypeData::CREATE(ref mut tdn)) = tx.type_data { + tdn.create_ts = cr.create_ts.as_unix(); + tdn.last_access_ts = cr.last_access_ts.as_unix(); + tdn.last_write_ts = cr.last_write_ts.as_unix(); + tdn.last_change_ts = cr.last_change_ts.as_unix(); + tdn.size = cr.size; + tdn.guid = cr.guid.to_vec(); } } - _ => { - events.push(SMBEvent::MalformedData); - }, + } else { + events.push(SMBEvent::MalformedData); } true } else { @@ -777,50 +731,45 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) } SMB2_COMMAND_TREE_CONNECT => { if r.nt_status == SMB_NTSTATUS_SUCCESS { - match parse_smb2_response_tree_connect(r.data) { - Ok((_, tr)) => { - let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); - let mut share_name = Vec::new(); - let is_pipe = tr.share_type == 2; - let found = match state.get_treeconnect_tx(name_key) { - Some(tx) => { - if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { - tdn.share_type = tr.share_type; - tdn.is_pipe = is_pipe; - tdn.tree_id = r.tree_id; - share_name = tdn.share_name.to_vec(); - } - // update hdr now that we have a tree_id - tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); - tx.response_done = true; - tx.set_status(r.nt_status, false); - true - }, - None => { false }, - }; - if found { - let tree = SMBTree::new(share_name.to_vec(), is_pipe); - let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.insert(tree_key, tree); - } - true + if let Ok((_, tr)) = parse_smb2_response_tree_connect(r.data) { + let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); + let mut share_name = Vec::new(); + let is_pipe = tr.share_type == 2; + let found = match state.get_treeconnect_tx(name_key) { + Some(tx) => { + if let Some(SMBTransactionTypeData::TREECONNECT(ref mut tdn)) = tx.type_data { + tdn.share_type = tr.share_type; + tdn.is_pipe = is_pipe; + tdn.tree_id = r.tree_id; + share_name = tdn.share_name.to_vec(); + } + // update hdr now that we have a tree_id + tx.hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_HEADER); + tx.response_done = true; + tx.set_status(r.nt_status, false); + true + }, + None => { false }, + }; + if found { + let tree = SMBTree::new(share_name.to_vec(), is_pipe); + let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); + state.ssn2tree_map.insert(tree_key, tree); } - _ => { - events.push(SMBEvent::MalformedData); - false - }, + true + } else { + events.push(SMBEvent::MalformedData); + false } } else { let name_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_TREE); - let found = match state.get_treeconnect_tx(name_key) { - Some(tx) => { - tx.response_done = true; - tx.set_status(r.nt_status, false); - true - }, - None => { false }, - }; - found + if let Some(tx) = state.get_treeconnect_tx(name_key) { + tx.response_done = true; + tx.set_status(r.nt_status, false); + true + } else { + false + } } }, SMB2_COMMAND_NEGOTIATE_PROTOCOL => { @@ -829,52 +778,49 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) } else { parse_smb2_response_negotiate_protocol_error(r.data) }; - match res { - Ok((_, rd)) => { - SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect)); + if let Ok((_, rd)) = res { + SCLogDebug!("SERVER dialect => {}", &smb2_dialect_string(rd.dialect)); - let smb_cfg_max_read_size = unsafe { SMB_CFG_MAX_READ_SIZE }; - if smb_cfg_max_read_size != 0 && rd.max_read_size > smb_cfg_max_read_size { - state.set_event(SMBEvent::NegotiateMaxReadSizeTooLarge); - } - let smb_cfg_max_write_size = unsafe { SMB_CFG_MAX_WRITE_SIZE }; - if smb_cfg_max_write_size != 0 && rd.max_write_size > smb_cfg_max_write_size { - state.set_event(SMBEvent::NegotiateMaxWriteSizeTooLarge); - } + let smb_cfg_max_read_size = unsafe { SMB_CFG_MAX_READ_SIZE }; + if smb_cfg_max_read_size != 0 && rd.max_read_size > smb_cfg_max_read_size { + state.set_event(SMBEvent::NegotiateMaxReadSizeTooLarge); + } + let smb_cfg_max_write_size = unsafe { SMB_CFG_MAX_WRITE_SIZE }; + if smb_cfg_max_write_size != 0 && rd.max_write_size > smb_cfg_max_write_size { + state.set_event(SMBEvent::NegotiateMaxWriteSizeTooLarge); + } - state.dialect = rd.dialect; - state.max_read_size = rd.max_read_size; - state.max_write_size = rd.max_write_size; + state.dialect = rd.dialect; + state.max_read_size = rd.max_read_size; + state.max_write_size = rd.max_write_size; - let found2 = match state.get_negotiate_tx(2) { - Some(tx) => { - if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { - tdn.server_guid = rd.server_guid.to_vec(); - } - tx.set_status(r.nt_status, false); - tx.response_done = true; - true - }, - None => { false }, - }; - // SMB2 response to SMB1 request? - let found1 = !found2 && match state.get_negotiate_tx(1) { - Some(tx) => { - if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { - tdn.server_guid = rd.server_guid.to_vec(); - } - tx.set_status(r.nt_status, false); - tx.response_done = true; - true - }, - None => { false }, - }; - found1 || found2 - }, - _ => { - events.push(SMBEvent::MalformedData); - false - } + let found2 = match state.get_negotiate_tx(2) { + Some(tx) => { + if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { + tdn.server_guid = rd.server_guid.to_vec(); + } + tx.set_status(r.nt_status, false); + tx.response_done = true; + true + }, + None => { false }, + }; + // SMB2 response to SMB1 request? + let found1 = !found2 && match state.get_negotiate_tx(1) { + Some(tx) => { + if let Some(SMBTransactionTypeData::NEGOTIATE(ref mut tdn)) = tx.type_data { + tdn.server_guid = rd.server_guid.to_vec(); + } + tx.set_status(r.nt_status, false); + tx.response_done = true; + true + }, + None => { false }, + }; + found1 || found2 + } else { + events.push(SMBEvent::MalformedData); + false } }, _ => { From f901bd992ce68b524b23fa32a5eac85b3395cfd7 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 30 Jul 2024 14:01:14 +0200 Subject: [PATCH 03/11] smb: update to GAP handling Don't tag the session as gap'd when the GAP is in a precise location: 1. in "skip" data, where the GAP just fits the skip data 2. in file data, where we pass the GAP on to the file This reduces load of GAP post-processing that is unnecessary in these case. --- rust/src/smb/smb.rs | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 7dab8debbc74..f80191b03bc3 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -28,7 +28,6 @@ use std; use std::str; use std::ffi::{self, CString}; - use std::collections::HashMap; use std::collections::VecDeque; @@ -1860,19 +1859,30 @@ impl SMBState { /// handle a gap in the TOSERVER direction /// returns: 0 ok, 1 unrecoverable error pub fn parse_tcp_data_ts_gap(&mut self, gap_size: u32) -> AppLayerResult { + SCLogDebug!("GAP of size {} in toserver direction", gap_size); let consumed = self.handle_skip(Direction::ToServer, gap_size); + if consumed == gap_size { + /* no need to tag ssn as gap'd as we got it in our skip logic. */ + return AppLayerResult::ok(); + } + if consumed < gap_size { let new_gap_size = gap_size - consumed; let gap = vec![0; new_gap_size as usize]; let consumed2 = self.filetracker_update(Direction::ToServer, &gap, new_gap_size); + if consumed2 == new_gap_size { + /* no need to tag ssn as gap'd as we got it in our file logic. */ + return AppLayerResult::ok(); + } + if consumed2 > new_gap_size { SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size); self.set_event(SMBEvent::InternalError); return AppLayerResult::err(); } } - SCLogDebug!("GAP of size {} in toserver direction", gap_size); + self.ts_ssn_gap = true; self.ts_gap = true; return AppLayerResult::ok(); @@ -1881,19 +1891,30 @@ impl SMBState { /// handle a gap in the TOCLIENT direction /// returns: 0 ok, 1 unrecoverable error pub fn parse_tcp_data_tc_gap(&mut self, gap_size: u32) -> AppLayerResult { + SCLogDebug!("GAP of size {} in toclient direction", gap_size); let consumed = self.handle_skip(Direction::ToClient, gap_size); + if consumed == gap_size { + /* no need to tag ssn as gap'd as we got it in our skip logic. */ + return AppLayerResult::ok(); + } + if consumed < gap_size { let new_gap_size = gap_size - consumed; let gap = vec![0; new_gap_size as usize]; let consumed2 = self.filetracker_update(Direction::ToClient, &gap, new_gap_size); + if consumed2 == new_gap_size { + /* no need to tag ssn as gap'd as we got it in our file logic. */ + return AppLayerResult::ok(); + } + if consumed2 > new_gap_size { SCLogDebug!("consumed more than GAP size: {} > {}", consumed2, new_gap_size); self.set_event(SMBEvent::InternalError); return AppLayerResult::err(); } } - SCLogDebug!("GAP of size {} in toclient direction", gap_size); + self.tc_ssn_gap = true; self.tc_gap = true; return AppLayerResult::ok(); From 8331313c0308d6a3ae03e10fed45e7295304979e Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 30 Jul 2024 14:15:34 +0200 Subject: [PATCH 04/11] smb2: remove filename on close Ticket: #5672. --- rust/src/smb/smb2.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 180c15b61b51..0ccfdf0d88ab 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -576,6 +576,8 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) }, SMB2_COMMAND_CLOSE => { if let Ok((_, cd)) = parse_smb2_request_close(r.data) { + let _name = state.guid2name_map.remove(cd.guid); + let found_ts = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) { if !tx.request_done { if let Some(SMBTransactionTypeData::FILE(ref mut tdf)) = tx.type_data { From 77b38d63a2cc27764cc3089496ac9f55d392c9b1 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 30 Jul 2024 15:59:38 +0200 Subject: [PATCH 05/11] smb1: remove name on close Ticket: #5672. --- rust/src/smb/smb1.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index b0d4e4d3d9c2..9f3f1ea21b19 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -535,6 +535,8 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and Ok((_, cd)) => { let mut fid = cd.fid.to_vec(); fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); + + let _name = state.guid2name_map.remove(&fid); state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid); @@ -1008,6 +1010,7 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse state.set_file_left(Direction::ToServer, rd.len, rd.data.len() as u32, file_fid.to_vec()); if command == SMB1_COMMAND_WRITE_AND_CLOSE { + let _name = state.guid2name_map.remove(&file_fid); SCLogDebug!("closing FID {:?}", file_fid); smb1_close_file(state, &file_fid, Direction::ToServer); } From 770e59bdf6d9f2370afe52044b9a9c7ee5dde3e9 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Tue, 17 Sep 2024 17:10:19 +0200 Subject: [PATCH 06/11] smb: use lru for guid2name map; rename Use `lru` crate. Rename to reflect this. Add `app-layer.protocols.smb.max-guid-cache-size` to control the max size of the LRU cache. Ticket: #5672. --- rust/Cargo.lock.in | 39 ++++++++++++++++++++++++++++++++++++ rust/Cargo.toml.in | 1 + rust/src/lib.rs | 1 + rust/src/smb/debug.rs | 10 +++++++++- rust/src/smb/smb.rs | 46 +++++++++++++++++++++++++++++++++++++------ rust/src/smb/smb1.rs | 14 ++++++------- rust/src/smb/smb2.rs | 18 ++++++++--------- 7 files changed, 106 insertions(+), 23 deletions(-) diff --git a/rust/Cargo.lock.in b/rust/Cargo.lock.in index 311a98eff9bf..ad19cb5f3ff6 100644 --- a/rust/Cargo.lock.in +++ b/rust/Cargo.lock.in @@ -67,6 +67,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "asn1-rs" version = "0.6.2" @@ -296,6 +302,12 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "failure" version = "0.1.8" @@ -327,6 +339,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "fs_extra" version = "1.3.0" @@ -364,6 +382,17 @@ dependencies = [ "polyval", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "hex" version = "0.4.3" @@ -438,6 +467,15 @@ version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "lru" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +dependencies = [ + "hashbrown", +] + [[package]] name = "lzma-rs" version = "0.2.0" @@ -973,6 +1011,7 @@ dependencies = [ "lazy_static", "ldap-parser", "libc", + "lru", "lzma-rs", "md-5", "memchr", diff --git a/rust/Cargo.toml.in b/rust/Cargo.toml.in index c69db937708e..151bc74a6d06 100644 --- a/rust/Cargo.toml.in +++ b/rust/Cargo.toml.in @@ -40,6 +40,7 @@ brotli = "~3.4.0" hkdf = "~0.12.3" aes = "~0.7.5" aes-gcm = "~0.9.4" +lru = "~0.12.5" der-parser = { version = "~9.0.0", default-features = false } kerberos-parser = { version = "~0.8.0", default-features = false } diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 7a99cd2532e5..7b389f7b88fe 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -58,6 +58,7 @@ extern crate bitflags; extern crate byteorder; extern crate crc; extern crate memchr; +extern crate lru; #[macro_use] extern crate num_derive; extern crate widestring; diff --git a/rust/src/smb/debug.rs b/rust/src/smb/debug.rs index 86799dd7fa1d..f87180b6a8f3 100644 --- a/rust/src/smb/debug.rs +++ b/rust/src/smb/debug.rs @@ -69,6 +69,14 @@ impl SMBState { #[cfg(feature = "debug")] pub fn _debug_state_stats(&self) { - SCLogDebug!("ssn2vec_map {} guid2name_map {} ssn2vecoffset_map {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", self.ssn2vec_map.len(), self.guid2name_map.len(), self.ssn2vecoffset_map.len(), self.ssn2tree_map.len(), self.ssnguid2vec_map.len(), self.file_ts_guid.len(), self.file_tc_guid.len(), self.transactions.len()); + SCLogDebug!("ssn2vec_map {} guid2name_cache {} ssn2vecoffset_map {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", + self.ssn2vec_map.len(), + self.guid2name_cache.len(), + self.ssn2vecoffset_map.len(), + self.ssn2tree_map.len(), + self.ssnguid2vec_map.len(), + self.file_ts_guid.len(), + self.file_tc_guid.len(), + self.transactions.len()); } } diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index f80191b03bc3..e69399c71891 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -34,6 +34,9 @@ use std::collections::VecDeque; use nom7::{Err, Needed}; use nom7::error::{make_error, ErrorKind}; +use lru::LruCache; +use std::num::NonZeroUsize; + use crate::core::*; use crate::applayer; use crate::applayer::*; @@ -79,6 +82,8 @@ pub static mut SMB_CFG_MAX_READ_QUEUE_CNT: u32 = 64; pub static mut SMB_CFG_MAX_WRITE_SIZE: u32 = 16777216; pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864; pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64; +/// max size of the per state guid2name cache +pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -681,14 +686,23 @@ pub fn u32_as_bytes(i: u32) -> [u8;4] { return [o1, o2, o3, o4] } -#[derive(Default, Debug)] +#[derive(Debug)] pub struct SMBState<> { pub state_data: AppLayerStateData, /// map ssn/tree/msgid to vec (guid/name/share) pub ssn2vec_map: HashMap>, + /// map guid to filename - pub guid2name_map: HashMap, Vec>, + /// + /// Lifecycle of the members: + /// - Added by CREATE responses + /// - Removed by CLOSE requests + /// - Post GAP logic removes based on timestamp, as the CLOSE + /// commands may have been missed. + /// + pub guid2name_cache: LruCache, Vec>, + /// map ssn key to read offset pub ssn2vecoffset_map: HashMap, @@ -754,13 +768,19 @@ impl State for SMBState { } } +impl Default for SMBState { + fn default() -> Self { + Self::new() + } +} + impl SMBState { /// Allocation function for a new TLS parser instance pub fn new() -> Self { Self { state_data:AppLayerStateData::new(), ssn2vec_map:HashMap::new(), - guid2name_map:HashMap::new(), + guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()), ssn2vecoffset_map:HashMap::new(), ssn2tree_map:HashMap::new(), ssnguid2vec_map:HashMap::new(), @@ -784,8 +804,9 @@ impl SMBState { dialect:0, dialect_vec: None, dcerpc_ifaces: None, + max_read_size: 0, + max_write_size: 0, ts: 0, - ..Default::default() } } @@ -1042,9 +1063,9 @@ impl SMBState { return tx_ref.unwrap(); } - pub fn get_service_for_guid(&self, guid: &[u8]) -> (&'static str, bool) + pub fn get_service_for_guid(&mut self, guid: &[u8]) -> (&'static str, bool) { - let (name, is_dcerpc) = match self.guid2name_map.get(guid) { + let (name, is_dcerpc) = match self.guid2name_cache.get(guid) { Some(n) => { let mut s = n.as_slice(); // skip leading \ if we have it @@ -2422,10 +2443,23 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid value for smb.max-tx"); } } + let retval = conf_get("app-layer.protocols.smb.max-guid-cache-size"); + if let Some(val) = retval { + if let Ok(v) = val.parse::() { + if v > 0 { + SMB_CFG_MAX_GUID_CACHE_SIZE = v; + } else { + SCLogError!("Invalid max-guid-cache-size value"); + } + } else { + SCLogError!("Invalid max-guid-cache-size value"); + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_WRITE_SIZE, SMB_CFG_MAX_WRITE_QUEUE_CNT, SMB_CFG_MAX_WRITE_QUEUE_SIZE); + SCLogConfig!("guid: max cache size: {}", SMB_CFG_MAX_GUID_CACHE_SIZE); } else { SCLogDebug!("Protocol detector and parser disabled for SMB."); } diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index 9f3f1ea21b19..731bedeb3390 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -306,7 +306,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let mut frankenfid = pd.fid.to_vec(); frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); - let filename = match state.guid2name_map.get(&frankenfid) { + let filename = match state.guid2name_cache.get(&frankenfid) { Some(n) => n.to_vec(), None => b"".to_vec(), }; @@ -341,7 +341,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let mut frankenfid = pd.fid.to_vec(); frankenfid.extend_from_slice(&u32_as_bytes(r.ssn_id)); - let oldname = match state.guid2name_map.get(&frankenfid) { + let oldname = match state.guid2name_cache.get(&frankenfid) { Some(n) => n.to_vec(), None => b"".to_vec(), }; @@ -536,7 +536,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let mut fid = cd.fid.to_vec(); fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); - let _name = state.guid2name_map.remove(&fid); + let _name = state.guid2name_cache.pop(&fid); state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid); @@ -734,7 +734,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); SCLogDebug!("SMB1_COMMAND_NT_CREATE_ANDX fid {:?}", fid); SCLogDebug!("fid {:?} name {:?}", fid, p); - state.guid2name_map.insert(fid, p); + _ = state.guid2name_cache.put(fid, p); } else { SCLogDebug!("SMBv1 response: GUID NOT FOUND"); } @@ -954,7 +954,7 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse SCLogDebug!("SMBv1 WRITE: FID {:?} offset {}", file_fid, rd.offset); - let file_name = match state.guid2name_map.get(&file_fid) { + let file_name = match state.guid2name_cache.get(&file_fid) { Some(n) => n.to_vec(), None => b"".to_vec(), }; @@ -1010,7 +1010,7 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse state.set_file_left(Direction::ToServer, rd.len, rd.data.len() as u32, file_fid.to_vec()); if command == SMB1_COMMAND_WRITE_AND_CLOSE { - let _name = state.guid2name_map.remove(&file_fid); + let _name = state.guid2name_cache.pop(&file_fid); SCLogDebug!("closing FID {:?}", file_fid); smb1_close_file(state, &file_fid, Direction::ToServer); } @@ -1055,7 +1055,7 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse _ => { (false, Vec::new()) }, }; if !is_pipe { - let file_name = match state.guid2name_map.get(&file_fid) { + let file_name = match state.guid2name_cache.get(&file_fid) { Some(n) => n.to_vec(), None => Vec::new(), }; diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 0ccfdf0d88ab..f6c0d924f46e 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -210,7 +210,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); state.ssn2tree_map.insert(tree_key, tree); if !is_dcerpc { - state.guid2name_map.insert(file_guid.to_vec(), b"suricata::dcerpc".to_vec()); + _ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec()); } is_pipe = true; is_dcerpc = true; @@ -228,7 +228,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("non-DCERPC pipe"); state.set_skip(Direction::ToClient, nbss_remaining); } else { - let file_name = match state.guid2name_map.get(&file_guid) { + let file_name = match state.guid2name_cache.get(&file_guid) { Some(n) => { n.to_vec() } None => { b"".to_vec() } }; @@ -302,7 +302,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema state.ssn2vec_map.insert(guid_key, wr.guid.to_vec()); let file_guid = wr.guid.to_vec(); - let file_name = match state.guid2name_map.get(&file_guid) { + let file_name = match state.guid2name_cache.get(&file_guid) { Some(n) => n.to_vec(), None => Vec::new(), }; @@ -354,8 +354,8 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); state.ssn2tree_map.insert(tree_key, tree); if !is_dcerpc { - state.guid2name_map.insert(file_guid.to_vec(), - b"suricata::dcerpc".to_vec()); + _ = state.guid2name_cache.put(file_guid.to_vec(), + b"suricata::dcerpc".to_vec()); } is_pipe = true; is_dcerpc = true; @@ -427,7 +427,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); let mut newname = ren.name.to_vec(); newname.retain(|&i|i != 0x00); - let oldname = match state.guid2name_map.get(rd.guid) { + let oldname = match state.guid2name_cache.get(rd.guid) { Some(n) => { n.to_vec() }, None => { b"".to_vec() }, }; @@ -439,7 +439,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) } Smb2SetInfoRequestData::DISPOSITION(ref dis) => { let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); - let fname = match state.guid2name_map.get(rd.guid) { + let fname = match state.guid2name_cache.get(rd.guid) { Some(n) => { n.to_vec() }, None => { // try to find latest created file in case of chained commands @@ -576,7 +576,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) }, SMB2_COMMAND_CLOSE => { if let Ok((_, cd)) = parse_smb2_request_close(r.data) { - let _name = state.guid2name_map.remove(cd.guid); + let _name = state.guid2name_cache.pop(cd.guid); let found_ts = if let Some(tx) = state.get_file_tx_by_fuid(cd.guid, Direction::ToServer) { if !tx.request_done { @@ -695,7 +695,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { p.retain(|&i|i != 0x00); - state.guid2name_map.insert(cr.guid.to_vec(), p); + _ = state.guid2name_cache.put(cr.guid.to_vec(), p); } else { SCLogDebug!("SMBv2 response: GUID NOT FOUND"); } From 64e314d7b511323068dfa1eaffa08b6056cdaf57 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Oct 2024 14:18:48 +0200 Subject: [PATCH 07/11] smb: use lru for ssn2vecoffset_map; rename Rename to read_offset_cache. Add `app-layer.protocols.smb.max-read-offset-cache-size` option to control the limit. Ticket: #5672. --- rust/src/smb/debug.rs | 4 ++-- rust/src/smb/smb.rs | 18 ++++++++++++++++-- rust/src/smb/smb1.rs | 4 ++-- rust/src/smb/smb2.rs | 6 +++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/rust/src/smb/debug.rs b/rust/src/smb/debug.rs index f87180b6a8f3..e945de286e6f 100644 --- a/rust/src/smb/debug.rs +++ b/rust/src/smb/debug.rs @@ -69,10 +69,10 @@ impl SMBState { #[cfg(feature = "debug")] pub fn _debug_state_stats(&self) { - SCLogDebug!("ssn2vec_map {} guid2name_cache {} ssn2vecoffset_map {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", + SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", self.ssn2vec_map.len(), self.guid2name_cache.len(), - self.ssn2vecoffset_map.len(), + self.read_offset_cache.len(), self.ssn2tree_map.len(), self.ssnguid2vec_map.len(), self.file_ts_guid.len(), diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index e69399c71891..3ef84a355289 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -84,6 +84,8 @@ pub static mut SMB_CFG_MAX_WRITE_QUEUE_SIZE: u32 = 67108864; pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64; /// max size of the per state guid2name cache pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024; +/// SMBState::read_offset_cache +pub static mut SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE: usize = 128; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -704,7 +706,7 @@ pub struct SMBState<> { pub guid2name_cache: LruCache, Vec>, /// map ssn key to read offset - pub ssn2vecoffset_map: HashMap, + pub read_offset_cache: LruCache, pub ssn2tree_map: HashMap, @@ -781,7 +783,7 @@ impl SMBState { state_data:AppLayerStateData::new(), ssn2vec_map:HashMap::new(), guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()), - ssn2vecoffset_map:HashMap::new(), + read_offset_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE }).unwrap()), ssn2tree_map:HashMap::new(), ssnguid2vec_map:HashMap::new(), skip_ts:0, @@ -2455,6 +2457,18 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid max-guid-cache-size value"); } } + let retval = conf_get("app-layer.protocols.smb.max-read-offset-cache-size"); + if let Some(val) = retval { + if let Ok(v) = val.parse::() { + if v > 0 { + SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE = v; + } else { + SCLogError!("Invalid max-read-offset-cache-size value"); + } + } else { + SCLogError!("Invalid max-read-offset-cache-size value"); + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index 731bedeb3390..cce552de91a0 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -406,7 +406,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let mut fid = rr.fid.to_vec(); fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); let fidoff = SMBFileGUIDOffset::new(fid, rr.offset); - state.ssn2vecoffset_map.insert(fid_key, fidoff); + state.read_offset_cache.put(fid_key, fidoff); }, _ => { events.push(SMBEvent::MalformedData); @@ -1038,7 +1038,7 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse return; } let fid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_OFFSET); - let (offset, file_fid) = match state.ssn2vecoffset_map.remove(&fid_key) { + let (offset, file_fid) = match state.read_offset_cache.pop(&fid_key) { Some(o) => (o.offset, o.guid), None => { SCLogDebug!("SMBv1 READ response: reply to unknown request: left {} {:?}", diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index f6c0d924f46e..17603202aabe 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -152,7 +152,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema // get the request info. If we don't have it, there is nothing // we can do except skip this record. let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); - let (offset, file_guid) = match state.ssn2vecoffset_map.remove(&guid_key) { + let (offset, file_guid) = match state.read_offset_cache.pop(&guid_key) { Some(o) => (o.offset, o.guid), None => { SCLogDebug!("SMBv2 READ response: reply to unknown request {:?}",rd); @@ -544,7 +544,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) // store read guid,offset in map let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); let guidoff = SMBFileGUIDOffset::new(rd.guid.to_vec(), rd.rd_offset); - state.ssn2vecoffset_map.insert(guid_key, guidoff); + state.read_offset_cache.put(guid_key, guidoff); } } else { events.push(SMBEvent::MalformedData); @@ -667,7 +667,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) SCLogDebug!("SMBv2: read response => EOF"); let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_OFFSET); - let file_guid = if let Some(o) = state.ssn2vecoffset_map.remove(&guid_key) { + let file_guid = if let Some(o) = state.read_offset_cache.pop(&guid_key) { o.guid } else { SCLogDebug!("SMBv2 READ response: reply to unknown request"); From fe9a98f2d9776f7469bad3877b6806f2a062a42b Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Oct 2024 14:26:41 +0200 Subject: [PATCH 08/11] smb: use lru for ssn2tree; rename Turn the map mapping the smb session key to smb tree into a lru cache, limited to 1024 by default. Add `app-layer.protocols.smb.max-tree-cache-size` option to control the limit. Ticket: #5672. --- rust/src/smb/debug.rs | 4 ++-- rust/src/smb/smb.rs | 24 +++++++++++++++++++----- rust/src/smb/smb1.rs | 10 +++++----- rust/src/smb/smb2.rs | 14 +++++++------- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/rust/src/smb/debug.rs b/rust/src/smb/debug.rs index e945de286e6f..d4b28cb232c2 100644 --- a/rust/src/smb/debug.rs +++ b/rust/src/smb/debug.rs @@ -69,11 +69,11 @@ impl SMBState { #[cfg(feature = "debug")] pub fn _debug_state_stats(&self) { - SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_map {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", + SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", self.ssn2vec_map.len(), self.guid2name_cache.len(), self.read_offset_cache.len(), - self.ssn2tree_map.len(), + self.ssn2tree_cache.len(), self.ssnguid2vec_map.len(), self.file_ts_guid.len(), self.file_tc_guid.len(), diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 3ef84a355289..d195001daa6f 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -86,6 +86,8 @@ pub static mut SMB_CFG_MAX_WRITE_QUEUE_CNT: u32 = 64; pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024; /// SMBState::read_offset_cache pub static mut SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE: usize = 128; +/// For SMBState::ssn2tree_cache +pub static mut SMB_CFG_MAX_TREE_CACHE_SIZE: usize = 512; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -707,8 +709,8 @@ pub struct SMBState<> { /// map ssn key to read offset pub read_offset_cache: LruCache, - - pub ssn2tree_map: HashMap, + /// Map session key to SMBTree + pub ssn2tree_cache: LruCache, // store partial data records that are transferred in multiple // requests for DCERPC. @@ -784,7 +786,7 @@ impl SMBState { ssn2vec_map:HashMap::new(), guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()), read_offset_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE }).unwrap()), - ssn2tree_map:HashMap::new(), + ssn2tree_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_TREE_CACHE_SIZE }).unwrap()), ssnguid2vec_map:HashMap::new(), skip_ts:0, skip_tc:0, @@ -1302,7 +1304,7 @@ impl SMBState { // if complete. 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) { + let is_pipe = match self.ssn2tree_cache.get(&tree_key) { Some(n) => n.is_pipe, None => false, }; @@ -1638,7 +1640,7 @@ impl SMBState { 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) { + let is_pipe = match self.ssn2tree_cache.get(&tree_key) { Some(n) => n.is_pipe, None => false, }; @@ -2469,6 +2471,18 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid max-read-offset-cache-size value"); } } + let retval = conf_get("app-layer.protocols.smb.max-tree-cache-size"); + if let Some(val) = retval { + if let Ok(v) = val.parse::() { + if v > 0 { + SMB_CFG_MAX_TREE_CACHE_SIZE = v; + } else { + SCLogError!("Invalid max-tree-cache-size value"); + } + } else { + SCLogError!("Invalid max-tree-cache-size value"); + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index cce552de91a0..c169930d24d0 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -527,7 +527,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and }, SMB1_COMMAND_TREE_DISCONNECT => { let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.remove(&tree_key); + state.ssn2tree_cache.pop(&tree_key); false }, SMB1_COMMAND_CLOSE => { @@ -702,7 +702,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an if found { let tree = SMBTree::new(share_name.to_vec(), is_pipe); let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.insert(tree_key, tree); + state.ssn2tree_cache.put(tree_key, tree); } found }, @@ -716,7 +716,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an // normally removed when processing request, // but in case we missed that try again here let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.remove(&tree_key); + state.ssn2tree_cache.pop(&tree_key); false }, SMB1_COMMAND_NT_CREATE_ANDX => { @@ -977,7 +977,7 @@ pub fn smb1_write_request_record(state: &mut SMBState, r: &SmbRecord, andx_offse }; if !found { let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); - let (share_name, is_pipe) = match state.ssn2tree_map.get(&tree_key) { + let (share_name, is_pipe) = match state.ssn2tree_cache.get(&tree_key) { Some(n) => (n.name.to_vec(), n.is_pipe), None => (Vec::new(), false), }; @@ -1050,7 +1050,7 @@ pub fn smb1_read_response_record(state: &mut SMBState, r: &SmbRecord, andx_offse SCLogDebug!("SMBv1 READ: FID {:?} offset {}", file_fid, offset); let tree_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_SHARE); - let (is_pipe, share_name) = match state.ssn2tree_map.get(&tree_key) { + let (is_pipe, share_name) = match state.ssn2tree_cache.get(&tree_key) { Some(n) => (n.is_pipe, n.name.to_vec()), _ => { (false, Vec::new()) }, }; diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 17603202aabe..5eb57dd7b726 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -189,7 +189,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("existing file tx? {}", found); if !found { let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) { + let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) { Some(n) => (n.name.to_vec(), n.is_pipe), _ => { (Vec::new(), false) }, }; @@ -208,7 +208,7 @@ pub fn smb2_read_response_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("SMBv2/READ: looks like dcerpc"); // insert fake tree to assist in follow up lookups let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); - state.ssn2tree_map.insert(tree_key, tree); + state.ssn2tree_cache.put(tree_key, tree); if !is_dcerpc { _ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec()); } @@ -332,7 +332,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema }; if !found { let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - let (share_name, mut is_pipe) = match state.ssn2tree_map.get(&tree_key) { + let (share_name, mut is_pipe) = match state.ssn2tree_cache.get(&tree_key) { Some(n) => { (n.name.to_vec(), n.is_pipe) }, _ => { (Vec::new(), false) }, }; @@ -352,7 +352,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema SCLogDebug!("SMBv2/WRITE: looks like we have dcerpc"); let tree = SMBTree::new(b"suricata::dcerpc".to_vec(), true); - state.ssn2tree_map.insert(tree_key, tree); + state.ssn2tree_cache.put(tree_key, tree); if !is_dcerpc { _ = state.guid2name_cache.put(file_guid.to_vec(), b"suricata::dcerpc".to_vec()); @@ -484,7 +484,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) }, SMB2_COMMAND_TREE_DISCONNECT => { let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.remove(&tree_key); + state.ssn2tree_cache.pop(&tree_key); false } SMB2_COMMAND_NEGOTIATE_PROTOCOL => { @@ -728,7 +728,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) // normally removed when processing request, // but in case we missed that try again here let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.remove(&tree_key); + state.ssn2tree_cache.pop(&tree_key); false } SMB2_COMMAND_TREE_CONNECT => { @@ -756,7 +756,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) if found { let tree = SMBTree::new(share_name.to_vec(), is_pipe); let tree_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_SHARE); - state.ssn2tree_map.insert(tree_key, tree); + state.ssn2tree_cache.put(tree_key, tree); } true } else { From 2bcb393f637394bd6c811eb4b923c3d04a6df117 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Oct 2024 15:13:40 +0200 Subject: [PATCH 09/11] smb: use lru for ssnguid2vec_map; rename Reimplement the ssnguid2vec_map HashMap as a LruCache. Since this is a DCERPC record cache, name it as such. Default size is 128. Can be controlled by `app-layer.protocols.smb.max-dcerpc-frag-cache-size`. Ticket: #5672. --- rust/src/smb/dcerpc.rs | 4 ++-- rust/src/smb/debug.rs | 4 ++-- rust/src/smb/smb.rs | 22 ++++++++++++++++++---- rust/src/smb/smb1.rs | 2 +- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/rust/src/smb/dcerpc.rs b/rust/src/smb/dcerpc.rs index de6b8def73b3..771e2538c2ac 100644 --- a/rust/src/smb/dcerpc.rs +++ b/rust/src/smb/dcerpc.rs @@ -455,7 +455,7 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState, // msg_id 0 as this data crosses cmd/reply pairs let ehdr = SMBHashKeyHdrGuid::new(SMBCommonHdr::new(SMBHDR_TYPE_TRANS_FRAG, hdr.ssn_id, hdr.tree_id, 0_u64), guid.to_vec()); - let mut prevdata = state.ssnguid2vec_map.remove(&ehdr).unwrap_or_default(); + let mut prevdata = state.dcerpc_rec_frag_cache.pop(&ehdr).unwrap_or_default(); SCLogDebug!("indata {} prevdata {}", indata.len(), prevdata.len()); prevdata.extend_from_slice(indata); let data = prevdata; @@ -476,7 +476,7 @@ pub fn smb_read_dcerpc_record(state: &mut SMBState, if ntstatus == SMB_NTSTATUS_BUFFER_OVERFLOW && data.len() < dcer.frag_len as usize { SCLogDebug!("short record {} < {}: storing partial data in state", data.len(), dcer.frag_len); - state.ssnguid2vec_map.insert(ehdr, data.to_vec()); + state.dcerpc_rec_frag_cache.put(ehdr, data.to_vec()); return true; // TODO review } diff --git a/rust/src/smb/debug.rs b/rust/src/smb/debug.rs index d4b28cb232c2..f2c3d9a7a393 100644 --- a/rust/src/smb/debug.rs +++ b/rust/src/smb/debug.rs @@ -69,12 +69,12 @@ impl SMBState { #[cfg(feature = "debug")] pub fn _debug_state_stats(&self) { - SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} ssnguid2vec_map {} file_ts_guid {} file_tc_guid {} transactions {}", + SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} dcerpc_rec_frag_cache {} file_ts_guid {} file_tc_guid {} transactions {}", self.ssn2vec_map.len(), self.guid2name_cache.len(), self.read_offset_cache.len(), self.ssn2tree_cache.len(), - self.ssnguid2vec_map.len(), + self.dcerpc_rec_frag_cache.len(), self.file_ts_guid.len(), self.file_tc_guid.len(), self.transactions.len()); diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index d195001daa6f..004bff9f2fae 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -88,6 +88,8 @@ pub static mut SMB_CFG_MAX_GUID_CACHE_SIZE: usize = 1024; pub static mut SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE: usize = 128; /// For SMBState::ssn2tree_cache pub static mut SMB_CFG_MAX_TREE_CACHE_SIZE: usize = 512; +/// For SMBState::dcerpc_rec_frag_cache +pub static mut SMB_CFG_MAX_FRAG_CACHE_SIZE: usize = 128; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -712,9 +714,9 @@ pub struct SMBState<> { /// Map session key to SMBTree pub ssn2tree_cache: LruCache, - // store partial data records that are transferred in multiple - // requests for DCERPC. - pub ssnguid2vec_map: HashMap>, + /// store partial data records that are transferred in multiple + /// requests for DCERPC. + pub dcerpc_rec_frag_cache: LruCache>, skip_ts: u32, skip_tc: u32, @@ -787,7 +789,7 @@ impl SMBState { guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()), read_offset_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE }).unwrap()), ssn2tree_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_TREE_CACHE_SIZE }).unwrap()), - ssnguid2vec_map:HashMap::new(), + dcerpc_rec_frag_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_FRAG_CACHE_SIZE }).unwrap()), skip_ts:0, skip_tc:0, file_ts_left:0, @@ -2483,6 +2485,18 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid max-tree-cache-size value"); } } + let retval = conf_get("app-layer.protocols.smb.max-dcerpc-frag-cache-size"); + if let Some(val) = retval { + if let Ok(v) = val.parse::() { + if v > 0 { + SMB_CFG_MAX_FRAG_CACHE_SIZE = v; + } else { + SCLogError!("Invalid max-dcerpc-frag-cache-size value"); + } + } else { + SCLogError!("Invalid max-dcerpc-frag-cache-size value"); + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index c169930d24d0..7c49b3bdc8d4 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -910,7 +910,7 @@ pub fn smb1_trans_response_record(state: &mut SMBState, r: &SmbRecord) if r.nt_status == SMB_NTSTATUS_BUFFER_OVERFLOW { let key = SMBHashKeyHdrGuid::new(SMBCommonHdr::from1(r, SMBHDR_TYPE_TRANS_FRAG), fid); SCLogDebug!("SMBv1/TRANS: queueing data for len {} key {:?}", rd.data.len(), key); - state.ssnguid2vec_map.insert(key, rd.data.to_vec()); + state.dcerpc_rec_frag_cache.put(key, rd.data.to_vec()); } else if is_dcerpc { SCLogDebug!("SMBv1 TRANS TO PIPE"); let hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_HEADER); From e79c82026b587cb24c9d382c44961617fd0699c7 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 24 Oct 2024 15:29:21 +0200 Subject: [PATCH 10/11] smb: use lru for ssn2vec_map Generic ssn2vec_map was a HashMap used for mapping session key to different types of vector data: - GUID - filename - share name Turn this into a bounded LruCache. Rename to ssn2vec_cache. Size of the cache is 512 by default, and can be configured using: `app-layer.protocols.smb.max-session-cache-size` Ticket: #5672. --- rust/src/smb/debug.rs | 4 ++-- rust/src/smb/smb.rs | 19 ++++++++++++++++--- rust/src/smb/smb1.rs | 12 ++++++------ rust/src/smb/smb2.rs | 10 +++++----- 4 files changed, 29 insertions(+), 16 deletions(-) diff --git a/rust/src/smb/debug.rs b/rust/src/smb/debug.rs index f2c3d9a7a393..0c19a2c58d02 100644 --- a/rust/src/smb/debug.rs +++ b/rust/src/smb/debug.rs @@ -69,8 +69,8 @@ impl SMBState { #[cfg(feature = "debug")] pub fn _debug_state_stats(&self) { - SCLogDebug!("ssn2vec_map {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} dcerpc_rec_frag_cache {} file_ts_guid {} file_tc_guid {} transactions {}", - self.ssn2vec_map.len(), + SCLogDebug!("ssn2vec_cache {} guid2name_cache {} read_offset_cache {} ssn2tree_cache {} dcerpc_rec_frag_cache {} file_ts_guid {} file_tc_guid {} transactions {}", + self.ssn2vec_cache.len(), self.guid2name_cache.len(), self.read_offset_cache.len(), self.ssn2tree_cache.len(), diff --git a/rust/src/smb/smb.rs b/rust/src/smb/smb.rs index 004bff9f2fae..901ed2a23927 100644 --- a/rust/src/smb/smb.rs +++ b/rust/src/smb/smb.rs @@ -28,7 +28,6 @@ use std; use std::str; use std::ffi::{self, CString}; -use std::collections::HashMap; use std::collections::VecDeque; use nom7::{Err, Needed}; @@ -90,6 +89,8 @@ pub static mut SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE: usize = 128; pub static mut SMB_CFG_MAX_TREE_CACHE_SIZE: usize = 512; /// For SMBState::dcerpc_rec_frag_cache pub static mut SMB_CFG_MAX_FRAG_CACHE_SIZE: usize = 128; +/// For SMBState::ssn2vec_cache +pub static mut SMB_CFG_MAX_SSN2VEC_CACHE_SIZE: usize = 512; static mut ALPROTO_SMB: AppProto = ALPROTO_UNKNOWN; @@ -697,7 +698,7 @@ pub struct SMBState<> { pub state_data: AppLayerStateData, /// map ssn/tree/msgid to vec (guid/name/share) - pub ssn2vec_map: HashMap>, + pub ssn2vec_cache: LruCache>, /// map guid to filename /// @@ -785,7 +786,7 @@ impl SMBState { pub fn new() -> Self { Self { state_data:AppLayerStateData::new(), - ssn2vec_map:HashMap::new(), + ssn2vec_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_SSN2VEC_CACHE_SIZE }).unwrap()), guid2name_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_GUID_CACHE_SIZE }).unwrap()), read_offset_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_READ_OFFSET_CACHE_SIZE }).unwrap()), ssn2tree_cache:LruCache::new(NonZeroUsize::new(unsafe { SMB_CFG_MAX_TREE_CACHE_SIZE }).unwrap()), @@ -2497,6 +2498,18 @@ pub unsafe extern "C" fn rs_smb_register_parser() { SCLogError!("Invalid max-dcerpc-frag-cache-size value"); } } + let retval = conf_get("app-layer.protocols.smb.max-session-cache-size"); + if let Some(val) = retval { + if let Ok(v) = val.parse::() { + if v > 0 { + SMB_CFG_MAX_SSN2VEC_CACHE_SIZE = v; + } else { + SCLogError!("Invalid max-session-cache-size value"); + } + } else { + SCLogError!("Invalid max-session-cache-size value"); + } + } SCLogConfig!("read: max record size: {}, max queued chunks {}, max queued size {}", SMB_CFG_MAX_READ_SIZE, SMB_CFG_MAX_READ_QUEUE_CNT, SMB_CFG_MAX_READ_QUEUE_SIZE); SCLogConfig!("write: max record size: {}, max queued chunks {}, max queued size {}", diff --git a/rust/src/smb/smb1.rs b/rust/src/smb/smb1.rs index 7c49b3bdc8d4..c74ab5fd0269 100644 --- a/rust/src/smb/smb1.rs +++ b/rust/src/smb/smb1.rs @@ -479,7 +479,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and let name_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME); let name_val = cr.file_name.to_vec(); - state.ssn2vec_map.insert(name_key, name_val); + state.ssn2vec_cache.put(name_key, name_val); let tx_hdr = SMBCommonHdr::from1(r, SMBHDR_TYPE_GENERICTX); let tx = state.new_create_tx(&cr.file_name, @@ -537,7 +537,7 @@ fn smb1_request_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, and fid.extend_from_slice(&u32_as_bytes(r.ssn_id)); let _name = state.guid2name_cache.pop(&fid); - state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); + state.ssn2vec_cache.put(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), fid.to_vec()); SCLogDebug!("closing FID {:?}/{:?}", cd.fid, fid); smb1_close_file(state, &fid, Direction::ToServer); @@ -727,7 +727,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an SCLogDebug!("Create AndX {:?}", cr); let guid_key = SMBCommonHdr::from1(r, SMBHDR_TYPE_FILENAME); - if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { + if let Some(mut p) = state.ssn2vec_cache.pop(&guid_key) { p.retain(|&i|i != 0x00); let mut fid = cr.fid.to_vec(); @@ -767,7 +767,7 @@ fn smb1_response_record_one(state: &mut SMBState, r: &SmbRecord, command: u8, an } }, SMB1_COMMAND_CLOSE => { - let fid = state.ssn2vec_map.remove(&SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)); + let fid = state.ssn2vec_cache.pop(&SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)); if let Some(fid) = fid { SCLogDebug!("closing FID {:?}", fid); smb1_close_file(state, &fid, Direction::ToClient); @@ -857,7 +857,7 @@ pub fn smb1_trans_request_record(state: &mut SMBState, r: &SmbRecord) let mut pipe_dcerpc = false; if rd.pipe.is_some() { let pipe = rd.pipe.unwrap(); - state.ssn2vec_map.insert(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), + state.ssn2vec_cache.put(SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID), pipe.fid.to_vec()); let mut frankenfid = pipe.fid.to_vec(); @@ -893,7 +893,7 @@ pub fn smb1_trans_response_record(state: &mut SMBState, r: &SmbRecord) SCLogDebug!("TRANS response {:?}", rd); // see if we have a stored fid - let fid = state.ssn2vec_map.remove( + let fid = state.ssn2vec_cache.pop( &SMBCommonHdr::from1(r, SMBHDR_TYPE_GUID)).unwrap_or_default(); SCLogDebug!("FID {:?}", fid); diff --git a/rust/src/smb/smb2.rs b/rust/src/smb/smb2.rs index 5eb57dd7b726..97deb702ecef 100644 --- a/rust/src/smb/smb2.rs +++ b/rust/src/smb/smb2.rs @@ -299,7 +299,7 @@ pub fn smb2_write_request_record(state: &mut SMBState, r: &Smb2Record, nbss_rema /* update key-guid map */ let guid_key = SMBCommonHdr::from2(r, SMBHDR_TYPE_GUID); - state.ssn2vec_map.insert(guid_key, wr.guid.to_vec()); + state.ssn2vec_cache.put(guid_key, wr.guid.to_vec()); let file_guid = wr.guid.to_vec(); let file_name = match state.guid2name_cache.get(&file_guid) { @@ -448,7 +448,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) b"".to_vec() } else { guid_key.msg_id -= 1; - match state.ssn2vec_map.get(&guid_key) { + match state.ssn2vec_cache.get(&guid_key) { Some(n) => { n.to_vec() }, None => { b"".to_vec()}, } @@ -558,7 +558,7 @@ pub fn smb2_request_record(state: &mut SMBState, r: &Smb2Record) SCLogDebug!("create_options {:08x}", cr.create_options); let name_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); - state.ssn2vec_map.insert(name_key, cr.data.to_vec()); + state.ssn2vec_cache.put(name_key, cr.data.to_vec()); let tx_hdr = SMBCommonHdr::from2(r, SMBHDR_TYPE_GENERICTX); let tx = state.new_create_tx(cr.data, cr.disposition, del, dir, tx_hdr); @@ -651,7 +651,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) /* search key-guid map */ let guid_key = SMBCommonHdr::new(SMBHDR_TYPE_GUID, r.session_id, r.tree_id, r.message_id); - let _guid_vec = state.ssn2vec_map.remove(&guid_key).unwrap_or_default(); + let _guid_vec = state.ssn2vec_cache.pop(&guid_key).unwrap_or_default(); SCLogDebug!("SMBv2 write response for GUID {:?}", _guid_vec); } else { events.push(SMBEvent::MalformedData); @@ -693,7 +693,7 @@ pub fn smb2_response_record(state: &mut SMBState, r: &Smb2Record) SCLogDebug!("SMBv2: Create response => {:?}", cr); let guid_key = SMBCommonHdr::from2_notree(r, SMBHDR_TYPE_FILENAME); - if let Some(mut p) = state.ssn2vec_map.remove(&guid_key) { + if let Some(mut p) = state.ssn2vec_cache.pop(&guid_key) { p.retain(|&i|i != 0x00); _ = state.guid2name_cache.put(cr.guid.to_vec(), p); } else { From 268486033ecc7059c957b95060b8975c26bf3b23 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Fri, 25 Oct 2024 15:47:50 +0200 Subject: [PATCH 11/11] doc/userguide: document smb cache size limit options Ticket: #5672. --- doc/userguide/configuration/suricata-yaml.rst | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/doc/userguide/configuration/suricata-yaml.rst b/doc/userguide/configuration/suricata-yaml.rst index 3dfc950ec641..79243307db4d 100644 --- a/doc/userguide/configuration/suricata-yaml.rst +++ b/doc/userguide/configuration/suricata-yaml.rst @@ -1686,6 +1686,37 @@ the limits are exceeded, and an event will be raised. `max-write-queue-size` and `max-write-queue-cnt` are as the READ variants, but then for WRITEs. +Cache limits +^^^^^^^^^^^^ + +The SMB parser uses several per flow caches to track data between different records +and transactions. + +:: + + smb: + max-guid-cache-size: 1024 + max-rec-offset-cache-size: 128 + max-tree-cache-size: 512 + max-dcerpc-frag-cache-size: 128 + max-session-cache-size: 512 + +The `max-guid-cache-size` setting controls the size of the hash that maps the GUID to +filenames. These are added through CREATE commands and removed by CLOSE commands. + +`max-rec-offset-cache-size` controls the size of the hash that maps the READ offset +from READ commands to the READ responses. + +The `max-tree-cache-size` option contols the size of the SMB session to SMB tree hash. + +`max-dcerpc-frag-cache-size` controls the size of the hash that tracks partial DCERPC +over SMB records. These are buffered in this hash to only parse the DCERPC record when +it is fully reassembled. + +The `max-session-cache-size` setting controls the size of a generic hash table that maps +SMB session to filenames, GUIDs and share names. + + Configure HTTP2 ~~~~~~~~~~~~~~~