]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/iprep: implement isset and isnotset
authorVictor Julien <vjulien@oisf.net>
Mon, 13 May 2024 12:37:51 +0000 (14:37 +0200)
committerVictor Julien <victor@inliniac.net>
Sat, 15 Jun 2024 13:43:28 +0000 (15:43 +0200)
Implement special "isset" and "isnotset" modes.

"isset" matches if an IP address is part of an iprep category with any
value.

It is internally implemented as ">=,0", which should always be true if
there is a value to evaluate, as valid reputation values are 0-127.

"isnotset" matches if an IP address is not part of an iprep category.

Internally it is implemented outside the uint support.

Ticket: #6857.

rust/src/detect/iprep.rs
src/detect-iprep.c

index 09df0b2bcd4004a04e5381512ad7c370f77bac0e..6604628edd13404f471eeaca09016e5068ce5aa9 100644 (file)
@@ -54,12 +54,17 @@ impl std::str::FromStr for DetectIPRepDataCmd {
     }
 }
 
+/// value matching is done use `DetectUintData` logic.
+/// isset matching is done using special `DetectUintData` value ">= 0"
+/// isnotset matching bypasses `DetectUintData` and is handled directly
+/// in the match function (in C).
 #[derive(Debug)]
 #[repr(C)]
 pub struct DetectIPRepData {
     pub du8: DetectUintData<u8>,
     pub cat: u8,
     pub cmd: DetectIPRepDataCmd,
+    pub isnotset: bool, // if true, ignores `du8`
 }
 
 pub fn is_alphanumeric_or_slash(chr: char) -> bool {
@@ -86,33 +91,56 @@ pub fn detect_parse_iprep(i: &str) -> IResult<&str, DetectIPRepData, RuleParseEr
         preceded(multispace0, nom7::bytes::complete::is_not(",")),
     )(i)?;
 
