diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index eb49811f690a..3ba6bbe06a9a 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -318,90 +318,68 @@ Signature example:: In this example, we combine `flow.age` and `flowbits` to get an alert on the first packet after the flow's age is older than one hour. -flow.pkts_toclient ------------------- +flow.pkts +--------- -Flow number of packets to client (integer) +Flow number of packets (integer) This keyword does not wait for the end of the flow, but will be checked at each packet. -flow.pkts_toclient uses an :ref:`unsigned 32-bit integer `. +flow.pkts uses an :ref:`unsigned 32-bit integer ` and supports +following directions: -Syntax:: - - flow.pkts_toclient: [op] - -The number of packets can be matched exactly, or compared using the _op_ setting:: - - flow.pkts_toclient:3 # exactly 3 - flow.pkts_toclient:<3 # smaller than 3 - flow.pkts_toclient:>=2 # greater than or equal to 2 - -Signature example:: - - alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toclient:20; sid:1;) - -flow.pkts_toserver ------------------- +* toclient -Flow number of packets to server (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. +* toserver -flow.pkts_toserver uses an :ref:`unsigned 32-bit integer `. +* either Syntax:: - flow.pkts_toserver: [op] + flow.pkts:,[op] The number of packets can be matched exactly, or compared using the _op_ setting:: - flow.pkts_toserver:3 # exactly 3 - flow.pkts_toserver:<3 # smaller than 3 - flow.pkts_toserver:>=2 # greater than or equal to 2 + flow.pkts:toclient,3 # exactly 3 + flow.pkts:toserver,<3 # smaller than 3 + flow.pkts:either,>=2 # greater than or equal to 2 Signature example:: - alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toserver:20; sid:1;) - -flow.bytes_toclient -------------------- + alert ip any any -> any any (msg:"Flow has 20 packets in toclient dir"; flow.pkts:toclient,20; sid:1;) -Flow number of bytes to client (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. +.. note:: Suricata also supports ``flow.pkts_toclient`` and ``flow.pkts_toserver`` + keywords for ``flow.pkts:toclient`` and ``flow.pkts:toserver`` respectively but + that is not the preferred syntax. -flow.bytes_toclient uses an :ref:`unsigned 64-bit integer `. +flow.bytes +---------- -Syntax:: - - flow.bytes_toclient: [op] - -The number of packets can be matched exactly, or compared using the _op_ setting:: - - flow.bytes_toclient:3 # exactly 3 - flow.bytes_toclient:<3 # smaller than 3 - flow.bytes_toclient:>=2 # greater than or equal to 2 - -Signature example:: +Flow number of bytes (integer) +This keyword does not wait for the end of the flow, but will be checked at each packet. - alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toclient:<2000; sid:1;) +flow.bytes uses an :ref:`unsigned 64-bit integer ` and supports +following directions: -flow.bytes_toserver -------------------- +* toclient -Flow number of bytes to server (integer) -This keyword does not wait for the end of the flow, but will be checked at each packet. +* toserver -flow.bytes_toserver uses an :ref:`unsigned 64-bit integer `. +* either Syntax:: - flow.bytes_toserver: [op] + flow.bytes:,[op] -The number of packets can be matched exactly, or compared using the _op_ setting:: +The number of bytes can be matched exactly, or compared using the _op_ setting:: - flow.bytes_toserver:3 # exactly 3 - flow.bytes_toserver:<3 # smaller than 3 - flow.bytes_toserver:>=2 # greater than or equal to 2 + flow.bytes:toclient,3 # exactly 3 + flow.bytes:toserver,<3 # smaller than 3 + flow.bytes:either,>=2 # greater than or equal to 2 Signature example:: - alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toserver:<2000; sid:1;) + alert ip any any -> any any (msg:"Flow has less than 2000 bytes in toserver dir"; flow.bytes:toserver,<2000; sid:1;) + +.. note:: Suricata also supports ``flow.bytes_toclient`` and ``flow.bytes_toserver`` + keywords for ``flow.bytes:toclient`` and ``flow.bytes:toserver`` respectively but + that is not the preferred syntax. diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 9bddf0fd8437..82153807ad58 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -581,10 +581,12 @@ void SigTableSetup(void) DetectReplaceRegister(); DetectFlowRegister(); DetectFlowAgeRegister(); - DetectFlowPktsToClientRegister(); + DetectFlowPktsRegister(); DetectFlowPktsToServerRegister(); - DetectFlowBytesToClientRegister(); + DetectFlowPktsToClientRegister(); + DetectFlowBytesRegister(); DetectFlowBytesToServerRegister(); + DetectFlowBytesToClientRegister(); DetectRequiresRegister(); DetectWindowRegister(); DetectRpcRegister(); diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index b7a029998555..f46bf688f0f8 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -127,10 +127,12 @@ enum DetectKeywordId { DETECT_FRAME, DETECT_FLOW_AGE, - DETECT_FLOW_PKTS_TO_CLIENT, + DETECT_FLOW_PKTS, DETECT_FLOW_PKTS_TO_SERVER, - DETECT_FLOW_BYTES_TO_CLIENT, + DETECT_FLOW_PKTS_TO_CLIENT, + DETECT_FLOW_BYTES, DETECT_FLOW_BYTES_TO_SERVER, + DETECT_FLOW_BYTES_TO_CLIENT, DETECT_REQUIRES, diff --git a/src/detect-flow-pkts.c b/src/detect-flow-pkts.c index ef8fed369cd9..399eca9d2e27 100644 --- a/src/detect-flow-pkts.c +++ b/src/detect-flow-pkts.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* Copyright (C) 2023-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -23,32 +23,70 @@ #include "detect-engine-uint.h" #include "detect-parse.h" -static int DetectFlowPktsToClientMatch( +enum FlowDirection { + DETECT_FLOW_TOSERVER = 1, + DETECT_FLOW_TOCLIENT, + DETECT_FLOW_TOEITHER, +}; + +typedef struct DetectFlowPkts_ { + DetectU32Data *pkt_data; + enum FlowDirection dir; +} DetectFlowPkts; + +typedef struct DetectFlowBytes_ { + DetectU64Data *byte_data; + enum FlowDirection dir; +} DetectFlowBytes; + +static int DetectFlowPktsMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { if (p->flow == NULL) { return 0; } - uint32_t nb = p->flow->tosrcpktcnt; - const DetectU32Data *du32 = (const DetectU32Data *)ctx; - return DetectU32Match(nb, du32); + const DetectFlowPkts *df = (const DetectFlowPkts *)ctx; + if (df->dir == DETECT_FLOW_TOSERVER) { + return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TOCLIENT) { + return DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TOEITHER) { + if (DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data)) { + return 1; + } + return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); + } + return 0; } -static void DetectFlowPktsToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowPktsFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u32_free(ptr); + DetectFlowPkts *df = (DetectFlowPkts *)ptr; + if (df != NULL) { + rs_detect_u32_free(df->pkt_data); + SCFree(df); + } } -static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowPktsToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { DetectU32Data *du32 = DetectU32Parse(rawstr); if (du32 == NULL) return -1; - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_PKTS_TO_CLIENT, (SigMatchCtx *)du32, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowPktsToClientFree(de_ctx, du32); + DetectFlowPkts *df = SCCalloc(1, sizeof(DetectFlowPkts)); + if (df == NULL) { + rs_detect_u32_free(du32); + return -1; + } + + df->pkt_data = du32; + df->dir = DETECT_FLOW_TOSERVER; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsFree(de_ctx, df); return -1; } s->flags |= SIG_FLAG_REQUIRE_PACKET; @@ -56,147 +94,235 @@ static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, co return 0; } -static void PrefilterPacketFlowPktsToClientMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - const PrefilterPacketHeaderCtx *ctx = pectx; - if (!PrefilterPacketHeaderExtraMatch(ctx, p)) - return; + DetectU32Data *du32 = DetectU32Parse(rawstr); + if (du32 == NULL) + return -1; - DetectU32Data du32; - du32.mode = ctx->v1.u8[0]; - du32.arg1 = ctx->v1.u32[1]; - du32.arg2 = ctx->v1.u32[2]; - if (DetectFlowPktsToClientMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { - PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + DetectFlowPkts *df = SCCalloc(1, sizeof(DetectFlowPkts)); + if (df == NULL) { + rs_detect_u32_free(du32); + return -1; } -} + df->pkt_data = du32; + df->dir = DETECT_FLOW_TOCLIENT; -static int PrefilterSetupFlowPktsToClient(DetectEngineCtx *de_ctx, SigGroupHead *sgh) -{ - return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_TO_CLIENT, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToClientMatch); -} + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsFree(de_ctx, df); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; -static bool PrefilterFlowPktsToClientIsPrefilterable(const Signature *s) -{ - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_CLIENT); + return 0; } -void DetectFlowPktsToClientRegister(void) +static int DetectFlowPktsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].name = "flow.pkts_toclient"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].desc = "match flow number of packets to client"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].url = "/rules/flow-keywords.html#flow-pkts_toclient"; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Match = DetectFlowPktsToClientMatch; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Setup = DetectFlowPktsToClientSetup; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Free = DetectFlowPktsToClientFree; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SupportsPrefilter = - PrefilterFlowPktsToClientIsPrefilterable; - sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SetupPrefilter = PrefilterSetupFlowPktsToClient; -} + DetectFlowPkts *df = NULL; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + uint8_t num_tokens = 0; + uint8_t dir = 0; + char *pkt_data = NULL; + + while (token != NULL) { + if (num_tokens > 1) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (dir == 0 && num_tokens == 1) { + if (strcmp(token, "toserver") == 0) { + dir = DETECT_FLOW_TOSERVER; + } else if (strcmp(token, "toclient") == 0) { + dir = DETECT_FLOW_TOCLIENT; + } else if (strcmp(token, "either") == 0) { + dir = DETECT_FLOW_TOEITHER; + } else { + SCLogError("Invalid direction given: %s", token); + return -1; + } + } + + if (dir && num_tokens == 2) { + pkt_data = token; + } + + next: + token = strtok_r(NULL, ",", &context); + } -static int DetectFlowPktsToServerMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) -{ - if (p->flow == NULL) { - return 0; + DetectU32Data *du32 = DetectU32Parse(pkt_data); + if (du32 == NULL) + goto error; + df = SCCalloc(1, sizeof(DetectFlowPkts)); + if (df == NULL) { + rs_detect_u32_free(du32); + return -1; } - uint32_t nb = p->flow->todstpktcnt; + df->dir = dir; + df->pkt_data = du32; + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + goto error; + } + + s->flags |= SIG_FLAG_REQUIRE_PACKET; - const DetectU32Data *du32 = (const DetectU32Data *)ctx; - return DetectU32Match(nb, du32); + return 0; +error: + if (df != NULL) + DetectFlowPktsFree(de_ctx, df); + return -1; } -static void DetectFlowPktsToServerFree(DetectEngineCtx *de_ctx, void *ptr) +static void PrefilterPacketFlowPktsSet(PrefilterPacketHeaderValue *v, void *smctx) { - rs_detect_u32_free(ptr); + const DetectFlowPkts *df = smctx; + const DetectUintData_u32 *data = df->pkt_data; + v->u8[0] = data->mode; + v->u8[1] = (uint8_t)df->dir; + v->u32[1] = data->arg1; + v->u32[2] = data->arg2; } -static int DetectFlowPktsToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static bool PrefilterPacketFlowPktsCompare(PrefilterPacketHeaderValue v, void *smctx) { - DetectU32Data *du32 = DetectU32Parse(rawstr); - if (du32 == NULL) - return -1; - - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_PKTS_TO_SERVER, (SigMatchCtx *)du32, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowPktsToServerFree(de_ctx, du32); - return -1; + const DetectFlowPkts *df = smctx; + if (v.u8[0] == df->pkt_data->mode && v.u8[1] == df->dir && v.u32[1] == df->pkt_data->arg1 && + v.u32[2] == df->pkt_data->arg2) { + return true; } - s->flags |= SIG_FLAG_REQUIRE_PACKET; - - return 0; + return false; } -static void PrefilterPacketFlowPktsToServerMatch( +static void PrefilterPacketFlowPktsMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) { const PrefilterPacketHeaderCtx *ctx = pectx; if (!PrefilterPacketHeaderExtraMatch(ctx, p)) return; - DetectU32Data du32; - du32.mode = ctx->v1.u8[0]; - du32.arg1 = ctx->v1.u32[1]; - du32.arg2 = ctx->v1.u32[2]; - if (DetectFlowPktsToServerMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { + DetectFlowPkts df; + DetectUintData_u32 data = { + .mode = ctx->v1.u8[0], .arg1 = ctx->v1.u32[1], .arg2 = ctx->v1.u32[2] + }; + df.pkt_data = &data; + df.dir = ctx->v1.u8[1]; + + if (DetectFlowPktsMatch(det_ctx, p, NULL, (const SigMatchCtx *)&df)) { PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); } } -static int PrefilterSetupFlowPktsToServer(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +static int PrefilterSetupFlowPkts(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS, SIG_MASK_REQUIRE_FLOW, + PrefilterPacketFlowPktsSet, PrefilterPacketFlowPktsCompare, + PrefilterPacketFlowPktsMatch); +} + +static bool PrefilterFlowPktsIsPrefilterable(const Signature *s) { - return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_TO_SERVER, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToServerMatch); + return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS); } -static bool PrefilterFlowPktsToServerIsPrefilterable(const Signature *s) +void DetectFlowPktsRegister(void) { - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_SERVER); + sigmatch_table[DETECT_FLOW_PKTS].name = "flow.pkts"; + sigmatch_table[DETECT_FLOW_PKTS].desc = "match number of packets in a flow"; + sigmatch_table[DETECT_FLOW_PKTS].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS].Match = DetectFlowPktsMatch; + sigmatch_table[DETECT_FLOW_PKTS].Setup = DetectFlowPktsSetup; + sigmatch_table[DETECT_FLOW_PKTS].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS].SetupPrefilter = PrefilterSetupFlowPkts; } void DetectFlowPktsToServerRegister(void) { sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].name = "flow.pkts_toserver"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].desc = "match flow number of packets to server"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].url = "/rules/flow-keywords.html#flow-pkts_toserver"; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Match = DetectFlowPktsToServerMatch; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].desc = + "match number of packets in a flow in to server direction"; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Match = DetectFlowPktsMatch; sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Setup = DetectFlowPktsToServerSetup; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Free = DetectFlowPktsToServerFree; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SupportsPrefilter = - PrefilterFlowPktsToServerIsPrefilterable; - sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SetupPrefilter = PrefilterSetupFlowPktsToServer; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS_TO_SERVER].SetupPrefilter = PrefilterSetupFlowPkts; +} + +void DetectFlowPktsToClientRegister(void) +{ + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].name = "flow.pkts_toclient"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].desc = + "match number of packets in a flow in to client direction"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].url = "/rules/flow-keywords.html#flow-pkts"; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Match = DetectFlowPktsMatch; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Setup = DetectFlowPktsToClientSetup; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].Free = DetectFlowPktsFree; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SupportsPrefilter = PrefilterFlowPktsIsPrefilterable; + sigmatch_table[DETECT_FLOW_PKTS_TO_CLIENT].SetupPrefilter = PrefilterSetupFlowPkts; } -static int DetectFlowBytesToClientMatch( +static int DetectFlowBytesMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { if (p->flow == NULL) { return 0; } - uint64_t nb = p->flow->tosrcbytecnt; - const DetectU64Data *du64 = (const DetectU64Data *)ctx; - return DetectU64Match(nb, du64); + const DetectFlowBytes *df = (const DetectFlowBytes *)ctx; + if (df->dir == DETECT_FLOW_TOSERVER) { + return DetectU64Match(p->flow->todstbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TOCLIENT) { + return DetectU64Match(p->flow->tosrcbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TOEITHER) { + if (DetectU64Match(p->flow->tosrcbytecnt, df->byte_data)) { + return 1; + } + return DetectU64Match(p->flow->todstbytecnt, df->byte_data); + } + return 0; } -static void DetectFlowBytesToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowBytesFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u64_free(ptr); + DetectFlowBytes *df = (DetectFlowBytes *)ptr; + if (df != NULL) { + rs_detect_u64_free(df->byte_data); + SCFree(df); + } } -static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowBytesToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { DetectU64Data *du64 = DetectU64Parse(rawstr); if (du64 == NULL) return -1; - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_TO_CLIENT, (SigMatchCtx *)du64, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowBytesToClientFree(de_ctx, du64); + DetectFlowBytes *df = SCCalloc(1, sizeof(DetectFlowBytes)); + if (df == NULL) { + rs_detect_u64_free(du64); + return -1; + } + df->byte_data = du64; + df->dir = DETECT_FLOW_TOSERVER; + + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowBytesFree(de_ctx, df); return -1; } s->flags |= SIG_FLAG_REQUIRE_PACKET; @@ -204,57 +330,127 @@ static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, c return 0; } -void DetectFlowBytesToClientRegister(void) +static int DetectFlowBytesToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].name = "flow.bytes_toclient"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].desc = "match flow number of bytes to client"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].url = - "/rules/flow-keywords.html#flow-bytes_toclient"; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Match = DetectFlowBytesToClientMatch; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Setup = DetectFlowBytesToClientSetup; - sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Free = DetectFlowBytesToClientFree; -} + DetectU64Data *du64 = DetectU64Parse(rawstr); + if (du64 == NULL) + return -1; -static int DetectFlowBytesToServerMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) -{ - if (p->flow == NULL) { - return 0; + DetectFlowBytes *df = SCCalloc(1, sizeof(DetectFlowBytes)); + if (df == NULL) { + rs_detect_u64_free(du64); + return -1; } - uint64_t nb = p->flow->todstbytecnt; - const DetectU64Data *du64 = (const DetectU64Data *)ctx; - return DetectU64Match(nb, du64); -} + df->byte_data = du64; + df->dir = DETECT_FLOW_TOCLIENT; -static void DetectFlowBytesToServerFree(DetectEngineCtx *de_ctx, void *ptr) -{ - rs_detect_u64_free(ptr); + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowBytesFree(de_ctx, df); + return -1; + } + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; } -static int DetectFlowBytesToServerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) +static int DetectFlowBytesSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - DetectU64Data *du64 = DetectU64Parse(rawstr); - if (du64 == NULL) - return -1; + DetectFlowBytes *df = NULL; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + uint8_t num_tokens = 0; + uint8_t dir = 0; + char *byte_data = NULL; + + while (token != NULL) { + if (num_tokens > 1) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (dir == 0 && num_tokens == 1) { + if (strcmp(token, "toserver") == 0) { + dir = DETECT_FLOW_TOSERVER; + } else if (strcmp(token, "toclient") == 0) { + dir = DETECT_FLOW_TOCLIENT; + } else if (strcmp(token, "either") == 0) { + dir = DETECT_FLOW_TOEITHER; + } else { + SCLogError("Invalid direction given: %s", token); + return -1; + } + } + + if (dir && num_tokens == 2) { + byte_data = token; + } + + next: + token = strtok_r(NULL, ",", &context); + } - if (SigMatchAppendSMToList(de_ctx, s, DETECT_FLOW_BYTES_TO_SERVER, (SigMatchCtx *)du64, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowBytesToServerFree(de_ctx, du64); + DetectU64Data *du64 = DetectU64Parse(byte_data); + if (du64 == NULL) + goto error; + df = SCCalloc(1, sizeof(DetectFlowBytes)); + if (df == NULL) { + rs_detect_u64_free(du64); return -1; } + df->dir = dir; + df->byte_data = du64; + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_BYTES, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + goto error; + } s->flags |= SIG_FLAG_REQUIRE_PACKET; return 0; +error: + if (df) + DetectFlowBytesFree(de_ctx, df); + return -1; +} + +void DetectFlowBytesRegister(void) +{ + sigmatch_table[DETECT_FLOW_BYTES].name = "flow.bytes"; + sigmatch_table[DETECT_FLOW_BYTES].desc = "match number of bytes in a flow"; + sigmatch_table[DETECT_FLOW_BYTES].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES].Match = DetectFlowBytesMatch; + sigmatch_table[DETECT_FLOW_BYTES].Setup = DetectFlowBytesSetup; + sigmatch_table[DETECT_FLOW_BYTES].Free = DetectFlowBytesFree; } void DetectFlowBytesToServerRegister(void) { sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].name = "flow.bytes_toserver"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].desc = "match flow number of bytes to server"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].url = - "/rules/flow-keywords.html#flow-bytes_toserver"; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Match = DetectFlowBytesToServerMatch; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].desc = + "match number of bytes in a flow in to server dir"; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Match = DetectFlowBytesMatch; sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Setup = DetectFlowBytesToServerSetup; - sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Free = DetectFlowBytesToServerFree; + sigmatch_table[DETECT_FLOW_BYTES_TO_SERVER].Free = DetectFlowBytesFree; +} + +void DetectFlowBytesToClientRegister(void) +{ + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].name = "flow.bytes_toclient"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].desc = + "match number of bytes in a flow in to client dir"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].url = "/rules/flow-keywords.html#flow-bytes"; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Match = DetectFlowBytesMatch; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Setup = DetectFlowBytesToClientSetup; + sigmatch_table[DETECT_FLOW_BYTES_TO_CLIENT].Free = DetectFlowBytesFree; } diff --git a/src/detect-flow-pkts.h b/src/detect-flow-pkts.h index da1e0eb5a6aa..0bf47e1b7318 100644 --- a/src/detect-flow-pkts.h +++ b/src/detect-flow-pkts.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Open Information Security Foundation +/* Copyright (C) 2023-2024 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free @@ -18,9 +18,11 @@ #ifndef SURICATA_DETECT_FLOW_PKTS_H #define SURICATA_DETECT_FLOW_PKTS_H -void DetectFlowPktsToClientRegister(void); +void DetectFlowPktsRegister(void); void DetectFlowPktsToServerRegister(void); -void DetectFlowBytesToClientRegister(void); +void DetectFlowPktsToClientRegister(void); +void DetectFlowBytesRegister(void); void DetectFlowBytesToServerRegister(void); +void DetectFlowBytesToClientRegister(void); #endif /* SURICATA_DETECT_FLOW_PKTS_H */