diff options
Diffstat (limited to 'src/detect-icode.c')
-rw-r--r-- | src/detect-icode.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/src/detect-icode.c b/src/detect-icode.c new file mode 100644 index 0000000..3a601c2 --- /dev/null +++ b/src/detect-icode.c @@ -0,0 +1,407 @@ +/* Copyright (C) 2007-2021 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Gerardo Iglesias <iglesiasg@gmail.com> + * + * Implements icode keyword support + */ + +#include "suricata-common.h" +#include "decode.h" + +#include "detect.h" +#include "detect-parse.h" +#include "detect-engine-prefilter-common.h" +#include "detect-engine-uint.h" +#include "detect-engine-build.h" + +#include "detect-icode.h" + +#include "util-byte.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-debug.h" + +/** + *\brief Regex for parsing our icode options + */ + +static int DetectICodeMatch(DetectEngineThreadCtx *, Packet *, + const Signature *, const SigMatchCtx *); +static int DetectICodeSetup(DetectEngineCtx *, Signature *, const char *); +#ifdef UNITTESTS +static void DetectICodeRegisterTests(void); +#endif +void DetectICodeFree(DetectEngineCtx *, void *); + +static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh); +static bool PrefilterICodeIsPrefilterable(const Signature *s); + +/** + * \brief Registration function for icode: keyword + */ +void DetectICodeRegister (void) +{ + sigmatch_table[DETECT_ICODE].name = "icode"; + sigmatch_table[DETECT_ICODE].desc = "match on specific ICMP id-value"; + sigmatch_table[DETECT_ICODE].url = "/rules/header-keywords.html#icode"; + sigmatch_table[DETECT_ICODE].Match = DetectICodeMatch; + sigmatch_table[DETECT_ICODE].Setup = DetectICodeSetup; + sigmatch_table[DETECT_ICODE].Free = DetectICodeFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_ICODE].RegisterTests = DetectICodeRegisterTests; +#endif + sigmatch_table[DETECT_ICODE].SupportsPrefilter = PrefilterICodeIsPrefilterable; + sigmatch_table[DETECT_ICODE].SetupPrefilter = PrefilterSetupICode; +} + +/** + * \brief This function is used to match icode rule option set on a packet with those passed via + * icode: + * + * \param t pointer to thread vars + * \param det_ctx pointer to the pattern matcher thread + * \param p pointer to the current packet + * \param ctx pointer to the sigmatch that we will cast into DetectU8Data + * + * \retval 0 no match + * \retval 1 match + */ +static int DetectICodeMatch (DetectEngineThreadCtx *det_ctx, Packet *p, + const Signature *s, const SigMatchCtx *ctx) +{ + if (PKT_IS_PSEUDOPKT(p)) + return 0; + + uint8_t picode; + if (PKT_IS_ICMPV4(p)) { + picode = ICMPV4_GET_CODE(p); + } else if (PKT_IS_ICMPV6(p)) { + picode = ICMPV6_GET_CODE(p); + } else { + /* Packet not ICMPv4 nor ICMPv6 */ + return 0; + } + + const DetectU8Data *icd = (const DetectU8Data *)ctx; + return DetectU8Match(picode, icd); +} + +/** + * \brief this function is used to add the parsed icode data into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param icodestr pointer to the user provided icode options + * + * \retval 0 on Success + * \retval -1 on Failure + */ +static int DetectICodeSetup(DetectEngineCtx *de_ctx, Signature *s, const char *icodestr) +{ + + DetectU8Data *icd = NULL; + SigMatch *sm = NULL; + + icd = DetectU8Parse(icodestr); + if (icd == NULL) goto error; + + sm = SigMatchAlloc(); + if (sm == NULL) goto error; + + sm->type = DETECT_ICODE; + sm->ctx = (SigMatchCtx *)icd; + + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); + s->flags |= SIG_FLAG_REQUIRE_PACKET; + + return 0; + +error: + if (icd != NULL) + rs_detect_u8_free(icd); + if (sm != NULL) SCFree(sm); + return -1; +} + +/** + * \brief this function will free memory associated with DetectU8Data + * + * \param ptr pointer to DetectU8Data + */ +void DetectICodeFree(DetectEngineCtx *de_ctx, void *ptr) +{ + rs_detect_u8_free(ptr); +} + +/* prefilter code */ + +static void PrefilterPacketICodeMatch(DetectEngineThreadCtx *det_ctx, + Packet *p, const void *pectx) +{ + if (PKT_IS_PSEUDOPKT(p)) { + SCReturn; + } + + uint8_t picode; + if (PKT_IS_ICMPV4(p)) { + picode = ICMPV4_GET_CODE(p); + } else if (PKT_IS_ICMPV6(p)) { + picode = ICMPV6_GET_CODE(p); + } else { + /* Packet not ICMPv4 nor ICMPv6 */ + return; + } + + const PrefilterPacketU8HashCtx *h = pectx; + const SigsArray *sa = h->array[picode]; + if (sa) { + PrefilterAddSids(&det_ctx->pmq, sa->sigs, sa->cnt); + } +} + +static int PrefilterSetupICode(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeaderU8Hash(de_ctx, sgh, DETECT_ICODE, PrefilterPacketU8Set, + PrefilterPacketU8Compare, PrefilterPacketICodeMatch); +} + +static bool PrefilterICodeIsPrefilterable(const Signature *s) +{ + const SigMatch *sm; + for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH] ; sm != NULL; sm = sm->next) { + switch (sm->type) { + case DETECT_ICODE: + return true; + } + } + return false; +} + +#ifdef UNITTESTS +#include "detect-engine.h" +#include "detect-engine-mpm.h" +#include "detect-engine-alert.h" + +/** + * \test DetectICodeParseTest01 is a test for setting a valid icode value + */ +static int DetectICodeParseTest01(void) +{ + DetectU8Data *icd = DetectU8Parse("8"); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest02 is a test for setting a valid icode value + * with ">" operator + */ +static int DetectICodeParseTest02(void) +{ + DetectU8Data *icd = DetectU8Parse(">8"); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->mode == DETECT_UINT_GT); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest03 is a test for setting a valid icode value + * with "<" operator + */ +static int DetectICodeParseTest03(void) +{ + DetectU8Data *icd = DetectU8Parse("<8"); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->mode == DETECT_UINT_LT); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest04 is a test for setting a valid icode value + * with "<>" operator + */ +static int DetectICodeParseTest04(void) +{ + DetectU8Data *icd = DetectU8Parse("8<>20"); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->arg2 == 20); + FAIL_IF_NOT(icd->mode == DETECT_UINT_RA); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest05 is a test for setting a valid icode value + * with spaces all around + */ +static int DetectICodeParseTest05(void) +{ + DetectU8Data *icd = DetectU8Parse(" 8 "); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->mode == DETECT_UINT_EQ); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest06 is a test for setting a valid icode value + * with ">" operator and spaces all around + */ +static int DetectICodeParseTest06(void) +{ + DetectU8Data *icd = DetectU8Parse(" > 8 "); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->mode == DETECT_UINT_GT); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest07 is a test for setting a valid icode value + * with "<>" operator and spaces all around + */ +static int DetectICodeParseTest07(void) +{ + DetectU8Data *icd = DetectU8Parse(" 8 <> 20 "); + FAIL_IF_NULL(icd); + FAIL_IF_NOT(icd->arg1 == 8); + FAIL_IF_NOT(icd->arg2 == 20); + FAIL_IF_NOT(icd->mode == DETECT_UINT_RA); + DetectICodeFree(NULL, icd); + + PASS; +} + +/** + * \test DetectICodeParseTest08 is a test for setting an invalid icode value + */ +static int DetectICodeParseTest08(void) +{ + DetectU8Data *icd = DetectU8Parse("> 8 <> 20"); + FAIL_IF_NOT_NULL(icd); + + DetectICodeFree(NULL, icd); + PASS; +} + +/** + * \test DetectICodeParseTest09 is a test for setting an invalid icode value + * with "<<" operator + */ +static int DetectICodeParseTest09(void) +{ + DetectU8Data *icd = DetectU8Parse("8<<20"); + FAIL_IF_NOT_NULL(icd); + + DetectICodeFree(NULL, icd); + PASS; +} + +/** + * \test DetectICodeMatchTest01 is a test for checking the working of icode + * keyword by creating 5 rules and matching a crafted packet against + * them. 4 out of 5 rules shall trigger. + */ +static int DetectICodeMatchTest01(void) +{ + + Packet *p = NULL; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_ICMP); + + p->icmpv4h->code = 10; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:10; sid:1;)"); + FAIL_IF_NULL(s); + + s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:<15; sid:2;)"); + FAIL_IF_NULL(s); + + s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:>20; sid:3;)"); + FAIL_IF_NULL(s); + + s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:8<>20; sid:4;)"); + FAIL_IF_NULL(s); + + s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> any any (icode:20<>8; sid:5;)"); + FAIL_IF_NOT_NULL(s); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + + FAIL_IF(PacketAlertCheck(p, 1) == 0); + FAIL_IF(PacketAlertCheck(p, 2) == 0); + FAIL_IF(PacketAlertCheck(p, 3)); + FAIL_IF(PacketAlertCheck(p, 4) == 0); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + + UTHFreePackets(&p, 1); + + PASS; +} + +/** + * \brief this function registers unit tests for DetectICode + */ +void DetectICodeRegisterTests(void) +{ + UtRegisterTest("DetectICodeParseTest01", DetectICodeParseTest01); + UtRegisterTest("DetectICodeParseTest02", DetectICodeParseTest02); + UtRegisterTest("DetectICodeParseTest03", DetectICodeParseTest03); + UtRegisterTest("DetectICodeParseTest04", DetectICodeParseTest04); + UtRegisterTest("DetectICodeParseTest05", DetectICodeParseTest05); + UtRegisterTest("DetectICodeParseTest06", DetectICodeParseTest06); + UtRegisterTest("DetectICodeParseTest07", DetectICodeParseTest07); + UtRegisterTest("DetectICodeParseTest08", DetectICodeParseTest08); + UtRegisterTest("DetectICodeParseTest09", DetectICodeParseTest09); + UtRegisterTest("DetectICodeMatchTest01", DetectICodeMatchTest01); +} +#endif /* UNITTESTS */ |