diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index 712f85f8dc..7d8bcf19f5 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -64,7 +64,11 @@ static void *HTPStateAlloc(void) htp_state_memuse+=sizeof(HtpState); SCMutexUnlock(&htp_state_mem_lock); #endif - + /* Create a list_array of size 8 to store the incoming requests, the size of + 8 has been chosen as half the size of conn->transactions in the + HTP lib. As we are storing only requests here not responses!! */ + s->recent_in_tx = list_array_create(8); + htp_connp_set_user_data(s->connp, (void *)s); SCReturnPtr((void *)s, "void"); error: @@ -88,6 +92,8 @@ static void HTPStateFree(void *state) if (s->connp != NULL) { htp_connp_destroy_all(s->connp); } + if (s->recent_in_tx != NULL) + list_destroy(s->recent_in_tx); } free(s); @@ -260,6 +266,40 @@ void HTPFreeConfig(void) htp_config_destroy(cfg); } +/** + * \brief callback for request to store the recent incoming request + in to the recent_in_tx for the given htp state + * \param connp pointer to the current connection parser which has the htp + * state in it as user data + */ +static int HTPCallbackRequest(htp_connp_t *connp) { + SCEnter(); + HtpState *hstate = (HtpState *)connp->user_data; + + list_add(hstate->recent_in_tx, connp->in_tx); + SCReturnInt(0); +} + +/** + * \brief callback for response to remove the recent received requests + from the recent_in_tx for the given htp state + * \param connp pointer to the current connection parser which has the htp + * state in it as user data + */ +static int HTPCallbackResponse(htp_connp_t *connp) { + SCEnter(); + HtpState *hstate = (HtpState *)connp->user_data; + htp_tx_t *tx = NULL; + uint8_t i = 0; + + for (i=0; i < list_size(hstate->recent_in_tx) - 1; i++) { + tx = list_pop(hstate->recent_in_tx); + if (tx != NULL) + htp_tx_destroy(tx); + } + SCReturnInt(0); +} + /** * \brief Register the HTTP protocol and state handling functions to APP layer * of the engine. @@ -275,20 +315,16 @@ void RegisterHTPParsers(void) HTPHandleResponseData); cfg = htp_config_create(); + /* Register the callback for request to store the recent incoming request + in to the recent_in_tx for the given htp state */ + htp_config_register_request(cfg, HTPCallbackRequest); + /* Register the callback for response to remove the recently received request + from the recent_in_tx for the given htp state */ + htp_config_register_response(cfg, HTPCallbackResponse); + /* set the normalized request parsing to be used in uricontent matching */ + htp_config_set_generate_request_uri_normalized(cfg, 1); } -/** - * \brief Returns the main (first) HTTP transaction - * - * \param htp_state HTP library state - * \returns Main HTP transation - * - */ -htp_tx_t *HTPTransactionMain(const HtpState *htp_state) -{ - SCEnter(); - SCReturnPtr(list_get(htp_state->connp->conn->transactions, 0), "htp_tx_t"); -} #ifdef UNITTESTS /** \test Test case where chunks are sent in smaller chunks and check the diff --git a/src/app-layer-htp.h b/src/app-layer-htp.h index 228150f31b..6618a7700b 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -24,7 +24,7 @@ typedef struct HtpState_ { htp_connp_t *connp; /**< Connection parser structure for each connection */ uint8_t flags; - + list_t *recent_in_tx; /**< Point to the new received HTTP request */ } HtpState; htp_cfg_t *cfg; /**< Config structure for HTP library */ @@ -33,7 +33,6 @@ void RegisterHTPParsers(void); void HTPParserRegisterTests(void); void HTPAtExitPrintStats(void); void HTPFreeConfig(void); -htp_tx_t *HTPTransactionMain(const HtpState *); #endif /* __APP_LAYER_HTP_H__ */ diff --git a/src/app-layer-parser.c b/src/app-layer-parser.c index 0333251c57..71af7bb2fa 100644 --- a/src/app-layer-parser.c +++ b/src/app-layer-parser.c @@ -21,7 +21,9 @@ #include "util-debug.h" static uint16_t app_layer_sid = 0; -static AppLayerProto al_proto_table[ALPROTO_MAX]; +static AppLayerProto al_proto_table[ALPROTO_MAX]; /**< Application layer protocol + table mapped to their + corresponding parsers */ #define MAX_PARSERS 100 static AppLayerParserTableElement al_parser_table[MAX_PARSERS]; diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 5be50801cc..e3e98ee837 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -35,28 +35,75 @@ uint16_t PatternMatchDefaultMatcher(void) { * \param det_ctx detection engine thread ctx * \param p packet to scan */ -uint32_t PacketPatternScan(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p) { +uint32_t PacketPatternScan(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + Packet *p) +{ uint32_t ret; det_ctx->pmq.mode = PMQ_MODE_SCAN; - ret = mpm_table[det_ctx->sgh->mpm_ctx->mpm_type].Scan(det_ctx->sgh->mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, p->payload, p->payload_len); + ret = mpm_table[det_ctx->sgh->mpm_ctx->mpm_type].Scan + (det_ctx->sgh->mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, p->payload, + p->payload_len); //printf("PacketPatternScan: ret %" PRIu32 "\n", ret); return ret; } +/** \brief Uri Pattern match, scan part -- searches for only 'scan' patterns, + * normally one per signature. + * \param tv threadvars + * \param det_ctx detection engine thread ctx + * \param p packet to scan + */ +uint32_t UriPatternScan(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + uint8_t *content, uint16_t content_len) +{ + uint32_t ret; + + det_ctx->pmq.mode = PMQ_MODE_SCAN; + ret = mpm_table[det_ctx->sgh->mpm_uri_ctx->mpm_type].Scan + (det_ctx->sgh->mpm_uri_ctx, &det_ctx->mtcu, &det_ctx->pmq, + content, content_len); + + SCLogDebug("ret %" PRIu32 "", ret); + return ret; +} + /** \brief Pattern match, search part -- searches for all other patterns * \param tv threadvars * \param det_ctx detection engine thread ctx * \param p packet to scan */ -uint32_t PacketPatternMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p) { +uint32_t PacketPatternMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + Packet *p) +{ + uint32_t ret; + + det_ctx->pmq.mode = PMQ_MODE_SEARCH; + ret = mpm_table[det_ctx->sgh->mpm_ctx->mpm_type].Search + (det_ctx->sgh->mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, p->payload, + p->payload_len); + + SCLogDebug("ret %" PRIu32 "", ret); + return ret; +} + +/** \brief Uri Pattern match, search part -- searches for all other patterns + * \param tv threadvars + * \param det_ctx detection engine thread ctx + * \param p packet to scan + */ +uint32_t UriPatternMatch(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + uint8_t *content, uint16_t content_len) +{ uint32_t ret; det_ctx->pmq.mode = PMQ_MODE_SEARCH; - ret = mpm_table[det_ctx->sgh->mpm_ctx->mpm_type].Search(det_ctx->sgh->mpm_ctx, &det_ctx->mtc, &det_ctx->pmq, p->payload, p->payload_len); + ret = mpm_table[det_ctx->sgh->mpm_uri_ctx->mpm_type].Search + (det_ctx->sgh->mpm_uri_ctx, &det_ctx->mtcu, &det_ctx->pmq, content, + content_len); - //printf("PacketPatternMatch: ret %" PRIu32 "\n", ret); + SCLogDebug("ret %" PRIu32 "", ret); return ret; } diff --git a/src/detect-engine-mpm.h b/src/detect-engine-mpm.h index db6395e18c..cb18677fbd 100644 --- a/src/detect-engine-mpm.h +++ b/src/detect-engine-mpm.h @@ -7,6 +7,9 @@ uint16_t PatternMatchDefaultMatcher(void); uint32_t PacketPatternScan(ThreadVars *, DetectEngineThreadCtx *, Packet *); uint32_t PacketPatternMatch(ThreadVars *, DetectEngineThreadCtx *, Packet *); +uint32_t UriPatternScan(ThreadVars *, DetectEngineThreadCtx *, uint8_t *, uint16_t); +uint32_t UriPatternMatch(ThreadVars *, DetectEngineThreadCtx *, uint8_t *, uint16_t); + void PacketPatternCleanup(ThreadVars *, DetectEngineThreadCtx *); void PatternMatchPrepare(MpmCtx *, uint16_t); diff --git a/src/detect-http-cookie.c b/src/detect-http-cookie.c index ae1f0ed591..66c6f16cf6 100644 --- a/src/detect-http-cookie.c +++ b/src/detect-http-cookie.c @@ -99,27 +99,25 @@ int DetectHttpCookieMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, goto end; } - htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0); - if (tx == NULL) { - SCLogDebug("No HTTP transaction has received on the connection"); - goto end; - } - - htp_header_t *h = NULL; - h = (htp_header_t *)table_getc(tx->request_headers, "Cookie"); - if (h == NULL) { - SCLogDebug("no HTTP Cookie header in the received request"); - goto end; - } + htp_tx_t *tx = NULL; + list_iterator_reset(htp_state->recent_in_tx); + + while ((tx = list_iterator_next(htp_state->recent_in_tx)) != NULL) { + htp_header_t *h = NULL; + h = (htp_header_t *) table_getc(tx->request_headers, "Cookie"); + if (h == NULL) { + SCLogDebug("no HTTP Cookie header in the received request"); + goto end; + } - SCLogDebug("we have a cookie header"); + SCLogDebug("we have a cookie header"); - if (SpmSearch((uint8_t *)bstr_ptr(h->value), bstr_size(h->value), co->data, - co->data_len) != NULL) - { - SCLogDebug("match has been found in received request and given http_" - "cookie rule"); - ret = 1; + if (SpmSearch((uint8_t *) bstr_ptr(h->value), bstr_size(h->value), + co->data, co->data_len) != NULL) { + SCLogDebug("match has been found in received request and given http_" + "cookie rule"); + ret = 1; + } } end: diff --git a/src/detect-http-method.c b/src/detect-http-method.c index 3a6e1eb703..0360466735 100755 --- a/src/detect-http-method.c +++ b/src/detect-http-method.c @@ -72,7 +72,7 @@ int DetectHttpMethodMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, SCEnter(); DetectHttpMethodData *data = (DetectHttpMethodData *)m->ctx; HtpState *hs = (HtpState *)state; - htp_tx_t *tx; + htp_tx_t *tx = NULL; int ret = 0; if (hs == NULL) { @@ -81,34 +81,33 @@ int DetectHttpMethodMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, } SCMutexLock(&f->m); - tx = HTPTransactionMain(hs); - if (tx == NULL) { - SCLogDebug("No HTP transaction."); - goto end; - } - - /* Compare the numeric methods if they are known, otherwise compare - * the raw values. - */ - if (data->method != M_UNKNOWN) { - if (data->method == tx->request_method_number) { - SCLogDebug("Matched numeric HTTP method values."); - ret = 1; - } - } else if (tx->request_method != NULL) { - const uint8_t *meth_str = (const uint8_t *)bstr_ptr(tx->request_method); - - if ( (meth_str != NULL) - && SpmSearch((uint8_t*)meth_str, bstr_size(tx->request_method), - data->content, data->content_len) != NULL) - { - SCLogDebug("Matched raw HTTP method values."); - - ret = 1; + list_iterator_reset(hs->recent_in_tx); + + while ((tx = list_iterator_next(hs->recent_in_tx)) != NULL) { + + /* Compare the numeric methods if they are known, otherwise compare + * the raw values. + */ + if (data->method != M_UNKNOWN) { + if (data->method == tx->request_method_number) { + SCLogDebug("Matched numeric HTTP method values."); + ret = 1; + } + } else if (tx->request_method != NULL) { + const uint8_t *meth_str = (const uint8_t *) + bstr_ptr(tx->request_method); + + if ((meth_str != NULL) && + SpmSearch((uint8_t*) meth_str, bstr_size(tx->request_method), + data->content, data->content_len) != NULL) + { + SCLogDebug("Matched raw HTTP method values."); + + ret = 1; + } } } -end: SCMutexUnlock(&f->m); SCReturnInt(ret); } diff --git a/src/detect-pcre.c b/src/detect-pcre.c index 392c4d774d..0035545715 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -196,7 +196,7 @@ int DetectPcreMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, S /* indicate to uricontent that we have a uri, * we scanned it _AND_ we found pattern matches. */ - det_ctx->de_have_httpuri = 1; + det_ctx->de_have_httpuri = TRUE; } } } diff --git a/src/detect-uricontent.c b/src/detect-uricontent.c index 50bb7274c1..5dda77c6b5 100644 --- a/src/detect-uricontent.c +++ b/src/detect-uricontent.c @@ -1,41 +1,79 @@ -/* Simple uricontent match part of the detection engine. +/* Copyright (C) 2008 by Victor Julien + * Copyright (c) 2009 Open Information Security Foundation */ + +/** + * \file Simple uricontent match part of the detection engine. + * + * \author Victor Julien + * \author Gurvinder Singh * - * Copyright (C) 2008 by Victor Julien */ + */ #include "suricata-common.h" #include "decode.h" #include "detect.h" #include "detect-uricontent.h" #include "detect-engine-mpm.h" +#include "detect-parse.h" +#include "detect-engine.h" #include "flow.h" #include "detect-flow.h" #include "flow-var.h" #include "threads.h" +#include "flow-alert-sid.h" + +#include "stream.h" +#include "app-layer-parser.h" +#include "app-layer-protos.h" +#include "app-layer-htp.h" + #include "util-mpm.h" #include "util-print.h" #include "util-debug.h" #include "util-unittest.h" +#include "util-binsearch.h" -int DetectUricontentMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *); +/* prototypes */ +int DetectUricontentMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, + Signature *, SigMatch *); int DetectUricontentSetup (DetectEngineCtx *, Signature *, SigMatch *, char *); void HttpUriRegisterTests(void); -void DetectUricontentRegister (void) { +int DetectAppLayerUricontentMatch (ThreadVars *, DetectEngineThreadCtx *, + Flow *, uint8_t , void *, + Signature *, SigMatch *); + +/** + * \brief Registration function for uricontent: keyword + */ +void DetectUricontentRegister (void) +{ sigmatch_table[DETECT_URICONTENT].name = "uricontent"; + sigmatch_table[DETECT_URICONTENT].AppLayerMatch = DetectAppLayerUricontentMatch; sigmatch_table[DETECT_URICONTENT].Match = DetectUricontentMatch; sigmatch_table[DETECT_URICONTENT].Setup = DetectUricontentSetup; sigmatch_table[DETECT_URICONTENT].Free = NULL; sigmatch_table[DETECT_URICONTENT].RegisterTests = HttpUriRegisterTests; + sigmatch_table[DETECT_URICONTENT].alproto = ALPROTO_HTTP; sigmatch_table[DETECT_URICONTENT].flags |= SIGMATCH_PAYLOAD; } -/* pass on the uricontent_max_id */ -uint32_t DetectUricontentMaxId(DetectEngineCtx *de_ctx) { +/** + * \brief pass on the uricontent_max_id + * \param de_ctx pointer to the detect egine context whose max id is asked + */ +uint32_t DetectUricontentMaxId(DetectEngineCtx *de_ctx) +{ return de_ctx->uricontent_max_id; } -void PktHttpUriFree(Packet *p) { +/** + * \brief Free the stored http_uri in the given packet + * \param p pointer to the given packet whose uri has to be freed + */ +void PktHttpUriFree(Packet *p) +{ int i; for (i = 0; i < p->http_uri.cnt; i++) { @@ -45,10 +83,11 @@ void PktHttpUriFree(Packet *p) { p->http_uri.cnt = 0; } -static inline int -TestOffsetDepth(MpmMatch *m, DetectUricontentData *co) { +static inline int TestOffsetDepth(MpmMatch *m, DetectUricontentData *co) +{ if (co->offset == 0 || - (co->offset && ((m->offset+1) - co->uricontent_len) >= co->offset)) { + (co->offset && ((m->offset+1) - co->uricontent_len) >= co->offset)) + { if (co->depth == 0 || (co->depth && (m->offset+1) <= co->depth)) { @@ -65,8 +104,9 @@ TestOffsetDepth(MpmMatch *m, DetectUricontentData *co) { * was done like this is to make sure we can handle partial matches * that turn out to fail being followed by full matches later in the * packet. This adds some runtime complexity however. */ -static inline int -TestWithinDistanceOffsetDepth(ThreadVars *t, DetectEngineThreadCtx *det_ctx, MpmMatch *m, SigMatch *nsm) +static inline int TestWithinDistanceOffsetDepth(ThreadVars *t, + DetectEngineThreadCtx *det_ctx, + MpmMatch *m, SigMatch *nsm) { //printf("test_nextsigmatch m:%p, nsm:%p\n", m,nsm); if (nsm == NULL) @@ -76,21 +116,26 @@ TestWithinDistanceOffsetDepth(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Mpm MpmMatch *nm = det_ctx->mtcu.match[co->id].top; for (; nm; nm = nm->next) { - //printf("test_nextsigmatch: (nm->offset+1) %" PRIu32 ", (m->offset+1) %" PRIu32 "\n", (nm->offset+1), (m->offset+1)); + SCLogDebug("(nm->offset+1) %" PRIu32 ", (m->offset+1) %" PRIu32 "", + (nm->offset+1), (m->offset+1)); if ((co->within == 0 || (co->within && ((nm->offset+1) > (m->offset+1)) && ((nm->offset+1) - (m->offset+1) <= co->within)))) { - //printf("test_nextsigmatch: WITHIN (nm->offset+1) %" PRIu32 ", (m->offset+1) %" PRIu32 "\n", (nm->offset+1), (m->offset+1)); + SCLogDebug("WITHIN (nm->offset+1) %" PRIu32 ", (m->offset+1) " + "%" PRIu32 "", (nm->offset+1), (m->offset+1)); if (co->distance == 0 || (co->distance && ((nm->offset+1) > (m->offset+1)) && ((nm->offset+1) - (m->offset+1) >= co->distance))) { if (TestOffsetDepth(nm, co) == 1) { - //printf("test_nextsigmatch: DISTANCE (nm->offset+1) %" PRIu32 ", (m->offset+1) %" PRIu32 "\n", (nm->offset+1), (m->offset+1)); - return TestWithinDistanceOffsetDepth(t, det_ctx, nm, nsm->next); + SCLogDebug("DISTANCE (nm->offset+1) %" PRIu32 ", " + "(m->offset+1) %" PRIu32 "", (nm->offset+1), + (m->offset+1)); + return TestWithinDistanceOffsetDepth(t, det_ctx, nm, + nsm->next); } } } @@ -98,8 +143,9 @@ TestWithinDistanceOffsetDepth(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Mpm return 0; } -static inline int -DoDetectUricontent(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, SigMatch *sm, DetectUricontentData *co) +static inline int DoDetectUricontent(ThreadVars *t, DetectEngineThreadCtx *det_ctx, + Packet *p, SigMatch *sm, + DetectUricontentData *co) { int ret = 0; char match = 0; @@ -167,26 +213,34 @@ DoDetectUricontent(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Sig } -/* - * returns 0: no match - * 1: match - * -1: error +/** + * \brief Checks if the packet sent as the argument, has a uricontent which + * has been provided in the signature + * + * \param t Pointer to the tv for this detection module instance + * \param det_ctx Pointer to the detection engine thread context + * \param p Pointer to the Packet currently being matched + * \param s Pointer to the Signature, the packet is being currently + * matched with + * \param m Pointer to the keyword_structure(SigMatch) from the above + * Signature, the Packet is being currently matched with + * + * \retval 1 if the Packet contents match; 0 no match */ - -int DetectUricontentMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, SigMatch *m) +int DetectUricontentMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, + Packet *p, Signature *s, SigMatch *m) { + SCEnter(); uint32_t len = 0; - /* if we don't have a uri, don't bother scanning */ - if (det_ctx->de_have_httpuri == 0) - return 0; - DetectUricontentData *co = (DetectUricontentData *)m->ctx; /* see if we had a match */ len = det_ctx->mtcu.match[co->id].len; - if (len == 0) - return 0; + if (len == 0) { + SCLogDebug("We don't have match"); + SCReturnInt(0); + } #if 0 if (SCLogDebugEnabled()) { @@ -195,20 +249,26 @@ int DetectUricontentMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet printf("\' matched %" PRIu32 " time(s) at offsets: ", len); MpmMatch *tmpm = NULL; - for (tmpm = det_ctx->mtcu.match[co->id].top; tmpm != NULL; tmpm = tmpm->next) { + for (tmpm = det_ctx->mtcu.match[co->id].top; tmpm != NULL; + tmpm = tmpm->next) + { printf("%" PRIu32 " ", tmpm->offset); } printf("\n"); } #endif - return DoDetectUricontent(t, det_ctx, p, m, co); + SCReturnInt(DoDetectUricontent(t, det_ctx, p, m, co)); } -int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *contentstr) +/** + * \brief Setup the detecturicontent keyword data from the string defined in + * the rule set. + * \param contentstr Pointer to the string which has been defined in the rule + */ +DetectUricontentData *DoDetectUricontentSetup (char * contentstr) { DetectUricontentData *cd = NULL; - SigMatch *sm = NULL; char *temp = NULL; char *str = NULL; uint16_t len = 0; @@ -220,12 +280,12 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c if (strlen(temp) == 0) { free(temp); - return -1; + return NULL; } cd = malloc(sizeof(DetectUricontentData)); if (cd == NULL) { - printf("DetectContentSetup malloc failed\n"); + SCLogError(SC_ERR_MEM_ALLOC, "malloc failed"); goto error; } memset(cd,0,sizeof(DetectUricontentData)); @@ -237,7 +297,8 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c }; if (temp[pos] == '!') { - SCLogError(SC_ERR_NO_URICONTENT_NEGATION, "uricontent negation is not supported at this time. See bug #31."); + SCLogError(SC_ERR_NO_URICONTENT_NEGATION, "uricontent negation is not " + "supported at this time. See bug #31."); goto error; } @@ -254,14 +315,14 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c temp = NULL; len = strlen(str); - //printf("DetectUricontentSetup: \"%s\", len %" PRIu32 "\n", str, len); + SCLogDebug("\"%s\", len %" PRIu32 "", str, len); char converted = 0; { uint16_t i, x; uint8_t bin = 0, binstr[3] = "", 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; @@ -277,20 +338,21 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c str[i] == 'D' || str[i] == 'd' || 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++; if (binpos == 2) { - uint8_t c = strtol((char *)binstr, (char **) NULL, 16) & 0xFF; + uint8_t c = strtol((char *)binstr, (char **) NULL, + 16) & 0xFF; binpos = 0; str[x] = c; x++; converted = 1; } } else if (str[i] == ' ') { - // printf("space as part of binary string\n"); + SCLogDebug("space as part of binary string"); } } else { str[x] = str[i]; @@ -318,7 +380,7 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c if (cd->uricontent == NULL) { free(cd); free(str); - return -1; + return NULL;; } memcpy(cd->uricontent, str, len); @@ -329,6 +391,36 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c cd->distance = 0; cd->flags = 0; + free(str); + return cd; + +error: + free(str); + if (cd) free(cd); + return NULL; +} + +/** + * \brief Creates a SigMatch for the uricontent keyword being sent as argument, + * and appends it to the Signature(s). + * + * \param de_ctx Pointer to the detection engine context + * \param s Pointer to signature for the current Signature being parsed + * from the rules + * \param m Pointer to the head of the SigMatchs for the current rule +* being parsed + * \param contentstr Pointer to the string holding the keyword value + * + * \retval 0 on success, -1 on failure + */ +int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, + char *contentstr) +{ + SigMatch *sm = NULL; + DetectUricontentData *cd = DoDetectUricontentSetup(contentstr); + if (cd == NULL) + goto error; + /* Okay so far so good, lets get this into a SigMatch * and put it in the Signature. */ sm = SigMatchAlloc(); @@ -343,23 +435,650 @@ int DetectUricontentSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, c cd->id = de_ctx->uricontent_max_id; de_ctx->uricontent_max_id++; - free(str); + /* Flagged the signature as to scan the app layer data */ + s->flags |=SIG_FLAG_APPLAYER; + return 0; error: - free(str); - free(temp); if (cd) free(cd); - if (sm) free(sm); return -1; } +/** + * \brief Checks if the content sent as the argument, has a uricontent which + * has been provided in the rule. This match function matches the + * normalized http uri against the given rule using multi pattern + * scan/search algorithms. + * + * \param t Pointer to the tv for this detection module instance + * \param det_ctx Pointer to the detection engine thread context + * \param content Pointer to the uri content currently being matched + * \param content_len Content_len of the received uri content + * + * \retval 1 if the uri contents match; 0 no match + */ +int DoDetectAppLayerUricontentMatch (ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + uint8_t *content, uint16_t content_len) +{ + int ret = 0; + /* run the pattern matcher against the uri */ + if (det_ctx->sgh->mpm_uricontent_maxlen > content_len) { + SCLogDebug("not scanning as pkt payload is smaller than the " + "largest uricontent length we need to match"); + } else { + SCLogDebug("scan: (%p, maxlen %" PRIu32 ", sgh->sig_cnt " + "%" PRIu32 ")", det_ctx->sgh, det_ctx->sgh-> + mpm_uricontent_maxlen, det_ctx->sgh->sig_cnt); + + det_ctx->uris++; + + if (det_ctx->sgh->mpm_uricontent_maxlen == 1) det_ctx->pkts_uri_scanned1++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 2) det_ctx->pkts_uri_scanned2++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 3) det_ctx->pkts_uri_scanned3++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 4) det_ctx->pkts_uri_scanned4++; + else det_ctx->pkts_uri_scanned++; + + ret += UriPatternScan(tv, det_ctx, content, content_len); + + SCLogDebug("post scan: cnt %" PRIu32 ", searchable %" PRIu32 "", + ret, det_ctx->pmq.searchable); + if (det_ctx->pmq.searchable > 0) { + if (det_ctx->sgh->mpm_uricontent_maxlen == 1) det_ctx->pkts_uri_searched1++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 2) det_ctx->pkts_uri_searched2++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 3) det_ctx->pkts_uri_searched3++; + else if (det_ctx->sgh->mpm_uricontent_maxlen == 4) det_ctx->pkts_uri_searched4++; + else det_ctx->pkts_uri_searched++; + + ret += UriPatternMatch(tv, det_ctx, content, content_len); + + } + det_ctx->pmq.searchable = 0; + det_ctx->de_scanned_uri = TRUE; + } + 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 ret = 0; + int res = 0; + DetectUricontentData *co = (DetectUricontentData *)sm->ctx; + /* if we don't have a uri, don't bother scanning */ + if (det_ctx->de_have_httpuri == FALSE) { + SCLogDebug("We don't have uri"); + SCReturnInt(res); + } + + /* Check if we have scanned the URI already or not */ + if (det_ctx->de_scanned_uri == FALSE) { + SCMutexLock(&f->m); + TcpSession *ssn = (TcpSession *) f->protoctx; + if (ssn == NULL) { + SCLogDebug("no Tcp Session"); + det_ctx->de_have_httpuri = FALSE; + goto end; + } + + HtpState *htp_state = ssn->aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + SCLogDebug("no HTTP state"); + det_ctx->de_have_httpuri = FALSE; + goto end; + } + + htp_tx_t *tx = NULL; + list_iterator_reset(htp_state->recent_in_tx); + + while ((tx = list_iterator_next(htp_state->recent_in_tx)) != NULL) { + if (tx->request_uri_normalized == NULL) + continue; + + ret = DoDetectAppLayerUricontentMatch(tv, det_ctx, (uint8_t *) + bstr_ptr(tx->request_uri_normalized), + bstr_len(tx->request_uri_normalized)); + + if (ret > 0 && det_ctx->mtcu.match[co->id].len > 0) { + SCLogDebug("Match has been found in the received request and " + "given uricontent rule for s->id %"PRIu32"", s->id); + res = 1; + } + } + } else if (det_ctx->mtcu.match[co->id].len > 0) { + SCLogDebug("We have app layer URI match"); + res = 1; + } else { + SCLogDebug("We don't have app layer URI match"); + res = 0; + } + +end: + SCMutexUnlock(&f->m); + SCReturnInt(res); +} /* * TESTS + * UNITTTESTS */ -void HttpUriRegisterTests(void) { - /** none atm */ +#ifdef UNITTESTS + +#include "stream-tcp-reassemble.h" + +/** \test Test case where path traversal has been sent as a path string in the + * HTTP URL and normalized path string is checked */ +static int HTTPUriTest01(void) { + int result = 1; + Flow f; + uint8_t httpbuf1[] = "GET /../../images.gif HTTP/1.1\r\nHost: www.ExA" + "mPlE.cOM\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + int r = 0; + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START| + STREAM_EOF, httpbuf1, httplen1); + HtpState *htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0); + + if (htp_state->connp == NULL || tx->request_method_number != M_GET || + tx->request_protocol_number != HTTP_1_1) + { + printf("expected method GET and got %s: , expected protocol " + "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method), + bstr_tocstr(tx->request_protocol)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->hostname == NULL) || + (bstr_cmpc(tx->parsed_uri->hostname, "www.example.com") != 0)) + { + printf("expected www.example.com as hostname, but got: %s \n", + bstr_tocstr(tx->parsed_uri->hostname)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->path == NULL) || + (bstr_cmpc(tx->parsed_uri->path, "/images.gif") != 0)) + { + printf("expected /images.gif as path, but got: %s \n", + bstr_tocstr(tx->parsed_uri->path)); + result = 0; + goto end; + } + + +end: + return result; +} + +/** \test Test case where path traversal has been sent in special characters in + * HEX encoding in the HTTP URL and normalized path string is checked */ +static int HTTPUriTest02(void) { + int result = 1; + Flow f; + uint8_t httpbuf1[] = "GET /%2e%2e/images.gif HTTP/1.1\r\nHost: www.ExA" + "mPlE.cOM\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + int r = 0; + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START| + STREAM_EOF, httpbuf1, httplen1); + + HtpState *htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0); + + if (htp_state->connp == NULL || tx->request_method_number != M_GET || + tx->request_protocol_number != HTTP_1_1) + { + printf("expected method GET and got %s: , expected protocol " + "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method), + bstr_tocstr(tx->request_protocol)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->hostname == NULL) || + (bstr_cmpc(tx->parsed_uri->hostname, "www.example.com") != 0)) + { + printf("expected www.example.com as hostname, but got: %s \n", + bstr_tocstr(tx->parsed_uri->hostname)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->path == NULL) || + (bstr_cmpc(tx->parsed_uri->path, "/images.gif") != 0)) + { + printf("expected /images.gif as path, but got: %s \n", + bstr_tocstr(tx->parsed_uri->path)); + result = 0; + goto end; + } + + +end: + return result; +} + +/** \test Test case where NULL character has been sent in HEX encoding in the + * HTTP URL and normalized path string is checked */ +static int HTTPUriTest03(void) { + int result = 1; + Flow f; + uint8_t httpbuf1[] = "GET%00 /images.gif HTTP/1.1\r\nHost: www.ExA" + "mPlE.cOM\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + int r = 0; + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START| + STREAM_EOF, httpbuf1, httplen1); + + HtpState *htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0); + + if (htp_state->connp == NULL || tx->request_method_number != M_UNKNOWN || + tx->request_protocol_number != HTTP_1_1) + { + printf("expected method GET and got %s: , expected protocol " + "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method), + bstr_tocstr(tx->request_protocol)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->hostname == NULL) || + (bstr_cmpc(tx->parsed_uri->hostname, "www.example.com") != 0)) + { + printf("expected www.example.com as hostname, but got: %s \n", + bstr_tocstr(tx->parsed_uri->hostname)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->path == NULL) || + (bstr_cmpc(tx->parsed_uri->path, "/images.gif") != 0)) + { + printf("expected /images.gif as path, but got: %s \n", + bstr_tocstr(tx->parsed_uri->path)); + result = 0; + goto end; + } + +end: + return result; +} + + +/** \test Test case where self referencing directories request has been sent + * in the HTTP URL and normalized path string is checked */ +static int HTTPUriTest04(void) { + int result = 1; + Flow f; + uint8_t httpbuf1[] = "GET /./././images.gif HTTP/1.1\r\nHost: www.ExA" + "mPlE.cOM\r\n\r\n"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + TcpSession ssn; + int r = 0; + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START| + STREAM_EOF, httpbuf1, httplen1); + + HtpState *htp_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + htp_tx_t *tx = list_get(htp_state->connp->conn->transactions, 0); + + if (htp_state->connp == NULL || tx->request_method_number != M_GET || + tx->request_protocol_number != HTTP_1_1) + { + printf("expected method GET and got %s: , expected protocol " + "HTTP/1.1 and got %s \n", bstr_tocstr(tx->request_method), + bstr_tocstr(tx->request_protocol)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->hostname == NULL) || + (bstr_cmpc(tx->parsed_uri->hostname, "www.example.com") != 0)) + { + printf("expected www.example.com as hostname, but got: %s \n", + bstr_tocstr(tx->parsed_uri->hostname)); + result = 0; + goto end; + } + + if ((tx->parsed_uri->path == NULL) || + (bstr_cmpc(tx->parsed_uri->path, "/images.gif") != 0)) + { + printf("expected /images.gif as path, but got: %s \n", + bstr_tocstr(tx->parsed_uri->path)); + result = 0; + goto end; + } + + +end: + return result; } +/** + * \test Checks if a uricontent is registered in a Signature + */ +int DetectUriSigTest01(void) +{ + SigMatch *sm = NULL; + int result = 0; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx; + Signature *s = NULL; + + memset(&th_v, 0, sizeof(th_v)); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + s = de_ctx->sig_list = SigInit(de_ctx,"alert http any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"me\"; sid:1;)"); + if (s == NULL) { + goto end; + } + + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + sm = de_ctx->sig_list->match; + if (sm->type == DETECT_URICONTENT) { + result = 1; + } else { + result = 0; + } + + end: + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + return result; +} + +/** \test Check the signature working to alert when http_cookie is matched . */ +static int DetectUriSigTest02(void) { + int result = 0; + Flow f; + uint8_t httpbuf1[] = "POST /one 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; + + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + 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\"; sid:2;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"oisf\"; 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; + } + + HtpState *http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + 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 alerted, but it should not\n"); + goto end; + } + + result = 1; +end: + if (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + return result; +} + +/** \test Check the working of scan/search once per packet only in applayer + * match */ +static int DetectUriSigTest03(void) { + int result = 0; + Flow f; + uint8_t httpbuf1[] = "POST /one 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 */ + uint8_t httpbuf2[] = "POST /oneself HTTP/1.0\r\nUser-Agent: Mozilla/1.0\r\nCookie:" + " hellocatch\r\n\r\n"; + uint32_t httplen2 = sizeof(httpbuf2) - 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; + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; + + 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\"; sid:2;)"); + if (s == NULL) { + goto end; + } + + s = s->next = SigInit(de_ctx,"alert tcp any any -> any any (msg:" + "\" Test uricontent\"; " + "uricontent:\"self\"; 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); + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + HtpState *http_state = ssn.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (http_state == NULL) { + printf("no http state: "); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); + + 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 (de_ctx != NULL) SigCleanSignatures(de_ctx); + if (de_ctx != NULL) DetectEngineCtxFree(de_ctx); + + return result; +} + +#endif /* UNITTESTS */ + +void HttpUriRegisterTests(void) { +#ifdef UNITTESTS + UtRegisterTest("HTTPUriTest01", HTTPUriTest01, 1); + UtRegisterTest("HTTPUriTest02", HTTPUriTest02, 1); + UtRegisterTest("HTTPUriTest03", HTTPUriTest03, 1); + UtRegisterTest("HTTPUriTest04", HTTPUriTest04, 1); + UtRegisterTest("DetectUriSigTest01", DetectUriSigTest01, 1); + UtRegisterTest("DetectUriSigTest02", DetectUriSigTest02, 1); + UtRegisterTest("DetectUriSigTest03", DetectUriSigTest03, 1); +#endif /* UNITTESTS */ +} \ No newline at end of file diff --git a/src/detect.c b/src/detect.c index 1bfb4263cc..82bf68be29 100644 --- a/src/detect.c +++ b/src/detect.c @@ -553,8 +553,11 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh FlowSetIPOnlyFlag(p->flow, p->flowflags & FLOW_PKT_TOSERVER ? 1 : 0); } - /* we assume we don't have an uri when we start inspection */ - det_ctx->de_have_httpuri = 0; + /* we assume we have an uri when we start inspection */ + det_ctx->de_have_httpuri = TRUE; + + /* we don't scan the uri when we start inspection */ + det_ctx->de_scanned_uri = FALSE; det_ctx->sgh = SigMatchSignaturesGetSgh(th_v, de_ctx, det_ctx, p); /* if we didn't get a sig group head, we @@ -618,9 +621,18 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh /* filter out sigs that want pattern matches, but * have no matches */ if (!(det_ctx->pmq.sig_bitarray[(sig / 8)] & (1<<(sig % 8))) && - (s->flags & SIG_FLAG_MPM) && !(s->flags & SIG_FLAG_MPM_NEGCONTENT)) { - SCLogDebug("mpm sig without matches."); - continue; + (s->flags & SIG_FLAG_MPM) && !(s->flags & SIG_FLAG_MPM_NEGCONTENT)) { + /* If uri_ctx sigs are not scanned till now, we need to scan them + Once */ + if (det_ctx->sgh->flags & SIG_GROUP_HAVEURICONTENT) { + if (det_ctx->de_scanned_uri == TRUE) { + SCLogDebug("mpm sig without matches."); + continue; + } + } else { + SCLogDebug("mpm sig without matches."); + continue; + } } //printf("idx %" PRIu32 ", det_ctx->pmq.sig_id_array_cnt %" PRIu32 ", s->id %" PRIu32 " (MPM? %s)\n", idx, det_ctx->pmq.sig_id_array_cnt, s->id, s->flags & SIG_FLAG_MPM ? "TRUE":"FALSE"); @@ -697,7 +709,7 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh match = sigmatch_table[sm->type].Match(th_v, det_ctx, p, s, sm); } - if (match) { + if (match > 0) { /* okay, try the next match */ sm = sm->next; @@ -741,7 +753,7 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh match = sigmatch_table[sm->type].Match(th_v, det_ctx, p, s, sm); } - if (match) { + if (match > 0) { /* okay, try the next match */ sm = sm->next; @@ -2900,6 +2912,7 @@ void SigTableRegisterTests(void) { #ifdef UNITTESTS #include "flow-util.h" +#include "stream-tcp-reassemble.h" static const char *dummy_conf_string = "%YAML 1.1\n" @@ -3240,15 +3253,24 @@ static int SigTest06Real (int mpm_type) { Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; + Flow f; + TcpSession ssn; int result = 0; 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 = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { @@ -3273,6 +3295,13 @@ static int SigTest06Real (int mpm_type) { //PatternMatchPrepare(mpm_ctx, mpm_type); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 1) && PacketAlertCheck(&p, 2)) result = 1; @@ -3384,15 +3413,24 @@ static int SigTest08Real (int mpm_type) { Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; + Flow f; + TcpSession ssn; int result = 0; 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 = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { @@ -3417,6 +3455,13 @@ static int SigTest08Real (int mpm_type) { //PatternMatchPrepare(mpm_ctx, mpm_type); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx); + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 1) && PacketAlertCheck(&p, 2)) result = 1; @@ -3522,15 +3567,24 @@ static int SigTest10Real (int mpm_type) { Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; + Flow f; + TcpSession ssn; int result = 0; 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 = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { @@ -3555,6 +3609,13 @@ static int SigTest10Real (int mpm_type) { //PatternMatchPrepare(mpm_ctx, mpm_type); DetectEngineThreadCtxInit(&th_v, (void *)de_ctx,(void *)&det_ctx); + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, buf, buflen); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + SigMatchSignatures(&th_v, de_ctx, det_ctx, &p); if (PacketAlertCheck(&p, 1) && PacketAlertCheck(&p, 2)) result = 0; @@ -3587,15 +3648,24 @@ static int SigTest11Real (int mpm_type) { Packet p; ThreadVars th_v; DetectEngineThreadCtx *det_ctx; + Flow f; + TcpSession ssn; int result = 0; 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 = buf; p.payload_len = buflen; p.proto = IPPROTO_TCP; + StreamL7DataPtrInit(&ssn,StreamL7GetStorageSize()); + f.protoctx = (void *)&ssn; + p.flow = &f; + p.flowflags |= FLOW_PKT_TOSERVER; + ssn.alproto = ALPROTO_HTTP; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { diff --git a/src/detect.h b/src/detect.h index 9189dccb9b..eb194eb7a0 100644 --- a/src/detect.h +++ b/src/detect.h @@ -308,6 +308,9 @@ typedef struct DetectionEngineThreadCtx_ { /* http_uri stuff for uricontent */ char de_have_httpuri; + /* to indicate http_uri scanned or not */ + char de_scanned_uri; + /** pointer to the current mpm ctx that is stored * in a rule group head -- can be either a content * or uricontent ctx. */