]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #483 in SNORT/snort3 from sd_pattern-fix to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 31 May 2016 19:56:33 +0000 (15:56 -0400)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 31 May 2016 19:56:33 +0000 (15:56 -0400)
Squashed commit of the following:

commit 227c739765aac5c7c87bbd9a86976c845dab8488
Author: Victor Roemer <viroemer@cisco.com>
Date:   Thu May 19 15:57:56 2016 -0400

    Add new boundary matching pattern "\b"

src/ips_options/ips_sd_pattern.cc
src/ips_options/sd_pattern_match.cc
src/ips_options/sd_pattern_match.h

index b51e20c28540bfa0969d0c8951238b32bbc54ce0..883be0a79831cc48b1b67beaaaa6be6ef8720ab8 100644 (file)
@@ -67,20 +67,18 @@ private:
     unsigned SdSearch(Cursor&, Packet*);
 
     const SdPatternConfig config;
-    SdOptionData* sd_data;
-    SdContext* sd_context;
+    SdOptionData* opt;
 };
 
 SdPatternOption::SdPatternOption(const SdPatternConfig& c) :
     IpsOption(s_name, RULE_OPTION_TYPE_BUFFER_USE), config(c)
 {
-    sd_data = new SdOptionData(config.pii, config.threshold);
-    sd_context = new SdContext(sd_data);
+    opt = new SdOptionData(config.pii);
 }
 
 SdPatternOption::~SdPatternOption()
 {
-    delete(sd_context);
+    delete opt;
 }
 
 uint32_t SdPatternOption::hash() const
@@ -113,17 +111,12 @@ unsigned SdPatternOption::SdSearch(Cursor& c, Packet* p)
     uint16_t buflen = c.length();
     const uint8_t* const end = buf + buflen;
 
