]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect: add ldap.responses.message
authorAlice Akaki <akakialice@gmail.com>
Tue, 11 Feb 2025 08:20:33 +0000 (04:20 -0400)
committerVictor Julien <victor@inliniac.net>
Wed, 5 Mar 2025 14:59:53 +0000 (15:59 +0100)
ldap.responses.message matches on LDAPResult error message
This keyword maps the following eve fields:
ldap.responses[].bind_response.message
ldap.responses[].search_result_done.message
ldap.responses[].modify_response.message
ldap.responses[].add_response.message
ldap.responses[].del_response.message
ldap.responses[].mod_dn_response.message
ldap.responses[].compare_response.message
ldap.responses[].extended_response.message
It is a sticky buffer
Supports prefiltering

Ticket: #7532

doc/userguide/rules/ldap-keywords.rst
rust/src/ldap/detect.rs

index 2f545f7139f2f1c07b5585cfbfa36e2eeb2b4469..dd196a13e768ae0f97757757d67611d51ca44f0c 100644 (file)
@@ -415,3 +415,44 @@ This is an example of a signature that would alert if a ``success`` result code
 .. container:: example-rule
 
   alert ldap any any -> any any (msg:"Test LDAP success at last index"; :example-rule-emphasis:`ldap.responses.result_code:success,-1;` sid:1;)
+
+ldap.responses.message
+----------------------
+
+Matches on LDAP error messages from response operations.
+
+Comparison is case-sensitive.
+
+Syntax::
+
+ ldap.responses.message; content:"<content to match against>";
+
+``ldap.responses.message`` is a 'sticky buffer' and can be used as a ``fast_pattern``.
+
+``ldap.responses.message`` supports multiple buffer matching, see :doc:`multi-buffer-matching`.
+
+This keyword maps to the EVE fields:
+
+   - ``ldap.responses[].bind_response.message``
+   - ``ldap.responses[].search_result_done.message``
+   - ``ldap.responses[].modify_response.message``
+   - ``ldap.responses[].add_response.message``
+   - ``ldap.responses[].del_response.message``
+   - ``ldap.responses[].mod_dn_response.message``
+   - ``ldap.responses[].compare_response.message``
+   - ``ldap.responses[].extended_response.message``
+
+.. note::
+
+    If a response within the array does not contain the
+    error message field, this field will be interpreted
+    as an empty buffer.
+
+Example
+^^^^^^^
+
+Example of a signature that would alert if a packet has the LDAP error message ``Size limit exceeded``:
+
+.. container:: example-rule
+
+  alert ldap any any -> any any (msg:"Test LDAP error message"; ldap.responses.message; content:"Size limit exceeded"; sid:1;)
\ No newline at end of file
index b863643149cbe73a51f14f094745d172df1c79d9..bf49edd37b1ddf7b1b1eb00e33ef89bebf9e0fe0 100644 (file)
@@ -69,6 +69,7 @@ static mut G_LDAP_REQUEST_DN_BUFFER_ID: c_int = 0;
 static mut G_LDAP_RESPONSES_DN_BUFFER_ID: c_int = 0;
 static mut G_LDAP_RESPONSES_RESULT_CODE_KW_ID: c_int = 0;
 static mut G_LDAP_RESPONSES_RESULT_CODE_BUFFER_ID: c_int = 0;
