]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
alert/reference: Optionally add reference(s)
authorJeff Lucovsky <jeff.lucovsky@corelight.com>
Sat, 27 Apr 2024 13:55:38 +0000 (09:55 -0400)
committerVictor Julien <victor@inliniac.net>
Wed, 18 Sep 2024 08:31:03 +0000 (10:31 +0200)
Issue: 4974

Optionally include rule references with the alert. Since there can be
multiple reference keywords, they are collected into an array.

etc/schema.json
src/detect-reference.c
src/detect-reference.h
src/output-json-alert.c
suricata.yaml.in

index 74439841a22feaae3ea6b5de5e9ff13ca46b106c..9decf4c42bf9b8366d7cbb01ca8d5c5ac28ffb2a 100644 (file)
                     },
                     "additionalProperties": true
                 },
+                "references": {
+                    "type": "array",
+                    "minItems": 1,
+                    "items": {
+                        "type": "string"
+                    }
+                },
                 "source": {
                     "type": "object",
                     "properties": {
index aaa723db496320b9c572fca5b9891510207ce6dd..89b6292abd657e9447573478256bb8db83ccea7b 100644 (file)
 #include "util-byte.h"
 #include "util-debug.h"
 
-#define PARSE_REGEX "^\\s*([A-Za-z0-9]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z0-9\\-_\\.\\/\\?\\=]+)\"?\\s*\"?"
+/* Breakout key and scheme (optional) and domain/path (mandatory) */
+#define PARSE_REGEX                                                                                \
+    "^\\s*([A-Za-z0-9]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z]+:\\/\\/)?([a-zA-Z0-9\\-_\\.\\/"               \
+    "\\?\\=]+)\"?\\s*\"?"
 
 static DetectParseRegex parse_regex;
 
@@ -74,6 +77,9 @@ void DetectReferenceFree(DetectReference *ref)
 {
     SCEnter();
 
+    if (ref->key)
+        SCFree(ref->key);
+
     if (ref->reference != NULL) {
         SCFree(ref->reference);
     }
@@ -98,11 +104,12 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx
     int res = 0;
     size_t pcre2len;
     char key[REFERENCE_SYSTEM_NAME_MAX] = "";
-    char content[REFERENCE_CONTENT_NAME_MAX] = "";
+    char scheme[REFERENCE_SYSTEM_NAME_MAX] = "";
+    char uri[REFERENCE_CONTENT_NAME_MAX] = "";
 
     pcre2_match_data *match = NULL;
     int ret = DetectParsePcreExec(&parse_regex, &match, rawstr, 0, 0);
-    if (ret < 2) {
+    if (ret != 4) {
         SCLogError("Unable to parse \"reference\" "
                    "keyword argument - \"%s\".   Invalid argument.",
                 rawstr);
@@ -118,51 +125,72 @@ static DetectReference *DetectReferenceParse(const char *rawstr, DetectEngineCtx
         return NULL;
     }
 
+    /* Position 1 = key (mandatory) */
     pcre2len = sizeof(key);
     res = pcre2_substring_copy_bynumber(match, 1, (PCRE2_UCHAR8 *)key, &pcre2len);
     if (res < 0) {
-        SCLogError("pcre2_substring_copy_bynumber failed");
+        SCLogError("pcre2_substring_copy_bynumber key failed");
         goto error;
     }
 
-    pcre2len = sizeof(content);
-    res = pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)content, &pcre2len);
+    /* Position 2 = scheme (optional) */
+    pcre2len = sizeof(scheme);
+    (void)pcre2_substring_copy_bynumber(match, 2, (PCRE2_UCHAR8 *)scheme, &pcre2len);
+
+    /* Position 3 = domain-path (mandatory) */
+    pcre2len = sizeof(uri);
+    res = pcre2_substring_copy_bynumber(match, 3, (PCRE2_UCHAR8 *)uri, &pcre2len);
     if (res < 0) {
-        SCLogError("pcre2_substring_copy_bynumber failed");
+        SCLogError("pcre2_substring_copy_bynumber domain-path failed");
         goto error;
     }
 
-    if (strlen(key) == 0 || strlen(content) == 0)
+    int ref_len = strlen(uri);
+    /* no key, reference -- return an error */
+    if (strlen(key) == 0 || ref_len == 0)
         goto error;
 
-    SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx);
-    if (lookup_ref_conf != NULL) {
-        ref->key = lookup_ref_conf->url;
+    if (strlen(scheme)) {
+        SCLogConfig("scheme value %s overrides key %s", scheme, key);
+        ref->key = SCStrdup(scheme);
+        /* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */
+        ref->key_len = (uint16_t)strlen(scheme);
     } else {
-        if (SigMatchStrictEnabled(DETECT_REFERENCE)) {
-            SCLogError("unknown reference key \"%s\"", key);
-            goto error;
-        }
-
-        SCLogWarning("unknown reference key \"%s\"", key);
 
-        char str[2048];
-        snprintf(str, sizeof(str), "config reference: %s undefined\n", key);
-
-        if (SCRConfAddReference(de_ctx, str) < 0)
-            goto error;
-        lookup_ref_conf = SCRConfGetReference(key, de_ctx);
-        if (lookup_ref_conf == NULL)
-            goto error;
+        SCRConfReference *lookup_ref_conf = SCRConfGetReference(key, de_ctx);
+        if (lookup_ref_conf != NULL) {
+            ref->key = SCStrdup(lookup_ref_conf->url);
+            /* already bound checked to be REFERENCE_SYSTEM_NAME_MAX or less */
+            ref->key_len = (uint16_t)strlen(ref->key);
+        } else {
+            if (SigMatchStrictEnabled(DETECT_REFERENCE)) {
+                SCLogError("unknown reference key \"%s\"", key);
+                goto error;
+            }
+
+            SCLogWarning("unknown reference key \"%s\"", key);
+
+            char str[2048];
+            snprintf(str, sizeof(str), "config reference: %s undefined\n", key);
+
+            if (SCRConfAddReference(de_ctx, str) < 0)
+                goto error;
+            lookup_ref_conf = SCRConfGetReference(key, de_ctx);
+            if (lookup_ref_conf == NULL)
+                goto error;
+        }
     }
 
     /* make a copy so we can free pcre's substring */
-    ref->reference = SCStrdup(content);
+    ref->reference = SCStrdup(uri);
     if (ref->reference == NULL) {
         SCLogError("strdup failed: %s", strerror(errno));
         goto error;
     }
 
+    /* already bound checked to be REFERENCE_CONTENT_NAME_MAX or less */
+    ref->reference_len = (uint16_t)ref_len;
+
     pcre2_match_data_free(match);
     /* free the substrings */
     SCReturnPtr(ref, "Reference");
index c2c68f768bfb4b37b1f5b47ac6d5ba3ebd798bdf..db8e13ce88638651cdb8e31e0b3dc5e830206b3d 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2010 Open Information Security Foundation
+/* Copyright (C) 2007-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
@@ -32,6 +32,13 @@ typedef struct DetectReference_ {
     char *key;
     /* reference data */
     char *reference;
+
+    /*
+     * These have been length checked against REFERENCE_SYSTEM_NAME_MAX,
+     * and REFERENCE_CONTENT_NAME_MAX
+     */
+    uint16_t key_len;
+    uint16_t reference_len;
     /* next reference in the signature */
     struct DetectReference_ *next;
 } DetectReference;
index 624cb6e491fa01ff32ab2c66f19f8fc8b57542bf..f2e40641408b13a47622b897186018d79f2f4e7b 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2013-2023 Open Information Security Foundation
+/* Copyright (C) 2013-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
@@ -63,6 +63,7 @@
 #include "util-print.h"
 #include "util-optimize.h"
 #include "util-buffer.h"
+#include "util-reference-config.h"
 #include "util-validate.h"
 
 #include "action-globals.h"
@@ -83,6 +84,7 @@
 #define LOG_JSON_WEBSOCKET_PAYLOAD        BIT_U16(11)
 #define LOG_JSON_WEBSOCKET_PAYLOAD_BASE64 BIT_U16(12)
 #define LOG_JSON_PAYLOAD_LENGTH           BIT_U16(13)
+#define LOG_JSON_REFERENCE                BIT_U16(14)
 
 #define METADATA_DEFAULTS ( LOG_JSON_FLOW |                        \
             LOG_JSON_APP_LAYER  |                                  \
@@ -169,6 +171,27 @@ static void AlertJsonSourceTarget(const Packet *p, const PacketAlert *pa,
     jb_close(js);
 }
 
+static void AlertJsonReference(const PacketAlert *pa, JsonBuilder *jb)
+{
+    if (!pa->s->references) {
+        return;
+    }
+
+    const DetectReference *kv = pa->s->references;
+    jb_open_array(jb, "references");
+    while (kv) {
+        /* Note that the key and reference sizes have been bound
+         * checked during parsing
+         */
+        const size_t size_needed = kv->key_len + kv->reference_len + 1;
+        char kv_store[size_needed];
+        snprintf(kv_store, size_needed, "%s%s", kv->key, kv->reference);
+        jb_append_string(jb, kv_store);
+        kv = kv->next;
+    }
+    jb_close(jb);
+}
+
 static void AlertJsonMetadata(AlertJsonOutputCtx *json_output_ctx,
         const PacketAlert *pa, JsonBuilder *js)
 {
@@ -221,6 +244,10 @@ void AlertJsonHeader(void *ctx, const Packet *p, const PacketAlert *pa, JsonBuil
         AlertJsonSourceTarget(p, pa, js, addr);
     }
 
+    if ((json_output_ctx != NULL) && (flags & LOG_JSON_REFERENCE)) {
+        AlertJsonReference(pa, js);
+    }
+
     if ((json_output_ctx != NULL) && (flags & LOG_JSON_RULE_METADATA)) {
         AlertJsonMetadata(json_output_ctx, pa, js);
     }
@@ -902,6 +929,7 @@ static void JsonAlertLogSetupMetadata(AlertJsonOutputCtx *json_output_ctx,
                     SetFlag(rule_metadata, "raw", LOG_JSON_RULE, &flags);
                     SetFlag(rule_metadata, "metadata", LOG_JSON_RULE_METADATA,
                             &flags);
+                    SetFlag(rule_metadata, "reference", LOG_JSON_REFERENCE, &flags);
                 }
                 SetFlag(metadata, "flow", LOG_JSON_FLOW, &flags);
                 SetFlag(metadata, "app-layer", LOG_JSON_APP_LAYER, &flags);
index 9c116a3082a23a46aafb81639579436f738e8100..2e5f722c366c0ecca8b1af3fd1b22061e07080f6 100644 (file)
@@ -168,6 +168,19 @@ outputs:
             # payload-length: yes      # enable dumping payload length, including the gaps
             # packet: yes              # enable dumping of packet (without stream segments)
             # metadata: no             # enable inclusion of app layer metadata with alert. Default yes
+            # If you want metadata, use:
+            # metadata:
+              # Include the decoded application layer (ie. http, dns)
+              #app-layer: true
+              # Log the current state of the flow record.
+              #flow: true
+              #rule:
+                # Log the metadata field from the rule in a structured
+                # format.
+                #metadata: true
+                # Log the raw rule text.
+                #raw: false
+                #reference: false      # include reference information from the rule
             # http-body: yes           # Requires metadata; enable dumping of HTTP body in Base64
             # http-body-printable: yes # Requires metadata; enable dumping of HTTP body in printable format
             # websocket-payload: yes   # Requires metadata; enable dumping of WebSocket Payload in Base64