diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /src/detect-content.c | |
parent | Initial commit. (diff) | |
download | suricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip |
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/detect-content.c')
-rw-r--r-- | src/detect-content.c | 2973 |
1 files changed, 2973 insertions, 0 deletions
diff --git a/src/detect-content.c b/src/detect-content.c new file mode 100644 index 0000000..be1f125 --- /dev/null +++ b/src/detect-content.c @@ -0,0 +1,2973 @@ +/* Copyright (C) 2007-2022 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 + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Victor Julien <victor@inliniac.net> + * + * Simple content match part of the detection engine. + */ + +#include "suricata-common.h" +#include "decode.h" +#include "detect.h" +#include "detect-content.h" +#include "detect-uricontent.h" +#include "detect-engine-mpm.h" +#include "detect-engine.h" +#include "detect-engine-build.h" +#include "detect-engine-state.h" +#include "detect-parse.h" +#include "detect-pcre.h" +#include "util-mpm.h" +#include "flow.h" +#include "flow-util.h" +#include "flow-var.h" +#include "detect-flow.h" +#include "app-layer.h" +#include "util-unittest.h" +#include "util-print.h" +#include "util-debug.h" +#include "util-spm.h" +#include "threads.h" +#include "util-unittest-helper.h" +#include "pkt-var.h" +#include "host.h" +#include "util-profiling.h" +#include "detect-dsize.h" + +#ifdef UNITTESTS +static void DetectContentRegisterTests(void); +#endif + +void DetectContentRegister (void) +{ + sigmatch_table[DETECT_CONTENT].name = "content"; + sigmatch_table[DETECT_CONTENT].desc = "match on payload content"; + sigmatch_table[DETECT_CONTENT].url = "/rules/payload-keywords.html#content"; + sigmatch_table[DETECT_CONTENT].Match = NULL; + sigmatch_table[DETECT_CONTENT].Setup = DetectContentSetup; + sigmatch_table[DETECT_CONTENT].Free = DetectContentFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_CONTENT].RegisterTests = DetectContentRegisterTests; +#endif + sigmatch_table[DETECT_CONTENT].flags = (SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION); +} + +/** + * \brief Parse a content string, ie "abc|DE|fgh" + * + * \param content_str null terminated string containing the content + * \param result result pointer to pass the fully parsed byte array + * \param result_len size of the resulted data + * \param flags flags to be set by this parsing function + * + * \retval -1 error + * \retval 0 ok + */ +int DetectContentDataParse(const char *keyword, const char *contentstr, + uint8_t **pstr, uint16_t *plen) +{ + char *str = NULL; + size_t slen = 0; + + slen = strlen(contentstr); + if (slen == 0) { + return -1; + } + uint8_t buffer[slen + 1]; + strlcpy((char *)&buffer, contentstr, slen + 1); + str = (char *)buffer; + + SCLogDebug("\"%s\", len %" PRIuMAX, str, (uintmax_t)slen); + + //SCLogDebug("DetectContentParse: \"%s\", len %" PRIu32 "", str, len); + char converted = 0; + + { + size_t i, x; + uint8_t bin = 0; + uint8_t escape = 0; + uint8_t binstr[3] = ""; + uint8_t binpos = 0; + uint16_t bin_count = 0; + + for (i = 0, x = 0; i < slen; i++) { + // SCLogDebug("str[%02u]: %c", i, str[i]); + if (str[i] == '|') { + bin_count++; + if (bin) { + if (binpos > 0) { + SCLogError("Incomplete hex code in content - %s. Invalidating signature.", + contentstr); + goto error; + } + bin = 0; + } else { + bin = 1; + } + } else if(!escape && str[i] == '\\') { + escape = 1; + } else { + if (bin) { + if (isdigit((unsigned char)str[i]) || + str[i] == 'A' || str[i] == 'a' || + str[i] == 'B' || str[i] == 'b' || + str[i] == 'C' || str[i] == 'c' || + str[i] == 'D' || str[i] == 'd' || + str[i] == 'E' || str[i] == 'e' || + str[i] == 'F' || str[i] == 'f') + { + // SCLogDebug("part of binary: %c", str[i]); + + binstr[binpos] = (char)str[i]; + binpos++; + + if (binpos == 2) { + uint8_t c = strtol((char *)binstr, (char **) NULL, 16) & 0xFF; + binpos = 0; + str[x] = c; + x++; + converted = 1; + } + } else if (str[i] == ' ') { + // SCLogDebug("space as part of binary string"); + } + else if (str[i] != ',') { + SCLogError("Invalid hex code in " + "content - %s, hex %c. Invalidating signature.", + contentstr, str[i]); + goto error; + } + } else if (escape) { + if (str[i] == ':' || + str[i] == ';' || + str[i] == '\\' || + str[i] == '\"') + { + str[x] = str[i]; + x++; + } else { + SCLogError("'%c' has to be escaped", str[i - 1]); + goto error; + } + escape = 0; + converted = 1; + } else if (str[i] == '"') { + SCLogError("Invalid unescaped double quote within content section."); + goto error; + } else { + str[x] = str[i]; + x++; + } + } + } + + if (bin_count % 2 != 0) { + SCLogError("Invalid hex code assembly in " + "%s - %s. Invalidating signature.", + keyword, contentstr); + goto error; + } + + if (converted) { + slen = x; + } + } + + if (slen) { + uint8_t *ptr = SCCalloc(1, slen); + if (ptr == NULL) { + return -1; + } + memcpy(ptr, str, slen); + + *plen = (uint16_t)slen; + *pstr = ptr; + return 0; + } +error: + return -1; +} +/** + * \brief DetectContentParse + * \initonly + */ +DetectContentData *DetectContentParse(SpmGlobalThreadCtx *spm_global_thread_ctx, + const char *contentstr) +{ + DetectContentData *cd = NULL; + uint8_t *content = NULL; + uint16_t len = 0; + int ret; + + ret = DetectContentDataParse("content", contentstr, &content, &len); + if (ret == -1) { + return NULL; + } + + cd = SCCalloc(1, sizeof(DetectContentData) + len); + if (unlikely(cd == NULL)) { + SCFree(content); + exit(EXIT_FAILURE); + } + + cd->content = (uint8_t *)cd + sizeof(DetectContentData); + memcpy(cd->content, content, len); + cd->content_len = len; + + /* Prepare SPM search context. */ + cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 0, + spm_global_thread_ctx); + if (cd->spm_ctx == NULL) { + SCFree(content); + SCFree(cd); + return NULL; + } + + cd->depth = 0; + cd->offset = 0; + cd->within = 0; + cd->distance = 0; + + SCFree(content); + return cd; + +} + +DetectContentData *DetectContentParseEncloseQuotes(SpmGlobalThreadCtx *spm_global_thread_ctx, + const char *contentstr) +{ + return DetectContentParse(spm_global_thread_ctx, contentstr); +} + +/** + * \brief Helper function to print a DetectContentData + */ +void DetectContentPrint(DetectContentData *cd) +{ + int i = 0; + if (cd == NULL) { + SCLogDebug("DetectContentData \"cd\" is NULL"); + return; + } + char *tmpstr = SCMalloc(sizeof(char) * cd->content_len + 1); + if (tmpstr != NULL) { + for (i = 0; i < cd->content_len; i++) { + if (isprint(cd->content[i])) + tmpstr[i] = cd->content[i]; + else + tmpstr[i] = '.'; + } + tmpstr[i] = '\0'; + SCLogDebug("Content: \"%s\"", tmpstr); + SCFree(tmpstr); + } else { + SCLogDebug("Content: "); + for (i = 0; i < cd->content_len; i++) + SCLogDebug("%c", cd->content[i]); + } + + SCLogDebug("Content_id: %"PRIu32, cd->id); + SCLogDebug("Content_len: %"PRIu16, cd->content_len); + SCLogDebug("Depth: %"PRIu16, cd->depth); + SCLogDebug("Offset: %"PRIu16, cd->offset); + SCLogDebug("Within: %"PRIi32, cd->within); + SCLogDebug("Distance: %"PRIi32, cd->distance); + SCLogDebug("flags: %u ", cd->flags); + SCLogDebug("negated: %s ", cd->flags & DETECT_CONTENT_NEGATED ? "true" : "false"); + SCLogDebug("relative match next: %s ", cd->flags & DETECT_CONTENT_RELATIVE_NEXT ? "true" : "false"); + + if (cd->replace && cd->replace_len) { + char *tmprstr = SCMalloc(sizeof(char) * cd->replace_len + 1); + + if (tmprstr != NULL) { + for (i = 0; i < cd->replace_len; i++) { + if (isprint(cd->replace[i])) + tmprstr[i] = cd->replace[i]; + else + tmprstr[i] = '.'; + } + tmprstr[i] = '\0'; + SCLogDebug("Replace: \"%s\"", tmprstr); + SCFree(tmprstr); + } else { + SCLogDebug("Replace: "); + for (i = 0; i < cd->replace_len; i++) + SCLogDebug("%c", cd->replace[i]); + } + } + SCLogDebug("-----------"); +} + +/** + * \brief Function to setup a content pattern. + * + * \param de_ctx pointer to the current detection_engine + * \param s pointer to the current Signature + * \param m pointer to the last parsed SigMatch + * \param contentstr pointer to the current keyword content string + * \retval -1 if error + * \retval 0 if all was ok + */ +int DetectContentSetup(DetectEngineCtx *de_ctx, Signature *s, const char *contentstr) +{ + DetectContentData *cd = DetectContentParse(de_ctx->spm_global_thread_ctx, contentstr); + if (cd == NULL) + goto error; + if (s->init_data->negated == true) { + cd->flags |= DETECT_CONTENT_NEGATED; + } + + DetectContentPrint(cd); + + if (DetectBufferGetActiveList(de_ctx, s) == -1) + goto error; + + int sm_list = s->init_data->list; + if (sm_list == DETECT_SM_LIST_NOTSET) { + sm_list = DETECT_SM_LIST_PMATCH; + } else if (sm_list > DETECT_SM_LIST_MAX && + 0 == (cd->flags & DETECT_CONTENT_NEGATED)) { + /* Check transform compatibility */ + const char *tstr; + if (!DetectEngineBufferTypeValidateTransform( + de_ctx, sm_list, cd->content, cd->content_len, &tstr)) { + SCLogError("content string \"%s\" incompatible with %s transform", contentstr, tstr); + goto error; + } + } + + SigMatch *sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + sm->ctx = (void *)cd; + sm->type = DETECT_CONTENT; + SigMatchAppendSMToList(s, sm, sm_list); + + return 0; + +error: + DetectContentFree(de_ctx, cd); + return -1; +} + +/** + * \brief this function will SCFree memory associated with DetectContentData + * + * \param cd pointer to DetectContentData + */ +void DetectContentFree(DetectEngineCtx *de_ctx, void *ptr) +{ + SCEnter(); + DetectContentData *cd = (DetectContentData *)ptr; + + if (cd == NULL) + SCReturn; + + SpmDestroyCtx(cd->spm_ctx); + + SCFree(cd); + SCReturn; +} + +/* + * \brief Determine the size needed to accommodate the content + * elements of a signature + * \param s signature to get dsize value from + * \param max_size Maximum buffer/data size allowed. + * \param list signature match list. + * \param len Maximum length required + * \param offset Maximum offset encountered + * + * Note that negated content does not contribute to the maximum + * required size value. However, each negated content's values + * must not exceed the size value. + * + * Values from negated content blocks are used to determine if the + * negated content block requires a value that exceeds "max_size". The + * distance and within values from negated content blocks are added to + * the running total of required content size to see if the max_size + * would be exceeded. + * + * - Non-negated content contributes to the required size (content length, distance) + * - Negated content values are checked but not accumulated for the required size. + */ +void SigParseRequiredContentSize( + const Signature *s, const int max_size, const SigMatch *sm, int *len, int *offset) +{ + int max_offset = 0, total_len = 0; + bool first = true; + for (; sm != NULL; sm = sm->next) { + if (sm->type != DETECT_CONTENT || sm->ctx == NULL) { + continue; + } + + DetectContentData *cd = (DetectContentData *)sm->ctx; + SCLogDebug("content_len %d; negated: %s; distance: %d, offset: %d, depth: %d", + cd->content_len, cd->flags & DETECT_CONTENT_NEGATED ? "yes" : "no", cd->distance, + cd->offset, cd->depth); + + if (!first) { + /* only count content with relative modifiers */ + if (!((cd->flags & DETECT_CONTENT_DISTANCE) || (cd->flags & DETECT_CONTENT_WITHIN))) + continue; + + if (cd->flags & DETECT_CONTENT_NEGATED) { + /* Check if distance/within cause max to be exceeded */ + int check = total_len + cd->distance + cd->within; + if (max_size < check) { + *len = check; + return; + } + + continue; + } + } + SCLogDebug("content_len %d; distance: %d, offset: %d, depth: %d", cd->content_len, + cd->distance, cd->offset, cd->depth); + total_len += cd->content_len + cd->distance; + max_offset = MAX(max_offset, cd->offset); + first = false; + } + + *len = total_len; + *offset = max_offset; +} + +/** + * \retval true valid + * \retval false invalid + */ +bool DetectContentPMATCHValidateCallback(const Signature *s) +{ + if (!(s->flags & SIG_FLAG_DSIZE)) { + return true; + } + + int max_right_edge_i = SigParseGetMaxDsize(s); + if (max_right_edge_i < 0) { + return true; + } + + uint32_t max_right_edge = (uint32_t)max_right_edge_i; + + int min_dsize_required = SigParseMaxRequiredDsize(s); + if (min_dsize_required >= 0) { + SCLogDebug("min_dsize %d; max_right_edge %d", min_dsize_required, max_right_edge); + if ((uint32_t)min_dsize_required > max_right_edge) { + SCLogError("signature can't match as required content length %d exceeds dsize value %d", + min_dsize_required, max_right_edge); + return false; + } + } + + return true; +} + +/** \brief apply depth/offset and distance/within to content matches + * + * The idea is that any limitation we can set is a win, as the mpm + * can use this to reduce match candidates. + * + * E.g. if we have 'content:"1"; depth:1; content:"2"; distance:0; within:1;' + * we know that we can add 'offset:1; depth:2;' to the 2nd condition. This + * will then be used in mpm if the 2nd condition would be selected for mpm. + * + * Another example: 'content:"1"; depth:1; content:"2"; distance:0;'. Here we + * cannot set a depth, but we can set an offset of 'offset:1;'. This will + * make the mpm a bit more precise. + */ +static void PropagateLimits(Signature *s, SigMatch *sm_head) +{ +#define VALIDATE(e) \ + if (!(e)) { \ + return; \ + } + uint16_t offset = 0; + uint16_t offset_plus_pat = 0; + uint16_t depth = 0; + bool has_active_depth_chain = false; + + bool has_depth = false; + bool has_ends_with = false; + uint16_t ends_with_depth = 0; + + for (SigMatch *sm = sm_head; sm != NULL; sm = sm->next) { + switch (sm->type) { + case DETECT_CONTENT: { + DetectContentData *cd = (DetectContentData *)sm->ctx; + if ((cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET | + DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) == 0) { + offset = depth = 0; + offset_plus_pat = cd->content_len; + SCLogDebug("reset"); + has_active_depth_chain = false; + continue; + } + if (sm->prev == NULL) { + if (cd->distance >= 0 && cd->distance <= (int32_t)USHRT_MAX && + cd->within >= 0 && cd->within <= (int32_t)USHRT_MAX) { + if (cd->flags & DETECT_CONTENT_DISTANCE) { + if (cd->distance > 0) + cd->flags |= DETECT_CONTENT_OFFSET; + cd->flags &= ~DETECT_CONTENT_DISTANCE; + cd->offset = (uint16_t)cd->distance; + cd->distance = 0; + cd->flags |= DETECT_CONTENT_DISTANCE2OFFSET; + } + if (cd->flags & DETECT_CONTENT_WITHIN) { + cd->flags |= DETECT_CONTENT_DEPTH; + cd->flags &= ~DETECT_CONTENT_WITHIN; + cd->depth = (uint16_t)cd->within + cd->offset; + cd->within = 0; + cd->flags |= DETECT_CONTENT_WITHIN2DEPTH; + } + } + } + + if (cd->flags & DETECT_CONTENT_NEGATED) { + offset = depth = 0; + offset_plus_pat = 0; + SCLogDebug("reset because of negation"); + has_active_depth_chain = false; + continue; + } + + if (cd->depth) { + has_depth = true; + has_active_depth_chain = true; + } + + SCLogDebug("sm %p depth %u offset %u distance %d within %d", sm, cd->depth, + cd->offset, cd->distance, cd->within); + SCLogDebug("stored: offset %u depth %u offset_plus_pat %u", offset, depth, + offset_plus_pat); + + if ((cd->flags & (DETECT_DEPTH | DETECT_CONTENT_WITHIN)) == 0) { + if (depth) + SCLogDebug("no within, reset depth"); + depth = 0; + has_active_depth_chain = false; + } + if ((cd->flags & DETECT_CONTENT_DISTANCE) == 0) { + if (offset_plus_pat) + SCLogDebug("no distance, reset offset_plus_pat & offset"); + offset_plus_pat = offset = 0; + } + + SCLogDebug("stored: offset %u depth %u offset_plus_pat %u " + "has_active_depth_chain %s", + offset, depth, offset_plus_pat, has_active_depth_chain ? "true" : "false"); + if (cd->flags & DETECT_CONTENT_DISTANCE) { + if (cd->distance >= 0) { + VALIDATE((uint32_t)offset_plus_pat + cd->distance <= UINT16_MAX); + offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance); + SCLogDebug("distance %d: updated content to have offset %u", cd->distance, + cd->offset); + } else { + if (abs(cd->distance) > offset_plus_pat) + offset = cd->offset = 0; + else + offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance); + offset_plus_pat = offset + cd->content_len; + SCLogDebug("distance %d: updated content to have offset %u", cd->distance, + cd->offset); + } + } + if (has_active_depth_chain) { + if (offset_plus_pat && cd->flags & DETECT_CONTENT_WITHIN && cd->within >= 0) { + if (depth && depth > offset_plus_pat) { + int32_t dist = 0; + if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance > 0) { + dist = cd->distance; + SCLogDebug( + "distance to add: %u. depth + dist %u", dist, depth + dist); + } + SCLogDebug("depth %u + cd->within %u", depth, cd->within); + VALIDATE(depth + cd->within + dist >= 0 && + depth + cd->within + dist <= UINT16_MAX); + depth = cd->depth = (uint16_t)(depth + cd->within + dist); + } else { + SCLogDebug("offset %u + cd->within %u", offset, cd->within); + VALIDATE(depth + cd->within >= 0 && depth + cd->within <= UINT16_MAX); + depth = cd->depth = (uint16_t)(offset + cd->within); + } + SCLogDebug("updated content to have depth %u", cd->depth); + } else { + if (cd->depth == 0 && depth != 0) { + if (cd->within > 0) { + SCLogDebug("within %d distance %d", cd->within, cd->distance); + if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance >= 0) { + VALIDATE(offset_plus_pat + cd->distance >= 0 && + offset_plus_pat + cd->distance <= UINT16_MAX); + cd->offset = (uint16_t)(offset_plus_pat + cd->distance); + SCLogDebug("updated content to have offset %u", cd->offset); + } + + VALIDATE(depth + cd->within >= 0 && + depth + cd->within <= UINT16_MAX); + depth = cd->depth = (uint16_t)(cd->within + depth); + SCLogDebug("updated content to have depth %u", cd->depth); + + if (cd->flags & DETECT_CONTENT_ENDS_WITH) { + has_ends_with = true; + if (ends_with_depth == 0) + ends_with_depth = depth; + ends_with_depth = MIN(ends_with_depth, depth); + } + } + } + } + } + if (cd->offset == 0) { // && offset != 0) { + if (cd->flags & DETECT_CONTENT_DISTANCE && cd->distance >= 0) { + cd->offset = offset_plus_pat; + SCLogDebug("update content to have offset %u", cd->offset); + } + } + + if ((cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET | + DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) == + (DETECT_CONTENT_DISTANCE | DETECT_CONTENT_WITHIN) || + (cd->flags & (DETECT_CONTENT_DEPTH | DETECT_CONTENT_OFFSET | + DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)) == + (DETECT_CONTENT_DISTANCE)) { + if (cd->distance >= 0) { + // only distance + VALIDATE((uint32_t)offset_plus_pat + cd->distance <= UINT16_MAX); + offset = cd->offset = (uint16_t)(offset_plus_pat + cd->distance); + offset_plus_pat = offset + cd->content_len; + SCLogDebug("offset %u offset_plus_pat %u", offset, offset_plus_pat); + } + } + if (cd->flags & DETECT_CONTENT_OFFSET) { + offset = cd->offset; + offset_plus_pat = offset + cd->content_len; + SCLogDebug("stored offset %u offset_plus_pat %u", offset, offset_plus_pat); + } + if (cd->depth) { + depth = cd->depth; + SCLogDebug("stored depth now %u", depth); + offset_plus_pat = offset + cd->content_len; + if (cd->flags & DETECT_CONTENT_ENDS_WITH) { + has_ends_with = true; + if (ends_with_depth == 0) + ends_with_depth = depth; + ends_with_depth = MIN(ends_with_depth, depth); + } + } + if ((cd->flags & (DETECT_CONTENT_WITHIN | DETECT_CONTENT_DEPTH)) == 0) { + has_active_depth_chain = false; + depth = 0; + } + break; + } + case DETECT_PCRE: { + // relative could leave offset_plus_pat set. + const DetectPcreData *pd = (const DetectPcreData *)sm->ctx; + if (pd->flags & DETECT_PCRE_RELATIVE) { + depth = 0; + } else { + SCLogDebug("non-anchored PCRE not supported, reset offset_plus_pat & offset"); + offset_plus_pat = offset = depth = 0; + } + has_active_depth_chain = false; + break; + } + default: + SCLogDebug("keyword not supported, reset offset_plus_pat & offset"); + offset_plus_pat = offset = depth = 0; + has_active_depth_chain = false; + break; + } + } + /* apply anchored 'ends with' as depth to all patterns */ + if (has_depth && has_ends_with) { + for (SigMatch *sm = sm_head; sm != NULL; sm = sm->next) { + switch (sm->type) { + case DETECT_CONTENT: { + DetectContentData *cd = (DetectContentData *)sm->ctx; + if (cd->depth == 0) + cd->depth = ends_with_depth; + cd->depth = MIN(ends_with_depth, cd->depth); + if (cd->depth) + cd->flags |= DETECT_CONTENT_DEPTH; + break; + } + } + } + } +#undef VALIDATE +} + +void DetectContentPropagateLimits(Signature *s) +{ + PropagateLimits(s, s->init_data->smlists[DETECT_SM_LIST_PMATCH]); + for (uint32_t x = 0; x < s->init_data->buffer_index; x++) { + PropagateLimits(s, s->init_data->buffers[x].head); + } +} + +static inline bool NeedsAsHex(uint8_t c) +{ + if (!isprint(c)) + return true; + + switch (c) { + case '/': + case ';': + case ':': + case '\\': + case ' ': + case '|': + case '"': + case '`': + case '\'': + return true; + } + return false; +} + +void DetectContentPatternPrettyPrint(const DetectContentData *cd, char *str, size_t str_len) +{ + bool hex = false; + for (uint16_t i = 0; i < cd->content_len; i++) { + if (NeedsAsHex(cd->content[i])) { + char hex_str[4]; + snprintf(hex_str, sizeof(hex_str), "%s%02X", !hex ? "|" : " ", cd->content[i]); + strlcat(str, hex_str, str_len); + hex = true; + } else { + char p_str[3]; + snprintf(p_str, sizeof(p_str), "%s%c", hex ? "|" : "", cd->content[i]); + strlcat(str, p_str, str_len); + hex = false; + } + } + if (hex) { + strlcat(str, "|", str_len); + } +} + +int DetectContentConvertToNocase(DetectEngineCtx *de_ctx, DetectContentData *cd) +{ + if (cd->flags & DETECT_CONTENT_NOCASE) { + SCLogError("can't use multiple nocase modifiers with the same content"); + return -1; + } + + /* for consistency in later use (e.g. by MPM construction and hashing), + * coerce the content string to lower-case. */ + for (uint8_t *c = cd->content; c < cd->content + cd->content_len; c++) { + *c = u8_tolower(*c); + } + + cd->flags |= DETECT_CONTENT_NOCASE; + /* Recreate the context with nocase chars */ + SpmDestroyCtx(cd->spm_ctx); + cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx); + if (cd->spm_ctx == NULL) { + return -1; + } + return 0; +} + +#ifdef UNITTESTS /* UNITTESTS */ +#include "detect-engine-alert.h" +#include "packet.h" + +static bool TestLastContent(const Signature *s, uint16_t o, uint16_t d) +{ + const SigMatch *sm = s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]; + if (!sm) { + SCLogDebug("no sm"); + return false; + } + if (!(sm->type == DETECT_CONTENT)) { + SCLogDebug("not content"); + return false; + } + const DetectContentData *cd = (const DetectContentData *)sm->ctx; + if (o != cd->offset) { + SCLogDebug("offset mismatch %u != %u", o, cd->offset); + return false; + } + if (d != cd->depth) { + SCLogDebug("depth mismatch %u != %u", d, cd->depth); + return false; + } + return true; +} + +#define TEST_RUN(sig, o, d) \ + { \ + SCLogDebug("TEST_RUN start: '%s'", (sig)); \ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); \ + FAIL_IF_NULL(de_ctx); \ + de_ctx->flags |= DE_QUIET; \ + char rule[2048]; \ + snprintf(rule, sizeof(rule), "alert tcp any any -> any any (%s sid:1; rev:1;)", (sig)); \ + Signature *s = DetectEngineAppendSig(de_ctx, rule); \ + FAIL_IF_NULL(s); \ + SigAddressPrepareStage1(de_ctx); \ + bool res = TestLastContent(s, (o), (d)); \ + FAIL_IF(res == false); \ + DetectEngineCtxFree(de_ctx); \ + } + +#define TEST_DONE \ + PASS + +/** \test test propagation of depth/offset/distance/within */ +static int DetectContentDepthTest01(void) +{ + // straight depth/offset + TEST_RUN("content:\"abc\"; offset:1; depth:3;", 1, 4); + // dsize applied as depth + TEST_RUN("dsize:10; content:\"abc\";", 0, 10); + TEST_RUN("dsize:<10; content:\"abc\";", 0, 10); + TEST_RUN("dsize:5<>10; content:\"abc\";", 0, 10); + + // relative match, directly following anchored content + TEST_RUN("content:\"abc\"; depth:3; content:\"xyz\"; distance:0; within:3; ", 3, 6); + // relative match, directly following anchored content + TEST_RUN("content:\"abc\"; offset:3; depth:3; content:\"xyz\"; distance:0; within:3; ", 6, 9); + TEST_RUN("content:\"abc\"; depth:6; content:\"xyz\"; distance:0; within:3; ", 3, 9); + + // multiple relative matches after anchored content + TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; distance:0; within:3; content:\"xyz\"; distance:0; within:3; ", 6, 9); + // test 'reset' due to unanchored content + TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; content:\"xyz\"; distance:0; within:3; ", 3, 0); + // test 'reset' due to unanchored pcre + TEST_RUN("content:\"abc\"; depth:3; pcre:/\"klm\"/; content:\"xyz\"; distance:0; within:3; ", 0, 0); + // test relative pcre. We can use previous offset+pattern len + TEST_RUN("content:\"abc\"; depth:3; pcre:/\"klm\"/R; content:\"xyz\"; distance:0; within:3; ", 3, 0); + TEST_RUN("content:\"abc\"; offset:3; depth:3; pcre:/\"klm\"/R; content:\"xyz\"; distance:0; within:3; ", 6, 0); + + TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; within:3; content:\"xyz\"; within:3; ", 0, 9); + + TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; distance:0; content:\"xyz\"; distance:0; ", 6, 0); + + // tests to see if anchored 'ends_with' is applied to other content as depth + TEST_RUN("content:\"abc\"; depth:6; isdataat:!1,relative; content:\"klm\";", 0, 6); + TEST_RUN("content:\"abc\"; depth:3; content:\"klm\"; within:3; content:\"xyz\"; within:3; isdataat:!1,relative; content:\"def\"; ", 0, 9); + + TEST_RUN("content:\"|03|\"; depth:1; content:\"|e0|\"; distance:4; within:1;", 5, 6); + TEST_RUN("content:\"|03|\"; depth:1; content:\"|e0|\"; distance:4; within:1; content:\"Cookie|3a|\"; distance:5; within:7;", 11, 18); + + TEST_RUN("content:\"this\"; content:\"is\"; within:6; content:\"big\"; within:8; content:\"string\"; within:8;", 0, 0); + + TEST_RUN("dsize:<80; content:!\"|00 22 02 00|\"; depth: 4; content:\"|00 00 04|\"; distance:8; within:3; content:\"|00 00 00 00 00|\"; distance:6; within:5;", 17, 80); + TEST_RUN("content:!\"|00 22 02 00|\"; depth: 4; content:\"|00 00 04|\"; distance:8; within:3; content:\"|00 00 00 00 00|\"; distance:6; within:5;", 17, 0); + + TEST_RUN("content:\"|0d 0a 0d 0a|\"; content:\"code=\"; distance:0;", 4, 0); + TEST_RUN("content:\"|0d 0a 0d 0a|\"; content:\"code=\"; distance:0; content:\"xploit.class\"; distance:2; within:18;", 11, 0); + + TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0;", 2, 0); + TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0; content:\"|0d|LogMeIn, Inc.\"; distance:1; within:14;", 6, 0); + TEST_RUN("content:\"|16 03|\"; depth:2; content:\"|55 04 0a|\"; distance:0; content:\"|0d|LogMeIn, Inc.\"; distance:1; within:14; content:\".app\";", 0, 0); + + TEST_RUN("content:\"=\"; offset:4; depth:9;", 4, 13); + // low end: offset 4 + patlen 1 = 5. So 5 + distance 55 = 60. + // hi end: depth '13' (4+9) + distance 55 = 68 + within 2 = 70 + TEST_RUN("content:\"=\"; offset:4; depth:9; content:\"=&\"; distance:55; within:2;", 60, 70); + + // distance value is too high so we bail and not set anything on this content + TEST_RUN("content:\"0123456789\"; content:\"abcdef\"; distance:1048576;", 0, 0); + + // Bug #5162. + TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2;", 11, 18); + TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 " + "00 00|\"; distance:0;", + 13, 0); + TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 " + "00 00|\"; distance:0; content:\"|0c 00|\"; distance:19; within:2;", + 35, 0); + TEST_RUN("content:\"SMB\"; depth:8; content:\"|09 00|\"; distance:8; within:2; content:\"|05 " + "00 00|\"; distance:0; content:\"|0c 00|\"; distance:19; within:2; content:\"|15 00 " + "00 00|\"; distance:20; within:4;", + 57, 0); + + TEST_DONE; +} + +/** + * \brief Print list of DETECT_CONTENT SigMatch's allocated in a + * SigMatch list, from the current sm to the end + * \param sm pointer to the current SigMatch to start printing from + */ +static void DetectContentPrintAll(SigMatch *sm) +{ +#ifdef DEBUG + if (SCLogDebugEnabled()) { + int i = 0; + + if (sm == NULL) + return; + + SigMatch *first_sm = sm; + + /* Print all of them */ + for (; first_sm != NULL; first_sm = first_sm->next) { + if (first_sm->type == DETECT_CONTENT) { + SCLogDebug("Printing SigMatch DETECT_CONTENT %d", ++i); + DetectContentPrint((DetectContentData*)first_sm->ctx); + } + } + } +#endif /* DEBUG */ +} + +static int g_file_data_buffer_id = 0; +static int g_dce_stub_data_buffer_id = 0; + +/** + * \test DetectContentParseTest01 this is a test to make sure we can deal with escaped colons + */ +static int DetectContentParseTest01 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "abc\\:def"; + const char *teststringparsed = "abc:def"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { + SCLogDebug("expected %s got ", teststringparsed); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + } else { + SCLogDebug("expected %s got NULL: ", teststringparsed); + result = 0; + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest02 this is a test to make sure we can deal with escaped semi-colons + */ +static int DetectContentParseTest02 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "abc\\;def"; + const char *teststringparsed = "abc;def"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { + SCLogDebug("expected %s got ", teststringparsed); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + } else { + SCLogDebug("expected %s got NULL: ", teststringparsed); + result = 0; + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest03 this is a test to make sure we can deal with escaped double-quotes + */ +static int DetectContentParseTest03 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "abc\\\"def"; + const char *teststringparsed = "abc\"def"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { + SCLogDebug("expected %s got ", teststringparsed); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + } else { + SCLogDebug("expected %s got NULL: ", teststringparsed); + result = 0; + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest04 this is a test to make sure we can deal with escaped backslashes + */ +static int DetectContentParseTest04 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "abc\\\\def"; + const char *teststringparsed = "abc\\def"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + uint16_t len = (cd->content_len > strlen(teststringparsed)); + if (memcmp(cd->content, teststringparsed, len) != 0) { + SCLogDebug("expected %s got ", teststringparsed); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + } else { + SCLogDebug("expected %s got NULL: ", teststringparsed); + result = 0; + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest05 test illegal escape + */ +static int DetectContentParseTest05 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "abc\\def"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + SCLogDebug("expected NULL got "); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest06 test a binary content + */ +static int DetectContentParseTest06 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = "a|42|c|44|e|46|"; + const char *teststringparsed = "abcdef"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + uint16_t len = (cd->content_len > strlen(teststringparsed)); + if (memcmp(cd->content, teststringparsed, len) != 0) { + SCLogDebug("expected %s got ", teststringparsed); + PrintRawUriFp(stdout,cd->content,cd->content_len); + SCLogDebug(": "); + result = 0; + DetectContentFree(NULL, cd); + } + } else { + SCLogDebug("expected %s got NULL: ", teststringparsed); + result = 0; + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest07 test an empty content + */ +static int DetectContentParseTest07 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = ""; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + SCLogDebug("expected NULL got %p: ", cd); + result = 0; + DetectContentFree(NULL, cd); + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test DetectContentParseTest08 test an empty content + */ +static int DetectContentParseTest08 (void) +{ + int result = 1; + DetectContentData *cd = NULL; + const char *teststring = ""; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd != NULL) { + SCLogDebug("expected NULL got %p: ", cd); + result = 0; + DetectContentFree(NULL, cd); + } + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + return result; +} + +/** + * \test Test packet Matches + * \param raw_eth_pkt pointer to the ethernet packet + * \param pktsize size of the packet + * \param sig pointer to the signature to test + * \param sid sid number of the signature + * \retval return 1 if match + * \retval return 0 if not + */ +static int DetectContentLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize, const char *sig, + uint32_t sid) +{ + int result = 0; + + Packet *p = PacketGetFromAlloc(); + if (unlikely(p == NULL)) + return 0; + DecodeThreadVars dtv; + + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&th_v, 0, sizeof(th_v)); + + FlowInitConfig(FLOW_QUIET); + DecodeEthernet(&th_v, &dtv, p, raw_eth_pkt, pktsize); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, sig); + if (de_ctx->sig_list == NULL) { + goto end; + } + de_ctx->sig_list->next = NULL; + + if (de_ctx->sig_list->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) { + DetectContentData *co = (DetectContentData *)de_ctx->sig_list->init_data + ->smlists_tail[DETECT_SM_LIST_PMATCH] + ->ctx; + if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) { + printf("relative next flag set on final match which is content: "); + goto end; + } + } + + SCLogDebug("---DetectContentLongPatternMatchTest---"); + DetectContentPrintAll(de_ctx->sig_list->init_data->smlists[DETECT_SM_LIST_MATCH]); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, sid) != 1) { + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) + { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + } + PacketRecycle(p); + FlowShutdown(); + + SCFree(p); + return result; +} + +/** + * \brief Wrapper for DetectContentLongPatternMatchTest + */ +static int DetectContentLongPatternMatchTestWrp(const char *sig, uint32_t sid) +{ + /** Real packet with the following tcp data: + * "Hi, this is a big test to check content matches of splitted" + * "patterns between multiple chunks!" + * (without quotes! :) ) + */ + uint8_t raw_eth_pkt[] = { + 0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00, + 0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06, + 0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00, + 0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02, + 0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69, + 0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69, + 0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20, + 0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20, + 0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f, + 0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61, + 0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66, + 0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65, + 0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72, + 0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65, + 0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69, + 0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e, + 0x6b,0x73,0x21 }; /* end raw_eth_pkt */ + + return DetectContentLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt), + sig, sid); +} + +/** + * \test Check if we match a normal pattern (not splitted) + */ +static int DetectContentLongPatternMatchTest01(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" + " content:\"Hi, this is a big test\"; sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match a splitted pattern + */ +static int DetectContentLongPatternMatchTest02(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" + " content:\"Hi, this is a big test to check content matches of" + " splitted patterns between multiple chunks!\"; sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check that we don't match the signature if one of the splitted + * chunks doesn't match the packet + */ +static int DetectContentLongPatternMatchTest03(void) +{ + /** The last chunk of the content should not match */ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" + " content:\"Hi, this is a big test to check content matches of" + " splitted patterns between multiple splitted chunks!\"; sid:1;)"; + return (DetectContentLongPatternMatchTestWrp(sig, 1) == 0) ? 1: 0; +} + +/** + * \test Check if we match multiple content (not splitted) + */ +static int DetectContentLongPatternMatchTest04(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"Hi, this is\"; depth:15 ;content:\"a big test\"; " + " within:15; content:\"to check content matches of\"; " + " within:30; content:\"splitted patterns\"; distance:1; " + " within:30; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check that we match packets with multiple chunks and not chunks + * Here we should specify only contents that fit in 32 bytes + * Each of them with their modifier values + */ +static int DetectContentLongPatternMatchTest05(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"Hi, this is a big\"; depth:17; " + " isdataat:30, relative; " + " content:\"test\"; within: 5; distance:1; " + " isdataat:15, relative; " + " content:\"of splitted\"; within:37; distance:15; " + " isdataat:20,relative; " + " content:\"patterns\"; within:9; distance:1; " + " isdataat:10, relative; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check that we match packets with multiple chunks and not chunks + * Here we should specify contents that fit and contents that must be splitted + * Each of them with their modifier values + */ +static int DetectContentLongPatternMatchTest06(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"Hi, this is a big test to check cont\"; depth:36;" + " content:\"ent matches\"; within:11; distance:0; " + " content:\"of splitted patterns between multiple\"; " + " within:38; distance:1; " + " content:\"chunks!\"; within: 8; distance:1; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match contents that are in the payload + * but not in the same order as specified in the signature + */ +static int DetectContentLongPatternMatchTest07(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"chunks!\"; " + " content:\"content matches\"; offset:32; depth:47; " + " content:\"of splitted patterns between multiple\"; " + " content:\"Hi, this is a big\"; offset:0; depth:17; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match contents that are in the payload + * but not in the same order as specified in the signature + */ +static int DetectContentLongPatternMatchTest08(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"ent matches\"; " + " content:\"of splitted patterns between multiple\"; " + " within:38; distance:1; " + " content:\"chunks!\"; within: 8; distance:1; " + " content:\"Hi, this is a big test to check cont\"; depth:36;" + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match contents that are in the payload + * but not in the same order as specified in the signature + */ +static int DetectContentLongPatternMatchTest09(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"ent matches\"; " + " content:\"of splitted patterns between multiple\"; " + " offset:47; depth:85; " + " content:\"chunks!\"; within: 8; distance:1; " + " content:\"Hi, this is a big test to chec\"; depth:36;" + " content:\"k cont\"; distance:0; within:6;" + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match two consecutive simple contents + */ +static int DetectContentLongPatternMatchTest10(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"Hi, this is a big test to check \"; " + " content:\"con\"; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match two contents of length 1 + */ +static int DetectContentLongPatternMatchTest11(void) +{ + const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\"; " + " content:\"H\"; " + " content:\"i\"; " + " sid:1;)"; + return DetectContentLongPatternMatchTestWrp(sig, 1); +} + +static int DetectContentParseTest09(void) +{ + DetectContentData *cd = NULL; + const char *teststring = "boo"; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + FAIL_IF_NULL(cd); + DetectContentFree(NULL, cd); + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + PASS; +} + +/** + * \test Test cases where if within specified is < content length we invalidate + * the sig. + */ +static int DetectContentParseTest17(void) +{ + int result = 0; + const char *sigstr = "alert tcp any any -> any any (msg:\"Dummy\"; " + "content:\"one\"; content:\"two\"; within:2; sid:1;)"; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (de_ctx->sig_list != NULL) + goto end; + + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Test content for dce sig. + */ +static int DetectContentParseTest18(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + Signature *s = SigAlloc(); + FAIL_IF_NULL(s); + FAIL_IF(DetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0); + FAIL_IF_NOT(DetectContentSetup(de_ctx, s, "one") == 0); + FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id)); + FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL); + SigFree(de_ctx, s); + + s = SigAlloc(); + FAIL_IF_NULL(s); + FAIL_IF_NOT(DetectContentSetup(de_ctx, s, "one") == 0); + FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id)); + FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] != NULL); + SigFree(de_ctx, s); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \test Test content for dce sig. + */ + +static int DetectContentParseTest19(void) +{ + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s = + DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(msg:\"Testing dce iface, stub_data with content\"; " + "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; " + "dce_stub_data; " + "content:\"one\"; distance:0; sid:1;)"); + FAIL_IF_NULL(s); + + SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id); + FAIL_IF_NULL(sm); + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL); + + DetectContentData *data = (DetectContentData *)sm->ctx; + FAIL_IF_NOT(data->flags == DETECT_CONTENT_DISTANCE); + + s = DetectEngineAppendSig(de_ctx, + "alert tcp any any -> any any " + "(msg:\"Testing dce iface, stub_data with contents & distance, within\"; " + "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; " + "dce_stub_data; " + "content:\"one\"; distance:0; content:\"two\"; within:10; sid:2;)"); + FAIL_IF_NULL(s); + sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id); + FAIL_IF_NULL(sm); + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NULL(sm->next); + sm = sm->next; + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL); + + data = (DetectContentData *)sm->ctx; + FAIL_IF_NOT(data->flags == DETECT_CONTENT_WITHIN); + FAIL_IF_NOT(data->within == 10); + + s = DetectEngineAppendSig(de_ctx, + "alert tcp any any -> any any " + "(msg:\"Testing dce iface, stub with contents, distance, within\"; " + "dce_iface:3919286a-b10c-11d0-9ba8-00c04fd92ef5; " + "dce_stub_data; " + "content:\"one\"; distance:0; " + "content:\"two\"; within:10; distance:2; sid:3;)"); + FAIL_IF_NULL(s); + sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id); + FAIL_IF_NULL(sm); + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + FAIL_IF_NULL(sm->next); + sm = sm->next; + FAIL_IF_NOT(sm->type == DETECT_CONTENT); + data = (DetectContentData *)sm->ctx; + FAIL_IF_NOT(data->flags == (DETECT_CONTENT_WITHIN | DETECT_CONTENT_DISTANCE)); + FAIL_IF_NOT(data->within == 10); + FAIL_IF_NOT(data->distance == 2); + FAIL_IF_NOT(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL); + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(msg:\"Testing content\"; " + "content:\"one\"; sid:4;)"); + FAIL_IF_NULL(s); + FAIL_IF(DetectBufferIsPresent(s, g_dce_stub_data_buffer_id)); + FAIL_IF(s->init_data->smlists[DETECT_SM_LIST_PMATCH] == NULL); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \test Test content for dce sig. + */ +static int DetectContentParseTest20(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"\"; sid:238012;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest21(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"; sid:238012;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest22(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"boo; sid:238012;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest23(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:boo\"; sid:238012;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest24(void) +{ + DetectEngineCtx *de_ctx = NULL; + DetectContentData *cd = 0; + Signature *s = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + s = de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content: !\"boo\"; sid:238012;)"); + if (de_ctx->sig_list == NULL) { + printf("de_ctx->sig_list == NULL: "); + result = 0; + goto end; + } + + if (s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL || + s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx == NULL) { + printf("de_ctx->pmatch_tail == NULL || de_ctx->pmatch_tail->ctx == NULL: "); + result = 0; + goto end; + } + + cd = (DetectContentData *)s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx; + result = (strncmp("boo", (char *)cd->content, cd->content_len) == 0); + +end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest25(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest26(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest27(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"af|\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest28(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af|\"; sid:1;)"); + if (de_ctx->sig_list == NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest29(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"aast|\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest30(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"aast|af\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest31(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"aast|af|\"; sid:1;)"); + if (de_ctx->sig_list == NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest32(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af|asdf\"; sid:1;)"); + if (de_ctx->sig_list == NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest33(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af|af|\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest34(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af|af|af\"; sid:1;)"); + if (de_ctx->sig_list != NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +/** + * \test Parsing test + */ +static int DetectContentParseTest35(void) +{ + DetectEngineCtx *de_ctx = NULL; + int result = 1; + + de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert udp any any -> any any " + "(msg:\"test\"; content:\"|af|af|af|\"; sid:1;)"); + if (de_ctx->sig_list == NULL) { + result = 0; + goto end; + } + + end: + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + + return result; +} + +static int SigTestPositiveTestContent(const char *rule, uint8_t *buf) +{ + uint16_t buflen = strlen((char *)buf); + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + + memset(&th_v, 0, sizeof(th_v)); + Packet *p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); + FAIL_IF_NULL(p); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, rule); + FAIL_IF_NULL(de_ctx->sig_list); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + FAIL_IF_NULL(det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + + FAIL_IF(PacketAlertCheck(p, 1) != 1); + + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + + UTHFreePackets(&p, 1); + PASS; +} + +static int DetectContentParseTest41(void) +{ + int result = 1; + DetectContentData *cd = NULL; + int patlen = 255; + char *teststring = SCMalloc(sizeof(char) * (patlen + 1)); + if (unlikely(teststring == NULL)) + return 0; + int idx = 0; + for (int i = 0; i < patlen; idx++, i++) { + teststring[idx] = 'a'; + } + teststring[idx++] = '\0'; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd == NULL) { + SCLogDebug("expected not NULL"); + result = 0; + } + + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + SCFree(teststring); + DetectContentFree(NULL, cd); + return result; +} + +/** + * Tests that content lengths > 255 are supported. + */ +static int DetectContentParseTest42(void) +{ + int result = 1; + DetectContentData *cd = NULL; + int patlen = 256; + char *teststring = SCMalloc(sizeof(char) * (patlen + 1)); + if (unlikely(teststring == NULL)) + return 0; + int idx = 0; + for (int i = 0; i < patlen; idx++, i++) { + teststring[idx] = 'a'; + } + teststring[idx++] = '\0'; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd == NULL) { + SCLogDebug("expected not NULL"); + result = 0; + } + + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + SCFree(teststring); + DetectContentFree(NULL, cd); + return result; +} + +static int DetectContentParseTest43(void) +{ + int result = 1; + DetectContentData *cd = NULL; + int patlen = 258; + char *teststring = SCMalloc(sizeof(char) * (patlen + 1)); + if (unlikely(teststring == NULL)) + return 0; + int idx = 0; + teststring[idx++] = '|'; + teststring[idx++] = '4'; + teststring[idx++] = '6'; + teststring[idx++] = '|'; + for (int i = 0; i < (patlen - 4); idx++, i++) { + teststring[idx] = 'a'; + } + teststring[idx++] = '\0'; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd == NULL) { + SCLogDebug("expected not NULL"); + result = 0; + } + + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + SCFree(teststring); + DetectContentFree(NULL, cd); + return result; +} + +/** + * Tests that content lengths > 255 are supported. + */ +static int DetectContentParseTest44(void) +{ + int result = 1; + DetectContentData *cd = NULL; + int patlen = 259; + char *teststring = SCMalloc(sizeof(char) * (patlen + 1)); + if (unlikely(teststring == NULL)) + return 0; + int idx = 0; + teststring[idx++] = '|'; + teststring[idx++] = '4'; + teststring[idx++] = '6'; + teststring[idx++] = '|'; + for (int i = 0; i < (patlen - 4); idx++, i++) { + teststring[idx] = 'a'; + } + teststring[idx++] = '\0'; + + uint8_t spm_matcher = SinglePatternMatchDefaultMatcher(); + SpmGlobalThreadCtx *spm_global_thread_ctx = SpmInitGlobalThreadCtx(spm_matcher); + FAIL_IF(spm_global_thread_ctx == NULL); + + cd = DetectContentParse(spm_global_thread_ctx, teststring); + if (cd == NULL) { + SCLogDebug("expected not NULL"); + result = 0; + } + + SpmDestroyGlobalThreadCtx(spm_global_thread_ctx); + SCFree(teststring); + DetectContentFree(NULL, cd); + return result; +} + +/** + * \test Parsing test to check for unescaped quote within content section + */ +static int DetectContentParseTest45(void) +{ + DetectEngineCtx *de_ctx = NULL; + + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + + de_ctx->flags |= DE_QUIET; + de_ctx->sig_list = SigInit(de_ctx, + "alert tcp any any -> any any " + "(msg:\"test\"; content:\"|ff|\" content:\"TEST\"; sid:1;)"); + FAIL_IF_NOT_NULL(de_ctx->sig_list); + + DetectEngineCtxFree(de_ctx); + + PASS; +} + +static int SigTestNegativeTestContent(const char *rule, uint8_t *buf) +{ + uint16_t buflen = strlen((char *)buf); + Packet *p = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + int result = 0; + memset(&th_v, 0, sizeof(th_v)); + + p = UTHBuildPacket(buf, buflen, IPPROTO_TCP); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, rule); + if (de_ctx->sig_list == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1) != 0) { + goto end; + } + + result = 1; +end: + if (det_ctx != NULL) { + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + } + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } + UTHFreePackets(&p, 1); + return result; +} + +/** + * \test A positive test that checks that the content string doesn't contain + * the negated content + */ +static int SigTest41TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any " + "(msg:\"HTTP URI cap\"; content:!\"GES\"; sid:1;)", + + (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\n" + "GET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test crash condition: as packet has no direction, it defaults to toclient + * in stream ctx inspection of packet. There a null ptr deref happens + * We don't care about the match/nomatch here. + */ +static int SigTest41aTestNegatedContent(void) +{ + (void)SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; flow:to_server; content:\"GET\"; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); + return 1; +} + + +/** + * \test A positive test that checks that the content string doesn't contain + * the negated content within the specified depth + */ +static int SigTest42TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; depth:22; offset:35; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A negative test that checks that the content string doesn't contain + * the negated content within the specified depth, and also after the + * specified offset. Since the content is there, the match fails. + * + * Match is at offset:23, depth:34 + */ +static int SigTest43TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; depth:34; offset:23; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A negative test that checks that the content string doesn't contain + * the negated content after the specified offset and within the specified + * depth. + */ +static int SigTest44TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:!\"eeeeeeeeeee\"; offset:40; depth:35; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A positive test that uses a combination of content string with negated + * content string + */ +static int SigTest45TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:5; " + "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A negative test that uses a combination of content string with negated + * content string, with we receiving a failure for 'onee' itself. + */ +static int SigTest46TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaaE\"; " + "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A negative test that uses a combination of content string with negated + * content string, with we receiving a failure of first content's offset + * condition + */ +static int SigTest47TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; offset:5; " + "content:!\"eeeeeeeeeee\"; depth:23; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A positive test that checks that we don't have a negated content within + * the specified length from the previous content match. + */ +static int SigTest48TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:\"GET\"; content:!\"GES\"; within:26; sid:1;)", + (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ " + "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content with the use of within + */ +static int SigTest49TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:\"GET\"; content:!\"Host\"; within:26; sid:1;)", + (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ " + "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test A positive test that checks the combined use of content and negated + * content with the use of distance + */ +static int SigTest50TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:\"GET\"; content:!\"GES\"; distance:25; sid:1;)", + (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ " + "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content with the use of distance + * + * First GET at offset 0 + * First Host at offset 21 + */ +static int SigTest51TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"GET\"; content:!\"Host\"; distance:17; sid:1;)", (uint8_t *)"GET /one/ HTTP/1.1\r\nHost: one.example.org\r\n\r\n\r\nGET /two/ HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content, with the content not being present + */ +static int SigTest52TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:\"GES\"; content:!\"BOO\"; sid:1;)", + (uint8_t *)"GET /one/ HTTP/1.1\r\n Host: one.example.org\r\n\r\n\r\nGET /two/ " + "HTTP/1.1\r\nHost: two.example.org\r\n\r\n\r\n"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content, in the presence of within + */ +static int SigTest53TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:\"aaa\"; content:!\"Ggggg\"; within:56; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** + * \test A positive test that checks the combined use of content and negated + * content, in the presence of within + */ +static int SigTest54TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"gggggg\"; within:20; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff ggggggggg hhhhhhhh"); +} + +/** + * \test A negative test that checks the use of negated content along with + * the presence of depth + */ +static int SigTest55TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:!\"aaa\"; depth:5; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff gggggggg hhhhhhhh"); +} + +/** + * \test A positive test that checks the combined use of 2 contents in the + * presence of within + */ +static int SigTest56TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:\"aaa\"; content:\"Ggggg\"; within:56; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Gggggggg hhhhhhhh"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content, in the presence of within + */ +static int SigTest57TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:\"aaa\"; content:!\"Ggggg\"; within:56; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** + * \test A positive test that checks the combined use of content and negated + * content, in the presence of distance + */ +static int SigTest58TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"Ggggg\"; distance:57; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** + * \test A negative test that checks the combined use of content and negated + * content, in the presence of distance + */ +static int SigTest59TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"Gggg\"; distance:30; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest60TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:!\"aaa\"; content:\"Ggggg\"; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest61TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Ggggg\"; within:30; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** \test Test negation in combination with within and depth + * + * Match of "aaa" at offset:0, depth:3 + * Match of "Gggggg" at offset:46, depth:52 + * + * This signature should not match for the test to pass. + */ +static int SigTest62TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Gggggg\"; within:49; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest63TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Gggggg\"; within:56; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest64TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Gggggg\"; within:30; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** \test Test negation in combination with within and depth + * + * Match of "aaa" at offset:0, depth:3 + * Match of "gggggg" at offset:46, depth:52 + * + * This signature should not match for the test to pass. + */ +static int SigTest65TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Gggggg\"; distance:0; within:49; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest66TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"Gggggg\"; within:30; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest67TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:10; " + "content:!\"XXXX\"; within:56; sid:1;)", + (uint8_t *)"aaa bbbb cccc XXXXdddd eeeeeeeeeee ffffffffff XXXXggggg hhhhhhhh"); +} + +static int SigTest68TestNegatedContent(void) +{ + return SigTestPositiveTestContent( + "alert tcp any any -> any any (content:\"aaa\"; depth:10; content:\"cccc\"; offset:8; " + "content:!\"Gggggg\"; within:28; content:\"hhhhhhhh\"; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest69TestNegatedContent(void) +{ + return SigTestNegativeTestContent( + "alert tcp any any -> any any (content:\"aaa\"; depth:10; content:\"cccc\"; offset:8; " + "content:!\"Gggggg\"; within:48; content:\"hhhhhhhh\"; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest70TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"Gggggg\"; within:52; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +/** \test within and distance */ +static int SigTest71TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"Gggggg\"; within:40; distance:43; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest72TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; " + "content:!\"Gggggg\"; within:49; distance:43; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff Ggggggggg hhhhhhhh"); +} + +static int SigTest73TestNegatedContent(void) +{ + return SigTestNegativeTestContent("alert tcp any any -> any any (content:\"aaa\"; depth:5; " + "content:!\"eeeeeeeeeee\"; depth:35; sid:1;)", + (uint8_t *)"aaa bbbb cccc dddddddd eeeeeeeeeee ffffffffff ggggggggg hhhhhhhh"); +} + +static int SigTest74TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:!\"PASS\"; sid:1;)", (uint8_t *)"USER apple"); +} + +static int SigTest75TestNegatedContent(void) +{ + return SigTestPositiveTestContent("alert tcp any any -> any any (msg:\"HTTP URI cap\"; content:\"USER\"; content:\"!PASS\"; sid:1;)", (uint8_t *)"USER !PASS"); +} + +static int SigTest76TestBug134(void) +{ + uint8_t *buf = (uint8_t *)"test detect ${IFS} in traffic"; + uint16_t buflen = strlen((char *)buf); + Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_TCP); + int result = 0; + Flow f; + + memset(&f, 0, sizeof(Flow)); + FLOW_INITIALIZE(&f); + + p->dp = 515; + p->flowflags |= FLOW_PKT_ESTABLISHED; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flow = &f; + p->flags |= PKT_HAS_FLOW; + + char sig[] = "alert tcp any any -> any 515 " + "(msg:\"detect IFS\"; flow:to_server,established; content:\"${IFS}\";" + " depth:50; offset:0; sid:900091; rev:1;)"; + if (UTHPacketMatchSigMpm(p, sig, MPM_AC) == 0) { + result = 0; + goto end; + } + + result = 1; +end: + if (p != NULL) + UTHFreePacket(p); + + FLOW_DESTROY(&f); + return result; +} + +static int SigTest77TestBug139(void) +{ + uint8_t buf[] = { + 0x12, 0x23, 0x34, 0x35, 0x52, 0x52, 0x24, 0x42, 0x22, 0x24, + 0x52, 0x24, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x34 }; + uint16_t buflen = sizeof(buf); + Packet *p = UTHBuildPacket( buf, buflen, IPPROTO_UDP); + int result = 0; + + p->dp = 53; + char sig[] = "alert udp any any -> any 53 (msg:\"dns testing\";" + " content:\"|00 00|\"; depth:5; offset:13; sid:9436601;" + " rev:1;)"; + if (UTHPacketMatchSigMpm(p, sig, MPM_AC) == 0) { + result = 0; + goto end; + } + + result = 1; +end: + if (p != NULL) + UTHFreePacket(p); + return result; +} + +static int DetectLongContentTestCommon(const char *sig, uint32_t sid) +{ + /* Packet with 512 A's in it for testing long content. */ + static uint8_t pkt[739] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, + 0x02, 0xd5, 0x4a, 0x18, 0x40, 0x00, 0x40, 0x06, + 0xd7, 0xd6, 0x0a, 0x10, 0x01, 0x0b, 0x0a, 0x10, + 0x01, 0x0a, 0xdb, 0x36, 0x00, 0x50, 0xca, 0xc5, + 0xcc, 0xd1, 0x95, 0x77, 0x0f, 0x7d, 0x80, 0x18, + 0x00, 0xe5, 0x77, 0x9d, 0x00, 0x00, 0x01, 0x01, + 0x08, 0x0a, 0x1d, 0xe0, 0x86, 0xc6, 0xfc, 0x73, + 0x49, 0xf3, 0x50, 0x4f, 0x53, 0x54, 0x20, 0x2f, + 0x20, 0x48, 0x54, 0x54, 0x50, 0x2f, 0x31, 0x2e, + 0x31, 0x0d, 0x0a, 0x55, 0x73, 0x65, 0x72, 0x2d, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x3a, 0x20, 0x63, + 0x75, 0x72, 0x6c, 0x2f, 0x37, 0x2e, 0x33, 0x37, + 0x2e, 0x30, 0x0d, 0x0a, 0x48, 0x6f, 0x73, 0x74, + 0x3a, 0x20, 0x31, 0x30, 0x2e, 0x31, 0x36, 0x2e, + 0x31, 0x2e, 0x31, 0x30, 0x0d, 0x0a, 0x41, 0x63, + 0x63, 0x65, 0x70, 0x74, 0x3a, 0x20, 0x2a, 0x2f, + 0x2a, 0x0d, 0x0a, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x6e, 0x74, 0x2d, 0x4c, 0x65, 0x6e, 0x67, 0x74, + 0x68, 0x3a, 0x20, 0x35, 0x32, 0x38, 0x0d, 0x0a, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x2d, + 0x54, 0x79, 0x70, 0x65, 0x3a, 0x20, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2f, 0x78, 0x2d, 0x77, 0x77, 0x77, 0x2d, + 0x66, 0x6f, 0x72, 0x6d, 0x2d, 0x75, 0x72, 0x6c, + 0x65, 0x6e, 0x63, 0x6f, 0x64, 0x65, 0x64, 0x0d, + 0x0a, 0x0d, 0x0a, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, + 0x41, 0x41, 0x41, 0x58, 0x58, 0x58, 0x58, 0x58, + 0x58, 0x58, 0x58 + }; + + return DetectContentLongPatternMatchTest(pkt, (uint16_t)sizeof(pkt), sig, + sid); +} + +static int DetectLongContentTest1(void) +{ + /* Signature with 256 A's. */ + const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)"; + + return DetectLongContentTestCommon(sig, 1); +} + +static int DetectLongContentTest2(void) +{ + /* Signature with 512 A's. */ + const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)"; + + return DetectLongContentTestCommon(sig, 1); +} + +static int DetectLongContentTest3(void) +{ + /* Signature with 513 A's. */ + const char *sig = "alert tcp any any -> any any (msg:\"Test Rule\"; content:\"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"; sid:1;)"; + + return !DetectLongContentTestCommon(sig, 1); +} + +static int DetectBadBinContent(void) +{ + DetectEngineCtx *de_ctx = NULL; + de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + FAIL_IF_NOT_NULL(DetectEngineAppendSig( + de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|a|\"; sid:1;)")); + FAIL_IF_NOT_NULL(DetectEngineAppendSig( + de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|aa b|\"; sid:1;)")); + FAIL_IF_NOT_NULL(DetectEngineAppendSig( + de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|aa bz|\"; sid:1;)")); + /* https://redmine.openinfosecfoundation.org/issues/5201 */ + FAIL_IF_NOT_NULL(DetectEngineAppendSig( + de_ctx, "alert tcp any any -> any any (msg:\"test\"; content:\"|22 2 22|\"; sid:1;)")); + DetectEngineCtxFree(de_ctx); + PASS; +} + +/** + * \brief this function registers unit tests for DetectContent + */ +static void DetectContentRegisterTests(void) +{ + g_file_data_buffer_id = DetectBufferTypeGetByName("file_data"); + g_dce_stub_data_buffer_id = DetectBufferTypeGetByName("dce_stub_data"); + + UtRegisterTest("DetectContentDepthTest01", DetectContentDepthTest01); + + UtRegisterTest("DetectContentParseTest01", DetectContentParseTest01); + UtRegisterTest("DetectContentParseTest02", DetectContentParseTest02); + UtRegisterTest("DetectContentParseTest03", DetectContentParseTest03); + UtRegisterTest("DetectContentParseTest04", DetectContentParseTest04); + UtRegisterTest("DetectContentParseTest05", DetectContentParseTest05); + UtRegisterTest("DetectContentParseTest06", DetectContentParseTest06); + UtRegisterTest("DetectContentParseTest07", DetectContentParseTest07); + UtRegisterTest("DetectContentParseTest08", DetectContentParseTest08); + UtRegisterTest("DetectContentParseTest09", DetectContentParseTest09); + UtRegisterTest("DetectContentParseTest17", DetectContentParseTest17); + UtRegisterTest("DetectContentParseTest18", DetectContentParseTest18); + UtRegisterTest("DetectContentParseTest19", DetectContentParseTest19); + UtRegisterTest("DetectContentParseTest20", DetectContentParseTest20); + UtRegisterTest("DetectContentParseTest21", DetectContentParseTest21); + UtRegisterTest("DetectContentParseTest22", DetectContentParseTest22); + UtRegisterTest("DetectContentParseTest23", DetectContentParseTest23); + UtRegisterTest("DetectContentParseTest24", DetectContentParseTest24); + UtRegisterTest("DetectContentParseTest25", DetectContentParseTest25); + UtRegisterTest("DetectContentParseTest26", DetectContentParseTest26); + UtRegisterTest("DetectContentParseTest27", DetectContentParseTest27); + UtRegisterTest("DetectContentParseTest28", DetectContentParseTest28); + UtRegisterTest("DetectContentParseTest29", DetectContentParseTest29); + UtRegisterTest("DetectContentParseTest30", DetectContentParseTest30); + UtRegisterTest("DetectContentParseTest31", DetectContentParseTest31); + UtRegisterTest("DetectContentParseTest32", DetectContentParseTest32); + UtRegisterTest("DetectContentParseTest33", DetectContentParseTest33); + UtRegisterTest("DetectContentParseTest34", DetectContentParseTest34); + UtRegisterTest("DetectContentParseTest35", DetectContentParseTest35); + UtRegisterTest("DetectContentParseTest41", DetectContentParseTest41); + UtRegisterTest("DetectContentParseTest42", DetectContentParseTest42); + UtRegisterTest("DetectContentParseTest43", DetectContentParseTest43); + UtRegisterTest("DetectContentParseTest44", DetectContentParseTest44); + UtRegisterTest("DetectContentParseTest45", DetectContentParseTest45); + + /* The reals */ + UtRegisterTest("DetectContentLongPatternMatchTest01", + DetectContentLongPatternMatchTest01); + UtRegisterTest("DetectContentLongPatternMatchTest02", + DetectContentLongPatternMatchTest02); + UtRegisterTest("DetectContentLongPatternMatchTest03", + DetectContentLongPatternMatchTest03); + UtRegisterTest("DetectContentLongPatternMatchTest04", + DetectContentLongPatternMatchTest04); + UtRegisterTest("DetectContentLongPatternMatchTest05", + DetectContentLongPatternMatchTest05); + UtRegisterTest("DetectContentLongPatternMatchTest06", + DetectContentLongPatternMatchTest06); + UtRegisterTest("DetectContentLongPatternMatchTest07", + DetectContentLongPatternMatchTest07); + UtRegisterTest("DetectContentLongPatternMatchTest08", + DetectContentLongPatternMatchTest08); + UtRegisterTest("DetectContentLongPatternMatchTest09", + DetectContentLongPatternMatchTest09); + UtRegisterTest("DetectContentLongPatternMatchTest10", + DetectContentLongPatternMatchTest10); + UtRegisterTest("DetectContentLongPatternMatchTest11", + DetectContentLongPatternMatchTest11); + + /* Negated content tests */ + UtRegisterTest("SigTest41TestNegatedContent", SigTest41TestNegatedContent); + UtRegisterTest("SigTest41aTestNegatedContent", + SigTest41aTestNegatedContent); + UtRegisterTest("SigTest42TestNegatedContent", SigTest42TestNegatedContent); + UtRegisterTest("SigTest43TestNegatedContent", SigTest43TestNegatedContent); + UtRegisterTest("SigTest44TestNegatedContent", SigTest44TestNegatedContent); + UtRegisterTest("SigTest45TestNegatedContent", SigTest45TestNegatedContent); + UtRegisterTest("SigTest46TestNegatedContent", SigTest46TestNegatedContent); + UtRegisterTest("SigTest47TestNegatedContent", SigTest47TestNegatedContent); + UtRegisterTest("SigTest48TestNegatedContent", SigTest48TestNegatedContent); + UtRegisterTest("SigTest49TestNegatedContent", SigTest49TestNegatedContent); + UtRegisterTest("SigTest50TestNegatedContent", SigTest50TestNegatedContent); + UtRegisterTest("SigTest51TestNegatedContent", SigTest51TestNegatedContent); + UtRegisterTest("SigTest52TestNegatedContent", SigTest52TestNegatedContent); + UtRegisterTest("SigTest53TestNegatedContent", SigTest53TestNegatedContent); + UtRegisterTest("SigTest54TestNegatedContent", SigTest54TestNegatedContent); + UtRegisterTest("SigTest55TestNegatedContent", SigTest55TestNegatedContent); + UtRegisterTest("SigTest56TestNegatedContent", SigTest56TestNegatedContent); + UtRegisterTest("SigTest57TestNegatedContent", SigTest57TestNegatedContent); + UtRegisterTest("SigTest58TestNegatedContent", SigTest58TestNegatedContent); + UtRegisterTest("SigTest59TestNegatedContent", SigTest59TestNegatedContent); + UtRegisterTest("SigTest60TestNegatedContent", SigTest60TestNegatedContent); + UtRegisterTest("SigTest61TestNegatedContent", SigTest61TestNegatedContent); + UtRegisterTest("SigTest62TestNegatedContent", SigTest62TestNegatedContent); + UtRegisterTest("SigTest63TestNegatedContent", SigTest63TestNegatedContent); + UtRegisterTest("SigTest64TestNegatedContent", SigTest64TestNegatedContent); + UtRegisterTest("SigTest65TestNegatedContent", SigTest65TestNegatedContent); + UtRegisterTest("SigTest66TestNegatedContent", SigTest66TestNegatedContent); + UtRegisterTest("SigTest67TestNegatedContent", SigTest67TestNegatedContent); + UtRegisterTest("SigTest68TestNegatedContent", SigTest68TestNegatedContent); + UtRegisterTest("SigTest69TestNegatedContent", SigTest69TestNegatedContent); + UtRegisterTest("SigTest70TestNegatedContent", SigTest70TestNegatedContent); + UtRegisterTest("SigTest71TestNegatedContent", SigTest71TestNegatedContent); + UtRegisterTest("SigTest72TestNegatedContent", SigTest72TestNegatedContent); + UtRegisterTest("SigTest73TestNegatedContent", SigTest73TestNegatedContent); + UtRegisterTest("SigTest74TestNegatedContent", SigTest74TestNegatedContent); + UtRegisterTest("SigTest75TestNegatedContent", SigTest75TestNegatedContent); + + UtRegisterTest("SigTest76TestBug134", SigTest76TestBug134); + UtRegisterTest("SigTest77TestBug139", SigTest77TestBug139); + + UtRegisterTest("DetectLongContentTest1", DetectLongContentTest1); + UtRegisterTest("DetectLongContentTest2", DetectLongContentTest2); + UtRegisterTest("DetectLongContentTest3", DetectLongContentTest3); + + UtRegisterTest("DetectBadBinContent", DetectBadBinContent); +} +#endif /* UNITTESTS */ |