summaryrefslogtreecommitdiffstats
path: root/src/detect-cipservice.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
commita0aa2307322cd47bbf416810ac0292925e03be87 (patch)
tree37076262a026c4b48c8a0e84f44ff9187556ca35 /src/detect-cipservice.c
parentInitial commit. (diff)
downloadsuricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz
suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/detect-cipservice.c462
1 files changed, 462 insertions, 0 deletions
diff --git a/src/detect-cipservice.c b/src/detect-cipservice.c
new file mode 100644
index 0000000..00b9a75
--- /dev/null
+++ b/src/detect-cipservice.c
@@ -0,0 +1,462 @@
+/* Copyright (C) 2015 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 Kevin Wong <kwong@solananetworks.com>
+ *
+ * Set up ENIP Command and CIP Service rule parsing and entry point for matching
+ */
+
+#include "suricata-common.h"
+#include "util-unittest.h"
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "util-byte.h"
+
+#include "app-layer-enip-common.h"
+#include "detect-cipservice.h"
+#include "detect-engine-enip.h"
+
+/*
+ * CIP SERVICE CODE
+ */
+
+/**
+ * \brief CIP Service Detect Prototypes
+ */
+static int DetectCipServiceSetup(DetectEngineCtx *, Signature *, const char *);
+static void DetectCipServiceFree(DetectEngineCtx *, void *);
+#ifdef UNITTESTS
+static void DetectCipServiceRegisterTests(void);
+#endif
+static int g_cip_buffer_id = 0;
+
+/**
+ * \brief Registration function for cip_service: keyword
+ */
+void DetectCipServiceRegister(void)
+{
+ SCEnter();
+ sigmatch_table[DETECT_CIPSERVICE].name = "cip_service"; //rule keyword
+ sigmatch_table[DETECT_CIPSERVICE].desc = "match on CIP Service";
+ sigmatch_table[DETECT_CIPSERVICE].url = "/rules/enip-keyword.html#enip-cip-keywords";
+ sigmatch_table[DETECT_CIPSERVICE].Match = NULL;
+ sigmatch_table[DETECT_CIPSERVICE].Setup = DetectCipServiceSetup;
+ sigmatch_table[DETECT_CIPSERVICE].Free = DetectCipServiceFree;
+#ifdef UNITTESTS
+ sigmatch_table[DETECT_CIPSERVICE].RegisterTests
+ = DetectCipServiceRegisterTests;
+#endif
+ DetectAppLayerInspectEngineRegister2(
+ "cip", ALPROTO_ENIP, SIG_FLAG_TOSERVER, 0, DetectEngineInspectCIP, NULL);
+ DetectAppLayerInspectEngineRegister2(
+ "cip", ALPROTO_ENIP, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectCIP, NULL);
+
+ g_cip_buffer_id = DetectBufferTypeGetByName("cip");
+
+ SCReturn;
+}
+
+/**
+ * \brief This function is used to parse cip_service options passed via cip_service: keyword
+ *
+ * \param rulestr Pointer to the user provided rulestr options
+ * Takes comma seperated string with numeric tokens. Only first 3 are used
+ *
+ * \retval cipserviced pointer to DetectCipServiceData on success
+ * \retval NULL on failure
+ */
+static DetectCipServiceData *DetectCipServiceParse(const char *rulestrc)
+{
+ const char delims[] = ",";
+ DetectCipServiceData *cipserviced = NULL;
+
+ //SCLogDebug("DetectCipServiceParse - rule string %s", rulestr);
+
+ /* strtok_r modifies the string so work with a copy */
+ char *rulestr = SCStrdup(rulestrc);
+ if (unlikely(rulestr == NULL))
+ goto error;
+
+ cipserviced = SCMalloc(sizeof(DetectCipServiceData));
+ if (unlikely(cipserviced == NULL))
+ goto error;
+
+ cipserviced->cipservice = 0;
+ cipserviced->cipclass = 0;
+ cipserviced->matchattribute = 1;
+ cipserviced->cipattribute = 0;
+
+ char* token;
+ char *save;
+ uint8_t var;
+ uint8_t input[3] = { 0, 0, 0 };
+ uint8_t i = 0;
+
+ token = strtok_r(rulestr, delims, &save);
+ while (token != NULL)
+ {
+ if (i > 2) //for now only need 3 parameters
+ {
+ SCLogError("too many parameters");
+ goto error;
+ }
+
+ if (i < 2) //if on service or class
+ {
+ if (!isdigit((int) *token))
+ {
+ SCLogError("parameter error %s", token);
+ goto error;
+ }
+ } else //if on attribute
+ {
+
+ if (token[0] == '!')
+ {
+ cipserviced->matchattribute = 0;
+ token++;
+ }
+
+ if (!isdigit((int) *token))
+ {
+ SCLogError("attribute error %s", token);
+ goto error;
+ }
+
+ }
+
+ unsigned long num = atol(token);
+ if ((num > MAX_CIP_SERVICE) && (i == 0))//if service greater than 7 bit
+ {
+ SCLogError("invalid CIP service %lu", num);
+ goto error;
+ } else if ((num > MAX_CIP_CLASS) && (i == 1))//if service greater than 16 bit
+ {
+ SCLogError("invalid CIP class %lu", num);
+ goto error;
+ } else if ((num > MAX_CIP_ATTRIBUTE) && (i == 2))//if service greater than 16 bit
+ {
+ SCLogError("invalid CIP attribute %lu", num);
+ goto error;
+ }
+
+ sscanf(token, "%2" SCNu8, &var);
+ input[i++] = var;
+
+ token = strtok_r(NULL, delims, &save);
+ }
+
+ if (i == 0) {
+ SCLogError("no tokens found");
+ goto error;
+ }
+
+ cipserviced->cipservice = input[0];
+ cipserviced->cipclass = input[1];
+ cipserviced->cipattribute = input[2];
+ cipserviced->tokens = i;
+
+ SCLogDebug("DetectCipServiceParse - tokens %d", cipserviced->tokens);
+ SCLogDebug("DetectCipServiceParse - service %d", cipserviced->cipservice);
+ SCLogDebug("DetectCipServiceParse - class %d", cipserviced->cipclass);
+ SCLogDebug("DetectCipServiceParse - match attribute %d",
+ cipserviced->matchattribute);
+ SCLogDebug("DetectCipServiceParse - attribute %d",
+ cipserviced->cipattribute);
+
+ SCFree(rulestr);
+ SCReturnPtr(cipserviced, "DetectENIPFunction");
+
+error:
+ if (cipserviced)
+ SCFree(cipserviced);
+ if (rulestr)
+ SCFree(rulestr);
+ SCReturnPtr(NULL, "DetectENIP");
+}
+
+/**
+ * \brief this function is used to a cipserviced the parsed cip_service data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rulestr pointer to the user provided cip_service options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectCipServiceSetup(DetectEngineCtx *de_ctx, Signature *s,
+ const char *rulestr)
+{
+ SCEnter();
+
+ DetectCipServiceData *cipserviced = NULL;
+ SigMatch *sm = NULL;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_ENIP) != 0)
+ return -1;
+
+ cipserviced = DetectCipServiceParse(rulestr);
+ if (cipserviced == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_CIPSERVICE;
+ sm->ctx = (void *) cipserviced;
+
+ SigMatchAppendSMToList(s, sm, g_cip_buffer_id);
+ SCReturnInt(0);
+
+error:
+ if (cipserviced != NULL)
+ DetectCipServiceFree(de_ctx, cipserviced);
+ if (sm != NULL)
+ SCFree(sm);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief this function will free memory associated with DetectCipServiceData
+ *
+ * \param ptr pointer to DetectCipServiceData
+ */
+static void DetectCipServiceFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ DetectCipServiceData *cipserviced = (DetectCipServiceData *) ptr;
+ SCFree(cipserviced);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test Test CIP Command parameter parsing
+ */
+static int DetectCipServiceParseTest01 (void)
+{
+ DetectCipServiceData *cipserviced = NULL;
+ cipserviced = DetectCipServiceParse("7");
+ FAIL_IF_NULL(cipserviced);
+ FAIL_IF(cipserviced->cipservice != 7);
+ DetectCipServiceFree(NULL, cipserviced);
+ PASS;
+}
+
+/**
+ * \test Test CIP Service signature
+ */
+static int DetectCipServiceSignatureTest01 (void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (cip_service:1; sid:1; rev:1;)");
+ FAIL_IF_NULL(sig);
+ DetectEngineCtxFree(de_ctx);
+ PASS;
+}
+
+/**
+ * \brief this function registers unit tests for DetectCipService
+ */
+static void DetectCipServiceRegisterTests(void)
+{
+ UtRegisterTest("DetectCipServiceParseTest01",
+ DetectCipServiceParseTest01);
+ UtRegisterTest("DetectCipServiceSignatureTest01",
+ DetectCipServiceSignatureTest01);
+}
+#endif /* UNITTESTS */
+
+/*
+ * ENIP COMMAND CODE
+ */
+
+/**
+ * \brief ENIP Command Detect Prototypes
+ */
+static int DetectEnipCommandSetup(DetectEngineCtx *, Signature *, const char *);
+static void DetectEnipCommandFree(DetectEngineCtx *, void *);
+#ifdef UNITTESTS
+static void DetectEnipCommandRegisterTests(void);
+#endif
+static int g_enip_buffer_id = 0;
+
+/**
+ * \brief Registration function for enip_command: keyword
+ */
+void DetectEnipCommandRegister(void)
+{
+ sigmatch_table[DETECT_ENIPCOMMAND].name = "enip_command"; //rule keyword
+ sigmatch_table[DETECT_ENIPCOMMAND].desc
+ = "rules for detecting EtherNet/IP command";
+ sigmatch_table[DETECT_ENIPCOMMAND].url = "/rules/enip-keyword.html#enip-cip-keywords";
+ sigmatch_table[DETECT_ENIPCOMMAND].Match = NULL;
+ sigmatch_table[DETECT_ENIPCOMMAND].Setup = DetectEnipCommandSetup;
+ sigmatch_table[DETECT_ENIPCOMMAND].Free = DetectEnipCommandFree;
+#ifdef UNITTESTS
+ sigmatch_table[DETECT_ENIPCOMMAND].RegisterTests
+ = DetectEnipCommandRegisterTests;
+#endif
+ DetectAppLayerInspectEngineRegister2(
+ "enip", ALPROTO_ENIP, SIG_FLAG_TOSERVER, 0, DetectEngineInspectENIP, NULL);
+ DetectAppLayerInspectEngineRegister2(
+ "enip", ALPROTO_ENIP, SIG_FLAG_TOCLIENT, 0, DetectEngineInspectENIP, NULL);
+
+ g_enip_buffer_id = DetectBufferTypeGetByName("enip");
+}
+
+/**
+ * \brief This function is used to parse cip_service options passed via enip_command: keyword
+ *
+ * \param rulestr Pointer to the user provided rulestr options
+ * Takes single numeric value
+ *
+ * \retval enipcmdd pointer to DetectCipServiceData on success
+ * \retval NULL on failure
+ */
+static DetectEnipCommandData *DetectEnipCommandParse(const char *rulestr)
+{
+ DetectEnipCommandData *enipcmdd = NULL;
+
+ enipcmdd = SCMalloc(sizeof(DetectEnipCommandData));
+ if (unlikely(enipcmdd == NULL))
+ goto error;
+
+ if (!(isdigit((int) *rulestr))) {
+ SCLogError("invalid ENIP command %s", rulestr);
+ goto error;
+ }
+
+ uint16_t cmd;
+ if (StringParseUint16(&cmd, 10, 0, rulestr) < 0) {
+ SCLogError("invalid ENIP command"
+ ": \"%s\"",
+ rulestr);
+ goto error;
+ }
+
+ enipcmdd->enipcommand = cmd;
+
+ return enipcmdd;
+
+error:
+ if (enipcmdd)
+ SCFree(enipcmdd);
+ return NULL;
+}
+
+/**
+ * \brief this function is used by enipcmdd to parse enip_command data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param rulestr pointer to the user provided enip command options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectEnipCommandSetup(DetectEngineCtx *de_ctx, Signature *s,
+ const char *rulestr)
+{
+ DetectEnipCommandData *enipcmdd = NULL;
+ SigMatch *sm = NULL;
+
+ if (DetectSignatureSetAppProto(s, ALPROTO_ENIP) != 0)
+ return -1;
+
+ enipcmdd = DetectEnipCommandParse(rulestr);
+ if (enipcmdd == NULL)
+ goto error;
+
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_ENIPCOMMAND;
+ sm->ctx = (void *) enipcmdd;
+
+ SigMatchAppendSMToList(s, sm, g_enip_buffer_id);
+ SCReturnInt(0);
+
+error:
+ if (enipcmdd != NULL)
+ DetectEnipCommandFree(de_ctx, enipcmdd);
+ if (sm != NULL)
+ SCFree(sm);
+ SCReturnInt(-1);
+}
+
+/**
+ * \brief this function will free memory associated with DetectEnipCommandData
+ *
+ * \param ptr pointer to DetectEnipCommandData
+ */
+static void DetectEnipCommandFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ DetectEnipCommandData *enipcmdd = (DetectEnipCommandData *) ptr;
+ SCFree(enipcmdd);
+}
+
+#ifdef UNITTESTS
+
+/**
+ * \test ENIP parameter test
+ */
+
+static int DetectEnipCommandParseTest01 (void)
+{
+ DetectEnipCommandData *enipcmdd = NULL;
+
+ enipcmdd = DetectEnipCommandParse("1");
+ FAIL_IF_NULL(enipcmdd);
+ FAIL_IF_NOT(enipcmdd->enipcommand == 1);
+
+ DetectEnipCommandFree(NULL, enipcmdd);
+ PASS;
+}
+
+/**
+ * \test ENIP Command signature test
+ */
+static int DetectEnipCommandSignatureTest01 (void)
+{
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+
+ Signature *sig = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (enip_command:1; sid:1; rev:1;)");
+ FAIL_IF_NULL(sig);
+
+ DetectEngineCtxFree(de_ctx);
+ PASS;
+}
+
+/**
+ * \brief this function registers unit tests for DetectEnipCommand
+ */
+static void DetectEnipCommandRegisterTests(void)
+{
+ UtRegisterTest("DetectEnipCommandParseTest01",
+ DetectEnipCommandParseTest01);
+ UtRegisterTest("DetectEnipCommandSignatureTest01",
+ DetectEnipCommandSignatureTest01);
+}
+#endif /* UNITTESTS */