From: Alice Akaki Date: Thu, 6 Feb 2025 04:16:20 +0000 (-0400) Subject: detect: add ldap.responses.dn X-Git-Tag: suricata-8.0.0-beta1~386 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=73ae6e997f6c325b9ef2df8f715f921be441b5b0;p=thirdparty%2Fsuricata.git detect: add ldap.responses.dn ldap.responses.dn matches on LDAPDN from responses operations This keyword maps the following eve fields: ldap.responses[].search_result_entry.base_object ldap.responses[].bind_response.matched_dn ldap.responses[].search_result_done.matched_dn ldap.responses[].modify_response.matched_dn ldap.responses[].add_response.matched_dn ldap.responses[].del_response.matched_dn ldap.responses[].mod_dn_response.matched_dn ldap.responses[].compare_response.matched_dn ldap.responses[].extended_response.matched_dn It is a sticky buffer Supports prefiltering Ticket: #7471 --- diff --git a/doc/userguide/rules/ldap-keywords.rst b/doc/userguide/rules/ldap-keywords.rst index 47e240a191..b37d099bf2 100644 --- a/doc/userguide/rules/ldap-keywords.rst +++ b/doc/userguide/rules/ldap-keywords.rst @@ -205,3 +205,49 @@ search request operation and contains the LDAP distinguished name .. container:: example-rule alert ldap any any -> any any (msg:"Test LDAPDN and operation"; :example-rule-emphasis:`ldap.request.operation:search_request; ldap.request.dn; content:"dc=example,dc=com";` sid:1;) + +ldap.responses.dn +----------------- + +Matches on LDAP distinguished names from response operations. + +Comparison is case-sensitive. + +Syntax:: + + ldap.responses.dn; content:dc=example,dc=com; + +``ldap.responses.dn`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +``ldap.responses.dn`` supports multiple buffer matching, see :doc:`multi-buffer-matching`. + +This keyword maps to the EVE fields: +``ldap.responses[].search_result_entry.base_object`` +``ldap.responses[].bind_response.matched_dn`` +``ldap.responses[].search_result_done.matched_dn`` +``ldap.responses[].modify_response.matched_dn`` +``ldap.responses[].add_response.matched_dn`` +``ldap.responses[].del_response.matched_dn`` +``ldap.responses[].mod_dn_response.matched_dn`` +``ldap.responses[].compare_response.matched_dn`` +``ldap.responses[].extended_response.matched_dn`` + +Example +^^^^^^^ + +Example of a signature that would alert if a packet has the LDAP distinguished name ``dc=example,dc=com``: + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test LDAPDN"; :example-rule-emphasis:`ldap.responses.dn; content:"dc=example,dc=com";` sid:1;) + +It is possible to use the keyword ``ldap.responses.operation`` in the same rule to +specify the operation to match. + +Here is an example of a signature that would alert if a packet has an LDAP +search result entry operation at index 1 on the responses array, +and contains the LDAP distinguished name ``dc=example,dc=com``. + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test LDAPDN and operation"; :example-rule-emphasis:`ldap.responses.operation:search_result_entry,1; ldap.responses.dn; content:"dc=example,dc=com";` sid:1;) diff --git a/rust/src/ldap/detect.rs b/rust/src/ldap/detect.rs index 6088575112..c982be5148 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -22,7 +22,8 @@ use crate::detect::uint::{ }; use crate::detect::{ DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperBufferRegister, - DetectHelperGetData, DetectHelperKeywordRegister, DetectSignatureSetAppProto, SCSigTableElmt, + DetectHelperGetData, DetectHelperGetMultiData, DetectHelperKeywordRegister, + DetectHelperMultiBufferMpmRegister, DetectSignatureSetAppProto, SCSigTableElmt, SigMatchAppendSMToList, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT, }; use crate::ldap::types::{LdapMessage, ProtocolOp, ProtocolOpCode}; @@ -55,6 +56,7 @@ 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; static mut G_LDAP_REQUEST_DN_BUFFER_ID: c_int = 0; +static mut G_LDAP_RESPONSES_DN_BUFFER_ID: c_int = 0; unsafe extern "C" fn ldap_parse_protocol_req_op( ustr: *const std::os::raw::c_char, @@ -318,6 +320,65 @@ unsafe extern "C" fn ldap_tx_get_request_dn( return false; } +unsafe extern "C" fn ldap_detect_responses_dn_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_DN_BUFFER_ID) < 0 { + return -1; + } + return 0; +} + +unsafe extern "C" fn ldap_detect_responses_dn_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_dn, + ); +} + +unsafe extern "C" fn ldap_tx_get_responses_dn( + 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::SearchResultEntry(req) => req.object_name.0.as_str(), + ProtocolOp::BindResponse(req) => req.result.matched_dn.0.as_str(), + ProtocolOp::SearchResultDone(req) => req.matched_dn.0.as_str(), + ProtocolOp::ModifyResponse(req) => req.result.matched_dn.0.as_str(), + ProtocolOp::AddResponse(req) => req.matched_dn.0.as_str(), + ProtocolOp::DelResponse(req) => req.matched_dn.0.as_str(), + ProtocolOp::ModDnResponse(req) => req.matched_dn.0.as_str(), + ProtocolOp::CompareResponse(req) => req.matched_dn.0.as_str(), + ProtocolOp::ExtendedResponse(req) => req.result.matched_dn.0.as_str(), + _ => return false, + }; + + *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 { @@ -387,4 +448,22 @@ pub unsafe extern "C" fn SCDetectLdapRegister() { true, //to server ldap_detect_request_dn_get_data, ); + let kw = SCSigTableElmt { + name: b"ldap.responses.dn\0".as_ptr() as *const libc::c_char, + desc: b"match responses LDAPDN\0".as_ptr() as *const libc::c_char, + url: b"/rules/ldap-keywords.html#ldap.responses.dn\0".as_ptr() as *const libc::c_char, + Setup: ldap_detect_responses_dn_setup, + flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, + AppLayerTxMatch: None, + Free: None, + }; + let _g_ldap_responses_dn_kw_id = DetectHelperKeywordRegister(&kw); + G_LDAP_RESPONSES_DN_BUFFER_ID = DetectHelperMultiBufferMpmRegister( + b"ldap.responses.dn\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_dn_get_data, + ); }