]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/xform: Support transform identity data
authorJeff Lucovsky <jlucovsky@oisf.net>
Sat, 5 Apr 2025 20:08:39 +0000 (16:08 -0400)
committerVictor Julien <victor@inliniac.net>
Wed, 14 May 2025 19:36:26 +0000 (21:36 +0200)
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.

17 files changed:
rust/src/detect/transform_base64.rs
rust/src/detect/transforms/casechange.rs
rust/src/detect/transforms/compress_whitespace.rs
rust/src/detect/transforms/domain.rs
rust/src/detect/transforms/dotprefix.rs
rust/src/detect/transforms/hash.rs
rust/src/detect/transforms/http_headers.rs
rust/src/detect/transforms/strip_whitespace.rs
rust/src/detect/transforms/urldecode.rs
rust/src/detect/transforms/xor.rs
rust/sys/src/sys.rs
src/detect-engine-helper.c
src/detect-engine-helper.h
src/detect-engine.c
src/detect-transform-base64.c
src/detect-transform-pcrexform.c
src/detect.h

index a85622b2e524212e5861d1b65e524bfe50a398d1..7d867ff23e29b5942512a31889be5a692e52f4ea 100644 (file)
@@ -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,
index cea9d9248e204b0c994086ad1836f8cea5989325..6cdf9c7da4abf97c30a09727dcaae2a39dd469b5 100644 (file)
@@ -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 {
index 7d338256140f05691d5c9fb3921eabf4c210d5ee..704b285e71ef78d7f0b62eacb95e028d5a95ba3d 100644 (file)
@@ -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 {
index 709c9d3ee6c3ffb2c28a74809c8c9149d8061f66..49df1aa5a329a7432687d0bc600fee0d765fd538 100644 (file)
@@ -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);
index ac2693cbfe3436d81709c1c5727845c0bb536f38..76e745e5b53a829548a7ac3bb5f4e529643a5d44 100644 (file)
@@ -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);
index 7a4a27f9747d071676df196ae9503c9513825339..8452247d95cd9745cc6e2e2ba36e2155df6374aa 100644 (file)
@@ -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 {
index 7bd9495038ed8434e51a8e93a8ad3bac9744a3d6..b56470cba27eb48428ff61b4b1288ca33699979e 100644 (file)
@@ -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 {
index dbc77fb5319dab948e9193574d2b730e2ffcef87..25c6fe41a6d31f3699e413b779122b2520b945fb 100644 (file)
@@ -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);
index a7317db6b5b1ec01958cb352cb69f21f59c26731..fa4c403fc392832f0c34fe00e6d5571185b19bc2 100644 (file)
@@ -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 {
index e4f46f2f09e87bb28cc328e337da8a2fff1af36f..a416bd9de8500a86d22c215bf01a4777372a8faf 100644 (file)
@@ -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();
index b7441777031ba406bffe40a29dc9b9c4f31536a4..b4b30d3c0d18d2d1a2c9643d96e69dfd2336b9a7 100644 (file)
@@ -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;
index 87af81742199fa63cfc0729b27dc2d66ab074c79..7c6221ca4515a9b9ca614d5cbc543e64c0c3fa37 100644 (file)
@@ -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;
 }
index 284be4571378e980792d28cf67105c1ac9d777f0..170cda374e9ffb32da2f9dbbd800fd8f49e4ab2e 100644 (file)
@@ -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);
index f0ff76fc1f2f07cf8ba5e87ba4820d011a307da9..5117c382033ed6759c591ac771c799a37a72b738 100644 (file)
@@ -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);
index a488a7fce666678c221e0473885b1e9af05b8ecf..6e3b58c800d7a47b4a7626e96a7ac07f5c9e3114 100644 (file)
 #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)
index 24b02bfb4af40f0ca3ded561b501aeea63b9b166..b901c9987532bae28404b0149393a8b29404edbf 100644 (file)
@@ -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);
index bbe9273e757768821c9c2b4b5b8513ce2ca10efe..2d4ecb5d382d191e0ff87bd03f7e34c5c0e71b34 100644 (file)
@@ -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 *);