pull/16233/merge
Catena cyber 13 hours ago committed by GitHub
commit 3897cdfc4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -73,6 +73,60 @@ So with the Suricata behavior prior to Suricata 7, the signature
would not fire in this case since both content conditions will
not be met.
Indexes for multi-buffers
-------------------------
As for :ref:`multi-integer <multi-integers>`, multi-buffers can precise
which index to match on.
The syntax is::
buffer: index; content: "toto";
.. table:: **Index values for multi-buffers keywords**
=============== ===========================================================
Value Description
=============== ===========================================================
[default] Match with any index
any Match with any index
any_or_absent Match with any index or matches on an empty list
all Match only if all and at least one indexes match
all_or_absent Match only if all indexes match or matches on an empty list
matches <value> Matches a number of times
index <value> Match specific index
oob_or <value> Match with specific index or index out of bounds
=============== ===========================================================
.. container:: example-rule
alert ip any any -> any 5353 (:example-rule-emphasis:`email.received: all;` content:"abc"; sid:1;)
.. container:: example-rule
alert ip any any -> any 5353 (:example-rule-emphasis:`email.received: matches >2;` content:"def"; sid:1;)
.. container:: example-rule
alert ip any any -> any 5353 (:example-rule-emphasis:`email.received: index 0;` content:"ghi"; sid:1;)
**Please note that:**
The index ``all`` will not match if there is no value.
The index ``all_or_absent`` will match if there is no value
and behaves like ``all`` if there is at least one value.
If needed, these keywords will wait for transaction completion to run,
to be sure to have the final number of elements.
The index ``matches`` accepts all comparison modes as integer keywords.
For example ``matches>3`` will match only if more than 3 integers in the
array match the value.
List of multi-buffers
---------------------
Multiple buffer matching is currently enabled for use with the
following keywords:

