From 2f740fc8aa705df046a6d32fc98e2787df0e47e1 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 26 Sep 2023 01:50:02 +0200
Subject: Handle tags linked to ROST rules.

---
 src/analysis/scan/grammar.y     | 23 ++++++++--
 src/analysis/scan/options-int.h |  3 ++
 src/analysis/scan/options.c     | 76 +++++++++++++++++++++++++++++++++
 src/analysis/scan/options.h     |  6 +++
 src/analysis/scan/rule-int.h    |  3 ++
 src/analysis/scan/rule.c        | 95 ++++++++++++++++++++++++++++++++++++++---
 src/analysis/scan/rule.h        |  6 +++
 src/analysis/scan/tokens.l      |  6 ++-
 src/rost.c                      | 15 ++++---
 9 files changed, 215 insertions(+), 18 deletions(-)

diff --git a/src/analysis/scan/grammar.y b/src/analysis/scan/grammar.y
index e1f0e9e..741b394 100644
--- a/src/analysis/scan/grammar.y
+++ b/src/analysis/scan/grammar.y
@@ -116,7 +116,7 @@ YY_DECL;
 %token INCLUDE          "include"
 
 %token RAW_RULE
-%token RULE_NAME
+%token RULE_IDENTIFIER
 
 %token META "meta"
 %token BYTES "bytes"
@@ -216,7 +216,7 @@ YY_DECL;
 %token IN               "in"
 
 
-%type <sized_cstring> RULE_NAME
+%type <sized_cstring> RULE_IDENTIFIER
 
 %type <sized_cstring> INFO_KEY
 
@@ -346,12 +346,12 @@ YY_DECL;
  * Définition de règle.
  */
 
-              rule : rule_flags RAW_RULE RULE_NAME
+              rule : rule_flags RAW_RULE RULE_IDENTIFIER
                    {
                        *built_rule = g_scan_rule_new($1, $3.data);
                        $<rule>$ = *built_rule;
                    }
-                   BRACE_IN meta bytes condition BRACE_OUT
+                   tags BRACE_IN meta bytes condition BRACE_OUT
                    {
                        $$ = $<rule>4;
                    }
@@ -379,6 +379,21 @@ YY_DECL;
                    ;
 
 
+              tags : /* empty */
+                   | ":" tag_list
+                   ;
+
+          tag_list : RULE_IDENTIFIER
+                   {
+                       g_scan_rule_add_tag(*built_rule, $1.data);
+                   }
+                   | tag_list RULE_IDENTIFIER
+                   {
+                       g_scan_rule_add_tag(*built_rule, $2.data);
+                   }
+                   ;
+
+
 /**
  * Section "meta:" d'une définition de règle.
  */
diff --git a/src/analysis/scan/options-int.h b/src/analysis/scan/options-int.h
index e8ae428..9e1c7d8 100644
--- a/src/analysis/scan/options-int.h
+++ b/src/analysis/scan/options-int.h
@@ -42,6 +42,9 @@ struct _GScanOptions
     bool print_strings;                     /* Affichage de correspondances*/
     bool print_stats;                       /* Affichage de statistiques ? */
 
+    char **selected_tags;                   /* Etiquettes sélectionnées    */
+    size_t selected_count;                  /* Nombre de ces étiquettes    */
+
 };
 
 /* Rassemblement d'options d'analyses (classe) */
diff --git a/src/analysis/scan/options.c b/src/analysis/scan/options.c
index 637c821..0b50495 100644
--- a/src/analysis/scan/options.c
+++ b/src/analysis/scan/options.c
@@ -24,6 +24,10 @@
 #include "options.h"
 
 
+#include <malloc.h>
+#include <string.h>
+
+
 #include "options-int.h"
 
 
@@ -92,6 +96,9 @@ static void g_scan_options_init(GScanOptions *options)
     options->print_strings = false;
     options->print_stats = false;
 
+    options->selected_tags = NULL;
+    options->selected_count = 0;
+
 }
 
 