-    SdSessionData ssn;
-    memset(&ssn, 0, sizeof(ssn));
-
     unsigned count = 0;
     while (buf < end && count < config.threshold)
     {
-        SdTreeNode* matched_node;
         uint16_t match_len = 0;
 
-        matched_node = FindPii(sd_context->head_node, buf, &match_len, buflen, &ssn);
-        if ( matched_node )
+        if ( opt->match(buf, &match_len, buflen) )
         {
             if ( !p->obfuscator )
                 p->obfuscator = new Obfuscator();
index 878dd9b8bee9ccda03a54600020b49f0ccffcbcd..6a511702a0ef6e09cf40fe76e28641fe74a9c111 100644 (file)
 
 #include "log/messages.h"
 
-static SdTreeNode* AddChild(SdTreeNode *node, SdOptionData *data, char *pattern);
-
-
-SdOptionData::SdOptionData(std::string pattern, uint8_t threshold)
+SdOptionData::SdOptionData(std::string pattern_)
 {
-    validate_func = nullptr;
-    match_success = 0;
-
-    if (pattern == "credit_card")
+    if (pattern_ == "credit_card")
     {
-        pattern = SD_CREDIT_PATTERN_ALL;
-        validate_func = SdLuhnAlgorithm;
+        pattern_ = SD_CREDIT_PATTERN_ALL;
+        validate = SdLuhnAlgorithm;
     }
-    else if (pattern == "us_social")
-        pattern = SD_SOCIAL_PATTERN;
-    else if (pattern == "us_social_nodashes")
-        pattern = SD_SOCIAL_NODASHES_PATTERN;
 
-    pii = strdup(pattern.c_str());
-    if (!pii)
-        FatalError("Failed to copy sd_pattern");
+    else if (pattern_ == "us_social")
+        pattern_ = SD_SOCIAL_PATTERN;
 
-    count = threshold;
-}
+    else if (pattern_ == "us_social_nodashes")
+        pattern_ = SD_SOCIAL_NODASHES_PATTERN;
 
-SdContext::SdContext(SdOptionData *sd_data)
-{
-    head_node = (SdTreeNode*)calloc(1, sizeof(*head_node));
-    if (!head_node)
-        FatalError("Failed to allocate SdContext node\n");
-
-    sd_data->counter_index = num_patterns++;
-    AddPii(head_node, sd_data);
-}
-
-// Main pattern-adding function.
-// Arguments:
-//  head => pointer to top node in PII tree
-//  data => pointer to SdOptionData struct w/ new pattern
-// Return values:
-//  -1: error
-//   1: pattern added successfully
-//
-static int AddPiiPattern(SdTreeNode *head, SdOptionData *data)
-{
-    AddChild(head, data, data->pii);
-    return 1;
+    pattern = strdup(pattern_.c_str());
+    if (!pattern)
+    {
+        FatalError("Failed to copy sd_pattern");
+    }
+    ExpandBrackets();
 }
 
-// Check that the brackets in a pattern match up, and only contain numbers.
-//
-// Arguments:
-//   pii - string containing pattern.
-//
-// Returns: void function. Raises fatal error if there's a problem.
-//
-static void ExpandBrackets(char **pii)
+void SdOptionData::ExpandBrackets(void)
 {
-    char *bracket_index, *new_pii, *endptr, *pii_position;
+    char* bracket_index, * new_pii, * endptr, * pii_position;
     unsigned long int new_pii_size, repetitions, total_reps = 0;
     unsigned int num_brackets = 0;
 
-    if (pii == NULL || *pii == NULL)
+    if ( !pattern )
         return;
 
-    // Locate first '{'
-    bracket_index = strchr(*pii, '{');
+    bracket_index = strchr(pattern, '{');
 
-    // Brackets at the beginning have nothing to modify.
-    if (bracket_index == *pii)
-    {
-        ParseError("sd_pattern \"%s\" starts with curly brackets which have nothing to modify.", *pii);
-    }
+    if ( bracket_index == pattern )
+        ParseError("sd_pattern \"%s\" starts with curly brackets which have nothing to modify.", pattern);
 
-    // Check for various error cases. Total up the # of bytes needed in new pattern
-    while (bracket_index)
+    while ( bracket_index )
     {
-        // Ignore escaped brackets 
-        if ((bracket_index > *pii) && (*(bracket_index-1) == '\\'))
+        if ( (bracket_index > pattern) && (*(bracket_index-1) == '\\') )
         {
+            // Ignore escaped brackets 
             bracket_index = strchr(bracket_index+1, '{');
             continue;
         }
 
         // Check for the case of one bracket set modifying another, i.e. "{3}{4}"
         // Note: "\}{4}" is OK
-        if ((bracket_index > (*pii)+1) &&
-            (*(bracket_index-1) == '}') &&
-            (*(bracket_index-2) != '\\') )
-        {
-            ParseError("sd_pattern \"%s\" contains curly brackets which have nothing to modify.", *pii);
-        }
+        if ( (bracket_index > pattern + 1) && (*(bracket_index - 1) == '}') && (*(bracket_index - 2) != '\\') )
+            ParseError("sd_pattern \"%s\" contains curly brackets which have nothing to modify.", pattern);
 
-        // Get the number from inside the brackets
         repetitions = strtoul(bracket_index+1, &endptr, 10);
-        if (*endptr != '}' && *endptr != '\0')
-        {
-            ParseError("sd_pattern \"%s\" contains curly brackets with non-digits inside.", *pii);
-        }
+        if ( *endptr != '}' && *endptr != '\0' )
+            ParseError("sd_pattern \"%s\" contains curly brackets with non-digits inside.", pattern);
+
         else if (*endptr == '\0')
-        {
-            ParseError("sd_pattern \"%s\" contains an unterminated curly bracket.", *pii);
-        }
+            ParseError("sd_pattern \"%s\" contains an unterminated curly bracket.", pattern);
 
-        // The brackets look OK. Increase the rep count.
-        if ((bracket_index > (*pii)+1) && (*(bracket_index-2) == '\\'))
+        if ( (bracket_index > pattern+1) && (*(bracket_index-2) == '\\') )
             total_reps += (repetitions * 2);
         else
             total_reps += repetitions;
 
         num_brackets++;
 
-        // Next bracket
         bracket_index = strchr(bracket_index+1, '{');
     }
 
-    // By this point, the brackets all match up.
-    if (num_brackets == 0)
+    if ( num_brackets == 0 )
         return;
 
-    // Allocate the new pii string.
-    new_pii_size = (strlen(*pii) + total_reps - 2*num_brackets + 1);
-    new_pii = (char*)calloc(new_pii_size, sizeof(char));
-    if (new_pii == NULL)
-    {
+    new_pii_size = (strlen(pattern) + total_reps - 2 * num_brackets + 1);
+    new_pii = (char*) calloc(new_pii_size, sizeof(*new_pii));
+    if ( !new_pii )
         FatalError("Failed to allocate memory for sd_pattern rule option\n");
-    }
 
-    // Copy the PII string, expanding repeated sections.
-    pii_position = *pii;
+    pii_position = pattern;
     while (*pii_position != '\0')
     {
         char repeated_section[3] = {'\0'};
@@ -180,244 +126,123 @@ static void ExpandBrackets(char **pii)
             pii_position = endptr+1;
         }
 
-        // Channeling "Shlemiel the Painter" here.
         for (i = 0; i < reps; i++)
-        {
             strncat(new_pii, repeated_section, 2);
-        }
-    }
-
-    // Switch out the pii strings.
-    free(*pii);
-    *pii = new_pii;
-}
-
-// Perform any modifications needed to a pattern string, then add it to the
-// tree.
-int AddPii(SdTreeNode *head, SdOptionData *data)
-{
-    ExpandBrackets(&(data->pii));
-
-    return AddPiiPattern(head, data);
-}
-
-// Create a new tree node, and add it as a child to the current node.
-static SdTreeNode * AddChild(SdTreeNode *node, SdOptionData *data, char *pattern)
-{
-    SdTreeNode * new_node = NULL;
-
-    // Take care not to step on the other children
-    node->children = (SdTreeNode**)calloc(1,sizeof(SdTreeNode*));
-    if (node->children == NULL)
-    {
-        FatalError("Could not allocate node children\n");
-    }
-
-    node->children[0] = (SdTreeNode*)calloc(1,sizeof(SdTreeNode));
-    if (node->children[0] == NULL)
-    {
-        FatalError("Could not allocate node children[0]\n");
-    }
-
-    node->num_children = 1;
-    new_node = node->children[0];
-
-    new_node->pattern = strdup(pattern);
-    if (new_node->pattern == NULL)
-    {
-        FatalError("Could not allocate node pattern\n");
     }
 
-    new_node->num_option_data = 1;
-    new_node->option_data_list = (SdOptionData**)calloc(1, sizeof(SdOptionData*));
-    if (new_node->option_data_list == NULL)
-    {
-        FatalError("Could not allocate node list\n");
-    }
-
-    new_node->option_data_list[0] = data;
-
-    return new_node;
+    free(pattern);
+    pattern = new_pii;
 }
 
-// Frees an entire PII tree.
-int FreePiiTree(SdTreeNode *node)
+bool SdOptionData::match(const uint8_t * const buf, uint16_t * const buf_index, uint16_t buflen)
 {
-    uint16_t i;
+    uint16_t pattern_index = 0;
+    bool node_match = true;
 
-    for (i = 0; i < node->num_children; i++)
+    while ( *buf_index < buflen && pattern[pattern_index] != '\0' && node_match )
     {
-        FreePiiTree(node->children[i]);
-    }
-
-    free(node->pattern);
-    free(node->children);
+        char const * const pc = &pattern[pattern_index]; 
 
-    for (i = 0; i < node->num_option_data; i++)
-        delete node->option_data_list[i];
-
-    free(node->option_data_list);
-    free(node);
-
-    return 0;
-}
-
-// Returns an SdTreeNode that matches the pattern
-SdTreeNode * FindPiiRecursively(SdTreeNode *node, const uint8_t *buf, uint16_t *buf_index,
-        uint16_t buflen, uint16_t *partial_index, SdTreeNode **partial_node)
-{
-    uint16_t old_buf_index;
-    uint16_t pattern_index = *partial_index;
-    int node_match = 1;
-
-    *partial_index = 0;
-    *partial_node = NULL;
-
-    old_buf_index = *buf_index;
-
-    // NOTE: node->pattern is a NULL-terminated string, but buf is network data
-    //       and may legitimately contain NULL bytes.
-    while (*buf_index < buflen &&
-           *(node->pattern + pattern_index) != '\0' &&
-           node_match )
-    {
-        // Match a byte at a time.
-        if ( *(node->pattern + pattern_index) == '\\' &&
-             *(node->pattern + pattern_index + 1) != '\0' )
+        if ( pc[0] == '\\' && pc[1] != '\0' )
         {
-            // Escape sequence found
+match__rescan: 
             pattern_index++;
-            switch ( *(node->pattern + pattern_index) )
+            switch ( pattern[pattern_index] )
             {
                 // Escaped special character
                 case '\\':
                 case '{':
                 case '}':
                 case '?':
-                    node_match = (*(buf + *buf_index) == *(node->pattern + pattern_index));
+                    node_match = (buf[*buf_index] == pattern[pattern_index]);
                     break;
 
                 // \d : match digit
                 case 'd':
-                    node_match = isdigit( (int)(*(buf + *buf_index)) );
+                    node_match = isdigit((int)buf[*buf_index]);
                     break;
+
                 // \D : match non-digit
                 case 'D':
-                    node_match = !isdigit( (int)(*(buf + *buf_index)) );
+                    node_match = !isdigit((int)buf[*buf_index]);
                     break;
 
                 // \w : match alphanumeric
                 case 'w':
-                    node_match = isalnum( (int)(*(buf + *buf_index)) );
+                    node_match = isalnum((int)buf[*buf_index]);
                     break;
+
                 // \W : match non-alphanumeric */
                 case 'W':
-                    node_match = !isalnum( (int)(*(buf + *buf_index)) );
+                    node_match = !isalnum((int)buf[*buf_index]);
                     break;
 
                 // \l : match a letter
                 case 'l':
-                    node_match = isalpha( (int)(*(buf + *buf_index)) );
+                    node_match = isalpha((int)buf[*buf_index]);
                     break;
+
                 // \L : match a non-letter
                 case 'L':
-                    node_match = !isalpha( (int)(*(buf + *buf_index)) );
+                    node_match = !isalpha((int)buf[*buf_index]);
                     break;
+
+                // \b : match a numeric boundary
+                case 'b':
+                    node_match = !isdigit((int)buf[*buf_index]);
+                    if ( !node_match && *buf_index == 0 
+                      && pattern[pattern_index+1] != '\0'
+                      && pattern[pattern_index+2] != '\0' )
+                    {
+                        pattern_index++;
+                        goto match__rescan;
+                    }
             }
         }
         else
         {
-            // Normal byte
-            node_match = (*(buf + *buf_index) == *(node->pattern + pattern_index));
+            // Normal byte match
+            node_match = (buf[*buf_index] == pattern[pattern_index]);
         }
 
         // Handle optional characters
-        if (*(node->pattern + pattern_index + 1) == '?')
+        if (pattern[pattern_index + 1] == '?')
         {
-            // Advance past the '?' in the pattern string.
-            // Only advance in the buffer if we matched the optional char.
             pattern_index += 2;
             if (node_match)
                 (*buf_index)++;
             else
-                node_match = 1;
+                node_match = true;
         }
         else
         {
-            // Advance to next byte
             (*buf_index)++;
             pattern_index++;
         }
     }
 
-    if (node_match)
-    {
-        uint16_t j;
-        bool node_contains_matches = false;
-        SdTreeNode *matched_node = NULL;
+    if ( !node_match )
+        return false;
 
-        if(*buf_index == buflen)
-        {
-            if( (*(node->pattern + pattern_index) != '\0')
-              || ((strlen(node->pattern) == pattern_index) && node->num_children))
-            {
-                *partial_index = pattern_index;
-                *partial_node = node;
-                return NULL;
-            }
-        }
-
-        if ( matched_node || *partial_index )
-            return matched_node;
+    if( *buf_index == buflen )
+    {
+        char const * const pc = &pattern[pattern_index];
 
-        // An SdTreeNode holds multiple SdOptionData. It's possible to get
-        // some with validation funs and some without. Evaluate them independently.
-        for (j = 0; j < node->num_option_data; j++)
+        // '\b' can match EOM 
+        if ( !(pc[0] == '\\' && pc[1] == 'b') )
         {
-            SdOptionData *option_data = node->option_data_list[j];
-
-            // Run eval func, return NULL if it exists but fails
-            if ( option_data->validate_func
-              && option_data->validate_func(buf, *buf_index) != 1 )
-            {
-                *buf_index = old_buf_index;
-                option_data->match_success = 0;
-            }
+            if( (pc[0] == '\0') )
+                return true;
+                
             else
-            {
-                // No eval func necessary, or an eval func existed and returned 1
-                option_data->match_success = 1;
-                node_contains_matches = true;
-            }
+                return false;
         }
-
-        if (node_contains_matches)
-            return node;
     }
 
-    // No match here.
-    *buf_index = old_buf_index;
-    return NULL;
-}
-
-SdTreeNode * FindPii(const SdTreeNode *head, 
-        const uint8_t *buf, uint16_t *buf_index, uint16_t buflen,
-        SdSessionData *session)
-{
-    uint16_t i;
-    uint16_t *partial_index = &(session->part_match_index);
-    SdTreeNode **partial_node = &(session->part_match_node);
-    *partial_index = 0;
-
-    for (i = 0; i < head->num_children; i++)
-    {
-        SdTreeNode * matched_node = FindPiiRecursively(head->children[i], 
-                buf, buf_index, buflen, partial_index, partial_node);
-        
-        if (matched_node || *partial_index)
-            return matched_node;
-    }
+    if ( validate && validate(buf, *buf_index) != 1 )
+        return false;
 
-    return NULL;
+    // Success!
+    return true;
 }
 
index b8e443b7485956bc60fa1f994b1fea1129b93321..bd748c453c770f021b43477e1a5eb48fd263fc93 100644 (file)
 #include <iostream>
 #include <stdint.h>
 
-#define SD_SOCIAL_PATTERN          "\\d{3}-\\d{2}-\\d{4}"
-#define SD_SOCIAL_NODASHES_PATTERN "\\d{9}"
-#define SD_CREDIT_PATTERN_ALL      "\\d{4} ?-?\\d{4} ?-?\\d{2} ?-?\\d{2} ?-?\\d{3}\\d?"
+#define SD_SOCIAL_PATTERN          "\\b\\d{3}-\\d{2}-\\d{4}\\b"
+#define SD_SOCIAL_NODASHES_PATTERN "\\b\\d{9}\\b"
+#define SD_CREDIT_PATTERN_ALL      "\\b\\d{4} ?-?\\d{4} ?-?\\d{2} ?-?\\d{2} ?-?\\d{3}\\d?\\b"
 
-struct SdOptionData
-{
-    char *pii;
-    uint32_t counter_index;
-    int (*validate_func)(const uint8_t* buf, uint32_t buflen);
-    uint8_t count;
-    uint8_t match_success;
-
-    SdOptionData(std::string pattern, uint8_t threshold);
-
-    ~SdOptionData()
-    {
-        free(pii);
-        pii = nullptr;
-        validate_func = nullptr;
-    }
-};
-
-struct SdTreeNode
+class SdOptionData
 {
+public:
+    SdOptionData(std::string pattern);
+    ~SdOptionData(void)
+    { free(pattern); }
+    void ExpandBrackets(void);
+    bool match(const uint8_t* const buf, uint16_t* const buf_index, uint16_t buflen);
+
+private:
     char* pattern;
-    uint16_t num_children;
-    uint16_t num_option_data;
-    SdTreeNode** children;
-    SdOptionData** option_data_list;
+    int (*validate)(const uint8_t* buf, uint32_t buflen) = nullptr;
 };
 
-int FreePiiTree(SdTreeNode *node);
-
-struct SdContext
-{
-    SdTreeNode *head_node;
-    uint32_t num_patterns;
-
-    SdContext(SdOptionData*);
-
-    ~SdContext()
-    {
-        FreePiiTree(head_node);
-    }
-};
-
-struct SdSessionData
-{
-    SdTreeNode *part_match_node;
-    uint16_t part_match_index;
-    uint32_t num_patterns;
-    uint32_t global_counter;
-    uint8_t *counters;
-};
-
-int AddPii(SdTreeNode *head, SdOptionData *data);
-
-
-SdTreeNode * FindPiiRecursively(SdTreeNode *node, const uint8_t *buf, uint16_t *buf_index,
-        uint16_t buflen, uint16_t *partial_index, SdTreeNode **partial_node);
-
-SdTreeNode * FindPii(const SdTreeNode *head, const uint8_t *buf, uint16_t *buf_index,
-        uint16_t buflen, SdSessionData *session);
-
 #endif
 
-