diff --git a/doc/userguide/rules/flow-keywords.rst b/doc/userguide/rules/flow-keywords.rst index 00801352303e..089c8ba8cc9e 100644 --- a/doc/userguide/rules/flow-keywords.rst +++ b/doc/userguide/rules/flow-keywords.rst @@ -318,90 +318,61 @@ 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 +* toclient -Signature example:: +* toserver - alert ip any any -> any any (msg:"Flow has 20 packets"; flow.pkts_toclient:20; sid:1;) - -flow.pkts_toserver ------------------- - -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. - -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. - -flow.bytes_toclient uses an :ref:`unsigned 64-bit integer `. - -Syntax:: - - flow.bytes_toclient: [op] -The number of packets can be matched exactly, or compared using the _op_ setting:: +flow.bytes +---------- - flow.bytes_toclient:3 # exactly 3 - flow.bytes_toclient:<3 # smaller than 3 - flow.bytes_toclient:>=2 # greater than or equal to 2 +Flow number of bytes (integer) +This keyword does not wait for the end of the flow, but will be checked at each packet. -Signature example:: +flow.bytes uses an :ref:`unsigned 64-bit integer ` and supports +following directions: - alert ip any any -> any any (msg:"Flow has less than 2000 bytes"; flow.bytes_toclient:<2000; sid:1;) +* toclient -flow.bytes_toserver -------------------- +* toserver -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. - -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;) diff --git a/src/detect-engine-register.c b/src/detect-engine-register.c index 903cc158cf5f..5ba805e2d71a 100644 --- a/src/detect-engine-register.c +++ b/src/detect-engine-register.c @@ -559,10 +559,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 e5f550be7547..d1decdd07666 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -125,10 +125,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..c09e71edfa6d 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,64 @@ #include "detect-engine-uint.h" #include "detect-parse.h" -static int DetectFlowPktsToClientMatch( +#define DETECT_FLOW_TO_SERVER 1 +#define DETECT_FLOW_TO_CLIENT 2 +#define DETECT_FLOW_TO_EITHER 3 + +typedef struct DetectFlow_ { + DetectU32Data *pkt_data; + DetectU64Data *byte_data; + uint8_t dir; +} DetectFlow; + +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 DetectFlow *df = (const DetectFlow *)ctx; + if (df->dir == DETECT_FLOW_TO_SERVER) { + return DetectU32Match(p->flow->todstpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TO_CLIENT) { + return DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data); + } else if (df->dir == DETECT_FLOW_TO_EITHER) { + if (DetectU32Match(p->flow->tosrcpktcnt, df->pkt_data)) { + return 1; + } + if (DetectU32Match(p->flow->todstpktcnt, df->pkt_data)) { + return 1; + } + } + return 0; } -static void DetectFlowPktsToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowPktsFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u32_free(ptr); + DetectFlow *df = (DetectFlow *)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); + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + + df->pkt_data = du32; + df->dir = DETECT_FLOW_TO_SERVER; + + 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,81 +88,86 @@ static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, co return 0; } -static void PrefilterPacketFlowPktsToClientMatch( - 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 (DetectFlowPktsToClientMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { - PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); - } -} - -static int PrefilterSetupFlowPktsToClient(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +static int DetectFlowPktsToClientSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS_TO_CLIENT, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToClientMatch); -} + DetectU32Data *du32 = DetectU32Parse(rawstr); + if (du32 == NULL) + return -1; -static bool PrefilterFlowPktsToClientIsPrefilterable(const Signature *s) -{ - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_CLIENT); -} + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; -void DetectFlowPktsToClientRegister(void) -{ - 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; -} + df->pkt_data = du32; + df->dir = DETECT_FLOW_TO_CLIENT; -static int DetectFlowPktsToServerMatch( - DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) -{ - if (p->flow == NULL) { - return 0; + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + DetectFlowPktsFree(de_ctx, df); + return -1; } - uint32_t nb = p->flow->todstpktcnt; + s->flags |= SIG_FLAG_REQUIRE_PACKET; - const DetectU32Data *du32 = (const DetectU32Data *)ctx; - return DetectU32Match(nb, du32); + return 0; } -static void DetectFlowPktsToServerFree(DetectEngineCtx *de_ctx, void *ptr) +static int DetectFlowPktsSetup(DetectEngineCtx *de_ctx, Signature *s, const char *rawstr) { - rs_detect_u32_free(ptr); -} - -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_SERVER, (SigMatchCtx *)du32, - DETECT_SM_LIST_MATCH) == NULL) { - DetectFlowPktsToServerFree(de_ctx, du32); + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) return -1; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + bool dir_set = false; + uint8_t num_tokens = 1; + while (token != NULL) { + if (num_tokens > 2) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (strcmp(token, "toserver") == 0) { + df->dir = DETECT_FLOW_TO_SERVER; + } else if (strcmp(token, "toclient") == 0) { + df->dir = DETECT_FLOW_TO_CLIENT; + } else if (strcmp(token, "either") == 0) { + df->dir = DETECT_FLOW_TO_EITHER; + } + + if (dir_set) { + DetectU32Data *du32 = DetectU32Parse(token); + if (du32 == NULL) + goto error; + df->pkt_data = du32; + } + if (df->dir) + dir_set = true; + next: + token = strtok_r(NULL, ",", &context); + } + if (SigMatchAppendSMToList( + de_ctx, s, DETECT_FLOW_PKTS, (SigMatchCtx *)df, DETECT_SM_LIST_MATCH) == NULL) { + goto error; } s->flags |= SIG_FLAG_REQUIRE_PACKET; return 0; +error: + if (df != NULL) + DetectFlowPktsFree(de_ctx, df); + return -1; } -static void PrefilterPacketFlowPktsToServerMatch( +static void PrefilterPacketFlowPktsMatch( DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) { const PrefilterPacketHeaderCtx *ctx = pectx; @@ -141,62 +178,108 @@ static void PrefilterPacketFlowPktsToServerMatch( 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)) { + if (DetectFlowPktsMatch(det_ctx, p, NULL, (const SigMatchCtx *)&du32)) { 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_TO_SERVER, - SIG_MASK_REQUIRE_FLOW, PrefilterPacketU32Set, PrefilterPacketU32Compare, - PrefilterPacketFlowPktsToServerMatch); + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_FLOW_PKTS, SIG_MASK_REQUIRE_FLOW, + PrefilterPacketU32Set, PrefilterPacketU32Compare, PrefilterPacketFlowPktsMatch); } -static bool PrefilterFlowPktsToServerIsPrefilterable(const Signature *s) +static bool PrefilterFlowPktsIsPrefilterable(const Signature *s) { - return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS_TO_SERVER); + return PrefilterIsPrefilterableById(s, DETECT_FLOW_PKTS); +} + +void DetectFlowPktsRegister(void) +{ + 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; } -static int DetectFlowBytesToClientMatch( +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 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 DetectFlow *df = (const DetectFlow *)ctx; + if (df->dir == DETECT_FLOW_TO_SERVER) { + return DetectU64Match(p->flow->todstbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TO_CLIENT) { + return DetectU64Match(p->flow->tosrcbytecnt, df->byte_data); + } else if (df->dir == DETECT_FLOW_TO_EITHER) { + if (DetectU64Match(p->flow->tosrcbytecnt, df->byte_data)) { + return 1; + } + if (DetectU64Match(p->flow->todstbytecnt, df->byte_data)) { + return 1; + } + } + return 0; } -static void DetectFlowBytesToClientFree(DetectEngineCtx *de_ctx, void *ptr) +static void DetectFlowBytesFree(DetectEngineCtx *de_ctx, void *ptr) { - rs_detect_u64_free(ptr); + DetectFlow *df = (DetectFlow *)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); + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; + + df->byte_data = du64; + df->dir = DETECT_FLOW_TO_SERVER; + + 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 +287,114 @@ 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; - } - uint64_t nb = p->flow->todstbytecnt; + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) + return -1; - const DetectU64Data *du64 = (const DetectU64Data *)ctx; - return DetectU64Match(nb, du64); -} + df->byte_data = du64; + df->dir = DETECT_FLOW_TO_CLIENT; -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) + DetectFlow *df = SCCalloc(1, sizeof(DetectFlow)); + if (df == NULL) return -1; + char copy[strlen(rawstr) + 1]; + strlcpy(copy, rawstr, sizeof(copy)); + char *context = NULL; + char *token = strtok_r(copy, ",", &context); + bool dir_set = false; + uint8_t num_tokens = 1; + while (token != NULL) { + if (num_tokens > 2) + goto error; + + while (*token != '\0' && isblank(*token)) { + token++; + } + if (strlen(token) == 0) { + goto next; + } + + num_tokens++; + + if (strcmp(token, "toserver") == 0) { + df->dir = DETECT_FLOW_TO_SERVER; + } else if (strcmp(token, "toclient") == 0) { + df->dir = DETECT_FLOW_TO_CLIENT; + } else if (strcmp(token, "either") == 0) { + df->dir = DETECT_FLOW_TO_EITHER; + } + + if (dir_set) { + DetectU64Data *du64 = DetectU64Parse(token); + if (du64 == NULL) + goto error; + df->byte_data = du64; + } + if (df->dir) + dir_set = true; + 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); - return -1; + 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 */