@@ -128,6 +135,14 @@ static void g_scan_options_dispose(GScanOptions *options)
 
 static void g_scan_options_finalize(GScanOptions *options)
 {
+    size_t i;                               /* Boucle de parcours          */
+
+    for (i = 0; i < options->selected_count; i++)
+        free(options->selected_tags[i]);
+
+    if (options->selected_tags != NULL)
+        free(options->selected_tags);
+
     G_OBJECT_CLASS(g_scan_options_parent_class)->finalize(G_OBJECT(options));
 
 }
@@ -369,3 +384,64 @@ void g_scan_options_set_print_stats(GScanOptions *options, bool state)
     options->print_stats = state;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : options = ensemble d'options d'analyses à compléter.         *
+*                tag     = étiquette de règle à sélectionner.                 *
+*                                                                             *
+*  Description : Inscrit une étiquette comme sélection de règles à afficher.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_scan_options_select_tag(GScanOptions *options, const char *tag)
+{
+    options->selected_tags = realloc(options->selected_tags, ++options->selected_count * sizeof(char *));
+
+    options->selected_tags[options->selected_count - 1] = strdup(tag);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : options = ensemble d'options d'analyses à compléter.         *
+*                tag     = étiquette de règle à auditionner.                  *
+*                                                                             *
+*  Description : Détermine si une étiquette donnée conduit à un affichage.    *
+*                                                                             *
+*  Retour      : true si une règle portant l'étiquette doit être affichée.    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_scan_options_has_tag_as_selected(const GScanOptions *options, const char *tag)
+{
+    bool result;                            /* Bilan à retourner           */
+    size_t i;                               /* Boucle de parcours          */
+
+    if (tag == NULL)
+        result = (options->selected_count == 0);
+
+    else
+    {
+        result = false;
+
+        for (i = 0; i < options->selected_count; i++)
+            if (strcmp(options->selected_tags[i], tag) == 0)
+            {
+                result = true;
+                break;
+            }
+
+    }
+
+    return result;
+
+}
diff --git a/src/analysis/scan/options.h b/src/analysis/scan/options.h
index c6db838..fc1b7d3 100644
--- a/src/analysis/scan/options.h
+++ b/src/analysis/scan/options.h
@@ -81,6 +81,12 @@ bool g_scan_options_get_print_stats(const GScanOptions *);
 /* Mémorise un besoin de statistiques en fin de compilation. */
 void g_scan_options_set_print_stats(GScanOptions *, bool);
 
+/* Inscrit une étiquette comme sélection de règles à afficher. */
+void g_scan_options_select_tag(GScanOptions *, const char *);
+
+/* Détermine si une étiquette donnée conduit à un affichage. */
+bool g_scan_options_has_tag_as_selected(const GScanOptions *, const char *);
+
 
 
 #endif  /* _ANALYSIS_SCAN_OPTIONS_H */
diff --git a/src/analysis/scan/rule-int.h b/src/analysis/scan/rule-int.h
index 1ca2b7f..17d4dc2 100644
--- a/src/analysis/scan/rule-int.h
+++ b/src/analysis/scan/rule-int.h
@@ -42,6 +42,9 @@ struct _GScanRule
     char *name;                             /* Désignation de la règle     */
     fnv64_t name_hash;                      /* Empreinte de la désignation */
 
+    char **tags;                            /* Etiquettes associées        */
+    char tags_count;                        /* Quantité de ces étiquettes  */
+
     GSearchPattern **bytes_locals;          /* Variables de données        */
     size_t bytes_allocated;                 /* Taille allouée du tableau   */
     size_t bytes_used;                      /* Nombre d'éléments présents  */
diff --git a/src/analysis/scan/rule.c b/src/analysis/scan/rule.c
index 68222dd..29ae826 100644
--- a/src/analysis/scan/rule.c
+++ b/src/analysis/scan/rule.c
@@ -102,6 +102,9 @@ static void g_scan_rule_init(GScanRule *rule)
     rule->name = NULL;
     rule->name_hash = 0;
 
+    rule->tags = NULL;
+    rule->tags_count = 0;
+
     rule->bytes_locals = NULL;
     rule->bytes_allocated = 0;
     rule->bytes_used = 0;
@@ -151,9 +154,17 @@ static void g_scan_rule_dispose(GScanRule *rule)
 
 static void g_scan_rule_finalize(GScanRule *rule)
 {
+    size_t i;                               /* Boucle de parcours          */
+
     if (rule->name != NULL)
         free(rule->name);
 
+    for (i = 0; i < rule->tags_count; i++)
+        free(rule->tags[i]);
+
+    if (rule->tags != NULL)
+        free(rule->tags);
+
     G_OBJECT_CLASS(g_scan_rule_parent_class)->finalize(G_OBJECT(rule));
 
 }
@@ -270,6 +281,54 @@ const char *g_scan_rule_get_name(const GScanRule *rule, fnv64_t *hash)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : rule = règle de détection à compléter.                       *
+*                tag  = étiquette à associer à la règle.                      *
+*                                                                             *
+*  Description : Lie une règle à une nouvelle étiquette.                      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_scan_rule_add_tag(GScanRule *rule, const char *tag)
+{
+    rule->tags = realloc(rule->tags, ++rule->tags_count * sizeof(char *));
+
+    rule->tags[rule->tags_count - 1] = strdup(tag);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : rule  = règle de détection à consulter.                      *
+*                count = quantité d'éléments retournés. [OUT]                 *
+*                                                                             *
+*  Description : Indique les éventuelles étiquettes associées à une règle.    *
+*                                                                             *
+*  Retour      : Liste d'étiquettes associées à la règle consultée.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+const char * const *g_scan_rule_list_tags(const GScanRule *rule, size_t *count)
+{
+    const char * const *result;             /* Liste à retourner           */
+
+    result = rule->tags;
+
+    *count = rule->tags_count;
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : rule    = règle de détection à compléter.                    *
 *                pattern = nouveau motif de détection.                        *
 *                                                                             *
@@ -597,19 +656,41 @@ void g_scan_rule_check(GScanRule *rule, GEngineBackend *backend, GScanContext *c
 
 void g_scan_rule_output_to_text(const GScanRule *rule, GScanContext *context, bool full, int fd)
 {
+    GScanOptions *options;                  /* Options de l'utilisateur    */
+    bool selected;                          /* Affichage attendu ?         */
     size_t i;                               /* Boucle de parcours          */
 
-    if (full)
-        for (i = 0; i < rule->bytes_used; i++)
-            g_search_pattern_output_to_text(rule->bytes_locals[i], context, fd);
+    options = g_scan_context_get_options(context);
 
-    if (g_scan_context_has_match_for_rule(context, rule->name))
+    if (rule->tags_count == 0)
+        selected = g_scan_options_has_tag_as_selected(options, NULL);
+
+    else
+    {
+        selected = false;
+
+        for (i = 0; i < rule->tags_count && !selected; i++)
+            selected = g_scan_options_has_tag_as_selected(options, rule->tags[i]);
+
+    }
+
+    if (selected)
     {
-        write(fd, "Rule '", 6);
-        write(fd, rule->name, strlen(rule->name));
-        write(fd, "' has matched!\n", 15);
+        if (full)
+            for (i = 0; i < rule->bytes_used; i++)
+                g_search_pattern_output_to_text(rule->bytes_locals[i], context, fd);
+
+        if (g_scan_context_has_match_for_rule(context, rule->name))
+        {
+            write(fd, "Rule '", 6);
+            write(fd, rule->name, strlen(rule->name));
+            write(fd, "' has matched!\n", 15);
+        }
+
     }
 
+    g_object_unref(G_OBJECT(options));
+
 }
 
 
diff --git a/src/analysis/scan/rule.h b/src/analysis/scan/rule.h
index 3e6fe9d..b73ee1a 100644
--- a/src/analysis/scan/rule.h
+++ b/src/analysis/scan/rule.h
@@ -75,6 +75,12 @@ ScanRuleFlags g_scan_rule_get_flags(const GScanRule *);
 /* Indique le nom associé à une règle de détection. */
 const char *g_scan_rule_get_name(const GScanRule *, fnv64_t *);
 
+/* Lie une règle à une nouvelle étiquette. */
+void g_scan_rule_add_tag(GScanRule *, const char *);
+
+/* Indique les éventuelles étiquettes associées à une règle. */
+const char * const *g_scan_rule_list_tags(const GScanRule *, size_t *);
+
 /* Intègre une nouvelle variable locale à une règle. */
 void g_scan_rule_add_local_variable(GScanRule *, GSearchPattern *);
 
diff --git a/src/analysis/scan/tokens.l b/src/analysis/scan/tokens.l
index 1a17344..b284128 100644
--- a/src/analysis/scan/tokens.l
+++ b/src/analysis/scan/tokens.l
@@ -411,12 +411,14 @@ bytes_fuzzy_id [\*A-Za-z_][\*A-Za-z0-9_]*
                                         return RAW_RULE;
                                     }
 
-          <rule_intro>[A-Za-z0-9_]+ {
+             <rule_intro>{bytes_id} {
                                         yylval->sized_cstring.data = yytext;
                                         yylval->sized_cstring.len = yyleng;
-                                        return RULE_NAME;
+                                        return RULE_IDENTIFIER;
                                     }
 
+                    <rule_intro>":" { return COLON; }
+
                  <rule_intro>[ \t]* { }
 
                     <rule_intro>"{" {
diff --git a/src/rost.c b/src/rost.c
index efe18e5..8a7e806 100644
--- a/src/rost.c
+++ b/src/rost.c
@@ -91,11 +91,12 @@ static void show_rost_help(const char *name)
 
     printf("\n");
 
-    printf("\t-A --algorithm=name\tSelect one of the available algorithms for data: bitmap, acism (default: acsim).\n");
+    printf("\t-A --algorithm=NAME\tSelect one of the available algorithms for data: bitmap, acism (default: acsim).\n");
     printf("\t-C --check-only\t\tValidate the rule syntax without performing a scan (discard the file/dir argument).\n");
-    printf("\t-j --print-json\t\tPrint matching strings in JSON format.\n");
-    printf("\t-s --print-strings\tPrint matching strings.\n");
+    printf("\t-j --print-json\t\tPrint matching strings in JSON format instead of simple text.\n");
+    printf("\t-s --print-strings\tPrint matching strings (default text format only).\n");
     printf("\t-S --print-stats\tPrint rules' statistics.\n");
+    printf("\t-t --tag=TAG\t\tprint only matching rules tagged as TAG (default text format only).\n");
     printf("\t-V --verbosity=level\tSet the log level (0 for all messages, %u for none).\n", LMT_COUNT);
 
     printf("\n");
@@ -240,6 +241,7 @@ int main(int argc, char **argv)
         { "print-json",     no_argument,        NULL,   'j' },
         { "print-strings",  no_argument,        NULL,   's' },
         { "print-stats",    no_argument,        NULL,   'S' },
+        { "tag",            required_argument,  NULL,   't' },
         { "verbosity",      required_argument,  NULL,   'V' },
         { NULL,             0,                  NULL,   0 }
     };
@@ -260,7 +262,7 @@ int main(int argc, char **argv)
 
     while (true)
     {
-        ret = getopt_long(argc, argv, "hvA:CjsSV:", long_options, &index);
+        ret = getopt_long(argc, argv, "hvA:CjsSt:V:", long_options, &index);
         if (ret == -1) break;
 
         switch (ret)
@@ -299,6 +301,10 @@ int main(int argc, char **argv)
                 g_scan_options_set_print_stats(options, true);
                 break;
 
+            case 't':
+                g_scan_options_select_tag(options, optarg);
+                break;
+
             case 'V':
                 verbosity = strtoul(optarg, NULL, 10);
                 break;
@@ -310,7 +316,6 @@ int main(int argc, char **argv)
     if ((check_only && (optind + 0) != argc && (optind + 1) != argc)
         || (!check_only && (optind + 1) != argc && (optind + 2) != argc))
     {
-        printf("failed: check=%d optind=%d argc=%d\n", check_only, optind, argc);
         show_rost_help(argv[0]);
         goto done;
     }
-- 
cgit v0.11.2-87-g4458