+static mut G_LDAP_RESPONSES_MSG_BUFFER_ID: c_int = 0;
 
 unsafe extern "C" fn ldap_parse_protocol_req_op(
     ustr: *const std::os::raw::c_char,
@@ -499,6 +500,66 @@ unsafe extern "C" fn ldap_detect_responses_result_code_free(_de: *mut c_void, ct
     std::mem::drop(Box::from_raw(ctx));
 }
 
+unsafe extern "C" fn ldap_detect_responses_msg_setup(
+    de: *mut c_void, s: *mut c_void, _raw: *const std::os::raw::c_char,
+) -> c_int {
+    if DetectSignatureSetAppProto(s, ALPROTO_LDAP) != 0 {
+        return -1;
+    }
+    if DetectBufferSetActiveList(de, s, G_LDAP_RESPONSES_MSG_BUFFER_ID) < 0 {
+        return -1;
+    }
+    return 0;
+}
+
+unsafe extern "C" fn ldap_detect_responses_msg_get_data(
+    de: *mut c_void, transforms: *const c_void, flow: *const c_void, flow_flags: u8,
+    tx: *const c_void, list_id: c_int, local_id: u32,
+) -> *mut c_void {
+    return DetectHelperGetMultiData(
+        de,
+        transforms,
+        flow,
+        flow_flags,
+        tx,
+        list_id,
+        local_id,
+        ldap_tx_get_responses_msg,
+    );
+}
+
+unsafe extern "C" fn ldap_tx_get_responses_msg(
+    tx: *const c_void, _flags: u8, local_id: u32, buffer: *mut *const u8, buffer_len: *mut u32,
+) -> bool {
+    let tx = cast_pointer!(tx, LdapTransaction);
+
+    if local_id as usize >= tx.responses.len() {
+        return false;
+    }
+    *buffer = std::ptr::null();
+    *buffer_len = 0;
+
+    let response = &tx.responses[local_id as usize];
+    // We expect every response in one tx to be the same protocol_op
+    let str_buffer: &str = match &response.protocol_op {
+        ProtocolOp::BindResponse(resp) => resp.result.diagnostic_message.0.as_str(),
+        ProtocolOp::SearchResultDone(resp) => resp.diagnostic_message.0.as_str(),
+        ProtocolOp::ModifyResponse(resp) => resp.result.diagnostic_message.0.as_str(),
+        ProtocolOp::AddResponse(resp) => resp.diagnostic_message.0.as_str(),
+        ProtocolOp::DelResponse(resp) => resp.diagnostic_message.0.as_str(),
+        ProtocolOp::ModDnResponse(resp) => resp.diagnostic_message.0.as_str(),
+        ProtocolOp::CompareResponse(resp) => resp.diagnostic_message.0.as_str(),
+        ProtocolOp::ExtendedResponse(resp) => resp.result.diagnostic_message.0.as_str(),
+        _ => "",
+        // This ensures that the iteration continues,
+        // allowing other responses in the transaction to be processed correctly
+    };
+
+    *buffer = str_buffer.as_ptr();
+    *buffer_len = str_buffer.len() as u32;
+    return true;
+}
+
 #[no_mangle]
 pub unsafe extern "C" fn SCDetectLdapRegister() {
     let kw = SCSigTableElmt {
@@ -603,4 +664,22 @@ pub unsafe extern "C" fn SCDetectLdapRegister() {
         true,  //to client
         false, //to server
     );
+    let kw = SCSigTableElmt {
+        name: b"ldap.responses.message\0".as_ptr() as *const libc::c_char,
+        desc: b"match LDAPResult message for responses\0".as_ptr() as *const libc::c_char,
+        url: b"/rules/ldap-keywords.html#ldap.responses.message\0".as_ptr() as *const libc::c_char,
+        Setup: ldap_detect_responses_msg_setup,
+        flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER,
+        AppLayerTxMatch: None,
+        Free: None,
+    };
+    let _g_ldap_responses_dn_kw_id = DetectHelperKeywordRegister(&kw);
+    G_LDAP_RESPONSES_MSG_BUFFER_ID = DetectHelperMultiBufferMpmRegister(
+        b"ldap.responses.message\0".as_ptr() as *const libc::c_char,
+        b"LDAP RESPONSES DISTINGUISHED_NAME\0".as_ptr() as *const libc::c_char,
+        ALPROTO_LDAP,
+        true,  //to client
+        false, //to server
+        ldap_detect_responses_msg_get_data,
+    );
 }