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;
} else {
return Err(make_error(format!(
"invalid offset value: must be between 0 and {}: {}",
- u16::MAX, val
+ u16::MAX,
+ val
)));
}
}
} else {
return Err(make_error(format!(
"invalid bytes value: must be between {} and {}: {}",
- 0, u16::MAX, val
+ 0,
+ u16::MAX,
+ val
)));
}
}
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)),
}
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,
Transform: Some(tolower_transform),
Free: None,
TransformValidate: Some(tolower_validate),
+ TransformId: None,
};
G_TRANSFORM_TOLOWER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_TOLOWER_ID < 0 {
Transform: Some(toupper_transform),
Free: None,
TransformValidate: Some(toupper_validate),
+ TransformId: None,
};
G_TRANSFORM_TOUPPER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_TOUPPER_ID < 0 {
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 {
Transform: Some(domain_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
unsafe {
G_TRANSFORM_DOMAIN_ID = SCDetectHelperTransformRegister(&kw);
Transform: Some(tld_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
unsafe {
G_TRANSFORM_TLD_ID = SCDetectHelperTransformRegister(&kw);
Transform: Some(dot_prefix_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
unsafe {
G_TRANSFORM_DOT_PREFIX_ID = SCDetectHelperTransformRegister(&kw);
Transform: Some(md5_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
G_TRANSFORM_MD5_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_MD5_ID < 0 {
Transform: Some(sha1_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
G_TRANSFORM_SHA1_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_SHA1_ID < 0 {
Transform: Some(sha256_transform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
G_TRANSFORM_SHA256_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_SHA256_ID < 0 {
Transform: Some(header_lowertransform),
Free: None,
TransformValidate: None,
+ TransformId: None,
};
G_TRANSFORM_HEADER_LOWER_ID = SCDetectHelperTransformRegister(&kw);
if G_TRANSFORM_HEADER_LOWER_ID < 0 {
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 {
Transform: Some(strip_whitespace_transform),
Free: None,
TransformValidate: Some(strip_whitespace_validate),
+ TransformId: None,
};
unsafe {
G_TRANSFORM_STRIP_WHITESPACE_ID = SCDetectHelperTransformRegister(&kw);
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 {
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 {
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");
}
}
}
);
}
+ #[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();
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;
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;
}
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);
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;
}
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;
}
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);
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);
#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)
}
}
+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)
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 *);
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";
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 =
{
if (ptr != NULL) {
DetectTransformPcrexformData *pxd = (DetectTransformPcrexformData *) ptr;
+
pcre2_match_context_free(pxd->context);
pcre2_code_free(pxd->regex);
+
+ SCFree(pxd->id_data);
SCFree(pxd);
}
}
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) {
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);
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];
bool (*ValidateCallback)(
const struct Signature_ *, const char **sigerror, const struct DetectBufferType_ *);
DetectEngineTransforms transforms;
+ TransformIdData xform_id[DETECT_TRANSFORMS_MAX];
} DetectBufferType;
struct DetectEnginePktInspectionEngine;
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 *);