From f233f9fa31053332289f8fa68573e52f10cb3c54 Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Fri, 6 Nov 2009 17:51:05 +0100 Subject: [PATCH] Adding detect_content chunks handling for max_pattern_length and unittests. Updating modifiers to use it. --- src/detect-content.c | 1577 +++++++++++++++++++++++++++++++++++++-- src/detect-content.h | 58 +- src/detect-depth.c | 22 + src/detect-distance.c | 20 +- src/detect-id.c | 4 +- src/detect-isdataat.c | 114 ++- src/detect-isdataat.h | 2 +- src/detect-offset.c | 22 + src/detect-within.c | 20 +- src/detect.h | 1 + src/util-mpm-b2g.c | 1 + src/util-mpm-b3g.c | 1 + src/util-mpm-wumanber.c | 1 + src/util-mpm.c | 13 + src/util-mpm.h | 4 + 15 files changed, 1735 insertions(+), 125 deletions(-) diff --git a/src/detect-content.c b/src/detect-content.c index 46d380797d..5f8381ba3d 100644 --- a/src/detect-content.c +++ b/src/detect-content.c @@ -20,6 +20,14 @@ * match to a specific part of the payload, while the rest can be handled * by content and pcre matches. * + * 06/11/2009 -- PR: + * Now Patterns that exceed the max_pattern_length allowed by the current mpm + * are split into multiple chunk. The modifiers must be set in the first + * chunk of a group of chunks, and after a modifier is set, the modifiers of the + * next chunks must be recalculated (propagated). This way, each DETECT_CONTENT + * installed should be completely independent, as if it were loaded in another + * content option of the signature. + * * TODO: add a 'recursive depth' to limit the depth to do the recursion on... * * XXX more later.... @@ -32,6 +40,8 @@ #include "detect-content.h" #include "detect-uricontent.h" #include "detect-engine-mpm.h" +#include "detect-engine.h" +#include "detect-parse.h" #include "util-mpm.h" #include "flow.h" #include "flow-var.h" @@ -57,7 +67,7 @@ void DetectContentRegister (void) { /* pass on the content_max_id */ uint32_t DetectContentMaxId(DetectEngineCtx *de_ctx) { - //printf("DetectContentMaxId: %" PRIu32 "\n", de_ctx->content_max_id); + //SCLogDebug("DetectContentMaxId: %" PRIu32 "", de_ctx->content_max_id); return de_ctx->content_max_id; } @@ -307,14 +317,14 @@ int DetectContentMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p #if 0 if (SCLogDebugEnabled()) { - printf("content \""); PrintRawUriFp(stdout, co->content, co->content_len); - printf("\" matched %" PRIu32 " time(s) at offsets: ", len); + SCLogDebug("content \""); PrintRawUriFp(stdout, co->content, co->content_len); + SCLogDebug("\" matched %" PRIu32 " time(s) at offsets: ", len); MpmMatch *tmpm = NULL; for (tmpm = det_ctx->mtc.match[co->id].top; tmpm != NULL; tmpm = tmpm->next) { - printf("%" PRIu32 " ", tmpm->offset); + SCLogDebug("%" PRIu32 " ", tmpm->offset); } - printf("\n"); + SCLogDebug(""); } #endif @@ -343,12 +353,12 @@ DetectContentData *DetectContentParse (char *contentstr) cd = malloc(sizeof(DetectContentData)); if (cd == NULL) { - printf("DetectContentParse malloc failed\n"); + SCLogDebug("DetectContentParse malloc failed"); goto error; } memset(cd,0,sizeof(DetectContentData)); - //printf("DetectContentParse: \"%s\", len %" PRIu32 "\n", str, len); + //SCLogDebug("DetectContentParse: \"%s\", len %" PRIu32 "", str, len); char converted = 0; { @@ -359,7 +369,7 @@ DetectContentData *DetectContentParse (char *contentstr) uint8_t binpos = 0; for (i = 0, x = 0; i < len; i++) { - // printf("str[%02u]: %c\n", i, str[i]); + // SCLogDebug("str[%02u]: %c", i, str[i]); if (str[i] == '|') { if (bin) { bin = 0; @@ -378,7 +388,7 @@ DetectContentData *DetectContentParse (char *contentstr) str[i] == 'E' || str[i] == 'e' || str[i] == 'F' || str[i] == 'f') { - // printf("part of binary: %c\n", str[i]); + // SCLogDebug("part of binary: %c", str[i]); binstr[binpos] = (char)str[i]; binpos++; @@ -391,7 +401,7 @@ DetectContentData *DetectContentParse (char *contentstr) converted = 1; } } else if (str[i] == ' ') { - // printf("space as part of binary string\n"); + // SCLogDebug("space as part of binary string"); } } else if (escape) { if (str[i] == ':' || @@ -402,7 +412,7 @@ DetectContentData *DetectContentParse (char *contentstr) str[x] = str[i]; x++; } else { - //printf("Can't escape %c\n", str[i]); + //SCLogDebug("Can't escape %c", str[i]); goto error; } escape = 0; @@ -416,10 +426,10 @@ DetectContentData *DetectContentParse (char *contentstr) #if 0//def DEBUG if (SCLogDebugEnabled()) { for (i = 0; i < x; i++) { - if (isprint(str[i])) printf("%c", str[i]); - else printf("\\x%02u", str[i]); + if (isprint(str[i])) SCLogDebug("%c", str[i]); + else SCLogDebug("\\x%02u", str[i]); } - printf("\n"); + SCLogDebug(""); } #endif @@ -453,27 +463,707 @@ error: return NULL; } +/** + * \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=malloc(sizeof(char) * cd->content_len + 1); + + if (tmpstr != NULL) { + for (i = 0; i < cd->content_len; i++) + tmpstr[i] = cd->content[i]; + tmpstr[i] = '\0'; + SCLogDebug("Content: \"%s\"", tmpstr); + free(tmpstr); + } else { + SCLogDebug("Content: "); + for (i = 0; i < cd->content_len; i++) + SCLogDebug("%c", cd->content[i]); + } + + SCLogDebug("Content_id: %u ", cd->id); + SCLogDebug("Content_len: %u ", cd->content_len); + SCLogDebug("Depth: %u ", cd->depth); + SCLogDebug("Offset: %u ", cd->offset); + SCLogDebug("Within: %u ", cd->within); + SCLogDebug("Distance: %u ", cd->distance); + SCLogDebug("Isdataat: %u ", cd->isdataat); + SCLogDebug("flags: %u ", cd->flags); + + /** If it's a chunk, print the data related */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) { + SCLogDebug("* Is_Chunk: is set"); + SCLogDebug("chunk_group_id: %u ", cd->chunk_group_id); + SCLogDebug("chunk_id: %u ", cd->chunk_id); + } else { + SCLogDebug("Is_Chunk: is not set"); + } + SCLogDebug("-----------"); +} + +/** + * \brief Function that return chunks of a original DetectContentData + * that need to be split + * \param origcd pointer to the real DetectContentData + * \param remaining_content_length to the real DetectContentData + * + * \retval NULL if something goes wrong + * \retval DetectContentData pointer to the new chunk + */ +DetectContentData *DetectContentSplitChunk(DetectContentData *origcd, + uint8_t remaining_content_len, + uint8_t index, int32_t mpl) +{ + DetectContentData *cd = malloc(sizeof(DetectContentData)); + if (cd == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "DetectContentData malloc failed"); + goto error; + } + memset(cd,0,sizeof(DetectContentData)); + + /* Get the length for this chunk */ + if (remaining_content_len < mpl) + cd->content_len = remaining_content_len; + else + cd->content_len = mpl; + + if (cd->content_len <= 0) + goto error; + + cd->content = (uint8_t*) malloc(sizeof(uint8_t) * cd->content_len); + if (cd->content == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "string for content malloc failed"); + goto error; + } + + memcpy(cd->content, origcd->content + index * sizeof(uint8_t), cd->content_len); + + return cd; + +error: + if (cd != NULL) { + if (cd->content != NULL) + free(cd->content); + free(cd); + } + return NULL; +} + +/** + * \brief Search the first DETECT_CONTENT chunk of the last group in the + * previous SigMatches or the first DETECT_CONTENT not chunked + * \retval pointer to the SigMatch holding the DetectContent + * \param sm pointer to the current SigMatch of a parsing process + * \retval null if no applicable DetectContent was found + * \retval pointer to the SigMatch that has the previous SigMatch + * of type DetectContent, (and is the first chunk if + * the pattern was splitted) + */ +SigMatch *DetectContentFindApplicableSM(SigMatch *sm) +{ + if (sm == NULL) + return NULL; + while (sm != NULL && sm->type != DETECT_CONTENT) + sm = sm->prev; + + if (sm == NULL) + return NULL; + + DetectContentData *cd = (DetectContentData*) sm->ctx; + if (cd == NULL) + return NULL; + + /** It's not a chunk, so its the only DetectContent for this pattern */ + if (!(cd->flags & DETECT_CONTENT_IS_CHUNK)) + return sm; + + /** Else search for the first chunk in this group of chunks */ + uint8_t chunk_group_id = cd->chunk_group_id; + while (sm != NULL && sm->type == DETECT_CONTENT) + { + cd = (DetectContentData*) sm->ctx; + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK)) + return NULL; + + /** Weird case, this means that the chunk are not consecutive + * or not likned correctly */ + if (cd->chunk_group_id != chunk_group_id) + return NULL; + + /** If we get the first one, return the SimMatch */ + if (cd->chunk_id == 0) + return sm; + + sm = sm->prev; + } + /* We should not be here */ + return NULL; +} + +/** + * \brief Count the number of chunks of a specified chunk group + * \param sm pointer to a SigMatch that belong to this chunk group + * \param chunk_group_id id of the group of chunks (to ensure) + * \retval -1 if something fail + * \retval count of the chunks of this group + */ +int DetectContentCountChunksInGroup(SigMatch *sm, uint8_t chunk_group_id) +{ + int count = 0; + if (sm == NULL || sm->type != DETECT_CONTENT) + return -1; + + DetectContentData *cd = NULL; + SigMatch *first_sm = DetectContentFindApplicableSM(sm); + for (; first_sm != NULL && + first_sm->type == DETECT_CONTENT && + first_sm->ctx != NULL && + (cd = (DetectContentData*) first_sm->ctx) && + (cd->flags & DETECT_CONTENT_IS_CHUNK) && + cd->chunk_group_id == chunk_group_id + ; first_sm = first_sm->next, count++); + + return count; +} + +/** + * \brief Get the remaining legth of a splitted pattern + * (current content not included!) + * \param first_sm pointer to a SigMatch that belong to this chunk group + * \retval pointer to the SigMatch holding the DetectContent + * \retval -1 if fail + * \retval length if every thing was ok + */ +int DetectContentChunksGetRemainingLength(SigMatch *first_sm) +{ + int length = 0; + if (first_sm == NULL) + return -1; + + if (first_sm->type != DETECT_CONTENT || first_sm->ctx == NULL) + return -1; + + DetectContentData *cd = (DetectContentData*) first_sm->ctx; + uint8_t chunk_group_id = cd->chunk_group_id; + + /** Skip the current content (not included) */ + first_sm = first_sm->next; + + /** sum the content_len's */ + for (; first_sm != NULL && + first_sm->type == DETECT_CONTENT && + first_sm->ctx != NULL && + (cd = (DetectContentData*) first_sm->ctx) && + (cd->flags & DETECT_CONTENT_IS_CHUNK) && + cd->chunk_group_id == chunk_group_id + ; first_sm = first_sm->next, length += cd->content_len); + + return length; +} + + +/** + * \brief Get the previous legth of a splitted pattern (current content not included!) + * \param first_sm pointer to a SigMatch that belong to this chunk group + * \retval pointer to the SigMatch holding the DetectContent + * \retval length if every thing was ok + * \retval -1 if fail + */ +int DetectContentChunksGetPreviousLength(SigMatch *sm) +{ + int length = 0; + if (sm == NULL) + return -1; + + if (sm->type != DETECT_CONTENT || sm->ctx == NULL) + return -1; + + DetectContentData *cd = (DetectContentData*) sm->ctx; + uint8_t chunk_group_id = cd->chunk_group_id; + uint8_t chunk_id = cd->chunk_id; + + SigMatch *first_sm = DetectContentFindApplicableSM(sm); + + for (; first_sm != NULL && + first_sm->type == DETECT_CONTENT && + first_sm->ctx != NULL && + (cd = (DetectContentData*) first_sm->ctx) && + (cd->flags & DETECT_CONTENT_IS_CHUNK) && + cd->chunk_group_id == chunk_group_id && + cd->chunk_id != chunk_id + ; first_sm = first_sm->next, length += cd->content_len); + + if (cd != NULL && cd->chunk_id == chunk_id) + return length; + + return 0; +} + +/** + * \brief Get the total legth of a splitted pattern + * \param first_sm pointer to a SigMatch that belong to this chunk group + * \retval pointer to the SigMatch holding the DetectContent + * \retval length if every thing was ok + * \retval -1 if fail + */ +int DetectContentChunksGetTotalLength(SigMatch *sm) +{ + int length = 0; + if (sm == NULL) + return -1; + + if (sm->type != DETECT_CONTENT || sm->ctx == NULL) + return -1; + + DetectContentData *cd = (DetectContentData*) sm->ctx; + uint8_t chunk_group_id = cd->chunk_group_id; + + /** Go to the first SigMatch of this Chunk group */ + SigMatch *first_sm = DetectContentFindApplicableSM(sm); + + for (; first_sm != NULL && + first_sm->type == DETECT_CONTENT && + first_sm->ctx != NULL && + (cd = (DetectContentData*) first_sm->ctx) && + (cd->flags & DETECT_CONTENT_IS_CHUNK) && + cd->chunk_group_id == chunk_group_id + ; first_sm = first_sm->next, length += cd->content_len); + + return length; +} + +/** + * \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 + */ +void DetectContentPrintAll(SigMatch *sm) +{ + if (SCLogDebugEnabled()) { + int i = 0; + if (sm == NULL) + return; + + /** Go to the first SigMatch of this Chunk group */ + 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(first_sm->ctx); + } + } +} + +/** + * \brief Function to update modifiers of a chunk group after setting depth + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateDepth(SigMatch *first_sm) +{ + int res = -1; + DetectContentData *cd = first_sm->ctx; + if (first_sm == NULL || first_sm->ctx == NULL) + return -1; + + if (cd->chunk_flags & CHUNK_UPDATED_DEPTH) + { + SCLogDebug("Depth already set for this pattern!!"); + return res; + } + + res = DetectContentPropagateModifiers(first_sm); + if (res == 1) { + cd->chunk_flags |= CHUNK_UPDATED_DEPTH; + } + return res; +} + +/** + * \brief Function to update modifiers of a chunk group after setting isdataat + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateIsdataat(SigMatch *first_sm) +{ + int res = -1; + DetectContentData *cd = first_sm->ctx; + + if (first_sm == NULL || first_sm->ctx == NULL) + return -1; + + if (cd->chunk_flags & CHUNK_UPDATED_ISDATAAT) + { + SCLogDebug("Depth already set for this pattern!!"); + return res; + } + + res = DetectContentPropagateModifiers(first_sm); + if (res == 1) { + cd->chunk_flags |= CHUNK_UPDATED_ISDATAAT; + } + return res; +} + +/** + * \brief Function to update modifiers of a chunk group after setting within + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateWithin(SigMatch *first_sm) +{ + int res = -1; + DetectContentData *cd = first_sm->ctx; + if (first_sm == NULL || first_sm->ctx == NULL) + return -1; + + if (cd->chunk_flags & CHUNK_UPDATED_WITHIN) + { + SCLogDebug("Depth already set for this pattern!!"); + return res; + } + + res = DetectContentPropagateModifiers(first_sm); + if (res == 1) { + cd->chunk_flags |= CHUNK_UPDATED_WITHIN; + } + return res; +} + +/** + * \brief Function to update modifiers of a chunk group after setting distance + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateDistance(SigMatch *first_sm) +{ + int res = -1; + DetectContentData *cd = first_sm->ctx; + if (first_sm == NULL || first_sm->ctx == NULL) + return -1; + + if (cd->chunk_flags & CHUNK_UPDATED_DISTANCE) + { + SCLogDebug("Depth already set for this pattern!!"); + return res; + } + + res = DetectContentPropagateModifiers(first_sm); + if (res == 1) { + cd->chunk_flags |= CHUNK_UPDATED_DISTANCE; + } + return res; +} + +/** + * \brief Function to update modifiers of a chunk group after setting Offset + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateOffset(SigMatch *first_sm) +{ + int res = -1; + DetectContentData *cd = first_sm->ctx; + if (first_sm == NULL || first_sm->ctx == NULL) + return -1; + + if (cd->chunk_flags & CHUNK_UPDATED_OFFSET) + { + SCLogDebug("Depth already set for this pattern!!"); + return res; + } + + res = DetectContentPropagateModifiers(first_sm); + if (res == 1) { + cd->chunk_flags |= CHUNK_UPDATED_OFFSET; + } + return res; +} + +/** + * \brief Function to update modifiers of a chunk group after setting a modifier + * This function should not be called directly from outside detect-content.c ! + * + * \param first_sm pointer to the head of this group of chunks to update + * \retval -1 if error + * \retval 1 if all was ok + */ +int DetectContentPropagateModifiers(SigMatch *first_sm) +{ + if (first_sm == NULL) + return -1; + + /** Rewind the pointer to the start of the chunk if we have a chunk group */ + first_sm = DetectContentFindApplicableSM(first_sm); + + if (first_sm->ctx == NULL) + return -1; + + DetectContentData *first_chunk = (DetectContentData*)first_sm->ctx; + if (first_chunk == NULL) + return -1; + + if ( !(first_chunk->flags & DETECT_CONTENT_IS_CHUNK)) + /** No modifiers to update */ + return 1; + + uint8_t chunk_group_id = first_chunk->chunk_group_id; + uint8_t num_chunks = DetectContentCountChunksInGroup(first_sm, chunk_group_id); + int16_t total_len = DetectContentChunksGetTotalLength(first_sm); + + if (num_chunks < 1 || total_len < 1) + return -1; + + DetectContentData *cur_chunk = NULL; + DetectContentData *last_chunk = first_chunk; + SigMatch *cur_sm = NULL; + + /** The first chunk has the real modifiers that we want to propagate */ + for (cur_sm = first_sm; + cur_sm != NULL && + cur_sm->type == DETECT_CONTENT && + (cur_chunk = (DetectContentData*) cur_sm->ctx) != NULL && + cur_chunk->chunk_group_id == chunk_group_id ; + cur_sm = cur_sm->next) { + + //SCLogDebug("Cur: %u %s Last: %u %s", cur_chunk->offset, cur_chunk->content, last_chunk->offset, last_chunk->content); + + int16_t remaining_len = DetectContentChunksGetRemainingLength(cur_sm); + int16_t previous_len = DetectContentChunksGetRemainingLength(cur_sm); + if (previous_len < 0 || remaining_len < 0) + return -1; + + + /** If we are in the first chunk */ + if (cur_chunk->chunk_id == 0) { + /** Reset the first depth removing the length of the remaining chunks + */ + SCLogDebug("CUR depth = %u remain_len %d ", cur_chunk->depth, remaining_len); + + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_DEPTH) && cur_chunk->depth > 0) + cur_chunk->depth -= remaining_len; + + /** Reset the first within removing the length of the remaining chunks + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_WITHIN) && cur_chunk->within > 0) + cur_chunk->within -= remaining_len; + + /** Reset the first isdataat adding the length of the remaining chunks + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_ISDATAAT) && cur_chunk->isdataat > 0) + cur_chunk->isdataat += remaining_len; + /** + * The offseth for the first chunk is the real offset, + * so no need to update it here + * The same is applicable here to offset and distance + */ + + /** If it's not the first chunk we need to propagate the changes */ + } else { + + /** Propagate the flags */ + cur_chunk->flags = last_chunk->flags; + + /** Update the depth adding the content_len of the current chunk + * to the previous chunk depth + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_DEPTH) && last_chunk->depth > 0) + cur_chunk->depth = last_chunk->depth + cur_chunk->content_len; + + /** Update the offset adding the content_len of the last chunk + * to the previous chunk offset + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_OFFSET) && last_chunk->offset > 0) + cur_chunk->offset = last_chunk->offset + last_chunk->content_len; + + /** We are iterating in the chunks after the first one, so within is + * relative to the previous chunks and should be exactly the size of + * its content_len since they are consecutive + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_WITHIN)) { + /** Even if they don't specify a within option, + * we set the flag, since they are relative to the chunks + * of the same pattern + */ + cur_chunk->flags |= DETECT_CONTENT_WITHIN; + cur_chunk->within = cur_chunk->content_len; + } + + /** We are iterating in the chunks after the first one, so distance + * must be 0 between the chunks, since they are consecutive + * splitted chunks + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_DISTANCE)) { + /** Even if they don't specify a within option, + * we set the flag, since they are relative to the chunks + * of the same pattern + */ + cur_chunk->flags |= DETECT_CONTENT_DISTANCE; + cur_chunk->distance = 0; + } + + /** The isdataat (relative) is updated to the + * last_chunk isdataat - the content_len of the current + * chunk content_len + */ + if ( !(cur_chunk->chunk_flags & CHUNK_UPDATED_ISDATAAT) && last_chunk->isdataat > 0) + cur_chunk->isdataat = last_chunk->isdataat - cur_chunk->content_len; + } + + last_chunk = cur_chunk; + } + + return 1; +} + +/** + * \brief Function to setup a content pattern. Patterns that doesn't fit the + * current max_pattern_length, are splitted into multiple chunks in independent + * DetectContentData structures with it's own modifiers. Each modifier must be + * recalculated for each chunk from the modifiers of the head of the chunk + * group, and will act as independent patterns + * + * \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, SigMatch *m, char *contentstr) { DetectContentData *cd = NULL; SigMatch *sm = NULL; + /* max_pattern_length */ + int32_t mpltmp = -1; + uint8_t mpl = 0; + uint8_t index = 0; cd = DetectContentParse(contentstr); if (cd == NULL) goto error; - sm = SigMatchAlloc(); - if (sm == NULL) - goto error; + mpltmp = MpmMatcherGetMaxPatternLength(de_ctx->mpm_matcher); + if (mpltmp < 0) + { + SCLogDebug("Unknown Matcher type. Exiting..."); + exit(EXIT_FAILURE); + } + mpl = mpltmp; + + SCLogDebug("Matcher type: %"PRIu16" max_pattern_length: %"PRIu32"", de_ctx->mpm_matcher, mpl); + + /** We are going to assign a chunk group to all the DetectContents, even + * if it's not splitted. This will give us the number of loaded patterns + * in this signature */ + if (s != NULL) { + s->nchunk_groups++; + } - sm->type = DETECT_CONTENT; - sm->ctx = (void *)cd; + /** Check if we need to split the content into chunks */ + if (mpl > 0 && cd->content_len > mpl) { + DetectContentData *aux = NULL; + SigMatch *first = NULL; + uint8_t chunk_id = 0; - SigMatchAppend(s,m,sm); - cd->id = de_ctx->content_max_id; - de_ctx->content_max_id++; + /** Split it from DetectContentSplitChunk() */ + for (index = 0; index < cd->content_len; index += mpl) + { + aux = DetectContentSplitChunk(cd, (uint8_t)(cd->content_len - index), index, mpl); + if ( aux == NULL) { + SCLogDebug("Couldn't split pattern chunks. Exiting..."); + exit(EXIT_FAILURE); + } + + aux->flags |= DETECT_CONTENT_IS_CHUNK; + + /** If we load a signature, assing the internal + * chunk_group_id of the sig + */ + if (s != NULL) + { + /** each group of chunks has it's own internal id in the sig */ + aux->chunk_group_id = s->nchunk_groups; + /** + * The first chunk will have id = 0 + * we need to search for applying the content modifiers + */ + aux->chunk_id = chunk_id++; + } - s->flags |= SIG_FLAG_MPM; + /** Allocate it as a normal SigMatch */ + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_CONTENT; + sm->ctx = (void *)aux; + SigMatchAppend(s,m,sm); + m=sm; + + aux->id = de_ctx->content_max_id; + de_ctx->content_max_id++; + + s->flags |= SIG_FLAG_MPM; + + /** We need to setup the modifiers for the chunks respect + * the last chunk installed inmediatelly before + * so do the propagation from the first one + * The function DetectContentPropagateModifiers() should + * be called when a new content modifier is + * parsed/installed + */ + if (aux->chunk_id == 0) + first = sm; + DetectContentPropagateModifiers(first); + //DetectContentPrint(aux); + } + + /** Free the original pattern */ + DetectContentFree(cd); + /** + * If we dont need to split it is because the matcher has no length limit + * or the payload fit in the current max pattern length, so no chunks here + */ + } else { + + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_CONTENT; + sm->ctx = (void *)cd; + SigMatchAppend(s,m,sm); + + if (s != NULL) { + /** each group of chunks has it's own internal id in the sig, + * if the content is not splitted we will assign a chunk group id + * anyway, so we know the real number of detect_content + * patterns loaded */ + cd->chunk_group_id = s->nchunk_groups; + } + + cd->id = de_ctx->content_max_id; + de_ctx->content_max_id++; + + s->flags |= SIG_FLAG_MPM; + + //DetectContentPrint(cd); + } return 0; @@ -514,14 +1204,14 @@ int DetectContentParseTest01 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { - printf("expected %s got ", teststringparsed); + SCLogDebug("expected %s got ", teststringparsed); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } } else { - printf("expected %s got NULL: ", teststringparsed); + SCLogDebug("expected %s got NULL: ", teststringparsed); result = 0; } return result; @@ -539,14 +1229,14 @@ int DetectContentParseTest02 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { - printf("expected %s got ", teststringparsed); + SCLogDebug("expected %s got ", teststringparsed); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } } else { - printf("expected %s got NULL: ", teststringparsed); + SCLogDebug("expected %s got NULL: ", teststringparsed); result = 0; } return result; @@ -564,14 +1254,14 @@ int DetectContentParseTest03 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { if (memcmp(cd->content, teststringparsed, strlen(teststringparsed)) != 0) { - printf("expected %s got ", teststringparsed); + SCLogDebug("expected %s got ", teststringparsed); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } } else { - printf("expected %s got NULL: ", teststringparsed); + SCLogDebug("expected %s got NULL: ", teststringparsed); result = 0; } return result; @@ -590,14 +1280,14 @@ int DetectContentParseTest04 (void) { if (cd != NULL) { uint16_t len = (cd->content_len > strlen(teststringparsed)); if (memcmp(cd->content, teststringparsed, len) != 0) { - printf("expected %s got ", teststringparsed); + SCLogDebug("expected %s got ", teststringparsed); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } } else { - printf("expected %s got NULL: ", teststringparsed); + SCLogDebug("expected %s got NULL: ", teststringparsed); result = 0; } return result; @@ -613,9 +1303,9 @@ int DetectContentParseTest05 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { - printf("expected NULL got "); + SCLogDebug("expected NULL got "); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } @@ -635,14 +1325,14 @@ int DetectContentParseTest06 (void) { if (cd != NULL) { uint16_t len = (cd->content_len > strlen(teststringparsed)); if (memcmp(cd->content, teststringparsed, len) != 0) { - printf("expected %s got ", teststringparsed); + SCLogDebug("expected %s got ", teststringparsed); PrintRawUriFp(stdout,cd->content,cd->content_len); - printf(": "); + SCLogDebug(": "); result = 0; DetectContentFree(cd); } } else { - printf("expected %s got NULL: ", teststringparsed); + SCLogDebug("expected %s got NULL: ", teststringparsed); result = 0; } return result; @@ -658,7 +1348,7 @@ int DetectContentParseTest07 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { - printf("expected NULL got %p: ", cd); + SCLogDebug("expected NULL got %p: ", cd); result = 0; DetectContentFree(cd); } @@ -675,16 +1365,803 @@ int DetectContentParseTest08 (void) { cd = DetectContentParse(teststring); if (cd != NULL) { - printf("expected NULL got %p: ", cd); + SCLogDebug("expected NULL got %p: ", cd); result = 0; DetectContentFree(cd); } return result; } + +/** + * \test DetectCotentParseChunksTest01B2G test split process + */ +int DetectContentChunkTestB2G01 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + /** MPM_B2G is currently 32 bytes word, so the number of chunks + * created should be 0, since the pattern is 32 bytes and fit in a word */ + de_ctx->mpm_matcher = MPM_B2G; + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly" + "32 bytes lentgh\"; content:\"12345678901234567890123456789012\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK) && m->next == NULL) + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectCotentParseChunksTest01B3G test split process + */ +int DetectContentChunkTestB3G01 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + /** MPM_B3G is currently 32 bytes word, so the number of chunks + * created should be 0, since the pattern is 32 bytes and fit in a word */ + de_ctx->mpm_matcher = MPM_B3G; + + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly" + "32 bytes lentgh\"; content:\"12345678901234567890123456789012\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK) && m->next == NULL) + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectCotentParseChunksTestB2G02 test split process + */ +int DetectContentChunkTestB2G02 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + /** MPM_B2G is currently 33 bytes word, so the number of chunks + * created should be 2, since the pattern is 33 bytes and + * wont fit in a word */ + de_ctx->mpm_matcher = MPM_B2G; + + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly 33 bytes length, so it should be splitted\"; content:\"123456789012345678901234567890123\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && cd->content_len == 32 && cd->chunk_id == 0)) + goto end; + + m = m->next; + if (m != NULL && m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + + if ( cd->flags & DETECT_CONTENT_IS_CHUNK && m->next == NULL && cd->content_len == 1 && cd->chunk_id == 1) + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectCotentParseChunksTestB3G02 test split proccess + */ +int DetectContentChunkTestB3G02 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + /** MPM_B3G is currently 33 bytes word, so the number of chunks + * created should be 2, since the pattern is 33 bytes and + * wont fit in a word */ + de_ctx->mpm_matcher = MPM_B3G; + + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly 33 bytes length, so it should be splitted\"; content:\"123456789012345678901234567890123\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && cd->content_len == 32 && cd->chunk_id == 0)) + goto end; + + m = m->next; + if (m != NULL && m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + + if ( cd->flags & DETECT_CONTENT_IS_CHUNK && m->next == NULL && cd->content_len == 1 && cd->chunk_id == 1) + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectCotentParseChunksTestB2G03 test split proccess + */ +int DetectContentChunkTestB2G03 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->mpm_matcher = MPM_B2G; + + /** content_len = 100, so 3 chunks of 32 and the last chunk length == 4 */ + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly 100 bytes length, so it should be splitted\"; content:\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + + uint8_t chunk_id = 0; + do { + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && cd->content_len == 32 && cd->chunk_id == chunk_id++)) + goto end; + + } while ((m = m->next) && m != NULL && m->next != NULL); + + /** Now let's see if the last Chunk hast the content_len of 4 */ + if (m == NULL || m->next != NULL) + goto end; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + if (cd->content_len != 4 || cd->chunk_id != chunk_id) + goto end; + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectCotentParseChunksTestB3G03 test split proccess + */ +int DetectContentChunkTestB3G03 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->mpm_matcher = MPM_B3G; + + /** content_len = 100, so 3 chunks of 32 and the last chunk length == 4 */ + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly 100 bytes length, so it should be splitted\"; content:\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\"; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + + uint8_t chunk_id = 0; + do { + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && cd->content_len == 32 && cd->chunk_id == chunk_id++)) + goto end; + + } while ((m = m->next) && m != NULL && m->next != NULL); + + /** Now let's see if the last Chunk hast the content_len of 4 */ + if (m == NULL || m->next != NULL) + goto end; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + if (cd->content_len != 4 || cd->chunk_id != chunk_id) + goto end; + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectContentChunkTestModifiers01 test modifiers propagation + * given a signature with just one pattern + */ +int DetectContentChunkModifiersTest01 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->mpm_matcher = MPM_B2G; + + /** content_len = 43, so 1 chunk of length 32 and another chunk of length == 11 */ + char *sigstr = "alert tcp any any -> any any (msg:\"This content is exactly 43 bytes length, so it should be splitted\"; content:\"1234567890123456789012345678901234567890123\"; depth:50; offset:10; isdataat:10, relative; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + SCLogDebug("---DetectContentChunkModifiersTest01---"); + DetectContentPrintAll(m); + + uint8_t chunk_id = 0; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && + cd->content_len == 32 && cd->chunk_id == chunk_id++)) + goto end; + + /** Check modifiers for the first chunk */ + if (cd->offset != 10 || cd->depth != 39 || cd->isdataat != 21 || + cd->within != 0 || cd->distance != 0) { + SCLogDebug("First Chunk has bad modifiers"); + goto end; + } + + /** Check specified flags (offset and depth have no flags) */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check not specified flags (should not be set + * automatically set for the first chunk) */ + if ( (cd->flags & DETECT_CONTENT_WITHIN) || + (cd->flags & DETECT_CONTENT_DISTANCE)) + goto end; + + /** Now let's see if the last Chunk of this first group has + * the content_len of 11 and the modifiers correctly set */ + m = m->next; + + if (m == NULL) + goto end; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if (!(cd->flags & DETECT_CONTENT_IS_CHUNK)) + goto end; + + if (cd->content_len != 11 || cd->chunk_id != chunk_id) + goto end; + + /** Check modifiers for the second chunk */ + if (cd->offset != 42 || cd->depth != 50 || cd->isdataat != 10 || + cd->within != 11 || cd->distance != 0) { + SCLogDebug("Second Chunk has bad modifiers"); + goto end; + } + + /** Check specified flags */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check flags, the second chunk should have a distance and depth + * relative to the first chunk, so flags should be automatically set */ + if ( !(cd->flags & DETECT_CONTENT_DISTANCE) || + !(cd->flags & DETECT_CONTENT_WITHIN)) + goto end; + + /* Great! */ + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test DetectContentChunkTestModifiers01 test modifiers propagation + * mixing splitted patterns with non splitted + */ +int DetectContentChunkModifiersTest02 (void) { + int result = 0; + DetectContentData *cd = NULL; + Signature *s = NULL; + SigMatch *m = NULL; + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) + goto end; + + de_ctx->mpm_matcher = MPM_B2G; + + /** content 1: content_len = 3, so not splitted + * content 2: content_len = 43, so 1 chunk of length 32 and another chunk of length == 11 + * content 3: content_len = 4, so not splitted + * content 4: content_len = 43, so 1 chunk of length 32 and another chunk of length == 11 + */ + char *sigstr = "alert tcp any any -> any any (msg:\"Lot of contents\"; content:\"GET\"; depth:3; offset:0; isdataat:43,relative ; content:\"1234567890123456789012345678901234567890123\"; distance: 1; within: 50; depth:50; offset:10; isdataat:10, relative; content:\"HTTP\"; distance:10; within:20; content:\"1234567890123456789012345678901234567890123\"; distance: 10; within: 50; depth:1000; offset:50; isdataat:20, relative; sid:1;)"; + + s = de_ctx->sig_list = SigInit(de_ctx, sigstr); + if (s == NULL) + goto end; + + if (de_ctx->sig_list->match == NULL) + goto end; + + m = de_ctx->sig_list->match; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + SCLogDebug("---DetectContentChunkModifiersTest02---"); + DetectContentPrintAll(m); + //uint8_t num_chunks = DetectContentCountChunksInGroup(first_sm, chunk_group_id); + + /** The first DetectContent should not be splitted */ + if ( (cd->flags & DETECT_CONTENT_IS_CHUNK) || m->next == NULL || + cd->depth != 3 || cd->isdataat != 43 || cd->offset!= 0 || + cd->within != 0 || cd->distance != 0 || cd->content_len != 3) + goto end; + + /** First detect content ok, now let's see the first group of chunks */ + m = m->next; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !(cd->flags & DETECT_CONTENT_IS_CHUNK && m->next != NULL && + cd->content_len == 32 && cd->chunk_id == 0)) + goto end; + + /** Check modifiers for the first chunk */ + if (cd->offset != 10 || cd->depth != 39 || cd->isdataat != 21 || + cd->within != 39 || cd->distance != 1) { + SCLogDebug("First Chunk has bad modifiers"); + goto end; + } + + /** Check specified flags (offset and depth have no flags) */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check specified flags relative to the previous DetectContent + * are correctly set for the first chunk) */ + if ( !(cd->flags & DETECT_CONTENT_WITHIN) || + !(cd->flags & DETECT_CONTENT_DISTANCE)) + goto end; + + /** Now let's see if the last Chunk of this first group has + * the content_len of 11 and the modifiers correctly set */ + m = m->next; + + if (m == NULL) + goto end; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if (!(cd->flags & DETECT_CONTENT_IS_CHUNK)) + goto end; + + if (cd->content_len != 11 || cd->chunk_id != 1) + goto end; + + /** Check modifiers for the second chunk */ + if (cd->offset != 42 || cd->depth != 50 || cd->isdataat != 10 || + cd->within != 11 || cd->distance != 0) { + SCLogDebug("Second Chunk has bad modifiers"); + goto end; + } + + /** Check specified flags */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check flags, the second chunk should have a distance and depth + * relative to the first chunk, so flags should be automatically set */ + if ( !(cd->flags & DETECT_CONTENT_DISTANCE) || + !(cd->flags & DETECT_CONTENT_WITHIN)) + goto end; + + /** The next DetectContent should not be splitted (pattern "HTTP") */ + m = m->next; + + if (m == NULL) + goto end; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + /** Should not be a chunk */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) + goto end; + + if (cd->content_len != 4) + goto end; + + /** Check modifiers for the second chunk */ + if (cd->offset != 0 || cd->depth != 0 || cd->isdataat != 0 || + cd->within != 20 || cd->distance != 10) { + SCLogDebug("Second Chunk has bad modifiers"); + goto end; + } + + /** Check not specified flags */ + if ( cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE) + goto end; + + /** Check specified flags */ + if ( !(cd->flags & DETECT_CONTENT_DISTANCE) || + !(cd->flags & DETECT_CONTENT_WITHIN)) + goto end; + + /** Ok, now the last group of chunks */ + m = m->next; + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if ( !((cd->flags & DETECT_CONTENT_IS_CHUNK) && m->next != NULL && + cd->content_len == 32 && cd->chunk_id == 0 && + cd->chunk_group_id == 4)) + goto end; + + /** Check modifiers for the first chunk */ + if (cd->offset != 50 || cd->depth != 989 || cd->isdataat != 31 || + cd->within != 39 || cd->distance != 10) { + SCLogDebug("First Chunk of last group has bad modifiers"); + goto end; + } + + /** Check specified flags (offset and depth have no flags) */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check specified flags relative to the previous DetectContent + * are correctly set for the first chunk) */ + if ( !(cd->flags & DETECT_CONTENT_WITHIN) || + !(cd->flags & DETECT_CONTENT_DISTANCE)) + goto end; + + /** Now let's see if the last Chunk of this last group has + * the content_len of 11 and the modifiers correctly set */ + m = m->next; + + if (m == NULL) + goto end; + + if (m->type == DETECT_CONTENT && m->ctx != NULL) + cd = m->ctx; + else + goto end; + + if (!(cd->flags & DETECT_CONTENT_IS_CHUNK)) + goto end; + + if (cd->content_len != 11 || cd->chunk_id != 1 || cd->chunk_group_id != 4) + goto end; + + /** Check modifiers for the second chunk */ + if (cd->offset != 82 || cd->depth != 1000 || cd->isdataat != 20 || + cd->within != 11 || cd->distance != 0) { + SCLogDebug("Second Chunk of last group has bad modifiers"); + goto end; + } + + /** Check specified flags */ + if ( !(cd->flags & DETECT_CONTENT_ISDATAAT_RELATIVE)) + goto end; + + /** Check flags, the second chunk should have a distance and depth + * relative to the first chunk, so flags should be automatically set */ + if ( !(cd->flags & DETECT_CONTENT_DISTANCE) || + !(cd->flags & DETECT_CONTENT_WITHIN)) + goto end; + + /** Great!!! */ + result = 1; + +end: + SigCleanSignatures(de_ctx); + if (de_ctx != NULL) + DetectEngineCtxFree(de_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 + */ +int DetectContentChunkMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize, char *sig, + uint32_t sid) +{ + int result = 1; + + Packet p; + DecodeThreadVars dtv; + + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + + memset(&p, 0, sizeof(Packet)); + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&th_v, 0, sizeof(th_v)); + + FlowInitConfig(FLOW_QUIET); + DecodeEthernet(&th_v, &dtv, &p, raw_eth_pkt, pktsize, NULL); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + result=0; + goto end; + } + + de_ctx->flags |= DE_QUIET; + + de_ctx->sig_list = SigInit(de_ctx, sig); + de_ctx->sig_list->next = NULL; + if (de_ctx->sig_list == NULL) { + result = 0; + goto end; + } + SCLogDebug("---DetectContentChunkMatchTest---"); + DetectContentPrintAll(de_ctx->sig_list->match); + + SigGroupBuild(de_ctx); + //PatternMatchPrepare(mpm_ctx, MPM_B2G); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + if (PacketAlertCheck(&p, sid) != 1) { + result = 0; + goto end; + } + +end: + if (de_ctx != NULL) + { + //PatternMatchDestroy(mpm_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + } + FlowShutdown(); + + return result; +} + +/** + * \brief Wrapper for DetectContentChunkMatchTest + */ +int DetectContentChunkMatchTestWrp(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 DetectContentChunkMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt), + sig, sid); +} + +/** + * \test Check if we match a normal pattern (not splitted) + */ +int DetectContentChunkMatchTest01() +{ + char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" + " content:\"Hi, this is a big test\"; sid:1;)"; + return DetectContentChunkMatchTestWrp(sig, 1); +} + +/** + * \test Check if we match a splitted pattern + */ +int DetectContentChunkMatchTest02() +{ + 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 DetectContentChunkMatchTestWrp(sig, 1); +} + +/** + * \test Check that we don't match the signature if one of the splitted + * chunks doesn't match the packet + */ +int DetectContentChunkMatchTest03() +{ + /** The last chunk of the content should not match */ + 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 (DetectContentChunkMatchTestWrp(sig, 1) == 0) ? 1: 0; +} + +/** + * \test Check if we match multiple content (not splitted) + */ +int DetectContentChunkMatchTest04() +{ + 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; depth:400;" + " sid:1;)"; + return DetectContentChunkMatchTestWrp(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 + */ +int DetectContentChunkMatchTest05() +{ + char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";" + " content:\"Hi, this is a big test to check content matches\";" + " content:\"of splitted\"; distance:1; within:12; " + //" content:\" patterns between multiple splitted chunks!\";" + " sid:1;)"; + return DetectContentChunkMatchTestWrp(sig, 1); +} + #endif /* UNITTESTS */ /** - * \brief this function registers unit tests for DetectFlow + * \brief this function registers unit tests for DetectContent */ void DetectContentRegisterTests(void) { #ifdef UNITTESTS /* UNITTESTS */ @@ -696,5 +2173,19 @@ void DetectContentRegisterTests(void) { UtRegisterTest("DetectContentParseTest06", DetectContentParseTest06, 1); UtRegisterTest("DetectContentParseTest07", DetectContentParseTest07, 1); UtRegisterTest("DetectContentParseTest08", DetectContentParseTest08, 1); + UtRegisterTest("DetectContentChunkTestB2G01 l=32", DetectContentChunkTestB2G01, 1); + UtRegisterTest("DetectContentChunkTestB3G01 l=32", DetectContentChunkTestB3G01, 1); + UtRegisterTest("DetectContentChunkTestB2G02 l=33", DetectContentChunkTestB2G02, 1); + UtRegisterTest("DetectContentChunkTestB3G02 l=33", DetectContentChunkTestB3G02, 1); + UtRegisterTest("DetectContentChunkTestB2G03 l=100", DetectContentChunkTestB2G03, 1); + UtRegisterTest("DetectContentChunkTestB3G03 l=100", DetectContentChunkTestB3G03, 1); + UtRegisterTest("DetectContentChunkModifiersTest01", DetectContentChunkModifiersTest01, 1); + UtRegisterTest("DetectContentChunkModifiersTest02", DetectContentChunkModifiersTest02, 1); + /* The reals */ + UtRegisterTest("DetectContentChunkMatchTest01", DetectContentChunkMatchTest01, 1); + UtRegisterTest("DetectContentChunkMatchTest02", DetectContentChunkMatchTest02, 1); + UtRegisterTest("DetectContentChunkMatchTest03", DetectContentChunkMatchTest03, 1); + UtRegisterTest("DetectContentChunkMatchTest04", DetectContentChunkMatchTest04, 1); + UtRegisterTest("DetectContentChunkMatchTest05", DetectContentChunkMatchTest05, 1); #endif /* UNITTESTS */ } diff --git a/src/detect-content.h b/src/detect-content.h index 28ff6ffceb..1eb87d4047 100644 --- a/src/detect-content.h +++ b/src/detect-content.h @@ -1,15 +1,27 @@ #ifndef __DETECT_CONTENT_H__ #define __DETECT_CONTENT_H__ -#define DETECT_CONTENT_NOCASE 0x01 -#define DETECT_CONTENT_DISTANCE 0x02 -#define DETECT_CONTENT_WITHIN 0x04 +#define DETECT_CONTENT_NOCASE 0x01 +#define DETECT_CONTENT_DISTANCE 0x02 +#define DETECT_CONTENT_WITHIN 0x04 -#define DETECT_CONTENT_DISTANCE_NEXT 0x08 -#define DETECT_CONTENT_WITHIN_NEXT 0x10 -#define DETECT_CONTENT_ISDATAAT_RELATIVE 0x16 +#define DETECT_CONTENT_DISTANCE_NEXT 0x08 +#define DETECT_CONTENT_WITHIN_NEXT 0x10 +#define DETECT_CONTENT_ISDATAAT_RELATIVE 0x20 -#define DETECT_CONTENT_RAWBYTES 0x20 +#define DETECT_CONTENT_RAWBYTES 0x40 + +/** Set if the pattern is split into multiple chunks */ +#define DETECT_CONTENT_IS_CHUNK 0x80 + + +/** Used for modifier propagations, to know if they are + * yet updated or not */ +#define CHUNK_UPDATED_DEPTH 0x01 +#define CHUNK_UPDATED_OFFSET 0x02 +#define CHUNK_UPDATED_ISDATAAT 0x04 +#define CHUNK_UPDATED_DISTANCE 0x08 +#define CHUNK_UPDATED_WITHIN 0x10 typedef struct DetectContentData_ { uint8_t *content; @@ -22,13 +34,43 @@ typedef struct DetectContentData_ { int32_t distance; int32_t within; uint8_t flags; + + /** The group this chunk belongs to, relative to the signature + * It start from 1, and the last SigMatch of the list should be + * also the total number of DetectContent "Real" Patterns loaded + * from the Signature */ + uint8_t chunk_group_id; + /** The id number for this chunk in the current group of chunks + * Starts from 0, and a chunk with chunk_id == 0 should be the + * of the current chunk group where real modifiers are set before + * propagation */ + uint8_t chunk_id; + /** For modifier propagations (the new flags) */ + uint8_t chunk_flags; } DetectContentData; /* prototypes */ void DetectContentRegister (void); uint32_t DetectContentMaxId(DetectEngineCtx *); DetectContentData *DetectContentParse (char *contentstr); + +void DetectContentPrint(DetectContentData *); + +/** This function search backwards the first applicable SigMatch holding + * a DETECT_CONTENT context. Modifiers must call this */ +SigMatch *DetectContentFindApplicableSM(SigMatch *); + +/** After setting a new modifier, we should call one of the followings */ +int DetectContentPropagateDepth(SigMatch *); +int DetectContentPropagateIsdataat(SigMatch *); +int DetectContentPropagateWithin(SigMatch *); +int DetectContentPropagateOffset(SigMatch *); +int DetectContentPropagateDistance(SigMatch *); +int DetectContentPropagateIsdataat(SigMatch *); + +/** This shall not be called from outside detect-content.c (used internally)*/ +int DetectContentPropagateModifiers(SigMatch *); + void DetectContentFree(void *); #endif /* __DETECT_CONTENT_H__ */ - diff --git a/src/detect-depth.c b/src/detect-depth.c index 2f96dc65ce..37306f7dd1 100644 --- a/src/detect-depth.c +++ b/src/detect-depth.c @@ -39,10 +39,32 @@ int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char * DetectPcreData *pe = (DetectPcreData *)pm->ctx; pe->depth = (uint32_t)atoi(str); //printf("DetectDepthSetup: set depth %" PRIu32 " for previous pcre\n", pe->depth); + } else if (pm->type == DETECT_CONTENT) { + /** Search for the first previous DetectContent + * SigMatch (it can be the same as this one) */ + pm = DetectContentFindApplicableSM(m); + if (pm == NULL) { + printf("DetectDepthSetup: Unknown previous keyword!\n"); + return -1; + } + DetectContentData *cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectDepthSetup: Unknown previous keyword!\n"); + return -1; + } + cd->depth = (uint32_t)atoi(str); + + /** Propagate the modifiers through the first chunk + * (SigMatch) if we're dealing with chunks */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) + DetectContentPropagateDepth(pm); + + //DetectContentPrint(cd); //printf("DetectDepthSetup: set depth %" PRIu32 " for previous content\n", cd->depth); + } else { printf("DetectDepthSetup: Unknown previous keyword!\n"); } diff --git a/src/detect-distance.c b/src/detect-distance.c index c14a68636d..93d0692ab9 100644 --- a/src/detect-distance.c +++ b/src/detect-distance.c @@ -45,14 +45,32 @@ int DetectDistanceSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, cha pe->distance = strtol(str, NULL, 10); pe->flags |= DETECT_PCRE_DISTANCE; - //printf("DetectDistanceSetup: set distance %" PRId32 " for previous pcre\n", pe->distance); + } else if (pm->type == DETECT_CONTENT) { + /** Search for the first previous DetectContent + * SigMatch (it can be the same as this one) */ + pm = DetectContentFindApplicableSM(m); + if (pm == NULL) { + printf("DetectDistanceSetup: Unknown previous keyword!\n"); + return -1; + } + DetectContentData *cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectDistanceSetup: Unknown previous keyword!\n"); + return -1; + } cd->distance = strtol(str, NULL, 10); cd->flags |= DETECT_CONTENT_DISTANCE; + /** Propagate the modifiers through the first chunk + * (SigMatch) if we're dealing with chunks */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) + DetectContentPropagateDistance(pm); + + //DetectContentPrint(cd); //printf("DetectDistanceSetup: set distance %" PRId32 " for previous content\n", cd->distance); } else if (pm->type == DETECT_URICONTENT) { DetectUricontentData *cd = (DetectUricontentData *)pm->ctx; diff --git a/src/detect-id.c b/src/detect-id.c index b1138038cd..7859475d8b 100644 --- a/src/detect-id.c +++ b/src/detect-id.c @@ -604,8 +604,8 @@ void DetectIdRegisterTests(void) { UtRegisterTest("DetectIdTestParse02", DetectIdTestParse02, 1); UtRegisterTest("DetectIdTestParse03", DetectIdTestParse03, 1); UtRegisterTest("DetectIdTestParse04", DetectIdTestParse04, 1); - UtRegisterTest("DetectIdTestPacket01" , DetectIdTestPacket01 , 1); - UtRegisterTest("DetectIdTestPacket02" , DetectIdTestPacket02 , 1); + UtRegisterTest("DetectIdTestPacket01", DetectIdTestPacket01 , 1); + UtRegisterTest("DetectIdTestPacket02", DetectIdTestPacket02 , 1); UtRegisterTest("DetectIdTestSig1", DetectIdTestSig1, 1); UtRegisterTest("DetectIdTestSig2", DetectIdTestSig2, 1); diff --git a/src/detect-isdataat.c b/src/detect-isdataat.c index 62752d8f5b..f24d8d64d9 100644 --- a/src/detect-isdataat.c +++ b/src/detect-isdataat.c @@ -19,11 +19,12 @@ #include "flow-var.h" #include "util-debug.h" +#include "util-byte.h" /** * \brief Regex for parsing our isdataat options */ -#define PARSE_REGEX "^\\s*([0-9]{1,10})\\s*(,\\s*relative)?\\s*(,\\s*rawbytes\\s*)?\\s*$" +#define PARSE_REGEX "^\\s*([0-9]{1,5})\\s*(,\\s*relative)?\\s*(,\\s*rawbytes\\s*)?\\s*$" static pcre *parse_regex; static pcre_extra *parse_regex_study; @@ -50,15 +51,13 @@ void DetectIsdataatRegister (void) { int opts = 0; parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); - if(parse_regex == NULL) - { + if(parse_regex == NULL) { printf("pcre compile of \"%s\" failed at offset %" PRId32 ": %s\n", PARSE_REGEX, eo, eb); goto error; } parse_regex_study = pcre_study(parse_regex, 0, &eb); - if(eb != NULL) - { + if(eb != NULL) { printf("pcre study failed: %s\n", eb); goto error; } @@ -88,16 +87,12 @@ int DetectIsdataatMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet * SCLogDebug("payload_len: %u , dataat? %u ; relative? %u...", p->payload_len,idad->dataat,idad->flags &ISDATAAT_RELATIVE); - if(idad->flags & ISDATAAT_RELATIVE) - { - /* Relative to the last matched content, is not performed here */ - } - else - if(!(idad->flags & ISDATAAT_RELATIVE) && p->payload_len >= idad->dataat) { - ret = 1; /* its not relative and we have more data in the packet than the offset of isdataat */ + /* Relative to the last matched content is not performed here */ + if(!(idad->flags & ISDATAAT_RELATIVE) && p->payload_len >= idad->dataat) { + ret = 1; /* its not relative and we have more data in the packet than the offset of isdataat */ - SCLogDebug("matched with payload_len: %u , dataat? %u ; relative? %u...", p->payload_len,idad->dataat,idad->flags &ISDATAAT_RELATIVE); - } + SCLogDebug("matched with payload_len: %u , dataat? %u ; relative? %u...", p->payload_len,idad->dataat,idad->flags &ISDATAAT_RELATIVE); + } return ret; } @@ -158,27 +153,28 @@ DetectIsdataatData *DetectIsdataatParse (char *isdataatstr) } idad->flags = 0; - idad->dataat= 0; - - if(args[0] != NULL) - idad->dataat=atoi(args[0]); - - if(idad->dataat < ISDATAAT_MIN || idad->dataat > ISDATAAT_MAX) { - printf("detect-isdataat: DetectIsdataatParse: isdataat out of range\n"); - free(idad); - idad=NULL; + idad->dataat = 0; + + if (args[0] != NULL) { + if (ByteExtractStringUint16(&idad->dataat, 10, + strlen(args[0]), args[0]) < 0 ) { + printf("detect-isdataat: DetectIsdataatParse: isdataat out of range\n"); + free(idad); + idad=NULL; + goto error; + } + } else { goto error; } - if(args[1] !=NULL) - { + if (args[1] !=NULL) { idad->flags |= ISDATAAT_RELATIVE; if(args[2] !=NULL) idad->flags |= ISDATAAT_RAWBYTES; } - for (i = 0; i < (ret -1); i++){ + for (i = 0; i < (ret -1); i++) { if (args[i] != NULL) free(args[i]); } @@ -212,64 +208,45 @@ int DetectIsdataatSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, cha { DetectIsdataatData *idad = NULL; SigMatch *sm = NULL; - SigMatch *search_sm_content = NULL; DetectContentData *cd = NULL; - idad = DetectIsdataatParse(isdataatstr); if (idad == NULL) goto error; - if(idad->flags & ISDATAAT_RELATIVE) - { - /// Set it in the last parsed contet because it is relative to that content match + if(idad->flags & ISDATAAT_RELATIVE) { + /** Set it in the last parsed contet because it is relative to that content match */ SCLogDebug("set it in the last parsed content because it is relative to that content match"); - if( m == NULL ) - { + if( m == NULL ) { printf("detect-isdataat: No previous content, the flag 'relative' cant be used without content\n"); goto error; - } - else - { - search_sm_content=m; - /// Searching last content - uint8_t found=0; - while(search_sm_content != NULL && !found) - { - if(search_sm_content->type== DETECT_CONTENT) //Found content - found=1; - else - search_sm_content=search_sm_content->prev; + } else { + SigMatch *pm = NULL; + /** Search for the first previous DetectContent + * SigMatch (it can be the same as this one) */ + pm = DetectContentFindApplicableSM(m); + if (pm == NULL) { + printf("DetectIsdataatSetup: Unknown previous keyword!\n"); + return -1; } - if(search_sm_content != NULL) - { - /* Found */ - cd=(DetectContentData*)search_sm_content->ctx; - if(cd != NULL) - { - cd->flags |= DETECT_CONTENT_ISDATAAT_RELATIVE; - cd->isdataat=idad->dataat; - } - else - { - printf("detect-isdataat: No content data found in a SigMatch of DETECT_CONTENT type\n"); - goto error; - } - } - else - { - printf("detect-isdataat: No previous content, the flag 'relative' cant be used without content\n"); - goto error; + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectIsdataatSetup: Unknown previous keyword!\n"); + return -1; } + cd->flags |= DETECT_CONTENT_ISDATAAT_RELATIVE; + cd->isdataat = idad->dataat; + + /** Propagate the changes */ + DetectContentPropagateIsdataat(pm); } } - else - { + else { SCLogDebug("set it as a normal SigMatch"); - /// else Set it as a normal SigMatch + /** else Set it as a normal SigMatch */ sm = SigMatchAlloc(); if (sm == NULL) goto error; @@ -362,8 +339,7 @@ int DetectIsdataatTestPacket01 (void) { /// Parse Isdataat Data: if packet data len is greater or equal than 50 byte it should match /// The packet has 190 bytes of data so it must match idad = DetectIsdataatParse("50"); - if (idad == NULL) - { + if (idad == NULL) { printf("DetectIsdataatTestPacket01: expected a DetectIsdataatData pointer (got NULL)\n"); return 0; } diff --git a/src/detect-isdataat.h b/src/detect-isdataat.h index 07f81336c5..b02bd3a3b4 100644 --- a/src/detect-isdataat.h +++ b/src/detect-isdataat.h @@ -8,7 +8,7 @@ #define ISDATAAT_MAX 65535 typedef struct DetectIsdataatData_ { - uint32_t dataat; /* data offset to match */ + uint16_t dataat; /* data offset to match */ uint8_t flags; /* isdataat options*/ } DetectIsdataatData; diff --git a/src/detect-offset.c b/src/detect-offset.c index ace3bff197..eee4f3c6fc 100644 --- a/src/detect-offset.c +++ b/src/detect-offset.c @@ -41,10 +41,32 @@ int DetectOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char //DetectPcreData *pe = (DetectPcreData *)pm->ctx; //pe->offset = (uint32_t)atoi(str); /* XXX */ //printf("DetectOffsetSetup: set offset %" PRIu32 " for previous pcre\n", pe->offset); + } else if (pm->type == DETECT_CONTENT) { + /** Search for the first previous DetectContent + * SigMatch (it can be the same as this one) */ + pm = DetectContentFindApplicableSM(m); + if (pm == NULL) { + printf("DetectOffsetSetup: Unknown previous keyword!\n"); + return -1; + } + DetectContentData *cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectOffsetSetup: Unknown previous keyword!\n"); + return -1; + } + cd->offset = (uint32_t)atoi(str); /* XXX */ + + /** Propagate the modifiers through the first chunk + * (SigMatch) if we're dealing with chunks */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) + DetectContentPropagateOffset(pm); + + //DetectContentPrint(cd); //printf("DetectOffsetSetup: set offset %" PRIu32 " for previous content\n", cd->offset); + } else { printf("DetectOffsetSetup: Unknown previous keyword!\n"); } diff --git a/src/detect-within.c b/src/detect-within.c index 8a2ab05cea..9135826a8d 100644 --- a/src/detect-within.c +++ b/src/detect-within.c @@ -50,14 +50,32 @@ int DetectWithinSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char pe->within = strtol(str, NULL, 10); pe->flags |= DETECT_PCRE_WITHIN; - //printf("DetectWithinSetup: set within %" PRId32 " for previous pcre\n", pe->within); + } else if (pm->type == DETECT_CONTENT) { + /** Search for the first previous DetectContent + * SigMatch (it can be the same as this one) */ + pm = DetectContentFindApplicableSM(m); + if (pm == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + return -1; + } + DetectContentData *cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + return -1; + } cd->within = strtol(str, NULL, 10); cd->flags |= DETECT_CONTENT_WITHIN; + /** Propagate the modifiers through the first chunk + * (SigMatch) if we're dealing with chunks */ + if (cd->flags & DETECT_CONTENT_IS_CHUNK) + DetectContentPropagateWithin(pm); + + //DetectContentPrint(cd); //printf("DetectWithinSetup: set within %" PRId32 " for previous content\n", cd->within); } else if (pm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)pm->ctx; diff --git a/src/detect.h b/src/detect.h index 781e986d82..1cfd1164eb 100644 --- a/src/detect.h +++ b/src/detect.h @@ -146,6 +146,7 @@ typedef struct Signature_ { uint32_t gid; /**< generator id */ SigIntId num; /**< signature number, internal id */ uint32_t id; /**< sid, set by the 'sid' rule keyword */ + uint8_t nchunk_groups; /**< Internal chunk grp id (for splitted patterns) */ char *msg; /** addresses, ports and proto this sig matches on */ diff --git a/src/util-mpm-b2g.c b/src/util-mpm-b2g.c index c5cfb748b6..f8188ab2ae 100644 --- a/src/util-mpm-b2g.c +++ b/src/util-mpm-b2g.c @@ -59,6 +59,7 @@ void B2gRegisterTests(void); void MpmB2gRegister (void) { mpm_table[MPM_B2G].name = "b2g"; + mpm_table[MPM_B2G].max_pattern_length = B2G_WORD_SIZE; mpm_table[MPM_B2G].InitCtx = B2gInitCtx; mpm_table[MPM_B2G].InitThreadCtx = B2gThreadInitCtx; mpm_table[MPM_B2G].DestroyCtx = B2gDestroyCtx; diff --git a/src/util-mpm-b3g.c b/src/util-mpm-b3g.c index da5382da78..7b40f25d5e 100644 --- a/src/util-mpm-b3g.c +++ b/src/util-mpm-b3g.c @@ -54,6 +54,7 @@ void B3gRegisterTests(void); void MpmB3gRegister (void) { mpm_table[MPM_B3G].name = "b3g"; + mpm_table[MPM_B3G].max_pattern_length = B3G_WORD_SIZE; mpm_table[MPM_B3G].InitCtx = B3gInitCtx; mpm_table[MPM_B3G].InitThreadCtx = B3gThreadInitCtx; mpm_table[MPM_B3G].DestroyCtx = B3gDestroyCtx; diff --git a/src/util-mpm-wumanber.c b/src/util-mpm-wumanber.c index f7f7deb2ac..646fe06e01 100644 --- a/src/util-mpm-wumanber.c +++ b/src/util-mpm-wumanber.c @@ -76,6 +76,7 @@ static uint8_t lowercasetable[256]; void MpmWuManberRegister (void) { mpm_table[MPM_WUMANBER].name = "wumanber"; + mpm_table[MPM_WUMANBER].max_pattern_length = 0; mpm_table[MPM_WUMANBER].InitCtx = WmInitCtx; mpm_table[MPM_WUMANBER].InitThreadCtx = WmThreadInitCtx; mpm_table[MPM_WUMANBER].DestroyCtx = WmDestroyCtx; diff --git a/src/util-mpm.c b/src/util-mpm.c index ae6ebf912d..612945313e 100644 --- a/src/util-mpm.c +++ b/src/util-mpm.c @@ -248,6 +248,19 @@ MpmEndMatch *MpmAllocEndMatch (MpmCtx *ctx) return e; } +/** + * \brief Return the pattern max length of a registered matcher + * \retval 0 if it has no limit + * \retval max_pattern_length of the specified matcher type + * \retval -1 if the type is not registered return -1 + */ +int32_t MpmMatcherGetMaxPatternLength(uint16_t matcher) { + if (matcher < MPM_TABLE_SIZE && matcher >= 0) + return mpm_table[matcher].max_pattern_length; + else + return -1; +} + void MpmEndMatchFree(MpmCtx *ctx, MpmEndMatch *em) { ctx->memory_cnt--; ctx->memory_size -= sizeof(MpmEndMatch); diff --git a/src/util-mpm.h b/src/util-mpm.h index bc4a870615..aae547f6a5 100644 --- a/src/util-mpm.h +++ b/src/util-mpm.h @@ -97,6 +97,7 @@ typedef struct MpmCtx_ { typedef struct MpmTableElmt_ { char *name; + uint8_t max_pattern_length; void (*InitCtx)(struct MpmCtx_ *); void (*InitThreadCtx)(struct MpmCtx_ *, struct MpmThreadCtx_ *, uint32_t); void (*DestroyCtx)(struct MpmCtx_ *); @@ -132,6 +133,9 @@ void MpmMatchFreeSpares(MpmThreadCtx *mpm_ctx, MpmMatch *m); void MpmTableSetup(void); void MpmRegisterTests(void); +/** Return the max pattern length of a Matcher type given as arg */ +int32_t MpmMatcherGetMaxPatternLength(uint16_t); + void MpmInitCtx (MpmCtx *mpm_ctx, uint16_t matcher); void MpmInitThreadCtx(MpmThreadCtx *mpm_thread_ctx, uint16_t, uint32_t);