summaryrefslogtreecommitdiffstats
path: root/src/detect-datarep.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-datarep.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 'src/detect-datarep.c')
-rw-r--r--src/detect-datarep.c380
1 files changed, 380 insertions, 0 deletions
diff --git a/src/detect-datarep.c b/src/detect-datarep.c
new file mode 100644
index 0000000..c9cc179
--- /dev/null
+++ b/src/detect-datarep.c
@@ -0,0 +1,380 @@
+/* Copyright (C) 2018-2020 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 Victor Julien <victor@inliniac.net>
+ *
+ * Implements the datarep keyword
+ */
+
+#include "suricata-common.h"
+#include "decode.h"
+#include "detect.h"
+#include "threads.h"
+#include "datasets.h"
+#include "detect-datarep.h"
+
+#include "detect-parse.h"
+#include "detect-engine.h"
+#include "detect-engine-mpm.h"
+#include "detect-engine-state.h"
+
+#include "util-byte.h"
+#include "util-debug.h"
+#include "util-print.h"
+#include "util-misc.h"
+#include "util-path.h"
+
+#define PARSE_REGEX "([a-z]+)(?:,\\s*([\\-_A-z0-9\\s\\.]+)){1,4}"
+static DetectParseRegex parse_regex;
+
+int DetectDatarepMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *,
+ const Signature *, const SigMatchCtx *);
+static int DetectDatarepSetup (DetectEngineCtx *, Signature *, const char *);
+void DetectDatarepFree (DetectEngineCtx *, void *);
+
+void DetectDatarepRegister (void)
+{
+ sigmatch_table[DETECT_DATAREP].name = "datarep";
+ sigmatch_table[DETECT_DATAREP].desc = "operate on datasets (experimental)";
+ sigmatch_table[DETECT_DATAREP].url = "/rules/dataset-keywords.html#datarep";
+ sigmatch_table[DETECT_DATAREP].Setup = DetectDatarepSetup;
+ sigmatch_table[DETECT_DATAREP].Free = DetectDatarepFree;
+
+ DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
+}
+
+/*
+ 1 match
+ 0 no match
+ -1 can't match
+ */
+int DetectDatarepBufferMatch(DetectEngineThreadCtx *det_ctx,
+ const DetectDatarepData *sd,
+ const uint8_t *data, const uint32_t data_len)
+{
+ if (data == NULL || data_len == 0)
+ return 0;
+
+ DataRepResultType r = DatasetLookupwRep(sd->set, data, data_len, &sd->rep);
+ if (!r.found)
+ return 0;
+
+ switch (sd->op) {
+ case DATAREP_OP_GT:
+ if (r.rep.value > sd->rep.value)
+ return 1;
+ break;
+ case DATAREP_OP_LT:
+ if (r.rep.value < sd->rep.value)
+ return 1;
+ break;
+ case DATAREP_OP_EQ:
+ if (r.rep.value == sd->rep.value)
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+static int DetectDatarepParse(const char *str, char *cmd, int cmd_len, char *name, int name_len,
+ enum DatasetTypes *type, char *load, size_t load_size, uint16_t *rep_value,
+ uint64_t *memcap, uint32_t *hashsize)
+{
+ bool cmd_set = false;
+ bool name_set = false;
+ bool value_set = false;
+
+ char copy[strlen(str)+1];
+ strlcpy(copy, str, sizeof(copy));
+ char *xsaveptr = NULL;
+ char *key = strtok_r(copy, ",", &xsaveptr);
+ while (key != NULL) {
+ while (*key != '\0' && isblank(*key)) {
+ key++;
+ }
+ char *val = strchr(key, ' ');
+ if (val != NULL) {
+ *val++ = '\0';
+ while (*val != '\0' && isblank(*val)) {
+ val++;
+ SCLogDebug("cmd %s val %s", key, val);
+ }
+ } else {
+ SCLogDebug("cmd %s", key);
+ }
+
+ if (strlen(key) == 0) {
+ goto next;
+ }
+
+ if (!name_set) {
+ if (val) {
+ return -1;
+ }
+ strlcpy(name, key, name_len);
+ name_set = true;
+ } else if (!cmd_set) {
+ if (val) {
+ return -1;
+ }
+ strlcpy(cmd, key, cmd_len);
+ cmd_set = true;
+ } else if (!value_set) {
+ if (val) {
+ return -1;
+ }
+
+ if (StringParseUint16(rep_value, 10, 0, key) < 0)
+ return -1;
+
+ value_set = true;
+ } else {
+ if (val == NULL) {
+ return -1;
+ }
+
+ if (strcmp(key, "type") == 0) {
+ SCLogDebug("type %s", val);
+
+ if (strcmp(val, "md5") == 0) {
+ *type = DATASET_TYPE_MD5;
+ } else if (strcmp(val, "sha256") == 0) {
+ *type = DATASET_TYPE_SHA256;
+ } else if (strcmp(val, "string") == 0) {
+ *type = DATASET_TYPE_STRING;
+ } else if (strcmp(val, "ipv4") == 0) {
+ *type = DATASET_TYPE_IPV4;
+ } else if (strcmp(val, "ip") == 0) {
+ *type = DATASET_TYPE_IPV6;
+ } else if (strcmp(val, "ipv6") == 0) {
+ *type = DATASET_TYPE_IPV6;
+ } else {
+ SCLogDebug("bad type %s", val);
+ return -1;
+ }
+
+ } else if (strcmp(key, "load") == 0) {
+ SCLogDebug("load %s", val);
+ strlcpy(load, val, load_size);
+ }
+ if (strcmp(key, "memcap") == 0) {
+ if (ParseSizeStringU64(val, memcap) < 0) {
+ SCLogWarning("invalid value for memcap: %s,"
+ " resetting to default",
+ val);
+ *memcap = 0;
+ }
+ }
+ if (strcmp(key, "hashsize") == 0) {
+ if (ParseSizeStringU32(val, hashsize) < 0) {
+ SCLogWarning("invalid value for hashsize: %s,"
+ " resetting to default",
+ val);
+ *hashsize = 0;
+ }
+ }
+ }
+
+ SCLogDebug("key: %s, value: %s", key, val);
+
+ next:
+ key = strtok_r(NULL, ",", &xsaveptr);
+ }
+
+ if (strlen(load) > 0 && *type == DATASET_TYPE_NOTSET) {
+ SCLogError("if load is used type must be set as well");
+ return 0;
+ }
+
+ if (!name_set || !cmd_set || !value_set) {
+ SCLogError("missing values");
+ return 0;
+ }
+
+ /* Trim trailing whitespace. */
+ while (strlen(name) > 0 && isblank(name[strlen(name) - 1])) {
+ name[strlen(name) - 1] = '\0';
+ }
+
+ /* Validate name, spaces are not allowed. */
+ for (size_t i = 0; i < strlen(name); i++) {
+ if (isblank(name[i])) {
+ SCLogError("spaces not allowed in dataset names");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/** \brief wrapper around dirname that does leave input untouched */
+static void GetDirName(const char *in, char *out, size_t outs)
+{
+ if (strlen(in) == 0) {
+ return;
+ }
+
+ size_t size = strlen(in) + 1;
+ char tmp[size];
+ strlcpy(tmp, in, size);
+
+ char *dir = dirname(tmp);
+ BUG_ON(dir == NULL);
+ strlcpy(out, dir, outs);
+ return;
+}
+
+static int SetupLoadPath(const DetectEngineCtx *de_ctx,
+ char *load, size_t load_size)
+{
+ SCLogDebug("load %s", load);
+
+ if (PathIsAbsolute(load)) {
+ return 0;
+ }
+
+ bool done = false;
+#ifdef HAVE_LIBGEN_H
+ BUG_ON(de_ctx->rule_file == NULL);
+
+ char dir[PATH_MAX] = "";
+ GetDirName(de_ctx->rule_file, dir, sizeof(dir));
+
+ SCLogDebug("rule_file %s dir %s", de_ctx->rule_file, dir);
+ char path[PATH_MAX];
+ if (snprintf(path, sizeof(path), "%s/%s", dir, load) >= (int)sizeof(path)) // TODO windows path
+ return -1;
+
+ if (SCPathExists(path)) {
+ done = true;
+ strlcpy(load, path, load_size);
+ SCLogDebug("using path '%s' (HAVE_LIBGEN_H)", load);
+ } else {
+ SCLogDebug("path '%s' does not exist (HAVE_LIBGEN_H)", path);
+ }
+#endif
+ if (!done) {
+ char *loadp = DetectLoadCompleteSigPath(de_ctx, load);
+ if (loadp == NULL) {
+ return -1;
+ }
+ SCLogDebug("loadp %s", loadp);
+
+ if (SCPathExists(loadp)) {
+ strlcpy(load, loadp, load_size);
+ SCLogDebug("using path '%s' (non-HAVE_LIBGEN_H)", load);
+ } else {
+ SCLogDebug("path '%s' does not exist (non-HAVE_LIBGEN_H)", loadp);
+ }
+ SCFree(loadp);
+
+ // TODO try data-dir as well?
+ }
+ return 0;
+}
+
+static int DetectDatarepSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
+{
+ SigMatch *sm = NULL;
+ char cmd_str[16] = "", name[64] = "";
+ enum DatasetTypes type = DATASET_TYPE_NOTSET;
+ char load[PATH_MAX] = "";
+ uint16_t value = 0;
+ uint64_t memcap = 0;
+ uint32_t hashsize = 0;
+
+ if (DetectBufferGetActiveList(de_ctx, s) == -1) {
+ SCLogError("datarep is only supported for sticky buffers");
+ SCReturnInt(-1);
+ }
+
+ int list = s->init_data->list;
+ if (list == DETECT_SM_LIST_NOTSET) {
+ SCLogError("datarep is only supported for sticky buffers");
+ SCReturnInt(-1);
+ }
+
+ if (!DetectDatarepParse(rawstr, cmd_str, sizeof(cmd_str), name, sizeof(name), &type, load,
+ sizeof(load), &value, &memcap, &hashsize)) {
+ return -1;
+ }
+
+ if (strlen(load) != 0) {
+ if (SetupLoadPath(de_ctx, load, sizeof(load)) != 0)
+ return -1;
+ }
+
+ enum DetectDatarepOp op;
+ if (strcmp(cmd_str,">") == 0) {
+ op = DATAREP_OP_GT;
+ } else if (strcmp(cmd_str,"<") == 0) {
+ op = DATAREP_OP_LT;
+ } else if (strcmp(cmd_str,"==") == 0) {
+ op = DATAREP_OP_EQ;
+ } else {
+ SCLogError("datarep operation \"%s\" is not supported.", cmd_str);
+ return -1;
+ }
+
+ Dataset *set = DatasetGet(name, type, /* no save */ NULL, load, memcap, hashsize);
+ if (set == NULL) {
+ SCLogError("failed to set up datarep set '%s'.", name);
+ return -1;
+ }
+
+ DetectDatarepData *cd = SCCalloc(1, sizeof(DetectDatarepData));
+ if (unlikely(cd == NULL))
+ goto error;
+
+ cd->set = set;
+ cd->op = op;
+ cd->rep.value = value;
+
+ SCLogDebug("cmd %s, name %s",
+ cmd_str, strlen(name) ? name : "(none)");
+
+ /* Okay so far so good, lets get this into a SigMatch
+ * and put it in the Signature. */
+ sm = SigMatchAlloc();
+ if (sm == NULL)
+ goto error;
+
+ sm->type = DETECT_DATAREP;
+ sm->ctx = (SigMatchCtx *)cd;
+ SigMatchAppendSMToList(s, sm, list);
+ return 0;
+
+error:
+ if (cd != NULL)
+ SCFree(cd);
+ if (sm != NULL)
+ SCFree(sm);
+ return -1;
+}
+
+void DetectDatarepFree (DetectEngineCtx *de_ctx, void *ptr)
+{
+ DetectDatarepData *fd = (DetectDatarepData *)ptr;
+
+ if (fd == NULL)
+ return;
+
+ SCFree(fd);
+}