diff --git a/src/Makefile.am b/src/Makefile.am index 4bb857a1b3..576bec31fe 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -56,6 +56,7 @@ detect-engine-siggroup.c detect-engine-siggroup.h \ detect-engine-mpm.c detect-engine-mpm.h \ detect-engine-iponly.c detect-engine-iponly.h \ detect-engine-payload.c detect-engine-payload.h \ +detect-engine-uricontent.c detect-engine-uricontent.h \ detect-parse.c detect-parse.h \ detect-ack.c detect-ack.h \ detect-seq.c detect-seq.h \ diff --git a/src/detect-content.c b/src/detect-content.c index 103833af47..583e66244f 100644 --- a/src/detect-content.c +++ b/src/detect-content.c @@ -339,7 +339,7 @@ SigMatch *SigMatchGetLastPattern(Signature *s) { BUG_ON(s == NULL); SigMatch *co_sm = DetectContentFindPrevApplicableSM(s->pmatch_tail); - SigMatch *ur_sm = SigMatchGetLastSM(s->match_tail, DETECT_URICONTENT); + SigMatch *ur_sm = SigMatchGetLastSM(s->umatch_tail, DETECT_URICONTENT); SigMatch *sm = NULL; if (co_sm != NULL && ur_sm != NULL) { diff --git a/src/detect-content.h b/src/detect-content.h index 6eae60d614..19169c3f38 100644 --- a/src/detect-content.h +++ b/src/detect-content.h @@ -57,6 +57,7 @@ SigMatch *DetectContentFindNextApplicableSM(SigMatch *); SigMatch *DetectContentHasPrevSMPattern(SigMatch *); SigMatch *SigMatchGetLastPattern(Signature *s); +void SigMatchAppendUricontent(Signature *, SigMatch *); void DetectContentFree(void *); diff --git a/src/detect-depth.c b/src/detect-depth.c index c6d44d3220..965416053b 100644 --- a/src/detect-depth.c +++ b/src/detect-depth.c @@ -5,6 +5,7 @@ #include "decode.h" #include "detect.h" +#include "detect-parse.h" #include "detect-content.h" #include "detect-uricontent.h" @@ -36,26 +37,51 @@ static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, char *depths dubbed = 1; } - /** Search for the first previous DetectContent + /** Search for the first previous DetectContent or uricontent * SigMatch (it can be the same as this one) */ - SigMatch *pm = DetectContentFindPrevApplicableSM(s->pmatch_tail); + SigMatch *pm = SignatureGetLastModifiableSM(s); if (pm == NULL) { SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content option"); if (dubbed) SCFree(str); return -1; } - DetectContentData *cd = (DetectContentData *)pm->ctx; - if (cd == NULL) { - SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument"); - if (dubbed) SCFree(str); - return -1; - } + DetectUricontentData *ud = NULL; + DetectContentData *cd = NULL; + switch (pm->type) { + case DETECT_URICONTENT: + ud = (DetectUricontentData *)pm->ctx; + if (ud == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argpment"); + if (dubbed) SCFree(str); + return -1; + } + ud->depth = (uint32_t)atoi(str); + if (ud->uricontent_len + ud->offset > ud->depth) { + SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", ud->uricontent_len + ud->offset); + ud->depth = ud->uricontent_len + ud->offset; + } + break; + + case DETECT_CONTENT: + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument"); + if (dubbed) SCFree(str); + return -1; + } + cd->depth = (uint32_t)atoi(str); + if (cd->content_len + cd->offset > cd->depth) { + SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", cd->content_len + cd->offset); + cd->depth = cd->content_len + cd->offset; + } + break; - cd->depth = (uint32_t)atoi(str); - if (cd->content_len + cd->offset > cd->depth) { - SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", cd->content_len + cd->offset); - cd->depth = cd->content_len + cd->offset; + default: + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content (or uricontent) option"); + if (dubbed) SCFree(str); + return -1; + break; } if (dubbed) SCFree(str); diff --git a/src/detect-distance.c b/src/detect-distance.c index bbc6a6168c..7ece0e825a 100644 --- a/src/detect-distance.c +++ b/src/detect-distance.c @@ -5,6 +5,7 @@ #include "decode.h" #include "detect.h" +#include "detect-parse.h" #include "detect-content.h" #include "detect-uricontent.h" @@ -36,53 +37,84 @@ static int DetectDistanceSetup (DetectEngineCtx *de_ctx, Signature *s, char *dis dubbed = 1; } - if (s->pmatch == NULL) { - SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); - goto error; - } - /** Search for the first previous DetectContent * SigMatch (it can be the same as this one) */ - SigMatch *pm = DetectContentFindPrevApplicableSM(s->pmatch_tail); + SigMatch *pm = SignatureGetLastModifiableSM(s); if (pm == NULL) { - SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); - if (dubbed) SCFree(str); - return -1; - } - if (DetectContentHasPrevSMPattern(pm) == NULL) { - SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "depth needs two preceeding content (or uricontent) options"); if (dubbed) SCFree(str); return -1; } - DetectContentData *cd = (DetectContentData *)pm->ctx; - if (cd == NULL) { - SCLogError(SC_ERR_RULE_KEYWORD_UNKNOWN, "Unknown previous keyword!"); - if (dubbed) SCFree(str); - return -1; - } - - cd->distance = strtol(str, NULL, 10); - cd->flags |= DETECT_CONTENT_DISTANCE; - - if (cd->flags & DETECT_CONTENT_WITHIN) { - if (cd->distance + cd->content_len > cd->within) { - cd->within = cd->distance + cd->content_len; - } - } - - pm = DetectContentFindPrevApplicableSM(s->pmatch_tail->prev); - if (pm == NULL) { - SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); - goto error; - } - - if (pm->type == DETECT_CONTENT) { - DetectContentData *cd = (DetectContentData *)pm->ctx; - cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; - } else { - SCLogError(SC_ERR_RULE_KEYWORD_UNKNOWN, "Unknown previous-previous keyword!"); - goto error; + DetectUricontentData *ud = NULL; + DetectContentData *cd = NULL; + + switch (pm->type) { + case DETECT_URICONTENT: + ud = (DetectUricontentData *)pm->ctx; + if (ud == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + goto error; + } + + ud->distance = strtol(str, NULL, 10); + ud->flags |= DETECT_URICONTENT_DISTANCE; + if (ud->flags & DETECT_URICONTENT_WITHIN) { + if (ud->distance + ud->uricontent_len > ud->within) { + ud->within = ud->distance + ud->uricontent_len; + } + } + + pm = DetectUricontentFindPrevApplicableSM(s->umatch_tail->prev); + if (pm == NULL) { + SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); + goto error; + } + + if (pm->type == DETECT_URICONTENT) { + ud = (DetectUricontentData *)pm->ctx; + ud->flags |= DETECT_URICONTENT_RELATIVE_NEXT; + } else { + SCLogError(SC_ERR_RULE_KEYWORD_UNKNOWN, "Unknown previous-previous keyword!"); + goto error; + } + break; + + case DETECT_CONTENT: + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + goto error; + } + + cd->distance = strtol(str, NULL, 10); + cd->flags |= DETECT_CONTENT_DISTANCE; + if (cd->flags & DETECT_CONTENT_WITHIN) { + if (cd->distance + cd->content_len > cd->within) { + cd->within = cd->distance + cd->content_len; + } + } + + pm = DetectContentFindPrevApplicableSM(s->pmatch_tail->prev); + if (pm == NULL) { + SCLogError(SC_ERR_DISTANCE_MISSING_CONTENT, "distance needs two preceeding content options"); + goto error; + } + + if (pm->type == DETECT_CONTENT) { + cd = (DetectContentData *)pm->ctx; + cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; + } else { + SCLogError(SC_ERR_RULE_KEYWORD_UNKNOWN, "Unknown previous-previous keyword!"); + goto error; + } + break; + + default: + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content (or uricontent) options"); + if (dubbed) SCFree(str); + return -1; + break; } if (dubbed) SCFree(str); diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 206c8d24ba..85f9907836 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -145,6 +145,7 @@ uint32_t UriPatternSearch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, SCReturnUInt(ret); } + /** \brief cleans up the mpm instance after a match */ void PacketPatternCleanup(ThreadVars *t, DetectEngineThreadCtx *det_ctx) { PmqReset(&det_ctx->pmq); @@ -586,7 +587,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh) } } - for (sm = s->match; sm != NULL; sm = sm->next) { + for (sm = s->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { ur_cnt++; s->flags |= SIG_FLAG_MPM; @@ -674,7 +675,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh) } } } - for (sm = s->match; sm != NULL; sm = sm->next) { + for (sm = s->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT && !(sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY)) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (ud->uricontent_len > uricontent_maxlen) @@ -809,7 +810,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh) content_minlen = cd->content_len; } } - for (sm = s->match; sm != NULL; sm = sm->next) { + for (sm = s->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT && !(sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY)) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (ud->uricontent_len > uricontent_maxlen) @@ -821,7 +822,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh) } } char uricontent_mpmadded = 0; - for (sm = s->match; sm != NULL; sm = sm->next) { + for (sm = s->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT && !(sh->flags & SIG_GROUP_HEAD_MPM_URI_COPY)) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; diff --git a/src/detect-engine-payload.c b/src/detect-engine-payload.c index 1568aed052..f5a2029410 100644 --- a/src/detect-engine-payload.c +++ b/src/detect-engine-payload.c @@ -49,7 +49,7 @@ * \retval 1 match */ static inline int DoInspectPacketPayload(DetectEngineCtx *de_ctx, - DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm, + DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm, Packet *p, uint8_t *payload, uint32_t payload_len) { SCEnter(); diff --git a/src/detect-engine-siggroup.c b/src/detect-engine-siggroup.c index 54951b49fc..29e3dd4521 100644 --- a/src/detect-engine-siggroup.c +++ b/src/detect-engine-siggroup.c @@ -1223,7 +1223,7 @@ int SigGroupHeadLoadUricontent(DetectEngineCtx *de_ctx, SigGroupHead *sgh) if (!(s->flags & SIG_FLAG_MPM)) continue; - sm = s->match; + sm = s->umatch; if (sm == NULL) continue; @@ -1246,7 +1246,7 @@ int SigGroupHeadLoadUricontent(DetectEngineCtx *de_ctx, SigGroupHead *sgh) * \param Pointer to the SigGroupHead whose uri_content_array would to be * cleared. * - * \ret 0 Always. + * \retval 0 Always. */ int SigGroupHeadClearUricontent(SigGroupHead *sh) { diff --git a/src/detect-engine-uricontent.c b/src/detect-engine-uricontent.c new file mode 100644 index 0000000000..c651a48175 --- /dev/null +++ b/src/detect-engine-uricontent.c @@ -0,0 +1,294 @@ +/** + * Copyright (c) 2009 Open Information Security Foundation + * + * \author Pablo Rincon Crespo + */ + +#include "suricata-common.h" +#include "suricata.h" +#include "decode.h" + +#include "detect.h" +#include "detect-uricontent.h" +#include "detect-pcre.h" +#include "detect-isdataat.h" +#include "detect-bytetest.h" +#include "detect-bytejump.h" + +#include "util-spm.h" +#include "util-debug.h" +#include "util-print.h" +#include "flow.h" +#include "detect-flow.h" +#include "flow-var.h" +#include "threads.h" +#include "flow-alert-sid.h" + +#include "stream-tcp.h" +#include "stream.h" +#include "app-layer-parser.h" + +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "app-layer.h" +#include "app-layer-htp.h" +#include "app-layer-protos.h" + +/** \brief Run the actual payload match function for uricontent + * + * For accounting the last match in relative matching the + * det_ctx->uricontent_payload_offset int is used. + * + * \param de_ctx Detection engine context + * \param det_ctx Detection engine thread context + * \param s Signature to inspect + * \param sm SigMatch to inspect + * \param p Packet + * \param payload ptr to the uricontent payload to inspect + * \param payload_len length of the uricontent payload + * + * \retval 0 no match + * \retval 1 match + */ +static inline int DoInspectPacketUricontentPayload(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch *sm, + Packet *p, uint8_t *payload, uint32_t payload_len) +{ + SCEnter(); + + if (sm == NULL) { + SCReturnInt(0); + } + + if (sm->type == DETECT_URICONTENT) { + if (payload_len == 0) { + SCReturnInt(0); + } + + DetectUricontentData *ud = NULL; + ud = (DetectUricontentData *)sm->ctx; + SCLogDebug("inspecting content %"PRIu32" payload_len %"PRIu32, ud->id, payload_len); + + /* rule parsers should take care of this */ + BUG_ON(ud->depth != 0 && ud->depth <= ud->offset); + + /* search for our pattern, checking the matches recursively. + * if we match we look for the next SigMatch as well */ + uint8_t *found = NULL; + uint32_t offset = 0; + uint32_t depth = payload_len; + uint32_t prev_offset = 0; /**< used in recursive searching */ + + do { + if (ud->flags & DETECT_URICONTENT_DISTANCE || + ud->flags & DETECT_URICONTENT_WITHIN) { + SCLogDebug("det_ctx->uricontent_payload_offset %"PRIu32, det_ctx->uricontent_payload_offset); + + offset = det_ctx->uricontent_payload_offset; + depth = payload_len; + + if (ud->flags & DETECT_URICONTENT_DISTANCE) { + if (ud->distance < 0 && (uint32_t)(abs(ud->distance)) > offset) + offset = 0; + else + offset += ud->distance; + + SCLogDebug("ud->distance %"PRIi32", offset %"PRIu32", depth %"PRIu32, + ud->distance, offset, depth); + } + + if (ud->flags & DETECT_URICONTENT_WITHIN) { + if ((int32_t)depth > (int32_t)(det_ctx->uricontent_payload_offset + ud->within)) { + depth = det_ctx->uricontent_payload_offset + ud->within; + } + + SCLogDebug("ud->within %"PRIi32", det_ctx->uricontent_payload_offset %"PRIu32", depth %"PRIu32, + ud->within, det_ctx->uricontent_payload_offset, depth); + } + + if (ud->depth != 0) { + if ((ud->depth + det_ctx->uricontent_payload_offset) < depth) { + depth = det_ctx->uricontent_payload_offset + ud->depth; + } + + SCLogDebug("ud->depth %"PRIu32", depth %"PRIu32, ud->depth, depth); + } + + if (ud->offset > offset) { + offset = ud->offset; + SCLogDebug("setting offset %"PRIu32, offset); + } + } else { /* implied no relative matches */ + /* set depth */ + if (ud->depth != 0) { + depth = ud->depth; + } + + /* set offset */ + offset = ud->offset; + } + + /* update offset with prev_offset if we're searching for + * matches after the first occurence. */ + SCLogDebug("offset %"PRIu32", prev_offset %"PRIu32, prev_offset, depth); + offset += prev_offset; + + SCLogDebug("offset %"PRIu32", depth %"PRIu32, offset, depth); + + if (depth > payload_len) + depth = payload_len; + + /* if offset is bigger than depth we can never match on a pattern. + * We can however, "match" on a negated pattern. */ + if (offset > depth || depth == 0) { + if (ud->flags & DETECT_URICONTENT_NEGATED) { + goto match; + } else { + SCReturnInt(0); + } + } + + uint8_t *spayload = payload + offset; + uint32_t spayload_len = depth - offset; + uint32_t match_offset = 0; + SCLogDebug("spayload_len %"PRIu32, spayload_len); + BUG_ON(spayload_len > payload_len); + + //PrintRawDataFp(stdout,ud->uricontent,ud->uricontent_len); + //PrintRawDataFp(stdout,spayload,spayload_len); + + /* do the actual search */ + if (ud->flags & DETECT_URICONTENT_NOCASE) + found = SpmNocaseSearch(spayload, spayload_len, ud->uricontent, ud->uricontent_len); + else + found = SpmSearch(spayload, spayload_len, ud->uricontent, ud->uricontent_len); + + /* next we evaluate the result in combination with the + * negation flag. */ + SCLogDebug("found %p ud negated %s", found, ud->flags & DETECT_URICONTENT_NEGATED ? "true" : "false"); + + if (found == NULL && !(ud->flags & DETECT_URICONTENT_NEGATED)) { + SCReturnInt(0); + } else if (found == NULL && ud->flags & DETECT_URICONTENT_NEGATED) { + goto match; + } else if (found != NULL && ud->flags & DETECT_URICONTENT_NEGATED) { + match_offset = (uint32_t)((found - payload) + ud->uricontent_len); + SCLogDebug("uricontent %"PRIu32" matched at offset %"PRIu32", but negated so no match", ud->id, match_offset); + SCReturnInt(0); + } else { + match_offset = (uint32_t)((found - payload) + ud->uricontent_len); + SCLogDebug("uricontent %"PRIu32" matched at offset %"PRIu32"", ud->id, match_offset); + det_ctx->uricontent_payload_offset = match_offset; + + if (!(ud->flags & DETECT_URICONTENT_RELATIVE_NEXT)) { + SCLogDebug("no relative match coming up, so this is a match"); + goto match; + } + + BUG_ON(sm->next == NULL); + SCLogDebug("uricontent %"PRIu32, ud->id); + + /* see if the next payload keywords match. If not, we will + * search for another occurence of this uricontent and see + * if the others match then until we run out of matches */ + int r = DoInspectPacketUricontentPayload(de_ctx,det_ctx,s,sm->next, p, payload, payload_len); + if (r == 1) { + SCReturnInt(1); + } + + /* set the previous match offset to the start of this match + 1 */ + prev_offset += (match_offset - (ud->uricontent_len - 1)); + SCLogDebug("trying to see if there is another match after prev_offset %"PRIu32, prev_offset); + } + + } while(1); + } else { + /* we should never get here, but bail out just in case */ + BUG_ON(1); + } + SCReturnInt(0); + +match: + /* this sigmatch matched, inspect the next one. If it was the last, + * the payload portion of the signature matched. */ + if (sm->next != NULL) { + int r = DoInspectPacketUricontentPayload(de_ctx,det_ctx,s,sm->next, p, payload, payload_len); + SCReturnInt(r); + } else { + SCReturnInt(1); + } +} + +/** \brief Do the content inspection & validation for a signature + * + * \param de_ctx Detection engine context + * \param det_ctx Detection engine thread context + * \param s Signature to inspect + * \param sm SigMatch to inspect + * \param f Flow + * \param flags app layer flags + * \param state App layer state + * \param p Packet + * + * \retval 0 no match + * \retval 1 match + */ +int DetectEngineInspectPacketUricontentPayload(DetectEngineCtx *de_ctx, + DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f, uint8_t flags, + void *alstate, Packet *p) +{ + SCEnter(); + SigMatch *sm = NULL; + int r = 1; + HtpState *htp_state = NULL; + + htp_state = (HtpState *)alstate; + if (htp_state == NULL) { + SCLogDebug("no HTTP state"); + r = 0; + goto end; + } + + /* locking the flow, we will inspect the htp state */ + SCMutexLock(&f->m); + + /* if we don't have a uri, don't bother inspecting */ + if (det_ctx->de_have_httpuri == FALSE) { + SCLogDebug("We don't have uri"); + r = 0; + goto end; + } + + sm = s->umatch; + + det_ctx->uricontent_payload_offset = 0; + + DetectUricontentData *co = (DetectUricontentData *)sm->ctx; + SCLogDebug("co->id %"PRIu32, co->id); + + size_t idx = 0; + htp_tx_t *tx = NULL; + + for (idx = htp_state->new_in_tx_index; + idx < list_size(htp_state->connp->conn->transactions); idx++) + { + tx = list_get(htp_state->connp->conn->transactions, idx); + if (tx == NULL || tx->request_uri_normalized == NULL) + continue; + + /* Inspect all the uricontents fetched on each transaction at the app layer */ + r = DoInspectPacketUricontentPayload(de_ctx, det_ctx, s, s->umatch, p, (uint8_t *) bstr_ptr(tx->request_uri_normalized), bstr_len(tx->request_uri_normalized)); + + if (r == 1) { + break; + } + } + + if (r < 1) + r = 0; + +end: + SCMutexUnlock(&f->m); + SCReturnInt(r); +} + diff --git a/src/detect-engine-uricontent.h b/src/detect-engine-uricontent.h new file mode 100644 index 0000000000..7c49ccb688 --- /dev/null +++ b/src/detect-engine-uricontent.h @@ -0,0 +1,9 @@ +#ifndef __DETECT_ENGINE_URICONTENT_H__ +#define __DETECT_ENGINE_URICONTENT_H__ + +int DetectEngineInspectPacketUricontentPayload(DetectEngineCtx *, + DetectEngineThreadCtx *, Signature *, Flow *, uint8_t, + void *, Packet *); + +#endif /* __DETECT_ENGINE_URICONTENT_H__ */ + diff --git a/src/detect-http-cookie.c b/src/detect-http-cookie.c index 280fedd70b..e1fed868c0 100644 --- a/src/detect-http-cookie.c +++ b/src/detect-http-cookie.c @@ -413,12 +413,12 @@ int DetectHttpCookieTest06(void) if (s->match->type != DETECT_AL_HTTP_COOKIE) goto end; - if (s->match->next == NULL) { + if (s->umatch == NULL) { printf("expected another SigMatch, got NULL: "); goto end; } - if (s->match->next->type != DETECT_URICONTENT) { + if (s->umatch->type != DETECT_URICONTENT) { goto end; } diff --git a/src/detect-nocase.c b/src/detect-nocase.c index 18a7fe6cea..ea436888ee 100644 --- a/src/detect-nocase.c +++ b/src/detect-nocase.c @@ -43,30 +43,39 @@ static int DetectNocaseSetup (DetectEngineCtx *de_ctx, Signature *s, char *nulls SCLogError(SC_ERR_INVALID_VALUE, "nocase has no value"); SCReturnInt(-1); } - - SigMatch *pm = SigMatchGetLastPattern(s); + /** Search for the first previous DetectContent or uricontent + * SigMatch (it can be the same as this one) */ + SigMatch *pm = SignatureGetLastModifiableSM(s); if (pm == NULL) { - SCLogError(SC_ERR_NOCASE_MISSING_PATTERN, "\"nocase\" needs a preceeding content or uricontent option."); + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content option"); SCReturnInt(-1); } + DetectUricontentData *ud = NULL; + DetectContentData *cd = NULL; switch (pm->type) { + case DETECT_URICONTENT: + ud = (DetectUricontentData *)pm->ctx; + if (ud == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argpment"); + SCReturnInt(-1); + } + ud->flags |= DETECT_URICONTENT_NOCASE; + break; + case DETECT_CONTENT: - { - DetectContentData *cd = (DetectContentData *)pm->ctx; + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument"); + SCReturnInt(-1); + } cd->flags |= DETECT_CONTENT_NOCASE; - break; - } - case DETECT_URICONTENT: - { - DetectUricontentData *cd = (DetectUricontentData *)pm->ctx; - cd->flags |= DETECT_URICONTENT_NOCASE; - break; - } - /* should never happen */ + break; + default: - BUG_ON(1); - break; + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content (or uricontent) option"); + SCReturnInt(-1); + break; } SCReturnInt(0); diff --git a/src/detect-offset.c b/src/detect-offset.c index d8ecc77c90..c52104bc8f 100644 --- a/src/detect-offset.c +++ b/src/detect-offset.c @@ -5,6 +5,7 @@ #include "decode.h" #include "detect.h" +#include "detect-parse.h" #include "detect-content.h" #include "detect-uricontent.h" @@ -36,30 +37,51 @@ int DetectOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, char *offsetstr) dubbed = 1; } - /* Search for the first previous DetectContent + /** Search for the first previous DetectContent or uricontent * SigMatch (it can be the same as this one) */ - SigMatch *pm = DetectContentFindPrevApplicableSM(s->pmatch_tail); + SigMatch *pm = SignatureGetLastModifiableSM(s); if (pm == NULL) { - SCLogError(SC_ERR_OFFSET_MISSING_CONTENT, "offset needs a preceeding content option"); + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content option"); if (dubbed) SCFree(str); return -1; } - DetectContentData *cd = (DetectContentData *)pm->ctx; - if (cd == NULL) { - SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument"); - if (dubbed) SCFree(str); - return -1; - } - - cd->offset = (uint32_t)atoi(str); - - /* check if offset and depth make sense with the pattern len */ - if (cd->depth != 0) { - if (cd->content_len + cd->offset > cd->depth) { - SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", cd->content_len + cd->offset); - cd->depth = cd->content_len + cd->offset; - } + DetectUricontentData *ud = NULL; + DetectContentData *cd = NULL; + switch (pm->type) { + case DETECT_URICONTENT: + ud = (DetectUricontentData *)pm->ctx; + if (ud == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argpment"); + if (dubbed) SCFree(str); + return -1; + } + ud->offset = (uint32_t)atoi(str); + if (ud->depth != 0) { + SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", ud->uricontent_len + ud->offset); + ud->depth = ud->uricontent_len + ud->offset; + } + break; + + case DETECT_CONTENT: + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + SCLogError(SC_ERR_INVALID_ARGUMENT, "invalid argument"); + if (dubbed) SCFree(str); + return -1; + } + cd->offset = (uint32_t)atoi(str); + if (cd->depth != 0) { + SCLogDebug("depth increased to %"PRIu32" to match pattern len and offset", cd->content_len + cd->offset); + cd->depth = cd->content_len + cd->offset; + } + break; + + default: + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "depth needs a preceeding content (or uricontent) option"); + if (dubbed) SCFree(str); + return -1; + break; } if (dubbed) SCFree(str); diff --git a/src/detect-parse.c b/src/detect-parse.c index 0f3bf84f89..467ca2a563 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -104,6 +104,30 @@ SigTableElmt *SigTableGet(char *name) { return NULL; } +/** + * \brief SigMatchAppendUricontent, append a SigMatch of type uricontent + * to the Signature structure + * \param s pointer to the Signature + * \param new pointer to the SigMatch of type uricontent to be appended + */ +void SigMatchAppendUricontent(Signature *s, SigMatch *new) { + if (s->umatch == NULL) { + s->umatch = new; + s->umatch_tail = new; + new->next = NULL; + new->prev = NULL; + } else { + SigMatch *cur = s->umatch_tail; + cur->next = new; + new->prev = cur; + new->next = NULL; + s->umatch_tail = new; + } + + new->idx = s->sm_cnt; + s->sm_cnt++; +} + void SigMatchAppendPayload(Signature *s, SigMatch *new) { if (s->pmatch == NULL) { s->pmatch = new; @@ -111,13 +135,10 @@ void SigMatchAppendPayload(Signature *s, SigMatch *new) { new->next = NULL; new->prev = NULL; } else { - SigMatch *cur = s->pmatch; - - for ( ; cur->next != NULL; cur = cur->next); - + SigMatch *cur = s->pmatch_tail; cur->next = new; - new->next = NULL; new->prev = cur; + new->next = NULL; s->pmatch_tail = new; } @@ -264,6 +285,48 @@ SigMatch *SigMatchGetLastSM(SigMatch *sm, uint8_t type) return NULL; } +/** + * \brief Returns a pointer to the last SigMatch instance that apply to Modifiers + * (atm: DETECT_CONTENT and DETECT_URICONTENT) + * + * \param s Pointer to the signature (it will search at pmatch and umatch) + * + * \retval match Pointer to the last SigMatch instance. + */ +SigMatch *SignatureGetLastModifiableSM(Signature *s) +{ + SigMatch *pm = s->pmatch_tail; + SigMatch *um = s->umatch_tail; + while (pm != NULL) { + if (pm->type == DETECT_CONTENT) { + break; + } + pm = pm->prev; + } + + while (um != NULL) { + if (um->type == DETECT_URICONTENT) { + break; + } + um = um->prev; + } + + if (um == NULL) + return pm; + + if (pm == NULL) + return um; + + /* Now we should have the latest content and uricontent. + * Let's see which one is more recent */ + if (um->idx > pm->idx) + return um; + else + return pm; + + return NULL; +} + void SigParsePrepare(void) { char *regexstr = CONFIG_PCRE; const char *eb; @@ -714,7 +777,7 @@ Signature *SigInit(DetectEngineCtx *de_ctx, char *sigstr) { } } } - for (sm = sig->match; sm != NULL; sm = sm->next) { + for (sm = sig->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (ud == NULL) @@ -742,7 +805,7 @@ Signature *SigInit(DetectEngineCtx *de_ctx, char *sigstr) { } } - for (sm = sig->match; sm != NULL; sm = sm->next) { + for (sm = sig->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (sig->mpm_uricontent_maxlen == 0) @@ -831,7 +894,7 @@ Signature *SigInitReal(DetectEngineCtx *de_ctx, char *sigstr) { } } - for (sm = sig->match; sm != NULL; sm = sm->next) { + for (sm = sig->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (ud == NULL) @@ -859,7 +922,7 @@ Signature *SigInitReal(DetectEngineCtx *de_ctx, char *sigstr) { } } - for (sm = sig->match; sm != NULL; sm = sm->next) { + for (sm = sig->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (sig->mpm_uricontent_maxlen == 0) @@ -901,7 +964,7 @@ Signature *SigInitReal(DetectEngineCtx *de_ctx, char *sigstr) { sig->next->mpm_content_maxlen = cd->content_len; } } - for (sm = sig->next->match; sm != NULL; sm = sm->next) { + for (sm = sig->next->umatch; sm != NULL; sm = sm->next) { if (sm->type == DETECT_URICONTENT) { DetectUricontentData *ud = (DetectUricontentData *)sm->ctx; if (sig->next->mpm_uricontent_maxlen == 0) diff --git a/src/detect-parse.h b/src/detect-parse.h index 194d32f4a1..d0f75a64a7 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -22,9 +22,12 @@ Signature *SigAlloc(void); void SigFree(Signature *s); Signature *SigInit(DetectEngineCtx *,char *sigstr); SigMatch *SigMatchGetLastSM(SigMatch *, uint8_t); +SigMatch *SignatureGetLastModifiableSM(Signature *); + void SigParsePrepare(void); void SigParseRegisterTests(void); Signature *DetectEngineAppendSig(DetectEngineCtx *, char *); +void SigMatchAppendUricontent(Signature *, SigMatch *); void SigMatchReplace(Signature *, SigMatch *, SigMatch *); void SigMatchReplaceContent(Signature *, SigMatch *, SigMatch *); diff --git a/src/detect-uricontent.c b/src/detect-uricontent.c index 6e4f73ea47..c9791eee69 100644 --- a/src/detect-uricontent.c +++ b/src/detect-uricontent.c @@ -12,6 +12,7 @@ #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-parse.h" @@ -36,8 +37,6 @@ #include "util-spm.h" /* prototypes */ -int DetectUricontentMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, - Signature *, SigMatch *); static int DetectUricontentSetup (DetectEngineCtx *, Signature *, char *); void HttpUriRegisterTests(void); @@ -51,7 +50,7 @@ int DetectAppLayerUricontentMatch (ThreadVars *, DetectEngineThreadCtx *, void DetectUricontentRegister (void) { sigmatch_table[DETECT_URICONTENT].name = "uricontent"; - sigmatch_table[DETECT_URICONTENT].AppLayerMatch = DetectAppLayerUricontentMatch; + sigmatch_table[DETECT_URICONTENT].AppLayerMatch = NULL; sigmatch_table[DETECT_URICONTENT].Match = NULL; sigmatch_table[DETECT_URICONTENT].Setup = DetectUricontentSetup; sigmatch_table[DETECT_URICONTENT].Free = NULL; @@ -70,6 +69,72 @@ uint32_t DetectUricontentMaxId(DetectEngineCtx *de_ctx) return de_ctx->uricontent_max_id; } +/** + * \brief Helper function to print a DetectContentData + */ +void DetectUricontentPrint(DetectUricontentData *cd) +{ + int i = 0; + if (cd == NULL) { + SCLogDebug("Detect UricontentData \"cd\" is NULL"); + return; + } + char *tmpstr = SCMalloc(sizeof(char) * cd->uricontent_len + 1); + + if (tmpstr != NULL) { + for (i = 0; i < cd->uricontent_len; i++) { + if (isprint(cd->uricontent[i])) + tmpstr[i] = cd->uricontent[i]; + else + tmpstr[i] = '.'; + } + tmpstr[i] = '\0'; + SCLogDebug("Uricontent: \"%s\"", tmpstr); + SCFree(tmpstr); + } else { + SCLogDebug("Uricontent: "); + for (i = 0; i < cd->uricontent_len; i++) + SCLogDebug("%c", cd->uricontent[i]); + } + + SCLogDebug("Uricontent_id: %"PRIu32, cd->id); + SCLogDebug("Uricontent_len: %"PRIu16, cd->uricontent_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_URICONTENT_NEGATED ? "true" : "false"); + SCLogDebug("relative match next: %s ", cd->flags & DETECT_URICONTENT_RELATIVE_NEXT ? "true" : "false"); + SCLogDebug("-----------"); +} + + +/** + * \brief Search the first DETECT_URICONTENT + * \retval pointer to the SigMatch holding the DetectUricontent + * \param sm pointer to the current SigMatch of a parsing process + * \retval null if no applicable DetectUricontent was found + * \retval pointer to the SigMatch that has the previous SigMatch + * of type DetectUricontent + */ +SigMatch *DetectUricontentFindPrevApplicableSM(SigMatch *sm) +{ + if (sm == NULL) + return NULL; + while (sm != NULL && sm->type != DETECT_URICONTENT) + sm = sm->prev; + + if (sm == NULL) + return NULL; + + DetectUricontentData *cd = (DetectUricontentData*) sm->ctx; + if (cd == NULL) + return NULL; + + return sm; +} + /** * \brief Setup the detecturicontent keyword data from the string defined in * the rule set. @@ -106,9 +171,8 @@ DetectUricontentData *DoDetectUricontentSetup (char * contentstr) }; if (temp[pos] == '!') { - SCLogError(SC_ERR_NO_URICONTENT_NEGATION, "uricontent negation is not " - "supported at this time. See bug #31."); - goto error; + cd->flags |= DETECT_URICONTENT_NEGATED; + pos++; } if (temp[pos] == '\"' && temp[strlen(temp)-1] == '\"') { @@ -238,7 +302,7 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, char *contents sm->type = DETECT_URICONTENT; sm->ctx = (void *)cd; - SigMatchAppendAppLayer(s, sm); + //SigMatchAppendAppLayer(s, sm); /** \todo use unique id here as well */ cd->id = de_ctx->uricontent_max_id; @@ -253,6 +317,9 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, char *contents } s->alproto = ALPROTO_HTTP; + + SigMatchAppendUricontent(s,sm); + SCReturnInt(0); error: @@ -303,79 +370,6 @@ int DoDetectAppLayerUricontentMatch (ThreadVars *tv, DetectEngineThreadCtx *det_ return ret; } - -/** - * \brief Checks if the received http request has a uricontent, which matches - * with the defined signature - * - * \param t Pointer to the tv for this detection module instance - * \param det_ctx Pointer to the detection engine thread context - * \param f pointer to the current flow - * \param flags flags to indicate the direction of the received packet - * \param state pointer the app layer state, which will cast into HtpState - * \param s pointer to the current signature - * \param m pointer to the sigmatch that we will cast into - * DetectUricontentData - * - * \retval 1 if the contents matches; 0 no match - */ -int DetectAppLayerUricontentMatch (ThreadVars *tv, DetectEngineThreadCtx *det_ctx, - Flow *f, uint8_t flags, void *state, - Signature *s, SigMatch *sm) -{ - SCEnter(); - int res = 0; - size_t idx = 0; - htp_tx_t *tx = NULL; - - /* if we don't have a uri, don't bother inspecting */ - if (det_ctx->de_have_httpuri == FALSE) { - SCLogDebug("We don't have uri"); - SCReturnInt(0); - } - - /* we're locking the flow as we'll be accessing the HTP state */ - SCMutexLock(&f->m); - - DetectUricontentData *co = (DetectUricontentData *)sm->ctx; - if (co == NULL) - goto end; - - SCLogDebug("co->id %"PRIu32, co->id); - - HtpState *htp_state = (HtpState *)state; - if (htp_state == NULL) { - SCLogDebug("no HTTP state"); - goto end; - } - - for (idx = htp_state->new_in_tx_index; - idx < list_size(htp_state->connp->conn->transactions); idx++) - { - tx = list_get(htp_state->connp->conn->transactions, idx); - if (tx == NULL || tx->request_uri_normalized == NULL) - continue; - - /* Search for the pattern in each uri. Bail out on the first match */ - if ((BasicSearch((uint8_t *) bstr_ptr(tx->request_uri_normalized), - bstr_len(tx->request_uri_normalized), - co->uricontent, co->uricontent_len)) != NULL) { - SCLogDebug("Match has been found in the received request and " - "signature s->id %"PRIu32"", s->id); - res = 1; - break; - } - } - - if (res == 0) { - SCLogDebug("We don't have app layer URI match"); - } - -end: - SCMutexUnlock(&f->m); - SCReturnInt(res); -} - /** \brief Run the pattern matcher against the uri(s) * * We run against _all_ uri(s) we have as the pattern matcher will @@ -713,7 +707,7 @@ int DetectUriSigTest01(void) BUG_ON(de_ctx->sig_list == NULL); - sm = de_ctx->sig_list->match; + sm = de_ctx->sig_list->umatch; if (sm->type == DETECT_URICONTENT) { result = 1; } else { @@ -948,6 +942,515 @@ end: return result; } +/** + * \test Check that modifiers of content apply only to content keywords + * and the same for uricontent modifiers + */ +static int DetectUriSigTest04(void) { + int result = 0; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + Signature *s = NULL; + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"foo\"; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch != NULL || + s->match != NULL) + { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + s->match != NULL) + { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8|| + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + s->match != NULL) + { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "content:\"foo\"; uricontent:\"bar\";" + " depth:10; offset: 5; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectUricontentData*)s->umatch->ctx)->depth != 8|| + ((DetectUricontentData*)s->umatch->ctx)->offset != 5 || + s->match != NULL) + { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; within:3; sid:1;)"); + if (s != NULL) { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; distance:3; sid:1;)"); + if (s != NULL) { + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; content:" + "\"two_contents\"; within:30; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8|| + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + ((DetectContentData*)s->pmatch_tail->ctx)->within != 30 || + s->match != NULL) + { + DetectContentPrint((DetectContentData*)s->pmatch_tail->ctx); + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; uricontent:" + "\"two_uricontents\"; within:30; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8|| + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + ((DetectContentData*)s->umatch_tail->ctx)->within != 30 || + s->match != NULL) + { + DetectUricontentPrint((DetectUricontentData*)s->umatch_tail->ctx); + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; content:" + "\"two_contents\"; distance:30; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8|| + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + ((DetectContentData*)s->pmatch_tail->ctx)->distance != 30 || + s->match != NULL) + { + DetectContentPrint((DetectContentData*)s->pmatch_tail->ctx); + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; uricontent:" + "\"two_uricontents\"; distance:30; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8|| + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + ((DetectContentData*)s->umatch_tail->ctx)->distance != 30 || + s->match != NULL) + { + DetectUricontentPrint((DetectUricontentData*)s->umatch_tail->ctx); + goto end; + } + + s = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent and content\"; " + "uricontent:\"foo\"; content:\"bar\";" + " depth:10; offset: 5; uricontent:" + "\"two_uricontents\"; distance:30; " + "within:60; content:\"two_contents\";" + " within:70; distance:45; sid:1;)"); + if (s == NULL || + s->umatch == NULL || + s->pmatch == NULL || + ((DetectContentData*)s->pmatch->ctx)->depth != 8 || + ((DetectContentData*)s->pmatch->ctx)->offset != 5 || + ((DetectContentData*)s->umatch_tail->ctx)->distance != 30 || + ((DetectContentData*)s->umatch_tail->ctx)->within != 60 || + ((DetectContentData*)s->pmatch_tail->ctx)->distance != 45 || + ((DetectContentData*)s->pmatch_tail->ctx)->within != 70 || + s->match != NULL) + { + DetectUricontentPrint((DetectUricontentData*)s->umatch_tail->ctx); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + return result; +} + +/** \test Check the modifiers for uricontent and content + * match + */ +static int DetectUriSigTest05(void) { + int result = 0; + Flow f; + HtpState *http_state = NULL; + uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:" + " hellocatch\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + Packet p; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = httpbuf1; + p.payload_len = httplen1; + p.proto = IPPROTO_TCP; + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + StreamL7DataPtrInit(&ssn); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->mpm_matcher = MPM_B2G; + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"foo\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; content:\"two\"; sid:2;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; offset:1; depth:10; " + "uricontent:\"two\"; distance:1; within: 4; " + "uricontent:\"three\"; distance:1; within: 6; " + "sid:3;)"); + + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + if ((PacketAlertCheck(&p, 1))) { + printf("sig: 1 alerted, but it should not\n"); + goto end; + } else if (! PacketAlertCheck(&p, 2)) { + printf("sig: 2 did not alerted, but it should\n"); + goto end; + } else if (! (PacketAlertCheck(&p, 3))) { + printf("sig: 3 did not alerted, but it should\n"); + goto end; + } + + result = 1; +end: + if (http_state != NULL) HTPStateFree(http_state); + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + StreamL7DataPtrFree(&ssn); + StreamTcpFreeConfig(TRUE); + return result; +} + +/** \test Check the modifiers for uricontent and content + * match + */ +static int DetectUriSigTest06(void) { + int result = 0; + Flow f; + HtpState *http_state = NULL; + uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:" + " hellocatch\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + Packet p; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = httpbuf1; + p.payload_len = httplen1; + p.proto = IPPROTO_TCP; + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + StreamL7DataPtrInit(&ssn); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->mpm_matcher = MPM_B2G; + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"foo\"; content:\"bar\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; offset:1; depth:10; " + "content:\"one\"; offset:1; depth:10; " + "uricontent:\"two\"; distance:1; within: 4; " + "content:\"two\"; distance:1; within: 4; " + "uricontent:\"three\"; distance:1; within: 6; " + "content:\"/three\"; distance:0; within: 7; " + "sid:2;)"); + + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; offset:1; depth:10; " + "uricontent:\"two\"; distance:1; within: 4; " + "uricontent:\"three\"; distance:1; within: 6; " + "sid:3;)"); + + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + if ((PacketAlertCheck(&p, 1))) { + printf("sig: 1 alerted, but it should not\n"); + goto end; + } else if (! PacketAlertCheck(&p, 2)) { + printf("sig: 2 did not alerted, but it should\n"); + goto end; + } else if (! (PacketAlertCheck(&p, 3))) { + printf("sig: 3 did not alerted, but it should\n"); + goto end; + } + + result = 1; +end: + if (http_state != NULL) HTPStateFree(http_state); + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + StreamL7DataPtrFree(&ssn); + StreamTcpFreeConfig(TRUE); + return result; +} + +/** \test Check the modifiers for uricontent and content + * match + */ +static int DetectUriSigTest07(void) { + int result = 0; + Flow f; + HtpState *http_state = NULL; + uint8_t httpbuf1[] = "POST /one/two/three HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:" + " hellocatch\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + Packet p; + Signature *s = NULL; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + + memset(&th_v, 0, sizeof(th_v)); + memset(&p, 0, sizeof(p)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p.src.family = AF_INET; + p.dst.family = AF_INET; + p.payload = httpbuf1; + p.payload_len = httplen1; + p.proto = IPPROTO_TCP; + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + StreamL7DataPtrInit(&ssn); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->mpm_matcher = MPM_B2G; + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"foo\"; content:\"bar\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; offset:1; depth:10; " + "content:\"one\"; offset:1; depth:10; " + "uricontent:\"two\"; distance:3; within: 4; " + "content:\"two\"; distance:1; within: 4; " + "uricontent:\"three\"; distance:1; within: 6; " + "content:\"/three\"; distance:0; within: 7; " + "sid:2;)"); + + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"one\"; offset:1; depth:10; " + "uricontent:\"two\"; distance:1; within: 4; " + "uricontent:\"six\"; distance:1; within: 6; " + "sid:3;)"); + + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + if (PacketAlertCheck(&p, 1)) { + printf("sig: 1 alerted, but it should not\n"); + goto end; + } else if (PacketAlertCheck(&p, 2)) { + printf("sig: 2 did not alerted, but it should\n"); + goto end; + } else if (PacketAlertCheck(&p, 3)) { + printf("sig: 3 did not alerted, but it should\n"); + goto end; + } + + result = 1; +end: + if (http_state != NULL) HTPStateFree(http_state); + if (de_ctx != NULL) SigGroupCleanup(de_ctx); + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (det_ctx != NULL) DetectEngineThreadCtxDeinit(&th_v, det_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + StreamL7DataPtrFree(&ssn); + StreamTcpFreeConfig(TRUE); + return result; +} #endif /* UNITTESTS */ void HttpUriRegisterTests(void) { @@ -959,5 +1462,9 @@ void HttpUriRegisterTests(void) { UtRegisterTest("DetectUriSigTest01", DetectUriSigTest01, 1); UtRegisterTest("DetectUriSigTest02", DetectUriSigTest02, 1); UtRegisterTest("DetectUriSigTest03", DetectUriSigTest03, 1); + UtRegisterTest("DetectUriSigTest04 - Modifiers", DetectUriSigTest04, 1); + UtRegisterTest("DetectUriSigTest05 - Inspection", DetectUriSigTest05, 1); + UtRegisterTest("DetectUriSigTest06 - Inspection", DetectUriSigTest06, 1); + UtRegisterTest("DetectUriSigTest07 - Inspection", DetectUriSigTest07, 1); #endif /* UNITTESTS */ } diff --git a/src/detect-uricontent.h b/src/detect-uricontent.h index 14eb0240dc..f64edd7dbf 100644 --- a/src/detect-uricontent.h +++ b/src/detect-uricontent.h @@ -9,6 +9,14 @@ #define DETECT_URICONTENT_WITHIN_NEXT 0x10 #define DETECT_URICONTENT_RAWBYTES 0x20 +#define DETECT_URICONTENT_NEGATED 0x40 +#define DETECT_URICONTENT_RELATIVE_NEXT 0x80 + +#define DETECT_URICONTENT_IS_SINGLE(c) (!((c)->flags & DETECT_URICONTENT_DISTANCE || \ + (c)->flags & DETECT_URICONTENT_WITHIN || \ + (c)->flags & DETECT_URICONTENT_RELATIVE || \ + (c)->depth > 0 || \ + (c)->within > 0)) typedef struct DetectUricontentData_ { uint8_t *uricontent; @@ -26,6 +34,8 @@ typedef struct DetectUricontentData_ { void DetectUricontentRegister (void); uint32_t DetectUricontentMaxId(DetectEngineCtx *); uint32_t DetectUricontentInspectMpm(ThreadVars *th_v, DetectEngineThreadCtx *det_ctx, void *alstate); +SigMatch *DetectUricontentFindPrevApplicableSM(SigMatch *); +void DetectUricontentPrint(DetectUricontentData *); #endif /* __DETECT_URICONTENT_H__ */ diff --git a/src/detect-within.c b/src/detect-within.c index c58caf7e1b..3c10279993 100644 --- a/src/detect-within.c +++ b/src/detect-within.c @@ -9,6 +9,7 @@ #include "decode.h" #include "detect.h" +#include "detect-parse.h" #include "detect-content.h" #include "detect-uricontent.h" @@ -47,55 +48,106 @@ static int DetectWithinSetup (DetectEngineCtx *de_ctx, Signature *s, char *withi dubbed = 1; } - if (s->pmatch == NULL) { - SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content options"); - goto error; - } - - /* Search for the first previous DetectContent + /** Search for the first previous DetectContent * SigMatch (it can be the same as this one) */ - SigMatch *pm = DetectContentFindPrevApplicableSM(s->pmatch_tail); - if (pm == NULL || DetectContentHasPrevSMPattern(pm) == NULL) { - SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content options"); - goto error; - } - - DetectContentData *cd = (DetectContentData *)pm->ctx; - if (cd == NULL) { - printf("DetectWithinSetup: Unknown previous keyword!\n"); - goto error; - } - - cd->within = strtol(str, NULL, 10); - if (cd->within < (int32_t)cd->content_len) { - SCLogError(SC_ERR_WITHIN_INVALID, "within argument \"%"PRIi32"\" is " - "less than the content length \"%"PRIu32"\" which is invalid, since " - "this will never match. Invalidating signature", cd->within, - cd->content_len); - goto error; - } - - cd->flags |= DETECT_CONTENT_WITHIN; - - if (cd->flags & DETECT_CONTENT_DISTANCE) { - if (cd->distance > (cd->content_len + cd->within)) { - cd->within = cd->distance + cd->content_len; - } - } - - pm = DetectContentFindPrevApplicableSM(s->pmatch_tail->prev); + SigMatch *pm = SignatureGetLastModifiableSM(s); if (pm == NULL) { - SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content options"); - goto error; + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "depth needs two preceeding content (or uricontent) options"); + if (dubbed) SCFree(str); + return -1; } - /* Set the relative next flag on the prev sigmatch */ - if (pm->type == DETECT_CONTENT) { - DetectContentData *cd = (DetectContentData *)pm->ctx; - cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; - } else { - printf("DetectWithinSetup: Unknown previous-previous keyword!\n"); - goto error; + DetectUricontentData *ud = NULL; + DetectContentData *cd = NULL; + + switch (pm->type) { + case DETECT_URICONTENT: + ud = (DetectUricontentData *)pm->ctx; + if (ud == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + goto error; + } + + ud->within = strtol(str, NULL, 10); + if (ud->within < (int32_t)ud->uricontent_len) { + SCLogError(SC_ERR_WITHIN_INVALID, "within argument \"%"PRIi32"\" is " + "less than the content length \"%"PRIu32"\" which is invalid, since " + "this will never match. Invalidating signature", ud->within, + ud->uricontent_len); + goto error; + } + + ud->flags |= DETECT_URICONTENT_WITHIN; + + if (ud->flags & DETECT_URICONTENT_DISTANCE) { + if (ud->distance > (ud->uricontent_len + ud->within)) { + ud->within = ud->distance + ud->uricontent_len; + } + } + + pm = DetectUricontentFindPrevApplicableSM(s->umatch_tail->prev); + if (pm == NULL) { + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content options"); + goto error; + } + + /* Set the relative next flag on the prev sigmatch */ + if (pm->type == DETECT_URICONTENT) { + ud = (DetectUricontentData *)pm->ctx; + ud->flags |= DETECT_URICONTENT_RELATIVE_NEXT; + } else { + printf("DetectWithinSetup: Unknown previous-previous keyword!\n"); + goto error; + } + DetectUricontentPrint(ud); + + break; + + case DETECT_CONTENT: + cd = (DetectContentData *)pm->ctx; + if (cd == NULL) { + printf("DetectWithinSetup: Unknown previous keyword!\n"); + goto error; + } + + cd->within = strtol(str, NULL, 10); + if (cd->within < (int32_t)cd->content_len) { + SCLogError(SC_ERR_WITHIN_INVALID, "within argument \"%"PRIi32"\" is " + "less than the content length \"%"PRIu32"\" which is invalid, since " + "this will never match. Invalidating signature", cd->within, + cd->content_len); + goto error; + } + + cd->flags |= DETECT_CONTENT_WITHIN; + + if (cd->flags & DETECT_CONTENT_DISTANCE) { + if (cd->distance > (cd->content_len + cd->within)) { + cd->within = cd->distance + cd->content_len; + } + } + + pm = DetectContentFindPrevApplicableSM(s->pmatch_tail->prev); + if (pm == NULL) { + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content options"); + goto error; + } + + /* Set the relative next flag on the prev sigmatch */ + if (pm->type == DETECT_CONTENT) { + cd = (DetectContentData *)pm->ctx; + cd->flags |= DETECT_CONTENT_RELATIVE_NEXT; + } else { + printf("DetectWithinSetup: Unknown previous-previous keyword!\n"); + goto error; + } + break; + + default: + SCLogError(SC_ERR_WITHIN_MISSING_CONTENT, "within needs two preceeding content (or uricontent) options"); + if (dubbed) SCFree(str); + return -1; + break; } if (dubbed) SCFree(str); diff --git a/src/detect.c b/src/detect.c index cc083d0b22..739e67a01b 100644 --- a/src/detect.c +++ b/src/detect.c @@ -18,6 +18,7 @@ #include "detect-engine-iponly.h" #include "detect-engine-threshold.h" #include "detect-engine-payload.h" +#include "detect-engine-uricontent.h" #include "detect-http-cookie.h" #include "detect-http-method.h" @@ -661,6 +662,12 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh continue; } + /* Check the uricontent keywords here. */ + if (s->umatch != NULL) { + if (DetectEngineInspectPacketUricontentPayload(de_ctx, det_ctx, s, p->flow, flags, alstate, p) != 1) + continue; + } + /* if we get here but have no sigmatches to match against, * we consider the sig matched. */ if (s->match == NULL) { @@ -882,6 +889,9 @@ int SignatureIsIPOnly(DetectEngineCtx *de_ctx, Signature *s) { if (s->pmatch != NULL) return 0; + if (s->umatch != NULL) + return 0; + SigMatch *sm = s->match; if (sm == NULL) goto iponly; @@ -3328,6 +3338,7 @@ static int SigTest06Real (int mpm_type) { result = 0; goto end; } + de_ctx->sig_list->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"HTTP URI test\"; uricontent:\"two\"; sid:2;)"); if (de_ctx->sig_list->next == NULL) { result = 0; diff --git a/src/detect.h b/src/detect.h index 97f0bbf929..bc0c783928 100644 --- a/src/detect.h +++ b/src/detect.h @@ -202,11 +202,13 @@ typedef struct Signature_ { DetectProto proto; DetectPort *sp, *dp; - /** ptr to the SigMatch list */ + /** ptr to the SigMatch lists */ struct SigMatch_ *match; /* non-payload matches */ struct SigMatch_ *match_tail; /* non-payload matches, tail of the list */ struct SigMatch_ *pmatch; /* payload matches */ struct SigMatch_ *pmatch_tail; /* payload matches, tail of the list */ + struct SigMatch_ *umatch; /* uricontent payload matches */ + struct SigMatch_ *umatch_tail; /* uricontent payload matches, tail of the list */ /** ptr to the next sig in the list */ struct Signature_ *next; @@ -389,10 +391,15 @@ typedef struct DetectionEngineThreadCtx_ { * content, pcre, etc */ uint32_t payload_offset; + /** offset into the uri payload of the last match by + * uricontent */ + uint32_t uricontent_payload_offset; + /** recursive counter */ uint8_t pkt_cnt; char de_checking_distancewithin; + char de_checking_uricontent_distancewithin; /* http_uri stuff for uricontent */ char de_have_httpuri; diff --git a/src/stream-tcp-reassemble.c b/src/stream-tcp-reassemble.c index 6c87ead3d1..0017884e19 100644 --- a/src/stream-tcp-reassemble.c +++ b/src/stream-tcp-reassemble.c @@ -33,7 +33,6 @@ #include "stream.h" - #include "util-debug.h" #include "app-layer-protos.h" //#define DEBUG