From 83976a4cd4ed0583c2c9f49a75e71d894ce65888 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Mon, 13 May 2024 14:37:51 +0200 Subject: [PATCH] detect/iprep: implement isset and isnotset 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 | 68 ++++++++++---- src/detect-iprep.c | 195 +++++++++++++++++++++++++++++++++++---- 2 files changed, 224 insertions(+), 39 deletions(-) diff --git a/rust/src/detect/iprep.rs b/rust/src/detect/iprep.rs index 09df0b2bcd40..6604628edd13 100644 --- a/rust/src/detect/iprep.rs +++ b/rust/src/detect/iprep.rs @@ -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, 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::() { - Ok(val) => val, - Err(_) => return Err(make_error("invalid value".to_string())), - }; - let du8 = DetectUintData:: { - 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::() { + Ok(val) => val, + Err(_) => return Err(make_error("invalid value".to_string())), + }; + let du8 = DetectUintData:: { + 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:: { + 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())); } diff --git a/src/detect-iprep.c b/src/detect-iprep.c index e5069b7072f0..93cf5b8b0e89 100644 --- a/src/detect-iprep.c +++ b/src/detect-iprep.c @@ -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 */