From: Alice Akaki Date: Mon, 20 Jan 2025 18:16:26 +0000 (-0400) Subject: detect: add ldap.responses.count X-Git-Tag: suricata-8.0.0-beta1~526 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=8416289752e5f199fa90fa08c4836a40ba7d90fc;p=thirdparty%2Fsuricata.git detect: add ldap.responses.count ldap.responses.count matches on the number of LDAP responses This keyword maps to the eve field len(ldap.responses[]) It is an unsigned 32-bit integer Doesn't support prefiltering Ticket: #7453 --- diff --git a/doc/userguide/rules/ldap-keywords.rst b/doc/userguide/rules/ldap-keywords.rst index 6d76b1cdf6..d3ea306c08 100644 --- a/doc/userguide/rules/ldap-keywords.rst +++ b/doc/userguide/rules/ldap-keywords.rst @@ -129,3 +129,37 @@ This is an example of a signature that would alert if a search_result_entry resp .. container:: example-rule alert tcp any any -> any any (msg:"Test LDAP search response"; :example-rule-emphasis:`ldap.responses.operation:search_result_entry,-1;` sid:1;) + +ldap.responses.count +-------------------- + +Matches based on the number of responses. + +Syntax:: + + ldap.responses.count: [op]number; + +It can be matched exactly, or compared using the ``op`` setting:: + + ldap.responses.count:3 # exactly 3 responses + ldap.responses.count:<3 # less than 3 responses + ldap.responses.count:>=2 # more or equal to 2 responses + +ldap.responses.count uses :ref:`unsigned 32-bit integer `. + +This keyword maps to the eve field ``len(ldap.responses[])`` + +Examples +^^^^^^^^ + +Example of a signature that would alert if a packet has 0 LDAP responses: + +.. container:: example-rule + + alert ip any any -> any any (msg:"Packet has 0 LDAP responses"; :example-rule-emphasis:`ldap.responses.count:0;` sid:1;) + +Example of a signature that would alert if a packet has more than 2 LDAP responses: + +.. container:: example-rule + + alert ip any any -> any any (msg:"Packet has more than 2 LDAP responses"; :example-rule-emphasis:`ldap.responses.count:>2;` sid:1;) diff --git a/rust/src/ldap/detect.rs b/rust/src/ldap/detect.rs index e77fc62d7e..3745630bfe 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -17,7 +17,8 @@ use super::ldap::{LdapTransaction, ALPROTO_LDAP}; use crate::detect::uint::{ - detect_parse_uint_enum, rs_detect_u8_free, rs_detect_u8_match, DetectUintData, + detect_parse_uint_enum, rs_detect_u32_free, rs_detect_u32_match, rs_detect_u32_parse, + rs_detect_u8_free, rs_detect_u8_match, DetectUintData, }; use crate::detect::{ DetectHelperBufferRegister, DetectHelperKeywordRegister, DetectSignatureSetAppProto, @@ -50,6 +51,8 @@ static mut G_LDAP_REQUEST_OPERATION_KW_ID: c_int = 0; static mut G_LDAP_REQUEST_OPERATION_BUFFER_ID: c_int = 0; static mut G_LDAP_RESPONSES_OPERATION_KW_ID: c_int = 0; static mut G_LDAP_RESPONSES_OPERATION_BUFFER_ID: c_int = 0; +static mut G_LDAP_RESPONSES_COUNT_KW_ID: c_int = 0; +static mut G_LDAP_RESPONSES_COUNT_BUFFER_ID: c_int = 0; unsafe extern "C" fn ldap_parse_protocol_req_op( ustr: *const std::os::raw::c_char, @@ -219,6 +222,47 @@ unsafe extern "C" fn ldap_detect_responses_free(_de: *mut c_void, ctx: *mut c_vo std::mem::drop(Box::from_raw(ctx)); } +unsafe extern "C" fn ldap_detect_responses_count_setup( + de: *mut c_void, s: *mut c_void, raw: *const libc::c_char, +) -> c_int { + if DetectSignatureSetAppProto(s, ALPROTO_LDAP) != 0 { + return -1; + } + let ctx = rs_detect_u32_parse(raw) as *mut c_void; + if ctx.is_null() { + return -1; + } + if SigMatchAppendSMToList( + de, + s, + G_LDAP_RESPONSES_COUNT_KW_ID, + ctx, + G_LDAP_RESPONSES_COUNT_BUFFER_ID, + ) + .is_null() + { + ldap_detect_responses_count_free(std::ptr::null_mut(), ctx); + return -1; + } + return 0; +} + +unsafe extern "C" fn ldap_detect_responses_count_match( + _de: *mut c_void, _f: *mut c_void, _flags: u8, _state: *mut c_void, tx: *mut c_void, + _sig: *const c_void, ctx: *const c_void, +) -> c_int { + let tx = cast_pointer!(tx, LdapTransaction); + let ctx = cast_pointer!(ctx, DetectUintData); + let len = tx.responses.len() as u32; + return rs_detect_u32_match(len, ctx); +} + +unsafe extern "C" fn ldap_detect_responses_count_free(_de: *mut c_void, ctx: *mut c_void) { + // Just unbox... + let ctx = cast_pointer!(ctx, DetectUintData); + rs_detect_u32_free(ctx); +} + #[no_mangle] pub unsafe extern "C" fn ScDetectLdapRegister() { let kw = SCSigTableElmt { @@ -254,4 +298,20 @@ pub unsafe extern "C" fn ScDetectLdapRegister() { true, //to client false, //to server ); + let kw = SCSigTableElmt { + name: b"ldap.responses.count\0".as_ptr() as *const libc::c_char, + desc: b"match number of LDAP responses\0".as_ptr() as *const libc::c_char, + url: b"/rules/ldap-keywords.html#ldap.responses.count\0".as_ptr() as *const libc::c_char, + AppLayerTxMatch: Some(ldap_detect_responses_count_match), + Setup: ldap_detect_responses_count_setup, + Free: Some(ldap_detect_responses_count_free), + flags: 0, + }; + G_LDAP_RESPONSES_COUNT_KW_ID = DetectHelperKeywordRegister(&kw); + G_LDAP_RESPONSES_COUNT_BUFFER_ID = DetectHelperBufferRegister( + b"ldap.responses.count\0".as_ptr() as *const libc::c_char, + ALPROTO_LDAP, + true, //to client + false, //to server + ); }