From e98d419d96b6df4ba4cd51325923e76a80f93f9e Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Thu, 26 Mar 2026 14:47:26 +0100 Subject: [PATCH] ldap: bound the number of responses Ticket: 8405 --- doc/userguide/upgrade.rst | 2 ++ rules/ldap-events.rules | 2 +- rust/src/ldap/ldap.rs | 34 +++++++++++++++++++++++++++++++--- suricata.yaml.in | 2 ++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index 564f8198b2..5b9c00acf1 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -92,6 +92,8 @@ Other Changes - `alert pkthdr` is now only available for decoder event rules. Previously it acted like `alert ip`. +- ``ldap`` has bound the maximum number of responses per transaction + to 1024 by default. Changes for Library Users and Plugin Developers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/rules/ldap-events.rules b/rules/ldap-events.rules index b172cd1026..3fda495661 100644 --- a/rules/ldap-events.rules +++ b/rules/ldap-events.rules @@ -5,4 +5,4 @@ alert ldap any any -> any any (msg:"SURICATA LDAP too many transactions"; app-layer-event:ldap.too_many_transactions; classtype:protocol-command-decode; sid:2237000; rev:1;) alert ldap any any -> any any (msg:"SURICATA LDAP invalid data"; app-layer-event:ldap.invalid_data; classtype:protocol-command-decode; sid:2237001; rev:1;) alert ldap any any -> any any (msg:"SURICATA LDAP request not found"; app-layer-event:ldap.request_not_found; classtype:protocol-command-decode; sid:2237002; rev:1;) - +alert ldap any any -> any any (msg:"SURICATA LDAP too many responses"; app-layer-event:ldap.too_many_responses; classtype:protocol-command-decode; sid:2237003; rev:1;) diff --git a/rust/src/ldap/ldap.rs b/rust/src/ldap/ldap.rs index 950fb2708f..48b57c221f 100644 --- a/rust/src/ldap/ldap.rs +++ b/rust/src/ldap/ldap.rs @@ -40,9 +40,10 @@ use super::types::*; use ldap_parser::ldap::*; static LDAP_MAX_TX_DEFAULT: usize = 256; - static mut LDAP_MAX_TX: usize = LDAP_MAX_TX_DEFAULT; +static mut LDAP_MAX_RESPONSES: usize = 1024; + pub(super) static mut ALPROTO_LDAP: AppProto = ALPROTO_UNKNOWN; const STARTTLS_OID: &str = "1.3.6.1.4.1.1466.20037"; @@ -58,6 +59,7 @@ enum LdapEvent { InvalidData, RequestNotFound, IncompleteData, + TooManyResponses, } #[derive(Debug)] @@ -86,6 +88,9 @@ impl LdapTransaction { tx_data: AppLayerTxData::new(), } } + fn set_event(&mut self, e: LdapEvent) { + self.tx_data.set_event(e as u8); + } } impl Transaction for LdapTransaction { @@ -313,7 +318,11 @@ impl LdapState { tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); let tx_id = tx.id(); tx.tx_data.0.updated_tc = true; - tx.responses.push(response.to_static()); + if tx.responses.len() < unsafe { LDAP_MAX_RESPONSES } { + tx.responses.push(response.to_static()); + } else { + tx.set_event(LdapEvent::TooManyResponses); + } sc_app_layer_parser_trigger_raw_stream_inspection( flow, Direction::ToClient as i32, @@ -375,6 +384,7 @@ impl LdapState { fn parse_request_udp(&mut self, flow: *mut Flow, stream_slice: StreamSlice) -> AppLayerResult { let input = stream_slice.as_slice(); + let _pdu = Frame::new( flow, &stream_slice, @@ -433,7 +443,11 @@ impl LdapState { if let Some(tx) = self.find_request(response.message_id) { tx.complete |= tx_is_complete(&response.protocol_op, Direction::ToClient); let tx_id = tx.id(); - tx.responses.push(response.to_static()); + if tx.responses.len() < unsafe { LDAP_MAX_RESPONSES } { + tx.responses.push(response.to_static()); + } else { + tx.set_event(LdapEvent::TooManyResponses); + } let consumed = start.len() - rem.len(); self.set_frame_tc(flow, tx_id, consumed as i64); } else if let ProtocolOp::ExtendedResponse(_) = response.protocol_op { @@ -715,6 +729,13 @@ pub unsafe extern "C" fn SCRegisterLdapTcpParser() { SCLogError!("Invalid value for ldap.max-tx"); } } + if let Some(val) = conf_get("app-layer.protocols.ldap.max-responses") { + if let Ok(v) = val.parse::() { + LDAP_MAX_RESPONSES = v; + } else { + SCLogWarning!("Invalid value for ldap.max-responses"); + } + } SCAppLayerParserRegisterLogger(IPPROTO_TCP, ALPROTO_LDAP); } else { SCLogDebug!("Protocol detection and parser disabled for LDAP/TCP."); @@ -774,6 +795,13 @@ pub unsafe extern "C" fn SCRegisterLdapUdpParser() { SCLogError!("Invalid value for ldap.max-tx"); } } + if let Some(val) = conf_get("app-layer.protocols.ldap.max-responses") { + if let Ok(v) = val.parse::() { + LDAP_MAX_RESPONSES = v; + } else { + SCLogWarning!("Invalid value for ldap.max-responses"); + } + } SCAppLayerParserRegisterLogger(IPPROTO_UDP, ALPROTO_LDAP); } else { SCLogDebug!("Protocol detection and parser disabled for LDAP/UDP."); diff --git a/suricata.yaml.in b/suricata.yaml.in index f43483a534..1527cc573c 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -1288,6 +1288,8 @@ app-layer: dp: 389, 3268 # Maximum number of live LDAP transactions per flow # max-tx: 1024 + # Maximum number of responses per LDAP transaction + # max-responses: 1024 mdns: enabled: yes