From: Russ Combs (rucombs) Date: Tue, 31 May 2016 19:56:33 +0000 (-0400) Subject: Merge pull request #483 in SNORT/snort3 from sd_pattern-fix to master X-Git-Tag: 3.0.0-233~385 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=366cd577f5582adae5ed07c36152346c256535e7;p=thirdparty%2Fsnort3.git Merge pull request #483 in SNORT/snort3 from sd_pattern-fix to master Squashed commit of the following: commit 227c739765aac5c7c87bbd9a86976c845dab8488 Author: Victor Roemer Date: Thu May 19 15:57:56 2016 -0400 Add new boundary matching pattern "\b" --- diff --git a/src/ips_options/ips_sd_pattern.cc b/src/ips_options/ips_sd_pattern.cc index b51e20c28..883be0a79 100644 --- a/src/ips_options/ips_sd_pattern.cc +++ b/src/ips_options/ips_sd_pattern.cc @@ -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(); diff --git a/src/ips_options/sd_pattern_match.cc b/src/ips_options/sd_pattern_match.cc index 878dd9b8b..6a511702a 100644 --- a/src/ips_options/sd_pattern_match.cc +++ b/src/ips_options/sd_pattern_match.cc @@ -29,136 +29,82 @@ #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; } diff --git a/src/ips_options/sd_pattern_match.h b/src/ips_options/sd_pattern_match.h index b8e443b74..bd748c453 100644 --- a/src/ips_options/sd_pattern_match.h +++ b/src/ips_options/sd_pattern_match.h @@ -25,70 +25,23 @@ #include #include -#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 -