detect/xform: Support transform identity data

Transforms that support optional strings, like from_base64 and
pcrexform, should also support identity-strings to treat transforms with
like transform options as the same.

This commit adds transform identity data handling:
- When computing a hash, include identity data from the transform
- When comparing, include the identity data from the transforms
- Omitting the "options" ptr from the transform hash/compare
- Modify xor, pcrexform and from_base64 to supply identification data for
  disambiguation in the compare/hash logic.
pull/13235/head
Jeff Lucovsky 7 months ago committed by Victor Julien
parent 0b53a19c81
commit 07205ab057

@ -19,9 +19,9 @@
use crate::detect::error::RuleParseError;
use crate::detect::parser::{parse_var, take_until_whitespace, ResultValue};
use crate::ffi::base64::SCBase64Mode;
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use crate::ffi::base64::SCBase64Mode;
use nom7::bytes::complete::tag;
use nom7::character::complete::multispace0;
@ -147,7 +147,8 @@ fn parse_transform_base64(
} else {
return Err(make_error(format!(
"invalid offset value: must be between 0 and {}: {}",
u16::MAX, val
u16::MAX,
val
)));
}
}
@ -179,7 +180,9 @@ fn parse_transform_base64(
} else {
return Err(make_error(format!(
"invalid bytes value: must be between {} and {}: {}",
0, u16::MAX, val
0,
u16::MAX,
val
)));
}
}
@ -215,9 +218,7 @@ pub unsafe extern "C" fn SCTransformBase64Parse(
return std::ptr::null_mut();
}
let arg = CStr::from_ptr(c_arg)
.to_str()
.unwrap_or("");
let arg = CStr::from_ptr(c_arg).to_str().unwrap_or("");
match parse_transform_base64(arg) {
Ok((_, detect)) => return Box::into_raw(Box::new(detect)),
@ -264,13 +265,8 @@ mod tests {
}
fn valid_test(
args: &str,
nbytes: u32,
nbytes_str: &str,
offset: u32,
offset_str: &str,
mode: SCBase64Mode,
flags: u8,
args: &str, nbytes: u32, nbytes_str: &str, offset: u32, offset_str: &str,
mode: SCBase64Mode, flags: u8,
) {
let tbd = SCDetectTransformFromBase64Data {
flags,

@ -83,6 +83,7 @@ pub unsafe extern "C" fn DetectTransformToLowerRegister() {
Transform: Some(tolower_transform),
Free: None,
TransformValidate: Some(tolower_validate),
TransformId: None,
};
G_TRANSFORM_TOLOWER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_TOLOWER_ID < 0 {
@ -145,6 +146,7 @@ pub unsafe extern "C" fn DetectTransformToUpperRegister() {
Transform: Some(toupper_transform),
Free: None,
TransformValidate: Some(toupper_validate),
TransformId: None,
};
G_TRANSFORM_TOUPPER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_TOUPPER_ID < 0 {

@ -106,6 +106,7 @@ pub unsafe extern "C" fn DetectTransformCompressWhitespaceRegister() {
Transform: Some(compress_whitespace_transform),
Free: None,
TransformValidate: Some(compress_whitespace_validate),
TransformId: None,
};
G_TRANSFORM_COMPRESS_WHITESPACE_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_COMPRESS_WHITESPACE_ID < 0 {

@ -116,6 +116,7 @@ pub unsafe extern "C" fn SCDetectTransformDomainRegister() {
Transform: Some(domain_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
unsafe {
G_TRANSFORM_DOMAIN_ID = SCDetectHelperTransformRegister(&kw);
@ -133,6 +134,7 @@ pub unsafe extern "C" fn SCDetectTransformDomainRegister() {
Transform: Some(tld_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
unsafe {
G_TRANSFORM_TLD_ID = SCDetectHelperTransformRegister(&kw);

@ -79,6 +79,7 @@ pub unsafe extern "C" fn DetectTransformDotPrefixRegister() {
Transform: Some(dot_prefix_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
unsafe {
G_TRANSFORM_DOT_PREFIX_ID = SCDetectHelperTransformRegister(&kw);

@ -84,6 +84,7 @@ pub unsafe extern "C" fn DetectTransformMd5Register() {
Transform: Some(md5_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_MD5_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_MD5_ID < 0 {
@ -138,6 +139,7 @@ pub unsafe extern "C" fn DetectTransformSha1Register() {
Transform: Some(sha1_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_SHA1_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_SHA1_ID < 0 {
@ -192,6 +194,7 @@ pub unsafe extern "C" fn DetectTransformSha256Register() {
Transform: Some(sha256_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_SHA256_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_SHA256_ID < 0 {

@ -86,6 +86,7 @@ pub unsafe extern "C" fn DetectTransformHeaderLowercaseRegister() {
Transform: Some(header_lowertransform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_HEADER_LOWER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_HEADER_LOWER_ID < 0 {
@ -150,6 +151,7 @@ pub unsafe extern "C" fn DetectTransformStripPseudoHeadersRegister() {
Transform: Some(strip_pseudo_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_STRIP_PSEUDO_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_STRIP_PSEUDO_ID < 0 {

@ -93,6 +93,7 @@ pub unsafe extern "C" fn DetectTransformStripWhitespaceRegister() {
Transform: Some(strip_whitespace_transform),
Free: None,
TransformValidate: Some(strip_whitespace_validate),
TransformId: None,
};
unsafe {
G_TRANSFORM_STRIP_WHITESPACE_ID = SCDetectHelperTransformRegister(&kw);

@ -121,6 +121,7 @@ pub unsafe extern "C" fn DetectTransformUrlDecodeRegister() {
Transform: Some(url_decode_transform),
Free: None,
TransformValidate: None,
TransformId: None,
};
G_TRANSFORM_URL_DECODE_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_URL_DECODE_ID < 0 {

@ -108,6 +108,16 @@ unsafe extern "C" fn xor_free(_de: *mut DetectEngineCtx, ctx: *mut c_void) {
std::mem::drop(Box::from_raw(ctx as *mut DetectTransformXorData));
}
unsafe extern "C" fn xor_id(data: *mut *const u8, length: *mut u32, ctx: *mut c_void) {
if data.is_null() || length.is_null() || ctx.is_null() {
return;
}
let ctx = cast_pointer!(ctx, DetectTransformXorData);
*data = ctx.key.as_ptr();
*length = ctx.key.len() as u32;
}
#[no_mangle]
pub unsafe extern "C" fn DetectTransformXorRegister() {
let kw = SCTransformTableElmt {
@ -119,11 +129,12 @@ pub unsafe extern "C" fn DetectTransformXorRegister() {
Transform: Some(xor_transform),
Free: Some(xor_free),
TransformValidate: None,
TransformId: Some(xor_id),
};
unsafe {
G_TRANSFORM_XOR_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_XOR_ID < 0 {
SCLogWarning!("Failed registering transform dot_prefix");
SCLogWarning!("Failed registering transform xor");
}
}
}
@ -142,6 +153,32 @@ mod tests {
);
}
#[test]
fn test_xor_id() {
let ctx = Box::new(DetectTransformXorData {
key: vec![1, 2, 3, 4, 5],
});
let ctx_ptr: *const c_void = &*ctx as *const _ as *const c_void;
let mut data_ptr: *const u8 = std::ptr::null();
let mut length: u32 = 0;
unsafe {
xor_id(
&mut data_ptr as *mut *const u8,
&mut length as *mut u32,
ctx_ptr as *mut c_void,
);
assert!(!data_ptr.is_null(), "data_ptr should not be null");
assert_eq!(length, 5);
let actual = std::slice::from_raw_parts(data_ptr, length as usize);
assert_eq!(actual, &[1, 2, 3, 4, 5]);
}
}
#[test]
fn test_xor_transform() {
let mut buf = Vec::new();

@ -325,6 +325,13 @@ pub struct SCTransformTableElmt {
context: *mut ::std::os::raw::c_void,
) -> bool,
>,
pub TransformId: ::std::option::Option<
unsafe extern "C" fn(
id_data: *mut *const u8,
id_length: *mut u32,
context: *mut ::std::os::raw::c_void,
),
>,
}
extern "C" {
pub fn SCDetectHelperNewKeywordId() -> ::std::os::raw::c_int;

@ -163,6 +163,8 @@ int SCDetectHelperTransformRegister(const SCTransformTableElmt *kw)
sigmatch_table[transform_id].Setup =
(int (*)(DetectEngineCtx * de, Signature * s, const char *raw)) kw->Setup;
sigmatch_table[transform_id].Free = (void (*)(DetectEngineCtx * de, void *ptr)) kw->Free;
sigmatch_table[transform_id].TransformId =
(void (*)(const uint8_t **id_data, uint32_t *length, void *context))kw->TransformId;
return transform_id;
}

@ -71,6 +71,7 @@ typedef struct SCTransformTableElmt {
void (*Free)(DetectEngineCtx *, void *);
void (*Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context);
bool (*TransformValidate)(const uint8_t *content, uint16_t content_len, void *context);
void (*TransformId)(const uint8_t **id_data, uint32_t *id_length, void *context);
} SCTransformTableElmt;
int SCDetectHelperNewKeywordId(void);

@ -984,12 +984,40 @@ int DetectBufferTypeMaxId(void)
return g_buffer_type_id;
}
static void DetectBufferAddTransformData(DetectBufferType *map)
{
for (int i = 0; i < map->transforms.cnt; i++) {
const TransformData *t = &map->transforms.transforms[i];
if (sigmatch_table[t->transform].TransformId) {
sigmatch_table[t->transform].TransformId(
&map->xform_id[i].id_data, &map->xform_id[i].id_data_len, t->options);
SCLogDebug("transform identity data: [%p] \"%s\" [%d]", map->xform_id[i].id_data,
(char *)map->xform_id[i].id_data, map->xform_id[i].id_data_len);
}
}
}
static uint32_t DetectBufferTypeHashNameFunc(HashListTable *ht, void *data, uint16_t datalen)
{
const DetectBufferType *map = (DetectBufferType *)data;
uint32_t hash = hashlittle_safe(map->name, strlen(map->name), 0);
hash += hashlittle_safe((uint8_t *)&map->transforms, sizeof(map->transforms), 0);
// Add the transform data
// - Collect transform id and position
// - Collect identity data, if any
hash += hashlittle_safe((uint8_t *)&map->transforms.cnt, sizeof(map->transforms.cnt), 0);
for (int i = 0; i < map->transforms.cnt; i++) {
const TransformData *t = &map->transforms.transforms[i];
int tval = t->transform;
hash += hashlittle_safe((uint8_t *)&tval, sizeof(tval), 0);
if (map->xform_id[i].id_data) {
hash += hashlittle_safe(
&map->xform_id[i].id_data_len, sizeof(map->xform_id[i].id_data_len), 0);
hash += hashlittle_safe(map->xform_id[i].id_data, map->xform_id[i].id_data_len, 0);
}
}
hash %= ht->array_size;
SCLogDebug("map->name %s, hash %d", map->name, hash);
return hash;
}
@ -1007,7 +1035,49 @@ static char DetectBufferTypeCompareNameFunc(void *data1, uint16_t len1, void *da
DetectBufferType *map2 = (DetectBufferType *)data2;
char r = (strcmp(map1->name, map2->name) == 0);
r &= (memcmp((uint8_t *)&map1->transforms, (uint8_t *)&map2->transforms, sizeof(map2->transforms)) == 0);
// Compare the transforms
// the transform supports identity, that data will also be added.
r &= map1->transforms.cnt == map2->transforms.cnt;
if (r && map1->transforms.cnt) {
for (int i = 0; i < map1->transforms.cnt; i++) {
if (map1->transforms.transforms[i].transform !=
map2->transforms.transforms[i].transform) {
r = 0;
break;
}
SCLogDebug("%s: transform ids match; checking specialized data", map1->name);
// Checks
// - Both NULL: --> ok, continue
// - One NULL: --> no match, break?
// - identity data lengths match: --> ok, continue
// - identity data matches: ok
// Stop if only one is NULL
if ((map1->xform_id[i].id_data == NULL) ^ (map2->xform_id[i].id_data == NULL)) {
SCLogDebug("identity data: only one is null");
r = 0;
break;
} else if (map1->xform_id[i].id_data == NULL) { /* continue when both are null */
SCLogDebug("identity data: both null");
r = 1;
continue;
} else if (map1->xform_id[i].id_data_len != map2->xform_id[i].id_data_len) {
// Stop when id data lengths aren't equal
SCLogDebug("id data: unequal lengths");
r = 0;
break;
}
// stop if the identity data is different
r &= memcmp(map1->xform_id[i].id_data, map2->xform_id[i].id_data,
map1->xform_id[i].id_data_len) == 0;
if (r == 0)
break;
SCLogDebug("identity data: data matches");
}
}
return r;
}
@ -1030,6 +1100,7 @@ static void DetectBufferTypeFreeFunc(void *data)
for (int i = 0; i < map->transforms.cnt; i++) {
if (map->transforms.transforms[i].options == NULL)
continue;
if (sigmatch_table[map->transforms.transforms[i].transform].Free == NULL) {
SCLogError("%s allocates transform option memory but has no free routine",
sigmatch_table[map->transforms.transforms[i].transform].name);
@ -1561,6 +1632,11 @@ int DetectEngineBufferTypeGetByIdTransforms(
memset(&lookup_map, 0, sizeof(lookup_map));
strlcpy(lookup_map.name, base_map->name, sizeof(lookup_map.name));
lookup_map.transforms = t;
/* Add transform identity data from transforms */
if (t.cnt) {
DetectBufferAddTransformData(&lookup_map);
}
DetectBufferType *res = HashListTableLookup(de_ctx->buffer_type_hash_name, &lookup_map, 0);
SCLogDebug("res %p", res);

@ -37,33 +37,28 @@
#include "util-unittest.h"
#include "util-print.h"
static int DetectTransformFromBase64DecodeSetup(DetectEngineCtx *, Signature *, const char *);
static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *, void *);
#ifdef UNITTESTS
#define DETECT_TRANSFORM_FROM_BASE64_MODE_DEFAULT (uint8_t) SCBase64ModeRFC4648
static void DetectTransformFromBase64DecodeRegisterTests(void);
#endif
static void TransformFromBase64Decode(
DetectEngineThreadCtx *det_ctx, InspectionBuffer *buffer, void *options);
void DetectTransformFromBase64DecodeRegister(void)
static void DetectTransformFromBase64Id(const uint8_t **data, uint32_t *length, void *context)
{
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].name = "from_base64";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup;
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode;
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].RegisterTests =
DetectTransformFromBase64DecodeRegisterTests;
#endif
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].flags |= SIGMATCH_OPTIONAL_OPT;
if (context) {
SCDetectTransformFromBase64Data *b64d = (SCDetectTransformFromBase64Data *)context;
/* Since the context structure contains the unique values for the keyword usage,
* a pointer to the context structure is returned.
*/
*data = (const uint8_t *)b64d;
*length = sizeof(*b64d);
}
}
static void DetectTransformFromBase64DecodeFree(DetectEngineCtx *de_ctx, void *ptr)
{
SCTransformBase64Free(ptr);
if (ptr) {
SCTransformBase64Free(ptr);
}
}
static SCDetectTransformFromBase64Data *DetectTransformFromBase64DecodeParse(const char *str)
@ -155,6 +150,22 @@ static void TransformFromBase64Decode(
}
}
void DetectTransformFromBase64DecodeRegister(void)
{
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].name = "from_base64";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].desc = "convert the base64 decode of the buffer";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].url = "/rules/transforms.html#from_base64";
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Setup = DetectTransformFromBase64DecodeSetup;
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Transform = TransformFromBase64Decode;
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].TransformId = DetectTransformFromBase64Id;
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].Free = DetectTransformFromBase64DecodeFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].RegisterTests =
DetectTransformFromBase64DecodeRegisterTests;
#endif
sigmatch_table[DETECT_TRANSFORM_FROM_BASE64].flags |= SIGMATCH_OPTIONAL_OPT;
}
#ifdef UNITTESTS
/* Simple success case -- check buffer */
static int DetectTransformFromBase64DecodeTest01(void)

@ -34,6 +34,8 @@
typedef struct DetectTransformPcrexformData {
pcre2_code *regex;
pcre2_match_context *context;
uint8_t *id_data;
uint32_t id_data_len;
} DetectTransformPcrexformData;
static int DetectTransformPcrexformSetup (DetectEngineCtx *, Signature *, const char *);
@ -44,6 +46,15 @@ static void DetectTransformPcrexform(
void DetectTransformPcrexformRegisterTests (void);
#endif
static void DetectTransformPcrexformId(const uint8_t **data, uint32_t *length, void *context)
{
if (context) {
DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *)context;
*data = (const uint8_t *)pxd->id_data;
*length = pxd->id_data_len;
}
}
void DetectTransformPcrexformRegister(void)
{
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].name = "pcrexform";
@ -52,6 +63,7 @@ void DetectTransformPcrexformRegister(void)
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].url = "/rules/transforms.html#pcre-xform";
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Transform =
DetectTransformPcrexform;
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].TransformId = DetectTransformPcrexformId;
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Free =
DetectTransformPcrexformFree;
sigmatch_table[DETECT_TRANSFORM_PCREXFORM].Setup =
@ -66,8 +78,11 @@ static void DetectTransformPcrexformFree(DetectEngineCtx *de_ctx, void *ptr)
{
if (ptr != NULL) {
DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *) ptr;
pcre2_match_context_free(pxd->context);
pcre2_code_free(pxd->regex);
SCFree(pxd->id_data);
SCFree(pxd);
}
}
@ -112,6 +127,7 @@ static int DetectTransformPcrexformSetup (DetectEngineCtx *de_ctx, Signature *s,
SCFree(pxd);
SCReturnInt(-1);
}
// check pcd->regex has exactly one capture expression
uint32_t nb;
if (pcre2_pattern_info(pxd->regex, PCRE2_INFO_CAPTURECOUNT, &nb) < 0) {
@ -125,6 +141,13 @@ static int DetectTransformPcrexformSetup (DetectEngineCtx *de_ctx, Signature *s,
SCReturnInt(-1);
}
pxd->id_data = (uint8_t *)SCStrdup(regexstr);
if (pxd->id_data == NULL) {
DetectTransformPcrexformFree(de_ctx, pxd);
SCReturnInt(-1);
}
pxd->id_data_len = strlen(regexstr);
int r = SCDetectSignatureAddTransform(s, DETECT_TRANSFORM_PCREXFORM, pxd);
if (r != 0) {
DetectTransformPcrexformFree(de_ctx, pxd);

@ -438,6 +438,11 @@ typedef struct DetectEngineAppInspectionEngine_ {
struct DetectEngineAppInspectionEngine_ *next;
} DetectEngineAppInspectionEngine;
typedef struct TransformIdData_ {
const uint8_t *id_data;
uint32_t id_data_len;
} TransformIdData;
typedef struct DetectBufferType_ {
char name[64];
char description[128];
@ -452,6 +457,7 @@ typedef struct DetectBufferType_ {
bool (*ValidateCallback)(
const struct Signature_ *, const char **sigerror, const struct DetectBufferType_ *);
DetectEngineTransforms transforms;
TransformIdData xform_id[DETECT_TRANSFORMS_MAX];
} DetectBufferType;
struct DetectEnginePktInspectionEngine;
@ -1386,6 +1392,9 @@ typedef struct SigTableElmt_ {
void (*Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context);
bool (*TransformValidate)(const uint8_t *content, uint16_t content_len, void *context);
/** Transform identity callback */
void (*TransformId)(const uint8_t **data, uint32_t *length, void *context);
/** keyword setup function pointer */
int (*Setup)(DetectEngineCtx *, Signature *, const char *);

Loading…
Cancel
Save