From 16dcee46fc8a9f15f07535ff60658492c5c04baa Mon Sep 17 00:00:00 2001 From: Alice Akaki Date: Mon, 3 Feb 2025 22:37:15 -0400 Subject: [PATCH] detect: add ldap.request.dn ldap.request.dn matches on LDAPDN from request operations This keyword maps the following eve fields: ldap.request.bind_request.name ldap.request.add_request.entry ldap.request.search_request.base_object ldap.request.modify_request.object ldap.request.del_request.dn ldap.request.mod_dn_request.entry ldap.request.compare_request.entry It is a sticky buffer Supports prefiltering Ticket: #7471 --- doc/userguide/rules/ldap-keywords.rst | 42 ++++++++++++++ rust/src/ldap/detect.rs | 79 ++++++++++++++++++++++++++- 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/doc/userguide/rules/ldap-keywords.rst b/doc/userguide/rules/ldap-keywords.rst index b839f4f7a0..47e240a191 100644 --- a/doc/userguide/rules/ldap-keywords.rst +++ b/doc/userguide/rules/ldap-keywords.rst @@ -163,3 +163,45 @@ Example of a signature that would alert if a packet has more than 2 LDAP respons .. container:: example-rule alert ldap any any -> any any (msg:"Packet has more than 2 LDAP responses"; :example-rule-emphasis:`ldap.responses.count:>2;` sid:1;) + +ldap.request.dn +--------------- + +Matches on LDAP distinguished names from request operations. + +Comparison is case-sensitive. + +Syntax:: + + ldap.request.dn; content:dc=example,dc=com; + +``ldap.request.dn`` is a 'sticky buffer' and can be used as a ``fast_pattern``. + +This keyword maps to the EVE fields: +``ldap.request.bind_request.name`` +``ldap.request.add_request.entry`` +``ldap.request.search_request.base_object`` +``ldap.request.modify_request.object`` +``ldap.request.del_request.dn`` +``ldap.request.mod_dn_request.entry`` +``ldap.request.compare_request.entry`` + +Example +^^^^^^^ + +Example of a signature that would alert if a packet has the LDAP distinguished name ``uid=jdoe,ou=People,dc=example,dc=com``: + +.. container:: example-rule + + alert ldap any any -> any any (msg:"Test LDAPDN"; :example-rule-emphasis:`ldap.request.dn; content:"uid=jdoe,ou=People,dc=example,dc=com";` sid:1;) + +It is possible to use the keyword ``ldap.request.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 request operation 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.request.operation:search_request; ldap.request.dn; content:"dc=example,dc=com";` sid:1;) diff --git a/rust/src/ldap/detect.rs b/rust/src/ldap/detect.rs index fbd0c56f5a..6088575112 100644 --- a/rust/src/ldap/detect.rs +++ b/rust/src/ldap/detect.rs @@ -21,10 +21,11 @@ use crate::detect::uint::{ rs_detect_u8_free, rs_detect_u8_match, DetectUintData, }; use crate::detect::{ - DetectHelperBufferRegister, DetectHelperKeywordRegister, DetectSignatureSetAppProto, - SCSigTableElmt, SigMatchAppendSMToList, + DetectBufferSetActiveList, DetectHelperBufferMpmRegister, DetectHelperBufferRegister, + DetectHelperGetData, DetectHelperKeywordRegister, DetectSignatureSetAppProto, SCSigTableElmt, + SigMatchAppendSMToList, SIGMATCH_INFO_STICKY_BUFFER, SIGMATCH_NOOPT, }; -use crate::ldap::types::{LdapMessage, ProtocolOpCode}; +use crate::ldap::types::{LdapMessage, ProtocolOp, ProtocolOpCode}; use std::ffi::CStr; use std::os::raw::{c_int, c_void}; @@ -53,6 +54,7 @@ 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; +static mut G_LDAP_REQUEST_DN_BUFFER_ID: c_int = 0; unsafe extern "C" fn ldap_parse_protocol_req_op( ustr: *const std::os::raw::c_char, @@ -263,6 +265,59 @@ unsafe extern "C" fn ldap_detect_responses_count_free(_de: *mut c_void, ctx: *mu rs_detect_u32_free(ctx); } +unsafe extern "C" fn ldap_detect_request_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_REQUEST_DN_BUFFER_ID) < 0 { + return -1; + } + return 0; +} + +unsafe extern "C" fn ldap_detect_request_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, +) -> *mut c_void { + return DetectHelperGetData( + de, + transforms, + flow, + flow_flags, + tx, + list_id, + ldap_tx_get_request_dn, + ); +} + +unsafe extern "C" fn ldap_tx_get_request_dn( + tx: *const c_void, _flags: u8, buffer: *mut *const u8, buffer_len: *mut u32, +) -> bool { + let tx = cast_pointer!(tx, LdapTransaction); + + *buffer = std::ptr::null(); + *buffer_len = 0; + + if let Some(request) = &tx.request { + let str_buffer: &str = match &request.protocol_op { + ProtocolOp::BindRequest(req) => req.name.0.as_str(), + ProtocolOp::AddRequest(req) => req.entry.0.as_str(), + ProtocolOp::SearchRequest(req) => req.base_object.0.as_str(), + ProtocolOp::ModifyRequest(req) => req.object.0.as_str(), + ProtocolOp::DelRequest(req) => req.0.as_str(), + ProtocolOp::ModDnRequest(req) => req.entry.0.as_str(), + ProtocolOp::CompareRequest(req) => req.entry.0.as_str(), + _ => return false, + }; + *buffer = str_buffer.as_ptr(); + *buffer_len = str_buffer.len() as u32; + return true; + } + return false; +} + #[no_mangle] pub unsafe extern "C" fn SCDetectLdapRegister() { let kw = SCSigTableElmt { @@ -314,4 +369,22 @@ pub unsafe extern "C" fn SCDetectLdapRegister() { true, //to client false, //to server ); + let kw = SCSigTableElmt { + name: b"ldap.request.dn\0".as_ptr() as *const libc::c_char, + desc: b"match request LDAPDN\0".as_ptr() as *const libc::c_char, + url: b"/rules/ldap-keywords.html#ldap.request.dn\0".as_ptr() as *const libc::c_char, + Setup: ldap_detect_request_dn_setup, + flags: SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER, + AppLayerTxMatch: None, + Free: None, + }; + let _g_ldap_request_dn_kw_id = DetectHelperKeywordRegister(&kw); + G_LDAP_REQUEST_DN_BUFFER_ID = DetectHelperBufferMpmRegister( + b"ldap.request.dn\0".as_ptr() as *const libc::c_char, + b"LDAP REQUEST DISTINGUISHED_NAME\0".as_ptr() as *const libc::c_char, + ALPROTO_LDAP, + false, //to client + true, //to server + ldap_detect_request_dn_get_data, + ); } -- 2.47.2