-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ldap.request.operation matches on Lightweight Directory Access Protocol request operations ldap.responses.operation matches on Lightweight Directory Access Protocol response operations Both are unsigned 8-bit integer Don't support prefiltering Ticket: #7453
- Loading branch information
1 parent
def22fa
commit 061a50a
Showing
7 changed files
with
410 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,3 +49,4 @@ Suricata Rules | |
differences-from-snort | ||
multi-buffer-matching | ||
tag | ||
ldap-keywords |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
LDAP Keywords | ||
============= | ||
|
||
.. role:: example-rule-action | ||
.. role:: example-rule-header | ||
.. role:: example-rule-options | ||
.. role:: example-rule-emphasis | ||
|
||
LDAP Request and Response operations | ||
------------------------------------ | ||
|
||
.. table:: **Operation values for ldap.request.operation and ldap.response.operation keywords** | ||
|
||
==== ================================================ | ||
Code Operation | ||
==== ================================================ | ||
0 bind_request | ||
1 bind_response | ||
2 unbind_request | ||
3 search_request | ||
4 search_result_entry | ||
5 search_result_done | ||
19 search_result_reference | ||
6 modify_request | ||
7 modify_response | ||
8 add_request | ||
9 add_response | ||
10 del_request | ||
11 del_response | ||
12 mod_dn_request | ||
13 mod_dn_response | ||
14 compare_request | ||
15 compare_response | ||
16 abandon_request | ||
23 extended_request | ||
24 extended_response | ||
25 intermediate_response | ||
==== ================================================ | ||
|
||
ldap.request.operation | ||
~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Suricata has a ``ldap.request.operation`` keyword that can be used in signatures to identify | ||
and filter network packets based on Lightweight Directory Access Protocol request operations. | ||
|
||
Syntax:: | ||
|
||
ldap.request.operation: operation; | ||
|
||
ldap.request.operation uses :ref:`unsigned 8-bit integer <rules-integer-keywords>`. | ||
|
||
Example | ||
^^^^^^^^ | ||
|
||
Example of a signature that would alert if the packet has an LDAP bind request operation: | ||
|
||
.. container:: example-rule | ||
|
||
alert tcp any any -> any any (msg:"Test LDAP bind request"; :example-rule-emphasis:`ldap.request.operation:0;` sid:1;) | ||
|
||
|
||
ldap.responses.operation | ||
~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
||
Suricata has a ``ldap.responses.operation`` keyword that can be used in signatures to identify | ||
and filter network packets based on Lightweight Directory Access Protocol response operations. | ||
|
||
Syntax:: | ||
|
||
ldap.responses.operation: operation[,index]; | ||
|
||
ldap.responses.operation uses :ref:`unsigned 8-bit integer <rules-integer-keywords>`. | ||
|
||
An LDAP request operation can receive multiple responses. By default, the ldap.responses.operation | ||
keyword matches all indices, but it is possible to specify a particular index for matching | ||
and also use flags such as ``all`` and ``any``. | ||
|
||
.. table:: **Index values for ldap.responses.operation keyword** | ||
|
||
========= ================================================ | ||
Value Description | ||
========= ================================================ | ||
[default] Match all indexes | ||
all Match only if all indexes match | ||
any Match all indexes | ||
0>= Match specific index | ||
========= ================================================ | ||
|
||
Examples | ||
^^^^^^^^ | ||
|
||
Example of a signature that would alert if the packet has an LDAP bind response operation: | ||
|
||
.. container:: example-rule | ||
|
||
alert tcp any any -> any any (msg:"Test LDAP bind response"; :example-rule-emphasis:`ldap.responses.operation:1;` sid:1;) | ||
|
||
Example of a signature that would alert if the packet has an LDAP search_result_done response operation at index 1: | ||
|
||
.. container:: example-rule | ||
|
||
alert tcp any any -> any any (msg:"Test LDAP search response"; :example-rule-emphasis:`ldap.responses.operation:search_result_done,1;` sid:1;) | ||
|
||
Example of a signature that would alert if all the responses are of type search_result_entry: | ||
|
||
.. container:: example-rule | ||
|
||
alert tcp any any -> any any (msg:"Test LDAP search response"; :example-rule-emphasis:`ldap.responses.operation:search_result_entry,all;` sid:1;) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
/* Copyright (C) 2024 Open Information Security Foundation | ||
* | ||
* You can copy, redistribute or modify this Program under the terms of | ||
* the GNU General Public License version 2 as published by the Free | ||
* Software Foundation. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* version 2 along with this program; if not, write to the Free Software | ||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
* 02110-1301, USA. | ||
*/ | ||
|
||
use super::ldap::{LdapTransaction, ALPROTO_LDAP}; | ||
use crate::detect::uint::{ | ||
detect_parse_uint_enum, rs_detect_u8_free, rs_detect_u8_match, DetectUintData, | ||
}; | ||
use crate::detect::{ | ||
DetectHelperBufferRegister, DetectHelperKeywordRegister, DetectSignatureSetAppProto, | ||
SCSigTableElmt, SigMatchAppendSMToList, | ||
}; | ||
use crate::ldap::types::{LdapMessage, ProtocolOpCode}; | ||
|
||
use std::ffi::CStr; | ||
use std::os::raw::{c_int, c_void}; | ||
use std::str::FromStr; | ||
|
||
pub const DETECT_LDAP_RESP_ANY: i8 = -1; | ||
pub const DETECT_LDAP_RESP_ALL: i8 = -2; | ||
|
||
#[derive(Debug, PartialEq)] | ||
pub struct DetectLdapRespData { | ||
pub response: DetectUintData<u8>, //ldap response code | ||
pub index: i8, | ||
} | ||
|
||
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; | ||
|
||
unsafe extern "C" fn ldap_parse_protocol_req_op( | ||
ustr: *const std::os::raw::c_char, | ||
) -> *mut DetectUintData<u8> { | ||
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe | ||
if let Ok(s) = ft_name.to_str() { | ||
if let Some(ctx) = detect_parse_uint_enum::<u8, ProtocolOpCode>(s) { | ||
let boxed = Box::new(ctx); | ||
return Box::into_raw(boxed) as *mut _; | ||
} | ||
} | ||
return std::ptr::null_mut(); | ||
} | ||
|
||
pub fn aux_ldap_parse_protocol_resp_op(s: &str) -> Option<DetectLdapRespData> { | ||
let parts: Vec<&str> = s.split(',').collect(); | ||
if parts.len() > 2 { | ||
return None; | ||
} | ||
let index = if parts.len() == 2 { | ||
if parts[1] == "all" { | ||
DETECT_LDAP_RESP_ALL | ||
} else if parts[1] == "any" { | ||
DETECT_LDAP_RESP_ANY | ||
} else { | ||
let u8_index = i8::from_str(parts[1]).ok()?; | ||
if u8_index < 0 { | ||
return None; | ||
} | ||
u8_index | ||
} | ||
} else { | ||
DETECT_LDAP_RESP_ANY | ||
}; | ||
if let Some(ctx) = detect_parse_uint_enum::<u8, ProtocolOpCode>(parts[0]) { | ||
let response = ctx; | ||
return Some(DetectLdapRespData { response, index }); | ||
} | ||
return None; | ||
} | ||
|
||
unsafe extern "C" fn ldap_parse_protocol_resp_op( | ||
ustr: *const std::os::raw::c_char, | ||
) -> *mut DetectUintData<u8> { | ||
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe | ||
if let Ok(s) = ft_name.to_str() { | ||
if let Some(ctx) = aux_ldap_parse_protocol_resp_op(s) { | ||
let boxed = Box::new(ctx); | ||
return Box::into_raw(boxed) as *mut _; | ||
} | ||
} | ||
return std::ptr::null_mut(); | ||
} | ||
|
||
unsafe extern "C" fn ldap_detect_request_operation_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 = ldap_parse_protocol_req_op(raw) as *mut c_void; | ||
if ctx.is_null() { | ||
return -1; | ||
} | ||
if SigMatchAppendSMToList( | ||
de, | ||
s, | ||
G_LDAP_REQUEST_OPERATION_KW_ID, | ||
ctx, | ||
G_LDAP_REQUEST_OPERATION_BUFFER_ID, | ||
) | ||
.is_null() | ||
{ | ||
ldap_detect_operation_free(std::ptr::null_mut(), ctx); | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
unsafe extern "C" fn ldap_detect_responses_operation_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 = ldap_parse_protocol_resp_op(raw) as *mut c_void; | ||
if ctx.is_null() { | ||
return -1; | ||
} | ||
if SigMatchAppendSMToList( | ||
de, | ||
s, | ||
G_LDAP_RESPONSES_OPERATION_KW_ID, | ||
ctx, | ||
G_LDAP_RESPONSES_OPERATION_BUFFER_ID, | ||
) | ||
.is_null() | ||
{ | ||
ldap_detect_operation_free(std::ptr::null_mut(), ctx); | ||
return -1; | ||
} | ||
return 0; | ||
} | ||
|
||
unsafe extern "C" fn ldap_detect_request_operation_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<u8>); | ||
if let Some(request) = &tx.request { | ||
let option: u8 = request.protocol_op.to_u8(); | ||
return rs_detect_u8_match(option, ctx); | ||
} | ||
return 0; | ||
} | ||
|
||
unsafe extern "C" fn ldap_detect_responses_operation_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, DetectLdapRespData); | ||
|
||
match ctx.index { | ||
DETECT_LDAP_RESP_ANY => { | ||
for response in &tx.responses { | ||
let option: u8 = response.protocol_op.to_u8(); | ||
if rs_detect_u8_match(option, &ctx.response) == 1 { | ||
return 1; | ||
} | ||
} | ||
return 0; | ||
} | ||
DETECT_LDAP_RESP_ALL => { | ||
for response in &tx.responses { | ||
let option: u8 = response.protocol_op.to_u8(); | ||
if rs_detect_u8_match(option, &ctx.response) == 0 { | ||
return 0; | ||
} | ||
} | ||
return 1; | ||
} | ||
_ => { | ||
let index: usize = ctx.index as usize; | ||
if tx.responses.len() <= index { | ||
return 0; | ||
} | ||
let response: &LdapMessage = &tx.responses[index]; | ||
let option: u8 = response.protocol_op.to_u8(); | ||
if rs_detect_u8_match(option, &ctx.response) == 1 { | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
} | ||
} | ||
|
||
unsafe extern "C" fn ldap_detect_operation_free(_de: *mut c_void, ctx: *mut c_void) { | ||
// Just unbox... | ||
let ctx = cast_pointer!(ctx, DetectUintData<u8>); | ||
rs_detect_u8_free(ctx); | ||
} | ||
|
||
#[no_mangle] | ||
pub unsafe extern "C" fn ScDetectLdapRegister() { | ||
let kw = SCSigTableElmt { | ||
name: b"ldap.request.operation\0".as_ptr() as *const libc::c_char, | ||
desc: b"match LDAP request operation\0".as_ptr() as *const libc::c_char, | ||
url: b"/rules/ldap-keywords.html#ldap.request.operation\0".as_ptr() as *const libc::c_char, | ||
AppLayerTxMatch: Some(ldap_detect_request_operation_match), | ||
Setup: ldap_detect_request_operation_setup, | ||
Free: Some(ldap_detect_operation_free), | ||
flags: 0, | ||
}; | ||
G_LDAP_REQUEST_OPERATION_KW_ID = DetectHelperKeywordRegister(&kw); | ||
G_LDAP_REQUEST_OPERATION_BUFFER_ID = DetectHelperBufferRegister( | ||
b"ldap.request.operation\0".as_ptr() as *const libc::c_char, | ||
ALPROTO_LDAP, | ||
false, //to client | ||
true, //to server | ||
); | ||
let kw = SCSigTableElmt { | ||
name: b"ldap.responses.operation\0".as_ptr() as *const libc::c_char, | ||
desc: b"match LDAP responses operation\0".as_ptr() as *const libc::c_char, | ||
url: b"/rules/ldap-keywords.html#ldap.responses.operation\0".as_ptr() | ||
as *const libc::c_char, | ||
AppLayerTxMatch: Some(ldap_detect_responses_operation_match), | ||
Setup: ldap_detect_responses_operation_setup, | ||
Free: Some(ldap_detect_operation_free), | ||
flags: 0, | ||
}; | ||
G_LDAP_RESPONSES_OPERATION_KW_ID = DetectHelperKeywordRegister(&kw); | ||
G_LDAP_RESPONSES_OPERATION_BUFFER_ID = DetectHelperBufferRegister( | ||
b"ldap.responses.operation\0".as_ptr() as *const libc::c_char, | ||
ALPROTO_LDAP, | ||
true, //to client | ||
false, //to server | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
|
||
// written by Giuseppe Longo <[email protected]> | ||
|
||
pub mod detect; | ||
pub mod filters; | ||
pub mod ldap; | ||
pub mod logger; | ||
|
Oops, something went wrong.