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.
pull/12094/head
Victor Julien 1 year ago committed by Victor Julien
parent 23f2317c6a
commit 91828ec00b

@ -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.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3"
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",

@ -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 }

@ -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;

@ -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());
}
}

@ -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<SMBCommonHdr, Vec<u8>>,
/// map guid to filename
pub guid2name_map: HashMap<Vec<u8>, Vec<u8>>,
///
/// 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<u8>, Vec<u8>>,
/// map ssn key to read offset
pub ssn2vecoffset_map: HashMap<SMBCommonHdr, SMBFileGUIDOffset>,
@ -754,13 +768,19 @@ impl State<SMBTransaction> 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::<usize>() {
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.");
}

@ -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"<unknown>".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"<unknown>".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"<unknown>".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(),
};

@ -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"<unknown>".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"<unknown>".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");
}

Loading…
Cancel
Save