]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
requires: pre-scan rule for requires expressions
authorJason Ish <jason.ish@oisf.net>
Wed, 29 Nov 2023 16:54:54 +0000 (10:54 -0600)
committerVictor Julien <victor@inliniac.net>
Tue, 19 Dec 2023 08:51:22 +0000 (09:51 +0100)
Add a "pre-scan" rule parse that will check for requires statement. It
will return a special error code (-4) if the requires fails due to
missing requirements.

Syntactic errors will also abort parsing here.

Feature: #5972

src/detect-parse.c

index b94d54dd6cd04fea60fc4ad4c220580b793e1b6c..493bee10ea9fd6dcf0a2b6b7be7641af67748344 100644 (file)
@@ -845,7 +845,8 @@ int SigMatchListSMBelongsTo(const Signature *s, const SigMatch *key_sm)
     return -1;
 }
 
-static int SigParseOptions(DetectEngineCtx *de_ctx, Signature *s, char *optstr, char *output, size_t output_size)
+static int SigParseOptions(DetectEngineCtx *de_ctx, Signature *s, char *optstr, char *output,
+        size_t output_size, bool requires)
 {
     SigTableElmt *st = NULL;
     char *optname = NULL;
@@ -899,6 +900,12 @@ static int SigParseOptions(DetectEngineCtx *de_ctx, Signature *s, char *optstr,
     }
     optname = optstr;
 
+    if (requires) {
+        if (strcmp(optname, "requires")) {
+            goto finish;
+        }
+    }
+
     /* Call option parsing */
     st = SigTableGet(optname);
     if (st == NULL || st->Setup == NULL) {
@@ -1038,6 +1045,7 @@ static int SigParseOptions(DetectEngineCtx *de_ctx, Signature *s, char *optstr,
     }
     s->init_data->negated = false;
 
+finish:
     if (strlen(optend) > 0) {
         strlcpy(output, optend, output_size);
         return 1;
@@ -1323,9 +1331,11 @@ static inline int SigParseList(char **input, char *output,
 /**
  *  \internal
  *  \brief split a signature string into a few blocks for further parsing
+ *
+ *  \param scan_only just scan, don't validate
  */
-static int SigParseBasics(DetectEngineCtx *de_ctx,
-        Signature *s, const char *sigstr, SignatureParser *parser, uint8_t addrs_direction)
+static int SigParseBasics(DetectEngineCtx *de_ctx, Signature *s, const char *sigstr,
+        SignatureParser *parser, uint8_t addrs_direction, bool scan_only)
 {
     char *index, dup[DETECT_MAX_RULE_SIZE];
 
@@ -1370,6 +1380,10 @@ static int SigParseBasics(DetectEngineCtx *de_ctx,
     }
     strlcpy(parser->opts, index, sizeof(parser->opts));
 
+    if (scan_only) {
+        return 0;
+    }
+
     /* Parse Action */
     if (SigParseAction(s, parser->action) < 0)
         goto error;
@@ -1431,12 +1445,13 @@ static inline bool CheckAscii(const char *str)
  *  \param s memory structure to store the signature in
  *  \param sigstr the raw signature as a null terminated string
  *  \param addrs_direction direction (for bi-directional sigs)
+ *  \param require only scan rule for requires
  *
  *  \param -1 parse error
  *  \param 0 ok
  */
-static int SigParse(DetectEngineCtx *de_ctx, Signature *s,
-        const char *sigstr, uint8_t addrs_direction, SignatureParser *parser)
+static int SigParse(DetectEngineCtx *de_ctx, Signature *s, const char *sigstr,
+        uint8_t addrs_direction, SignatureParser *parser, bool requires)
 {
     SCEnter();
 
@@ -1450,12 +1465,7 @@ static int SigParse(DetectEngineCtx *de_ctx, Signature *s,
         SCReturnInt(-1);
     }
 
-    s->sig_str = SCStrdup(sigstr);
-    if (unlikely(s->sig_str == NULL)) {
-        SCReturnInt(-1);
-    }
-
-    int ret = SigParseBasics(de_ctx, s, sigstr, parser, addrs_direction);
+    int ret = SigParseBasics(de_ctx, s, sigstr, parser, addrs_direction, requires);
     if (ret < 0) {
         SCLogDebug("SigParseBasics failed");
         SCReturnInt(-1);
@@ -1467,21 +1477,27 @@ static int SigParse(DetectEngineCtx *de_ctx, Signature *s,
         char input[buffer_size];
         char output[buffer_size];
         memset(input, 0x00, buffer_size);
-        memcpy(input, parser->opts, strlen(parser->opts)+1);
+        memcpy(input, parser->opts, strlen(parser->opts) + 1);
 
         /* loop the option parsing. Each run processes one option
          * and returns the rest of the option string through the
          * output variable. */
         do {
             memset(output, 0x00, buffer_size);
-            ret = SigParseOptions(de_ctx, s, input, output, buffer_size);
+            ret = SigParseOptions(de_ctx, s, input, output, buffer_size, requires);
             if (ret == 1) {
                 memcpy(input, output, buffer_size);
             }
 
         } while (ret == 1);
+
+        if (ret < 0) {
+            /* Suricata didn't meet the rule requirements, skip. */
+            goto end;
+        }
     }
 
+end:
     DetectIPProtoRemoveAllSMs(de_ctx, s);
 
     SCReturnInt(ret);
@@ -2142,17 +2158,33 @@ static Signature *SigInitHelper(DetectEngineCtx *de_ctx, const char *sigstr,
     if (sig == NULL)
         goto error;
 
+    sig->sig_str = SCStrdup(sigstr);
+    if (unlikely(sig->sig_str == NULL)) {
+        goto error;
+    }
+
     /* default gid to 1 */
     sig->gid = 1;
 
-    int ret = SigParse(de_ctx, sig, sigstr, dir, &parser);
+    /* We do a first parse of the rule in a requires, or scan-only
+     * mode. Syntactic errors will be picked up here, but the only
+     * part of the rule that is validated completely is the "requires"
+     * keyword. */
+    int ret = SigParse(de_ctx, sig, sigstr, dir, &parser, true);
     if (ret == -4) {
         /* Rule requirements not met. */
         de_ctx->sigerror_silent = true;
         de_ctx->sigerror_ok = true;
         de_ctx->sigerror_requires = true;
         goto error;
-    } else if (ret == -3) {
+    } else if (ret < 0) {
+        goto error;
+    }
+
+    /* Now completely parse the rule. */
+    ret = SigParse(de_ctx, sig, sigstr, dir, &parser, false);
+    BUG_ON(ret == -4);
+    if (ret == -3) {
         de_ctx->sigerror_silent = true;
         de_ctx->sigerror_ok = true;
         goto error;