From 06a65cb46061f0873daaeb136efb28ef913d4ec3 Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Tue, 31 Aug 2010 17:11:49 +0200 Subject: [PATCH] moving http_client_body logic to use it per transactions. Adding unittests --- src/app-layer-htp.c | 88 +++-- src/app-layer-htp.h | 7 +- src/detect-http-client-body.c | 515 +++++++++++++++++++++++-- src/detect-pcre.c | 704 +++++++++++++++++++++++++++++++--- src/util-mem.h | 34 +- 5 files changed, 1202 insertions(+), 146 deletions(-) diff --git a/src/app-layer-htp.c b/src/app-layer-htp.c index b5ba37ac21..f8742d780e 100644 --- a/src/app-layer-htp.c +++ b/src/app-layer-htp.c @@ -44,12 +44,19 @@ #include "app-layer-htp.h" #include "util-spm.h" -#include "util-unittest.h" #include "util-debug.h" #include "app-layer-htp.h" #include "util-time.h" #include +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "flow-util.h" + +#include "detect-engine.h" +#include "detect-engine-state.h" +#include "detect-parse.h" + #include "conf.h" /** Need a linked list in order to keep track of these */ @@ -146,10 +153,6 @@ static void *HTPStateAlloc(void) memset(s, 0x00, sizeof(HtpState)); - s->body.nchunks = 0; - s->body.operation = HTP_BODY_NONE; - s->body.pcre_flags = HTP_PCRE_NONE; - #ifdef DEBUG SCMutexLock(&htp_state_mem_lock); htp_state_memcnt++; @@ -176,15 +179,31 @@ void HTPStateFree(void *state) HtpState *s = (HtpState *)state; + /* Unset the body inspection */ + s->flags &=~ HTP_FLAG_NEW_BODY_SET; + /* free the connection parser memory used by HTP library */ if (s != NULL) { if (s->connp != NULL) { + size_t i; + /* free the list of body chunks */ + if (s->connp->conn != NULL) { + for (i = 0; i < list_size(s->connp->conn->transactions); i++) { + htp_tx_t *tx = (htp_tx_t *)list_get(s->connp->conn->transactions, i); + if (tx != NULL) { + + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); + if (htud != NULL) { + HtpBodyFree(&htud->body); + SCFree(htud); + } + htp_tx_set_user_data(tx, NULL); + + } + } + } htp_connp_destroy_all(s->connp); } - /* free the list of body chunks */ - if (s->body.nchunks > 0) { - HtpBodyFree(&s->body); - } } SCFree(s); @@ -669,27 +688,27 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d) "%"PRIu32"", hstate, d, d->data, (uint32_t)d->len); //PrintRawDataFp(stdout, d->data, d->len); + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(d->tx); + if (htud == NULL) { + htud = SCMalloc(sizeof(SCHtpTxUserData)); + if (htud == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + SCReturnInt(HOOK_OK); + } + memset(htud, 0, sizeof(SCHtpTxUserData)); + htud->body.operation = HTP_BODY_NONE; + htud->body.pcre_flags = HTP_PCRE_NONE; - /* If it has been inspected by pcre and there's no match, - * remove this chunks */ - if ( !(hstate->body.pcre_flags & HTP_PCRE_HAS_MATCH) && - (hstate->body.pcre_flags & HTP_PCRE_DONE)) - { - HtpBodyFree(&hstate->body); - } - - /* If its a new operation, remove the old data */ - if (hstate->body.operation == HTP_BODY_RESPONSE) { - HtpBodyFree(&hstate->body); - hstate->body.pcre_flags = HTP_PCRE_NONE; + /* Set the user data for handling body chunks on this transaction */ + htp_tx_set_user_data(d->tx, htud); } - hstate->body.operation = HTP_BODY_REQUEST; + htud->body.operation = HTP_BODY_REQUEST; - HtpBodyAppendChunk(&hstate->body, (uint8_t*)d->data, (uint32_t)d->len); - hstate->body.pcre_flags = HTP_PCRE_NONE; + HtpBodyAppendChunk(&htud->body, (uint8_t*)d->data, (uint32_t)d->len); + htud->body.pcre_flags = HTP_PCRE_NONE; if (SCLogDebugEnabled()) { - HtpBodyPrint(&hstate->body); + HtpBodyPrint(&htud->body); } /* set the new chunk flag */ @@ -766,12 +785,8 @@ static int HTPCallbackResponse(htp_connp_t *connp) { SCReturnInt(HOOK_ERROR); } - /* Free data when we have a response */ - if (hstate->body.nchunks > 0) - HtpBodyFree(&hstate->body); - - hstate->body.operation = HTP_BODY_RESPONSE; - hstate->body.pcre_flags = HTP_PCRE_NONE; + /* Unset the body inspection (if any) */ + hstate->flags &=~ HTP_FLAG_NEW_BODY_SET; /* remove obsolete transactions */ size_t idx; @@ -780,6 +795,14 @@ static int HTPCallbackResponse(htp_connp_t *connp) { if (tx == NULL) continue; + /* This will remove obsolete body chunks */ + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); + if (htud != NULL) { + HtpBodyFree(&htud->body); + htp_tx_set_user_data(tx, NULL); + SCFree(htud); + } + htp_tx_destroy(tx); } @@ -1807,6 +1830,7 @@ int HTPParserConfigTest03(void) { int result = 1; Flow f; + FLOW_INITIALIZE(&f); uint8_t httpbuf1[] = "POST / HTTP/1.0\r\nUser-Agent: Victor/1.0\r\n\r\nPost" " Data is c0oL!"; uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ @@ -1908,8 +1932,10 @@ end: StreamTcpFreeConfig(TRUE); if (htp_state != NULL) HTPStateFree(htp_state); + FLOW_DESTROY(&f); return result; } + #endif /* UNITTESTS */ /** diff --git a/src/app-layer-htp.h b/src/app-layer-htp.h index fc15158b7c..67572fe9f2 100644 --- a/src/app-layer-htp.h +++ b/src/app-layer-htp.h @@ -82,11 +82,16 @@ typedef struct HtpBody_ { any pcre (so we can free() without waiting) */ } HtpBody; +/** Now the Body Chunks will be stored per transaction, at + * the tx user data */ +typedef struct SCHtpTxUserData_ { + HtpBody body; /**< Body of the request (if any) */ +} SCHtpTxUserData; + typedef struct HtpState_ { htp_connp_t *connp; /**< Connection parser structure for each connection */ - HtpBody body; /**< Body of the request (if any) */ // size_t new_in_tx_index; /**< Index to indicate that after this we have // new requests to log */ uint8_t flags; diff --git a/src/detect-http-client-body.c b/src/detect-http-client-body.c index 8c1bfb880b..be4f933851 100644 --- a/src/detect-http-client-body.c +++ b/src/detect-http-client-body.c @@ -102,50 +102,66 @@ int DetectHttpClientBodyMatch(ThreadVars *t, DetectEngineThreadCtx *det_ctx, goto end; } - if (htp_state->body.nchunks == 0) { - SCLogDebug("No http chunks to inspect"); - goto end; - } else { - HtpBodyChunk *cur = htp_state->body.first; - /* no chunks?!! get out of here */ - if (cur == NULL) { + htp_tx_t *tx = NULL; + size_t idx = 0; + + for (idx = 0;//hs->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) + continue; + + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); + if (htud == NULL) + continue; + + HtpBodyChunk *cur = htud->body.first; + + if (htud->body.nchunks == 0) { SCLogDebug("No http chunks to inspect"); goto end; - } + } else { + /* no chunks?!! get out of here */ + if (cur == NULL) { + SCLogDebug("No http chunks to inspect"); + goto end; + } - /* this applies only for the client request body like the keyword name says */ - if (htp_state->body.operation != HTP_BODY_REQUEST) { - SCLogDebug("htp chunk not a request chunk"); - goto end; - } + /* this applies only for the client request body like the keyword name says */ + if (htud->body.operation != HTP_BODY_REQUEST) { + SCLogDebug("htp chunk not a request chunk"); + goto end; + } - /* this is not how we do it now. We can rather hold the PM state from - * the previous chunk that was matched, and continue right from where - * we left off. We need to devise a scheme to do that, not just for - * this keyword, but other keywords need it as well */ - uint8_t *chunks_buffer = NULL; - uint32_t total_chunks_len = 0; - /* club all the chunks into one whole buffer and call the SPM on the buffer */ - while (cur != NULL) { - total_chunks_len += cur->len; - if ( (chunks_buffer = SCRealloc(chunks_buffer, total_chunks_len)) == NULL) { - return 0; + /* this is not how we do it now. We can rather hold the PM state from + * the previous chunk that was matched, and continue right from where + * we left off. We need to devise a scheme to do that, not just for + * this keyword, but other keywords need it as well */ + uint8_t *chunks_buffer = NULL; + uint32_t total_chunks_len = 0; + /* club all the chunks into one whole buffer and call the SPM on the buffer */ + while (cur != NULL) { + total_chunks_len += cur->len; + if ( (chunks_buffer = SCRealloc(chunks_buffer, total_chunks_len)) == NULL) { + return 0; + } + memcpy(chunks_buffer + total_chunks_len - cur->len, cur->data, cur->len); + cur = cur->next; } - memcpy(chunks_buffer + total_chunks_len - cur->len, cur->data, cur->len); - cur = cur->next; - } - /* call the case insensitive version if nocase has been specified in the sig */ - if (hcbd->flags & DETECT_AL_HTTP_CLIENT_BODY_NOCASE) { - result = (BoyerMooreNocase(hcbd->content, hcbd->content_len, chunks_buffer, - total_chunks_len, hcbd->bm_ctx->bmGs, - hcbd->bm_ctx->bmBc) != NULL); - /* call the case sensitive version if nocase has been specified in the sig */ - } else { - result = (BoyerMoore(hcbd->content, hcbd->content_len, chunks_buffer, - total_chunks_len, hcbd->bm_ctx->bmGs, - hcbd->bm_ctx->bmBc) != NULL); + /* call the case insensitive version if nocase has been specified in the sig */ + if (hcbd->flags & DETECT_AL_HTTP_CLIENT_BODY_NOCASE) { + result = (BoyerMooreNocase(hcbd->content, hcbd->content_len, chunks_buffer, + total_chunks_len, hcbd->bm_ctx->bmGs, + hcbd->bm_ctx->bmBc) != NULL); + /* call the case sensitive version if nocase has been specified in the sig */ + } else { + result = (BoyerMoore(hcbd->content, hcbd->content_len, chunks_buffer, + total_chunks_len, hcbd->bm_ctx->bmGs, + hcbd->bm_ctx->bmBc) != NULL); + } + SCFree(chunks_buffer); } - SCFree(chunks_buffer); } SCMutexUnlock(&f->m); @@ -1320,6 +1336,427 @@ end: return result; } +/** \test multiple http transactions and body chunks of request handling */ +static int DetectHttpClientBodyTest14(void) { + int result = 0; + Signature *s = NULL; + DetectEngineThreadCtx *det_ctx = NULL; + ThreadVars th_v; + Flow f; + TcpSession ssn; + Packet *p = NULL; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; + uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; + uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; + uint8_t httpbuf4[] = "Body one!!"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ + uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ + uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; + uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; + uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; + uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ + uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ + uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.src.family = AF_INET; + f.dst.family = AF_INET; + + p->flow = &f; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flowflags |= FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)"); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)"); + if (s == NULL) { + printf("sig2 parse failed: "); + 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); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (2): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("signature matched, but shouldn't have: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); + if (r != 0) { + printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (5): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); + if (r != 0) { + printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { + printf("sig 1 alerted (request 2, chunk 6): "); + goto end; + } + p->alerts.cnt = 0; + + SCLogDebug("sending data chunk 7"); + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); + if (r != 0) { + printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 2))) { + printf("signature 2 didn't match, but should have: "); + goto end; + } + p->alerts.cnt = 0; + + HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + if (list_size(htp_state->connp->conn->transactions) != 2) { + printf("The http app layer doesn't have 2 transactions, but it should: "); + goto end; + } + + result = 1; +end: + if (det_ctx != NULL) { + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + } + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + } + + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \test multiple http transactions and body chunks of request handling */ +static int DetectHttpClientBodyTest15(void) { + int result = 0; + Signature *s = NULL; + DetectEngineThreadCtx *det_ctx = NULL; + ThreadVars th_v; + Flow f; + TcpSession ssn; + Packet *p = NULL; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; + uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; + uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; + uint8_t httpbuf4[] = "Body one!!"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ + uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ + uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; + uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; + uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; + uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ + uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ + uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.src.family = AF_INET; + f.dst.family = AF_INET; + + p->flow = &f; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flowflags |= FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"one\"; http_client_body; sid:1; rev:1;)"); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; content:\"two\"; http_client_body; sid:2; rev:1;)"); + if (s == NULL) { + printf("sig2 parse failed: "); + 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); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (2): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("signature matched, but shouldn't have: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); + if (r != 0) { + printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (5): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); + if (r != 0) { + printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { + printf("sig 1 alerted (request 2, chunk 6): "); + goto end; + } + p->alerts.cnt = 0; + + SCLogDebug("sending data chunk 7"); + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); + if (r != 0) { + printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 2))) { + printf("signature 2 didn't match, but should have: "); + goto end; + } + p->alerts.cnt = 0; + + HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + /* hardcoded check of the transactions and it's client body chunks */ + if (list_size(htp_state->connp->conn->transactions) != 2) { + printf("The http app layer doesn't have 2 transactions, but it should: "); + goto end; + } + + htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); + htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); + + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); + + HtpBodyChunk *cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); + + cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + result = 1; +end: + if (det_ctx != NULL) { + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + } + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + } + + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + + #endif /* UNITTESTS */ void DetectHttpClientBodyRegisterTests(void) @@ -1338,6 +1775,8 @@ void DetectHttpClientBodyRegisterTests(void) UtRegisterTest("DetectHttpClientBodyTest11", DetectHttpClientBodyTest11, 1); UtRegisterTest("DetectHttpClientBodyTest12", DetectHttpClientBodyTest12, 1); UtRegisterTest("DetectHttpClientBodyTest13", DetectHttpClientBodyTest13, 1); + UtRegisterTest("DetectHttpClientBodyTest14", DetectHttpClientBodyTest14, 1); + UtRegisterTest("DetectHttpClientBodyTest15", DetectHttpClientBodyTest15, 1); #endif /* UNITTESTS */ return; diff --git a/src/detect-pcre.c b/src/detect-pcre.c index fd20039838..d3005b6ab2 100644 --- a/src/detect-pcre.c +++ b/src/detect-pcre.c @@ -567,60 +567,77 @@ int DetectPcreALDoMatch(DetectEngineThreadCtx *det_ctx, Signature *s, SigMatch * if (htp_state == NULL) { SCLogDebug("No htp state, no match at http body data"); goto unlock; - } else if (htp_state->body.nchunks == 0) { - SCLogDebug("No body data to inspect"); - goto unlock; - } else { - pcreret = 0; - int wspace[255]; - int flags = PCRE_PARTIAL; + } + + htp_tx_t *tx = NULL; + size_t idx = 0; + + for (idx = 0;//hs->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) + continue; - HtpBodyChunk *cur = htp_state->body.first; - if (cur == NULL) { - SCLogDebug("No body chunks to inspect"); + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(tx); + if (htud == NULL) + continue; + + HtpBodyChunk *cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data to inspect"); goto unlock; - } - htp_state->body.pcre_flags |= HTP_PCRE_DONE; + } else { + pcreret = 0; + int wspace[255]; + int flags = PCRE_PARTIAL; - while (cur != NULL) { - if (SCLogDebugEnabled()) { - printf("\n"); - PrintRawUriFp(stdout, (uint8_t*)cur->data, cur->len); - printf("\n"); + if (cur == NULL) { + SCLogDebug("No body chunks to inspect"); + goto unlock; } - pcreret = pcre_dfa_exec(pe->re, NULL, (char*)cur->data, cur->len, 0, - flags|PCRE_DFA_SHORTEST, ov, MAX_SUBSTRINGS, - wspace, MAX_SUBSTRINGS); - cur = cur->next; - - SCLogDebug("Pcre Ret %d", pcreret); - switch (pcreret) { - case PCRE_ERROR_PARTIAL: - /* make pcre to use the working space of the last partial - * match, (match over multiple chunks) - */ - SCLogDebug("partial match"); - flags |= PCRE_DFA_RESTART; - htp_state->body.pcre_flags |= HTP_PCRE_HAS_MATCH; - break; - case PCRE_ERROR_NOMATCH: - SCLogDebug("no match"); - flags = PCRE_PARTIAL; - break; - case 0: - SCLogDebug("Perfect Match!"); - ret = 1; - goto unlock; - break; - default: - if (pcreret > 0) { - SCLogDebug("Match with captured data"); + htud->body.pcre_flags |= HTP_PCRE_DONE; + + while (cur != NULL) { + if (SCLogDebugEnabled()) { + printf("\n"); + PrintRawUriFp(stdout, (uint8_t*)cur->data, cur->len); + printf("\n"); + } + pcreret = pcre_dfa_exec(pe->re, NULL, (char*)cur->data, cur->len, 0, + flags|PCRE_DFA_SHORTEST, ov, MAX_SUBSTRINGS, + wspace, MAX_SUBSTRINGS); + cur = cur->next; + + SCLogDebug("Pcre Ret %d", pcreret); + switch (pcreret) { + case PCRE_ERROR_PARTIAL: + /* make pcre to use the working space of the last partial + * match, (match over multiple chunks) + */ + SCLogDebug("partial match"); + flags |= PCRE_DFA_RESTART; + htud->body.pcre_flags |= HTP_PCRE_HAS_MATCH; + break; + case PCRE_ERROR_NOMATCH: + SCLogDebug("no match"); + flags = PCRE_PARTIAL; + break; + case 0: + SCLogDebug("Perfect Match!"); ret = 1; - } else { - SCLogDebug("No match, pcre failed"); - ret = 0; - } - goto unlock; + goto unlock; + break; + default: + if (pcreret > 0) { + SCLogDebug("Match with captured data"); + ret = 1; + } else { + SCLogDebug("No match, pcre failed"); + ret = 0; + } + goto unlock; + } } } } @@ -2189,13 +2206,6 @@ static int DetectPcreModifPTest05(void) { /* do detect for p1 */ SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); - r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); - if (r != 0) { - printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); - result = 0; - goto end; - } - HtpState *http_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; if (http_state == NULL) { printf("no http state: "); @@ -2203,9 +2213,6 @@ static int DetectPcreModifPTest05(void) { goto end; } - /* do detect for p2 */ - SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); - if (!(PacketAlertCheck(p1, 1))) { printf("sid 1 didn't match on p1 but should have: "); goto end; @@ -2217,6 +2224,16 @@ static int DetectPcreModifPTest05(void) { goto end; } + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + /* do detect for p2 */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + if ((PacketAlertCheck(p2, 1))) { printf("sid 1 did match on p2 but should have: "); goto end; @@ -2842,6 +2859,572 @@ end: return result; } +/** \test Test tracking of body chunks per transactions (on requests) + */ +static int DetectPcreTxBodyChunksTest01(void) { + int result = 0; + Flow f; + TcpSession ssn; + Packet *p = NULL; + uint8_t httpbuf1[] = "GET / HTTP/1.1\r\n"; + uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; + uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; + uint8_t httpbuf4[] = "Body one!!"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ + uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ + uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; + uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; + uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; + uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ + uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ + uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.src.family = AF_INET; + f.dst.family = AF_INET; + + p->flow = &f; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flowflags |= FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + AppLayerHtpEnableRequestBodyCallback(); + + int r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1); + if (r != 0) { + printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); + if (r != 0) { + printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); + if (r != 0) { + printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); + if (r != 0) { + printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* Now we should have 2 transactions, each with it's own list + * of request body chunks (let's test it) */ + + HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + /* hardcoded check of the transactions and it's client body chunks */ + if (list_size(htp_state->connp->conn->transactions) != 2) { + printf("The http app layer doesn't have 2 transactions, but it should: "); + goto end; + } + + htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); + htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); + + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); + if (htud == NULL) { + printf("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + HtpBodyChunk *cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); + + cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + FlowL7DataPtrFree(&f); + + result = 1; +end: + + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \test test pcre P modifier with multiple pipelined http transactions */ +static int DetectPcreTxBodyChunksTest02(void) { + int result = 0; + Signature *s = NULL; + DetectEngineThreadCtx *det_ctx = NULL; + ThreadVars th_v; + Flow f; + TcpSession ssn; + Packet *p = NULL; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; + uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; + uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; + uint8_t httpbuf4[] = "Body one!!"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ + uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ + uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; + uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; + uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; + uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ + uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ + uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.src.family = AF_INET; + f.dst.family = AF_INET; + + p->flow = &f; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flowflags |= FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); + if (s == NULL) { + printf("sig2 parse failed: "); + 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); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (2): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("signature matched, but shouldn't have: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); + if (r != 0) { + printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (5): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); + if (r != 0) { + printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { + printf("sig 1 alerted (request 2, chunk 6): "); + goto end; + } + p->alerts.cnt = 0; + + SCLogDebug("sending data chunk 7"); + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); + if (r != 0) { + printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 2))) { + printf("signature 2 didn't match, but should have: "); + goto end; + } + p->alerts.cnt = 0; + + HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + /* hardcoded check of the transactions and it's client body chunks */ + if (list_size(htp_state->connp->conn->transactions) != 2) { + printf("The http app layer doesn't have 2 transactions, but it should: "); + goto end; + } + + htp_tx_t *t1 = list_get(htp_state->connp->conn->transactions, 0); + htp_tx_t *t2 = list_get(htp_state->connp->conn->transactions, 1); + + SCHtpTxUserData *htud = (SCHtpTxUserData *) htp_tx_get_user_data(t1); + + HtpBodyChunk *cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body one!!", strlen("Body one!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + htud = (SCHtpTxUserData *) htp_tx_get_user_data(t2); + + cur = htud->body.first; + if (htud->body.nchunks == 0) { + SCLogDebug("No body data in t1 (it should be removed only when the tx is destroyed): "); + goto end; + } + + if (memcmp(cur->data, "Body two!!", strlen("Body two!!")) != 0) { + SCLogDebug("Body data in t1 is not correctly set: "); + goto end; + } + + result = 1; +end: + if (det_ctx != NULL) { + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + } + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + } + + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + +/** \test multiple http transactions and body chunks of request handling */ +static int DetectPcreTxBodyChunksTest03(void) { + int result = 0; + Signature *s = NULL; + DetectEngineThreadCtx *det_ctx = NULL; + ThreadVars th_v; + Flow f; + TcpSession ssn; + Packet *p = NULL; + uint8_t httpbuf1[] = "POST / HTTP/1.1\r\n"; + uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\nContent-Length: 10\r\n"; + uint8_t httpbuf3[] = "Cookie: dummy\r\n\r\n"; + uint8_t httpbuf4[] = "Body one!!"; + uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */ + uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */ + uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */ + uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */ + uint8_t httpbuf5[] = "GET /?var=val HTTP/1.1\r\n"; + uint8_t httpbuf6[] = "User-Agent: Firefox/1.0\r\n"; + uint8_t httpbuf7[] = "Cookie: dummy2\r\nContent-Length: 10\r\n\r\nBody two!!"; + uint32_t httplen5 = sizeof(httpbuf5) - 1; /* minus the \0 */ + uint32_t httplen6 = sizeof(httpbuf6) - 1; /* minus the \0 */ + uint32_t httplen7 = sizeof(httpbuf7) - 1; /* minus the \0 */ + + memset(&th_v, 0, sizeof(th_v)); + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + p = UTHBuildPacket(NULL, 0, IPPROTO_TCP); + + FLOW_INITIALIZE(&f); + f.protoctx = (void *)&ssn; + f.proto = IPPROTO_TCP; + f.src.family = AF_INET; + f.dst.family = AF_INET; + + p->flow = &f; + p->flowflags |= FLOW_PKT_TOSERVER; + p->flowflags |= FLOW_PKT_ESTABLISHED; + f.alproto = ALPROTO_HTTP; + + StreamTcpInitConfig(TRUE); + FlowL7DataPtrInit(&f); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; pcre:\"/one/P\"; sid:1; rev:1;)"); + if (s == NULL) { + printf("sig parse failed: "); + goto end; + } + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; pcre:\"/two/P\"; sid:2; rev:1;)"); + if (s == NULL) { + printf("sig2 parse failed: "); + 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); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2); + if (r != 0) { + printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (2): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3); + if (r != 0) { + printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("signature matched, but shouldn't have: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4); + if (r != 0) { + printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r); + result = 0; + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 1))) { + printf("sig 1 didn't alert: "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5); + if (r != 0) { + printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (PacketAlertCheck(p, 1)) { + printf("sig 1 alerted (5): "); + goto end; + } + p->alerts.cnt = 0; + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6); + if (r != 0) { + printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) { + printf("sig 1 alerted (request 2, chunk 6): "); + goto end; + } + p->alerts.cnt = 0; + + SCLogDebug("sending data chunk 7"); + + r = AppLayerParse(&f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7); + if (r != 0) { + printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r); + goto end; + } + + /* do detect */ + SigMatchSignatures(&th_v, de_ctx, det_ctx, p); + if (!(PacketAlertCheck(p, 2))) { + printf("signature 2 didn't match, but should have: "); + goto end; + } + p->alerts.cnt = 0; + + HtpState *htp_state = f.aldata[AlpGetStateIdx(ALPROTO_HTTP)]; + if (htp_state == NULL) { + printf("no http state: "); + result = 0; + goto end; + } + + if (list_size(htp_state->connp->conn->transactions) != 2) { + printf("The http app layer doesn't have 2 transactions, but it should: "); + goto end; + } + + result = 1; +end: + if (det_ctx != NULL) { + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + } + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + DetectEngineCtxFree(de_ctx); + } + + FlowL7DataPtrFree(&f); + StreamTcpFreeConfig(TRUE); + FLOW_DESTROY(&f); + UTHFreePacket(p); + return result; +} + #endif /* UNITTESTS */ /** @@ -2880,6 +3463,9 @@ void DetectPcreRegisterTests(void) { UtRegisterTest("DetectPcreTestSig12 -- negated Method modifier", DetectPcreTestSig12, 1); UtRegisterTest("DetectPcreTestSig13 -- Header modifier", DetectPcreTestSig13, 1); UtRegisterTest("DetectPcreTestSig14 -- negated Header modifier", DetectPcreTestSig14, 1); + UtRegisterTest("DetectPcreTxBodyChunksTest01", DetectPcreTxBodyChunksTest01, 1); + UtRegisterTest("DetectPcreTxBodyChunksTest02 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest02, 1); + UtRegisterTest("DetectPcreTxBodyChunksTest03 -- modifier P, body chunks per tx", DetectPcreTxBodyChunksTest03, 1); #endif /* UNITTESTS */ } diff --git a/src/util-mem.h b/src/util-mem.h index d2d4da9d4c..ad7439beec 100644 --- a/src/util-mem.h +++ b/src/util-mem.h @@ -52,7 +52,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = malloc(a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -62,7 +62,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); global_mem += a; \ if (print_mem_flag == 1) \ SCLogInfo("SCMalloc return at %p of size %"PRIdMAX, \ - ptrmem, (intmax_t)a); \ + ptrmem, (size_t)a); \ \ (void*)ptrmem; \ }) @@ -75,7 +75,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = realloc(x, a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -85,7 +85,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); global_mem += a; \ if (print_mem_flag == 1) \ SCLogInfo("SCRealloc return at %p (old:%p) of size %"PRIdMAX, \ - ptrmem, x, (intmax_t)a); \ + ptrmem, x, (size_t)a); \ \ (void*)ptrmem; \ }) @@ -98,7 +98,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = calloc(nm, a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -107,8 +107,8 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); \ global_mem += a*nm; \ if (print_mem_flag == 1) \ - SCLogInfo("SCCalloc return at %p of size %"PRIdMAX" nm %"PRIdMAX, \ - ptrmem, (intmax_t)a, (intmax_t)nm); \ + SCLogInfo("SCCalloc return at %p of size %Zu nm %"PRIdMAX, \ + ptrmem, (size_t)a, (size_t)nm); \ \ (void*)ptrmem; \ }) @@ -117,12 +117,12 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); char *ptrmem = NULL; \ extern size_t global_mem; \ extern uint8_t print_mem_flag; \ - size_t len = strlen(a); \ \ ptrmem = strdup(a); \ - if (ptrmem == NULL && len > 0) { \ + if (ptrmem == NULL) { \ + size_t len = strlen(a); \ SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \ - "to allocate %"PRIu64" bytes", strerror(errno), (intmax_t)len); \ + "to allocate %"PRIuMAX" bytes", strerror(errno), (size_t)len); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -132,7 +132,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); global_mem += len; \ if (print_mem_flag == 1) \ SCLogInfo("SCStrdup return at %p of size %"PRIdMAX, \ - ptrmem, (intmax_t)len); \ + ptrmem, (size_t)len); \ \ (void*)ptrmem; \ }) @@ -160,7 +160,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = malloc(a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCMalloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -175,7 +175,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = realloc(x, a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCRealloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -190,7 +190,7 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); ptrmem = calloc(nm, a); \ if (ptrmem == NULL && a > 0) { \ SCLogError(SC_ERR_MEM_ALLOC, "SCCalloc failed: %s, while trying " \ - "to allocate %"PRIdMAX" bytes", strerror(errno), (intmax_t)a); \ + "to allocate %Zu bytes", strerror(errno), (size_t)a); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \ @@ -201,12 +201,12 @@ SC_ATOMIC_EXTERN(unsigned int, engine_stage); #define SCStrdup(a) ({ \ char *ptrmem = NULL; \ - size_t len = strlen(a); \ \ ptrmem = strdup(a); \ - if (ptrmem == NULL && len > 0) { \ + if (ptrmem == NULL) { \ + size_t len = strlen(a); \ SCLogError(SC_ERR_MEM_ALLOC, "SCStrdup failed: %s, while trying " \ - "to allocate %"PRIu64" bytes", strerror(errno), (intmax_t)len); \ + "to allocate %"PRIuMAX" bytes", strerror(errno), (size_t)len); \ if (SC_ATOMIC_GET(engine_stage) == SURICATA_INIT) {\ SCLogError(SC_ERR_FATAL, "Out of memory. The engine cannot be initialized. Exiting..."); \ exit(EXIT_FAILURE); \