-    if values.len() == 4 {
-        let cmd = match DetectIPRepDataCmd::from_str(values[0].trim()) {
-            Ok(val) => val,
-            Err(_) => return Err(make_error("invalid command".to_string())),
+    let args = values.len();
+    if args == 4 || args == 3 {
+        let cmd = if let Ok(cmd) = DetectIPRepDataCmd::from_str(values[0].trim()) {
+            cmd
+        } else {
+            return Err(make_error("invalid command".to_string()));
         };
         let name = values[1].trim();
-        let mode = match detect_parse_uint_mode(values[2].trim()) {
-            Ok(val) => val.1,
-            Err(_) => return Err(make_error("invalid mode".to_string())),
+        let namez = if let Ok(name) = CString::new(name) {
+            name
+        } else {
+            return Err(make_error("invalid name".to_string()));
         };
-
-        let namez = CString::new(name).unwrap();
         let cat = unsafe { SRepCatGetByShortname(namez.as_ptr()) };
         if cat == 0 {
             return Err(make_error("unknown category".to_string()));
         }
-        let arg1 = match values[3].trim().parse::<u8>() {
-            Ok(val) => val,
-            Err(_) => return Err(make_error("invalid value".to_string())),
-        };
-        let du8 = DetectUintData::<u8> {
-            arg1,
-            arg2: 0,
-            mode,
-        };
-        return Ok((i, DetectIPRepData { du8, cat, cmd }));
-    } else {
+
+        if args == 4 {
+            let mode = match detect_parse_uint_mode(values[2].trim()) {
+                Ok(val) => val.1,
+                Err(_) => return Err(make_error("invalid mode".to_string())),
+            };
+
+            let arg1 = match values[3].trim().parse::<u8>() {
+                Ok(val) => val,
+                Err(_) => return Err(make_error("invalid value".to_string())),
+            };
+            let du8 = DetectUintData::<u8> {
+                arg1,
+                arg2: 0,
+                mode,
+            };
+            return Ok((i, DetectIPRepData { du8, cat, cmd, isnotset: false, }));
+        } else {
+            let (isnotset, mode, arg1) = match values[2].trim() {
+                "isset" => { (false, DetectUintMode::DetectUintModeGte, 0) },
+                "isnotset" => { (true, DetectUintMode::DetectUintModeEqual, 0) },
+                _ => { return Err(make_error("invalid mode".to_string())); },
+            };
+            let du8 = DetectUintData::<u8> {
+                arg1,
+                arg2: 0,
+                mode,
+            };
+            return Ok((i, DetectIPRepData { du8, cat, cmd, isnotset, }));
+        }
+    } else if args < 3 {
+        return Err(make_error("too few arguments".to_string()));
+    } else  {
         return Err(make_error("too many arguments".to_string()));
     }
 
index e5069b7072f09b374523c2d6ae99cb5538bf4a62..93cf5b8b0e8957aa7bc7990b99ea84be35dff388 100644 (file)
@@ -158,18 +158,37 @@ static int DetectIPRepMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
     SCLogDebug("rd->cmd %u", rd->cmd);
     switch (rd->cmd) {
         case IPRepCmdAny:
-            val = GetHostRepSrc(p, rd->cat, version);
-            if (val < 0)
-                val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
-            if (val >= 0) {
-                if (DetectU8Match((uint8_t)val, &rd->du8))
+            if (!rd->isnotset) {
+                val = GetHostRepSrc(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val >= 0) {
+                    if (DetectU8Match((uint8_t)val, &rd->du8))
+                        return 1;
+                }
+                val = GetHostRepDst(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val >= 0) {
+                    return DetectU8Match((uint8_t)val, &rd->du8);
+                }
+            } else {
+                /* isnotset for any */
+
+                val = GetHostRepSrc(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val < 0) {
                     return 1;
-            }
-            val = GetHostRepDst(p, rd->cat, version);
-            if (val < 0)
-                val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
-            if (val >= 0) {
-                return DetectU8Match((uint8_t)val, &rd->du8);
+                }
+                val = GetHostRepDst(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val < 0) {
+                    return 1;
+                }
+                /* both have a value, so none 'isnotset' */
+                return 0;
             }
             break;
 
@@ -182,6 +201,10 @@ static int DetectIPRepMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
             if (val >= 0) {
                 return DetectU8Match((uint8_t)val, &rd->du8);
             }
+            /* implied: no value found */
+            if (rd->isnotset) {
+                return 1;
+            }
             break;
 
         case IPRepCmdDst:
@@ -192,19 +215,39 @@ static int DetectIPRepMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
             if (val >= 0) {
                 return DetectU8Match((uint8_t)val, &rd->du8);
             }
+            /* implied: no value found */
+            if (rd->isnotset) {
+                return 1;
+            }
             break;
 
         case IPRepCmdBoth:
-            val = GetHostRepSrc(p, rd->cat, version);
-            if (val < 0)
+            if (!rd->isnotset) {
+                val = GetHostRepSrc(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val < 0 || DetectU8Match((uint8_t)val, &rd->du8) == 0)
+                    return 0;
+                val = GetHostRepDst(p, rd->cat, version);
+                if (val < 0)
+                    val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
+                if (val >= 0) {
+                    return DetectU8Match((uint8_t)val, &rd->du8);
+                }
+            } else {
+                val = GetHostRepSrc(p, rd->cat, version);
+                if (val >= 0)
+                    return 0;
                 val = SRepCIDRGetIPRepSrc(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
-            if (val < 0 || DetectU8Match((uint8_t)val, &rd->du8) == 0)
-                return 0;
-            val = GetHostRepDst(p, rd->cat, version);
-            if (val < 0)
+                if (val >= 0)
+                    return 0;
+                val = GetHostRepDst(p, rd->cat, version);
+                if (val >= 0)
+                    return 0;
                 val = SRepCIDRGetIPRepDst(det_ctx->de_ctx->srepCIDR_ctx, p, rd->cat, version);
-            if (val >= 0) {
-                return DetectU8Match((uint8_t)val, &rd->du8);
+                if (val >= 0)
+                    return 0;
+                return 1;
             }
             break;
     }
@@ -775,6 +818,118 @@ static int DetectIPRepTest09(void)
     PASS;
 }
 
+static FILE *DetectIPRepGenerateNetworksDummy3(void)
+{
+    FILE *fd = NULL;
+    const char *buffer = "192.168.0.0/16,1,127"; // BadHosts
+
+    fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
+    if (fd == NULL)
+        SCLogDebug("Error with SCFmemopen()");
+
+    return fd;
+}
+
+static int DetectIPRepTest10(void)
+{
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Signature *sig = NULL;
+    FILE *fd = NULL;
+    int r = 0;
+    Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+    HostInitConfig(HOST_QUIET);
+    memset(&th_v, 0, sizeof(th_v));
+
+    FAIL_IF_NULL(de_ctx);
+    FAIL_IF_NULL(p);
+
+    p->src.addr_data32[0] = UTHSetIPv4Address("192.168.0.1");
+    p->dst.addr_data32[0] = UTHSetIPv4Address("192.168.0.2");
+    de_ctx->flags |= DE_QUIET;
+
+    SRepInit(de_ctx);
+    SRepResetVersion();
+
+    fd = DetectIPRepGenerateCategoriesDummy2();
+    r = SRepLoadCatFileFromFD(fd);
+    FAIL_IF(r < 0);
+
+    fd = DetectIPRepGenerateNetworksDummy3();
+    r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+    FAIL_IF(r < 0);
+
+    sig = DetectEngineAppendSig(de_ctx,
+            "alert tcp any any -> any any (msg:\"test\"; iprep:src,BadHosts,isset; sid:1; rev:1;)");
+    FAIL_IF_NULL(sig);
+
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+    FAIL_IF_NOT(p->alerts.cnt == 1);
+
+    UTHFreePacket(p);
+
+    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+    DetectEngineCtxFree(de_ctx);
+
+    HostShutdown();
+    PASS;
+}
+
+static int DetectIPRepTest11(void)
+{
+    ThreadVars th_v;
+    DetectEngineThreadCtx *det_ctx = NULL;
+    Signature *sig = NULL;
+    FILE *fd = NULL;
+    int r = 0;
+    Packet *p = UTHBuildPacket((uint8_t *)"lalala", 6, IPPROTO_TCP);
+    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+
+    HostInitConfig(HOST_QUIET);
+    memset(&th_v, 0, sizeof(th_v));
+
+    FAIL_IF_NULL(de_ctx);
+    FAIL_IF_NULL(p);
+
+    p->src.addr_data32[0] = UTHSetIPv4Address("10.0.0.1");
+    p->dst.addr_data32[0] = UTHSetIPv4Address("10.0.0.2");
+    de_ctx->flags |= DE_QUIET;
+
+    SRepInit(de_ctx);
+    SRepResetVersion();
+
+    fd = DetectIPRepGenerateCategoriesDummy2();
+    r = SRepLoadCatFileFromFD(fd);
+    FAIL_IF(r < 0);
+
+    fd = DetectIPRepGenerateNetworksDummy3();
+    r = SRepLoadFileFromFD(de_ctx->srepCIDR_ctx, fd);
+    FAIL_IF(r < 0);
+
+    sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (msg:\"test\"; "
+                                        "iprep:src,BadHosts,isnotset; sid:1; rev:1;)");
+    FAIL_IF_NULL(sig);
+
+    SigGroupBuild(de_ctx);
+    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+    FAIL_IF_NOT(p->alerts.cnt == 1);
+
+    UTHFreePacket(p);
+
+    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+    DetectEngineCtxFree(de_ctx);
+
+    HostShutdown();
+    PASS;
+}
+
 /**
  * \brief this function registers unit tests for IPRep
  */
@@ -789,5 +944,7 @@ void IPRepRegisterTests(void)
     UtRegisterTest("DetectIPRepTest07", DetectIPRepTest07);
     UtRegisterTest("DetectIPRepTest08", DetectIPRepTest08);
     UtRegisterTest("DetectIPRepTest09", DetectIPRepTest09);
+    UtRegisterTest("DetectIPRepTest10 -- isset", DetectIPRepTest10);
+    UtRegisterTest("DetectIPRepTest11 -- isnotset", DetectIPRepTest11);
 }
 #endif /* UNITTESTS */