@ -50,13 +50,20 @@ pub struct DetectUintData<T> {
pub mode: DetectUintMode,
}
#[repr(C)]
#[derive(Debug, PartialEq)]
pub struct DetectUintIndexPrecise {
pub oob: bool,
pub pos: i32,
}
#[derive(Debug, PartialEq)]
pub enum DetectUintIndex {
Any,
AllOrAbsent,
All,
OrAbsent,
Index((bool, i32)),
Index(DetectUintIndexPrecise),
NumberMatches(DetectUintData<u32>),
Count(DetectUintData<u32>),
}
@ -72,22 +79,35 @@ pub struct DetectUintArrayData<T> {
fn parse_uint_index_precise(s: &str) -> IResult<&str, DetectUintIndex> {
let (s, oob) = opt(tag("oob_or")).parse(s)?;
let (s, _explicit) = opt(tag("index")).parse(s)?;
let (s, _) = opt(is_a(" ")).parse(s)?;
let (s, i32_index) = nom_i32.parse(s)?;
Ok((s, DetectUintIndex::Index((oob.is_some(), i32_index))))
Ok((
s,
DetectUintIndex::Index(DetectUintIndexPrecise {
oob: oob.is_some(),
pos: i32_index,
}),
))
}
fn parse_uint_index_nb(s: &str) -> IResult<&str, DetectUintIndex> {
let (s, _) = tag("nb").parse(s)?;
let (s, _) = alt((tag("nb"), tag("matches"))).parse(s)?;
let (s, _) = opt(is_a(" ")).parse(s)?;
let (s, du32) = detect_parse_uint::<u32>(s)?;
Ok((s, DetectUintIndex::NumberMatches(du32)))
}
fn parse_uint_index_val(s: &str) -> Option<DetectUintIndex> {
let (_s, arg1) = alt((parse_uint_index_precise, parse_uint_index_nb))
let (s, arg1) = alt((parse_uint_index_precise, parse_uint_index_nb))
.parse(s)
.ok()?;
if let Ok((s, _)) = opt(is_a::<&str, &str, nom8::error::Error<_>>(" ")).parse(s) {
if !s.is_empty() {
SCLogError!("Invalid trailing data for index : {s}");
return None;
}
}
Some(arg1)
}
@ -114,7 +134,8 @@ fn parse_uint_subslice(parts: &[&str]) -> Option<(i32, i32)> {
return Some((start, end));
}
fn parse_uint_count(s: &str) -> IResult<&str, DetectUintData<u32>> {
fn parse_multi_count(s: &str) -> IResult<&str, DetectUintData<u32>> {
let (s, _) = opt(is_a(" ")).parse(s)?;
let (s, _) = tag("count").parse(s)?;
let (s, _) = opt(is_a(" ")).parse(s)?;
let (s, du32) = detect_parse_uint::<u32>(s)?;
@ -131,7 +152,7 @@ fn parse_uint_index(parts: &[&str]) -> Option<DetectUintIndex> {
// not only a literal, but some numeric value
_ => return parse_uint_index_val(parts[1]),
}
} else if let Ok((_, du)) = parse_uint_count(parts[0]) {
} else if let Ok((_, du)) = parse_multi_count(parts[0]) {
DetectUintIndex::Count(du)
} else {
DetectUintIndex::Any
@ -322,15 +343,15 @@ pub(crate) fn detect_uint_match_at_index<T, U: DetectIntType>(
}
return 0;
}
DetectUintIndex::Index((oob, idx)) => {
let index = if *idx < 0 {
DetectUintIndex::Index(prec) => {
let index = if prec.pos < 0 {
// negative values for backward indexing.
((subslice.len() as i32) + idx) as usize
((subslice.len() as i32) + prec.pos) as usize
} else {
*idx as usize
prec.pos as usize
};
if subslice.len() <= index {
if *oob && eof {
if prec.oob && eof {
return 1;
}
return 0;
@ -998,6 +1019,93 @@ pub unsafe extern "C" fn SCDetectU16Free(ctx: &mut DetectUintData<u16>) {
std::mem::drop(Box::from_raw(ctx));
}
#[no_mangle]
pub unsafe extern "C" fn SCDetectMultiCountParse(ustr: *const std::os::raw::c_char) -> *mut c_void {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Ok((_, ctx)) = parse_multi_count(s) {
let boxed = Box::new(ctx);
return Box::into_raw(boxed) as *mut c_void;
}
}
return std::ptr::null_mut();
}
/// just a u8 for FFI
#[derive(PartialEq, Eq, Clone, Debug)]
#[repr(u8)]
pub enum DetectMultiIndex {
DetectMultiIndexAny,
DetectMultiIndexAbsentOr,
DetectMultiIndexAll,
DetectMultiIndexAllOrAbsent,
DetectMultiIndexNb,
DetectMultiIndexPrecise,
DetectMultiIndexError,
}
impl DetectUintIndex {
// just borrow
fn index(&self) -> DetectMultiIndex {
match self {
DetectUintIndex::All => DetectMultiIndex::DetectMultiIndexAll,
DetectUintIndex::AllOrAbsent => DetectMultiIndex::DetectMultiIndexAllOrAbsent,
DetectUintIndex::Any => DetectMultiIndex::DetectMultiIndexAny,
DetectUintIndex::OrAbsent => DetectMultiIndex::DetectMultiIndexAbsentOr,
DetectUintIndex::NumberMatches(_) => DetectMultiIndex::DetectMultiIndexNb,
DetectUintIndex::Index(_) => DetectMultiIndex::DetectMultiIndexPrecise,
DetectUintIndex::Count(_) => DetectMultiIndex::DetectMultiIndexError,
}
}
// take ownership and move
fn into_box(self) -> *mut c_void {
match self {
DetectUintIndex::NumberMatches(du32) => {
let boxed = Box::new(du32);
Box::into_raw(boxed) as *mut c_void
}
DetectUintIndex::Index(prec) => {
let boxed = Box::new(prec);
Box::into_raw(boxed) as *mut c_void
}
_ => std::ptr::null_mut(),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn SCDetectMultiIndexFree(ctx: &mut DetectUintIndexPrecise) {
std::mem::drop(Box::from_raw(ctx));
}
fn parse_multi_index(s: &str) -> Option<DetectUintIndex> {
match s {
"all" => Some(DetectUintIndex::All),
"all_or_absent" => Some(DetectUintIndex::AllOrAbsent),
"any" => Some(DetectUintIndex::Any),
"absent_or" => Some(DetectUintIndex::OrAbsent),
"any_or_absent" => Some(DetectUintIndex::OrAbsent),
// not only a literal, but some numeric value
_ => return parse_uint_index_val(s),
}
}
#[no_mangle]
pub unsafe extern "C" fn SCDetectMultiIndexParse(
ustr: *const std::os::raw::c_char, it: *mut DetectMultiIndex,
) -> *mut c_void {
let ft_name: &CStr = CStr::from_ptr(ustr); //unsafe
if let Ok(s) = ft_name.to_str() {
if let Some(ctx) = parse_multi_index(s) {
*it = ctx.index();
return ctx.into_box();
}
}
*it = DetectMultiIndex::DetectMultiIndexError;
return std::ptr::null_mut();
}
#[cfg(test)]
mod tests {
use super::*;

@ -17,7 +17,7 @@
use super::uint::{
detect_parse_array_uint, detect_uint_match_at_index, DetectUintArrayData, DetectUintData,
DetectUintIndex,
DetectUintIndex, DetectUintIndexPrecise,
};
use std::ffi::{c_int, c_void, CStr};
@ -49,7 +49,7 @@ pub fn detect_parse_vlan_id(s: &str) -> Option<DetectUintArrayData<u16>> {
SCLogError!("vlan id should be less than 4096");
return None;
}
match a.index {
match &a.index {
DetectUintIndex::All => {
// keep previous behavior that vlan.id: all matched only if there was vlan
return Some(DetectUintArrayData {
@ -59,8 +59,8 @@ pub fn detect_parse_vlan_id(s: &str) -> Option<DetectUintArrayData<u16>> {
end: a.end,
});
}
DetectUintIndex::Index((_, i))
if !(-VLAN_MAX_LAYERS..=VLAN_MAX_LAYERS - 1).contains(&i) =>
DetectUintIndex::Index(prec)
if !(-VLAN_MAX_LAYERS..=VLAN_MAX_LAYERS - 1).contains(&prec.pos) =>
{
SCLogError!(
"vlan id index should belong in range {:?}",
@ -110,7 +110,10 @@ pub unsafe extern "C" fn SCDetectVlanIdPrefilterMatch(
DETECT_VLAN_ID_ALL => DetectUintIndex::All,
DETECT_VLAN_ID_ALL_OR_ABSENT => DetectUintIndex::AllOrAbsent,
DETECT_VLAN_ID_OR_ABSENT => DetectUintIndex::OrAbsent,
i => DetectUintIndex::Index((false, i.into())),
i => DetectUintIndex::Index(DetectUintIndexPrecise {
oob: false,
pos: i.into(),
}),
};
let ctx = DetectUintArrayData {
@ -127,12 +130,12 @@ pub unsafe extern "C" fn SCDetectVlanIdPrefilterMatch(
pub unsafe extern "C" fn SCDetectVlanIdPrefilter(
ctx: &DetectUintArrayData<u16>,
) -> DetectVlanIdDataPrefilter {
let layer = match ctx.index {
let layer = match &ctx.index {
DetectUintIndex::Any => DETECT_VLAN_ID_ANY,
DetectUintIndex::All => DETECT_VLAN_ID_ALL,
DetectUintIndex::AllOrAbsent => DETECT_VLAN_ID_ALL_OR_ABSENT,
DetectUintIndex::OrAbsent => DETECT_VLAN_ID_OR_ABSENT,
DetectUintIndex::Index((_, i)) => i as i8,
DetectUintIndex::Index(prec) => prec.pos as i8,
DetectUintIndex::NumberMatches(_) => DETECT_VLAN_ID_ERROR,
DetectUintIndex::Count(_) => DETECT_VLAN_ID_ERROR,
};
@ -148,13 +151,13 @@ pub unsafe extern "C" fn SCDetectVlanIdPrefilterable(ctx: *const c_void) -> bool
if ctx.start != 0 || ctx.end != 0 {
return false;
}
match ctx.index {
match &ctx.index {
DetectUintIndex::Any => true,
DetectUintIndex::All => true,
DetectUintIndex::AllOrAbsent => true,
DetectUintIndex::OrAbsent => true,
// do not prefilter for precise index with "or out of bounds"
DetectUintIndex::Index((oob, _)) => !oob,
DetectUintIndex::Index(prec) => !prec.oob,
DetectUintIndex::NumberMatches(_) => false,
DetectUintIndex::Count(_) => false,
}
@ -214,7 +217,7 @@ mod test {
arg2: 0,
mode: DetectUintMode::DetectUintModeEqual,
},
index: DetectUintIndex::Index((false, 1)),
index: DetectUintIndex::Index(DetectUintIndexPrecise { oob: false, pos: 1 }),
start: 0,
end: 0,
}
@ -227,7 +230,10 @@ mod test {
arg2: 0,
mode: DetectUintMode::DetectUintModeEqual,
},
index: DetectUintIndex::Index((false, -1)),
index: DetectUintIndex::Index(DetectUintIndexPrecise {
oob: false,
pos: -1
}),
start: 0,
end: 0,
}
@ -240,7 +246,7 @@ mod test {
arg2: 0,
mode: DetectUintMode::DetectUintModeNe,
},
index: DetectUintIndex::Index((false, 2)),
index: DetectUintIndex::Index(DetectUintIndexPrecise { oob: false, pos: 2 }),
start: 0,
end: 0,
}
@ -253,7 +259,7 @@ mod test {
arg2: 0,
mode: DetectUintMode::DetectUintModeGt,
},
index: DetectUintIndex::Index((false, 2)),
index: DetectUintIndex::Index(DetectUintIndexPrecise { oob: false, pos: 2 }),
start: 0,
end: 0,
}
@ -266,7 +272,7 @@ mod test {
arg2: 300,
mode: DetectUintMode::DetectUintModeRange,
},
index: DetectUintIndex::Index((false, 0)),
index: DetectUintIndex::Index(DetectUintIndexPrecise { oob: false, pos: 0 }),
start: 0,
end: 0,
}
@ -279,7 +285,7 @@ mod test {
arg2: 0,
mode: DetectUintMode::DetectUintModeEqual,
},
index: DetectUintIndex::Index((false, 2)),
index: DetectUintIndex::Index(DetectUintIndexPrecise { oob: false, pos: 2 }),
start: 0,
end: 0,
}

@ -248,6 +248,7 @@ noinst_HEADERS = \
detect-mark.h \
detect-metadata.h \
detect-msg.h \
detect-multi.h \
detect-noalert.h \
detect-nocase.h \
detect-offset.h \
@ -823,6 +824,7 @@ libsuricata_c_a_SOURCES = \
detect-mark.c \
detect-metadata.c \
detect-msg.c \
detect-multi.c \
detect-noalert.c \
detect-nocase.c \
detect-offset.c \

@ -21,6 +21,7 @@
#include "detect-parse.h"
#include "app-layer-smtp.h"
#include "detect-email.h"
#include "detect-multi.h"
#include "rust.h"
#include "detect-engine-content-inspection.h"
@ -205,6 +206,10 @@ static int DetectMimeEmailReceivedSetup(DetectEngineCtx *de_ctx, Signature *s, c
if (SCDetectSignatureSetAppProto(s, ALPROTO_SMTP) < 0)
return -1;
if (arg) {
return DetectMultiSetup(de_ctx, s, arg);
}
return 0;
}
@ -336,7 +341,7 @@ void DetectEmailRegister(void)
kw.desc = "'Received' field from an email";
kw.url = "/rules/email-keywords.html#email-received";
kw.Setup = DetectMimeEmailReceivedSetup;
kw.flags = SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
kw.flags = SIGMATCH_OPTIONAL_OPT | SIGMATCH_INFO_STICKY_BUFFER | SIGMATCH_INFO_MULTI_BUFFER;
SCDetectHelperKeywordRegister(&kw);
g_mime_email_received_buffer_id =
SCDetectHelperMultiBufferProgressMpmRegister("email.received", "MIME EMAIL RECEIVED",

@ -134,10 +134,22 @@ int DetectBufferGetActiveList(DetectEngineCtx *de_ctx, Signature *s)
s->init_data->transforms.cnt = 0;
if (s->init_data->curbuf && s->init_data->curbuf->head != NULL) {
const SigMatch *sm = s->init_data->curbuf->head;
if (sm != NULL && sm->next == NULL &&
(sm->type == DETECT_MULTI_ALL || sm->type == DETECT_MULTI_ALL_OR_ABSENT ||
sm->type == DETECT_MULTI_NB || sm->type == DETECT_MULTI_INDEX ||
sm->type == DETECT_ABSENT)) {
// just keep the same curbuf in case sticky_multi: all; transform; content: "abc";
// but update its id to the transfomed id
s->init_data->curbuf->id = new_list;
SCReturnInt(0);
}
if (SignatureInitDataBufferCheckExpand(s) < 0) {
SCLogError("failed to expand rule buffer array");
return -1;
}
s->init_data->curbuf = &s->init_data->buffers[s->init_data->buffer_index++];
s->init_data->curbuf->multi_capable =
DetectEngineBufferTypeSupportsMultiInstanceGetById(de_ctx, base_list);

@ -54,6 +54,7 @@
#include "detect-content.h"
#include "detect-engine-payload.h"
#include "detect-engine-uint.h"
#include "stream.h"
@ -1234,8 +1235,24 @@ void RetrieveFPForSig(const DetectEngineCtx *de_ctx, Signature *s)
for (SigMatch *sm = s->init_data->buffers[x].head; sm != NULL; sm = sm->next) {
// a buffer with absent keyword cannot be used as fast_pattern
if (sm->type == DETECT_ABSENT)
if (sm->type == DETECT_ABSENT || sm->type == DETECT_MULTI_ALL_OR_ABSENT)
break;
if (sm->type == DETECT_MULTI_NB) {
// Multi-buffers mode matches with "matches: 0" can succeed
// if there are no content that matches, so it cannot be used as fast pattern
const DetectU32Data *du32 = (DetectU32Data *)sm->ctx;
if (DetectU32Match(0, du32)) {
break;
}
}
if (sm->type == DETECT_MULTI_INDEX) {
// Multi-buffers mode index with "oob_or" can succeed
// if there are not enough elements, so it cannot be used as fast pattern
const DetectUintIndexPrecise *ip = (DetectUintIndexPrecise *)sm->ctx;
if (ip->oob) {
break;
}
}
if (sm->type != DETECT_CONTENT)
continue;

@ -202,6 +202,7 @@
#include "detect-ja4-hash.h"
#include "detect-ftp-command.h"
#include "detect-entropy.h"
#include "detect-multi.h"
#include "detect-ftp-command-data.h"
#include "detect-ftp-completion-code.h"
#include "detect-ftp-reply.h"
@ -723,6 +724,7 @@ void SigTableSetup(void)
DetectBytejumpRegister();
DetectBytemathRegister();
DetectEntropyRegister();
DetectMultiRegister();
DetectSameipRegister();
DetectGeoipRegister();
DetectL3ProtoRegister();

@ -103,6 +103,10 @@ enum DetectKeywordId {
DETECT_URILEN,
DETECT_ABSENT,
DETECT_ENTROPY,
DETECT_MULTI_ALL,
DETECT_MULTI_ALL_OR_ABSENT,
DETECT_MULTI_NB,
DETECT_MULTI_INDEX,
/* end of content inspection */
DETECT_METADATA,

@ -57,6 +57,8 @@
#include "detect-engine-payload.h"
#include "detect-fast-pattern.h"
#include "detect-byte-extract.h"
#include "detect-multi.h"
#include "detect-engine-uint.h"
#include "detect-content.h"
#include "detect-uricontent.h"
#include "detect-tcphdr.h"
@ -2387,6 +2389,78 @@ uint8_t DetectEngineInspectMultiBufferGeneric(DetectEngineCtx *de_ctx,
transforms = engine->v2.transforms;
}
bool stop_on_first_match = true;
const bool eof = DetectTxCompleted(f, txv, flags, engine);
SigMatchData *smd = engine->smd;
DetectUintIndexPrecise *prec;
switch (smd->type) {
case DETECT_MULTI_ALL:
// fallthrough
case DETECT_MULTI_ALL_OR_ABSENT:
if (!eof) {
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
stop_on_first_match = false;
smd++;
break;
case DETECT_MULTI_NB:
if (!eof) {
DetectU32Data *du32 = (DetectU32Data *)smd->ctx;
if (du32->mode != DETECT_UINT_GTE && du32->mode != DETECT_UINT_GT) {
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
}
stop_on_first_match = false;
smd++;
break;
case DETECT_MULTI_INDEX:
prec = (DetectUintIndexPrecise *)smd->ctx;
if (prec->pos < 0) {
if (!eof) {
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
// TODO unreachable so far
// need to get count if eof, and have local_id = count + prec->pos;
} else {
local_id = prec->pos;
}
// get the buffer
InspectionBuffer *buffer = DetectGetMultiData(det_ctx, transforms, f, flags, txv,
engine->sm_list, local_id, engine->v2.GetMultiData);
if (buffer == NULL || buffer->inspect == NULL) {
// no buffer
if (eof) {
// no more buffers coming
if (prec->oob) {
// match as out of bounds
return DETECT_ENGINE_INSPECT_SIG_MATCH;
}
// will never match
return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
}
// wait for more buffers
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
// smd + 1 is not NULL thanks to DetectMultiValidateContentCallback
const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, smd + 1,
NULL, f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
if (match) {
return DETECT_ENGINE_INSPECT_SIG_MATCH;
}
if (eof) {
return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
}
// wait for buffer to be complete
buffer = DetectGetMultiData(det_ctx, transforms, f, flags, txv, engine->sm_list,
local_id + 1, engine->v2.GetMultiData);
if (buffer && buffer->inspect) {
// we have next buffer in the multi-buffer
return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH;
}
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
uint32_t nb_matches = 0;
do {
InspectionBuffer *buffer = DetectGetMultiData(det_ctx, transforms, f, flags, txv,
engine->sm_list, local_id, engine->v2.GetMultiData);
@ -2396,16 +2470,33 @@ uint8_t DetectEngineInspectMultiBufferGeneric(DetectEngineCtx *de_ctx,
// The GetData functions set buffer->flags to DETECT_CI_FLAGS_SINGLE
// This is not meant for streaming buffers
const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, engine->smd,
NULL, f, buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
const bool match = DetectEngineContentInspectionBuffer(de_ctx, det_ctx, s, smd, NULL, f,
buffer, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
if (match) {
return DETECT_ENGINE_INSPECT_SIG_MATCH;
if (stop_on_first_match)
return DETECT_ENGINE_INSPECT_SIG_MATCH;
nb_matches++;
}
local_id++;
} while (1);
if (!stop_on_first_match) {
switch (engine->smd->type) {
case DETECT_MULTI_ALL:
if (nb_matches == local_id && nb_matches > 0)
return DETECT_ENGINE_INSPECT_SIG_MATCH;
break;
case DETECT_MULTI_ALL_OR_ABSENT:
if (nb_matches == local_id)
return DETECT_ENGINE_INSPECT_SIG_MATCH;
break;
case DETECT_MULTI_NB:
if (DetectU32Match(nb_matches, (DetectU32Data *)engine->smd->ctx))
return DETECT_ENGINE_INSPECT_SIG_MATCH;
}
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
}
if (local_id == 0) {
// That means we did not get even one buffer value from the multi-buffer
const bool eof = DetectTxCompleted(f, txv, flags, engine);
if (eof && engine->match_on_null) {
return DETECT_ENGINE_INSPECT_SIG_MATCH;
}

@ -0,0 +1,129 @@
/* Copyright (C) 2025 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.
*/
#include "suricata-common.h"
#include "rust.h"
#include "detect-multi.h"
#include "detect-engine-buffer.h"
#include "detect-engine-uint.h"
#include "detect-parse.h"
// DetectAbsentData
#include "detect-isdataat.h"
#include "util-validate.h"
static void DetectDu32Free(DetectEngineCtx *de_ctx, void *ptr)
{
SCDetectU32Free(ptr);
}
int DetectMultiSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
{
DetectMultiIndex index_type;
DetectUintIndexPrecise *prec;
DetectAbsentData *dad;
void *sm_ctx = SCDetectMultiIndexParse(arg, &index_type);
switch (index_type) {
case DetectMultiIndexAny:
// default case, nothing to do
return 0;
case DetectMultiIndexAbsentOr:
dad = SCMalloc(sizeof(DetectAbsentData));
if (unlikely(dad == NULL))
return -1;
dad->or_else = true;
if (SCSigMatchAppendSMToList(
de_ctx, s, DETECT_ABSENT, (SigMatchCtx *)dad, s->init_data->list) != NULL) {
return 0;
}
sigmatch_table[DETECT_ABSENT].Free(de_ctx, dad);
return -1;
case DetectMultiIndexAll:
if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_MULTI_ALL, NULL, s->init_data->list) !=
NULL) {
return 0;
}
return -1;
case DetectMultiIndexAllOrAbsent:
if (SCSigMatchAppendSMToList(
de_ctx, s, DETECT_MULTI_ALL_OR_ABSENT, NULL, s->init_data->list) != NULL) {
return 0;
}
return -1;
case DetectMultiIndexNb:
if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_MULTI_NB, sm_ctx, s->init_data->list) !=
NULL) {
return 0;
}
sigmatch_table[DETECT_MULTI_NB].Free(de_ctx, sm_ctx);
return -1;
case DetectMultiIndexPrecise:
prec = (DetectUintIndexPrecise *)sm_ctx;
if (prec->pos < 0) {
SCDetectMultiIndexFree(sm_ctx);
SCLogError("negative index is not yet supported");
return -1;
}
if (SCSigMatchAppendSMToList(
de_ctx, s, DETECT_MULTI_INDEX, sm_ctx, s->init_data->list) != NULL) {
return 0;
}
SCDetectMultiIndexFree(sm_ctx);
return -1;
default:
SCLogError("invalid argument for multi-buffer");
return -1;
}
}
static void DetectMultiIndexFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCDetectMultiIndexFree(ptr);
}
void DetectMultiRegister(void)
{
// These are not used as a regular keyword
// But as option that can be set on multi-buffers
sigmatch_table[DETECT_MULTI_NB].name = "__multi_nb";
sigmatch_table[DETECT_MULTI_NB].desc = "count number of matches in a multi-buffer";
sigmatch_table[DETECT_MULTI_NB].Free = DetectDu32Free;
sigmatch_table[DETECT_MULTI_INDEX].name = "__multi_index";
sigmatch_table[DETECT_MULTI_INDEX].desc = "try to match a multi-buffer at a specific index";
sigmatch_table[DETECT_MULTI_INDEX].Free = DetectMultiIndexFree;
// just a name for --engine-analysis
sigmatch_table[DETECT_MULTI_ALL].name = "__multi_all";
sigmatch_table[DETECT_MULTI_ALL_OR_ABSENT].name = "__multi_all_or_absent";
}
bool DetectMultiValidateContentCallback(const Signature *s, const SignatureInitDataBuffer *b)
{
const SigMatch *sm = b->head;
if (sm != NULL && sm->next == NULL &&
(sm->type == DETECT_MULTI_ALL || sm->type == DETECT_MULTI_ALL_OR_ABSENT ||
sm->type == DETECT_MULTI_NB || sm->type == DETECT_MULTI_INDEX)) {
SCLogError("signature with multi-buffer keyword: expects other keywords to test on such as "
"content");
return false;
}
return true;
}

@ -0,0 +1,25 @@
/* Copyright (C) 2025 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.
*/
#ifndef SURICATA_DETECT_MULTI_H
#define SURICATA_DETECT_MULTI_H
void DetectMultiRegister(void);
int DetectMultiSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
bool DetectMultiValidateContentCallback(const Signature *s, const SignatureInitDataBuffer *);
#endif

@ -36,6 +36,7 @@
#include "detect-content.h"
#include "detect-bsize.h"
#include "detect-isdataat.h"
#include "detect-multi.h"
#include "detect-pcre.h"
#include "detect-uricontent.h"
#include "detect-reference.h"
@ -3115,6 +3116,9 @@ static int SigValidateCheckBuffers(
if (!DetectAbsentValidateContentCallback(s, b)) {
SCReturnInt(0);
}
if (!DetectMultiValidateContentCallback(s, b)) {
SCReturnInt(0);
}
}
if (has_pmatch && has_frame) {

Loading…
Cancel
Save