You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-engine-state.c

1019 lines
28 KiB
C

/* Copyright (C) 2007-2010 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Victor Julien <victor@inliniac.net>
*/
#include "suricata-common.h"
#include "decode.h"
#include "detect.h"
#include "detect-engine.h"
#include "detect-parse.h"
#include "detect-engine-state.h"
#include "detect-engine-uri.h"
#include "detect-engine-dcepayload.h"
#include "stream-tcp.h"
#include "stream-tcp-private.h"
#include "stream-tcp-reassemble.h"
#include "app-layer-parser.h"
#include "app-layer-protos.h"
#include "app-layer-htp.h"
#include "util-unittest.h"
#include "util-profiling.h"
#define CASE_CODE(E) case E: return #E
const char *DeStateMatchResultToString(DeStateMatchResult res)
{
switch (res) {
CASE_CODE (DE_STATE_MATCH_FULL);
CASE_CODE (DE_STATE_MATCH_PARTIAL);
CASE_CODE (DE_STATE_MATCH_STORED);
CASE_CODE (DE_STATE_MATCH_NEW);
}
return NULL;
}
/**
* \brief Alloc a DeStateStore object
* \retval d alloc'd object
*/
DeStateStore *DeStateStoreAlloc(void) {
SCEnter();
DeStateStore *d = SCMalloc(sizeof(DeStateStore));
if (d == NULL) {
SCReturnPtr(NULL, "DeStateStore");
}
memset(d, 0x00, sizeof(DeStateStore));
SCReturnPtr(d, "DeStateStore");
}
/**
* \brief free a DeStateStore object (recursively)
* \param store DeStateStore object to free
*/
void DeStateStoreFree(DeStateStore *store) {
SCEnter();
if (store == NULL) {
SCReturn;
}
if (store->next != NULL) {
DeStateStoreFree(store->next);
}
SCFree(store);
SCReturn;
}
/**
* \brief Alloc a DetectEngineState object
* \param d alloc'd object
*/
DetectEngineState *DetectEngineStateAlloc(void) {
SCEnter();
DetectEngineState *d = SCMalloc(sizeof(DetectEngineState));
if (d == NULL) {
SCReturnPtr(NULL, "DetectEngineState");
}
memset(d, 0x00, sizeof(DetectEngineState));
SCMutexInit(&d->m, NULL);
SCReturnPtr(d, "DetectEngineState");
}
/**
* \brief Free a DetectEngineState object
* \param state DetectEngineState object to free
*/
void DetectEngineStateFree(DetectEngineState *state) {
if (state == NULL)
return;
if (state->head != NULL) {
DeStateStoreFree(state->head);
}
SCMutexDestroy(&state->m);
SCFree(state);
}
/**
* \brief reset a DetectEngineState state
* \param state LOCKED state
*/
void DetectEngineStateReset(DetectEngineState *state) {
SCEnter();
if (state == NULL) {
SCReturn;
}
SCMutexLock(&state->m);
if (state->head != NULL) {
DeStateStoreFree(state->head);
}
state->head = NULL;
state->tail = NULL;
state->cnt = 0;
SCMutexUnlock(&state->m);
SCReturn;
}
/**
* \brief update the transaction id
* \retval 2 current transaction done, new available
* \retval 1 current transaction done, no new (yet)
* \retval 0 current transaction is not done yet
*/
int DeStateUpdateInspectTransactionId(Flow *f) {
SCEnter();
int r = 0;
SCMutexLock(&f->m);
r = AppLayerTransactionUpdateInspectId(f);
SCMutexUnlock(&f->m);
SCReturnInt(r);
}
/**
* \brief Append a signature to the detect engine state
*
* \param state the detect engine state
* \param s signature
* \param sm sigmatch
* \param uri did uri already match (if any)
* \param dce did dce already match (if any)
*/
static void DeStateSignatureAppend(DetectEngineState *state, Signature *s, SigMatch *sm, char uri, char dce) {
DeStateStore *store = state->tail;
if (store == NULL) {
store = DeStateStoreAlloc();
if (store != NULL) {
state->head = store;
state->tail = store;
}
} else {
if ((state->cnt % DE_STATE_CHUNK_SIZE) == 0) {
store = DeStateStoreAlloc();
if (store != NULL) {
state->tail->next = store;
state->tail = store;
}
} else {
store = state->tail;
}
}
if (store == NULL) {
return;
}
SigIntId idx = state->cnt % DE_STATE_CHUNK_SIZE;
store->store[idx].sid = s->num;
store->store[idx].flags = 0;
if (uri) {
store->store[idx].flags |= DE_STATE_FLAG_URI_MATCH;
}
if (dce) {
store->store[idx].flags |= DE_STATE_FLAG_DCE_MATCH;
}
store->store[idx].nm = sm;
state->cnt++;
SCLogDebug("store %p idx %"PRIuMAX" cnt %"PRIuMAX" sig id %"PRIuMAX"",
store, (uintmax_t)idx, (uintmax_t)state->cnt,
(uintmax_t)store->store[idx].sid);
}
/*
on first detection run:
for each
(1) app layer signature
(2a) at least one app layer sm match OR
(2b) content/uri match AND other app layer sigmatches present
Cases:
multiple app layer sm's
content + uricontent
content + app layer sm
uricontent + app layer sm
*/
/** \brief Check if a flow already contains a flow detect state
* \retval 1 has state
* \retval 0 has no state
*/
int DeStateFlowHasState(Flow *f) {
SCEnter();
int r = 0;
SCMutexLock(&f->m);
if (f->de_state == NULL || f->de_state->cnt == 0)
r = 0;
else
r = 1;
SCMutexUnlock(&f->m);
SCReturnInt(r);
}
/** \brief Match app layer sig list against state. Set up state for non matches
* and partial matches.
* \retval 1 match
* \retval 0 no or partial match
*/
int DeStateDetectStartDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Signature *s, Flow *f, uint8_t flags,
void *alstate, uint16_t alproto)
{
SCEnter();
SigMatch *sm = s->amatch;
int match = 0;
int r = 0;
char umatch = 0;
char uinspect = 0;
char dmatch = 0;
char dinspect = 0;
char appinspect = 0;
char appmatch = 0;
if (alstate == NULL) {
SCReturnInt(0);
}
SCLogDebug("s->id %"PRIu32, s->id);
/* Check the uricontent keywords here. */
if (alproto == ALPROTO_HTTP && (flags & STREAM_TOSERVER)) {
if (s->umatch != NULL) {
uinspect = 1;
SCLogDebug("inspecting uri");
if (DetectEngineInspectPacketUris(de_ctx, det_ctx, s, f,
flags, alstate) == 1)
{
SCLogDebug("uri matched");
umatch = 1;
} else {
SCLogDebug("uri inspected but no match");
}
}
} else if (alproto == ALPROTO_DCERPC) {
if (s->dmatch != NULL) {
dinspect = 1;
SCLogDebug("inspecting dce payload");
if (DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
flags, alstate) == 1)
{
SCLogDebug("dce payload matched");
dmatch = 1;
} else {
SCLogDebug("dce payload inspected but no match");
}
}
}
appinspect = uinspect + dinspect;
appmatch = umatch + dmatch;
if (s->amatch != NULL) {
for ( ; sm != NULL; sm = sm->next) {
SCLogDebug("sm %p, sm->next %p", sm, sm->next);
if (sigmatch_table[sm->type].AppLayerMatch != NULL &&
alproto == sigmatch_table[sm->type].alproto)
{
match = sigmatch_table[sm->type].AppLayerMatch(tv, det_ctx, f,
flags, alstate, s, sm);
if (match == 0) {
break;
} else if (sm->next == NULL) {
sm = NULL; /* set to NULL as we have a match */
if (!appinspect || (appinspect == appmatch)) {
r = 1;
}
break;
}
}
}
} else {
if (appinspect > 0 && (appinspect == appmatch)) {
r = 1;
}
}
SCLogDebug("detection done, store results: sm %p, uri %d, dce %d",
sm, umatch, dmatch);
SCMutexLock(&f->m);
/* match or no match, we store the state anyway
* "sm" here is either NULL (complete match) or
* the last SigMatch that didn't match */
if (f->de_state == NULL) {
f->de_state = DetectEngineStateAlloc();
}
SCMutexUnlock(&f->m);
if (f->de_state != NULL) {
SCMutexLock(&f->de_state->m);
DeStateSignatureAppend(f->de_state, s, sm, umatch, dmatch);
SCMutexUnlock(&f->de_state->m);
}
SCReturnInt(r);
}
/** \brief Continue DeState detection of the signatures stored in the state.
*
* \retval 0 all is good
*/
int DeStateDetectContinueDetection(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *alstate, uint16_t alproto)
{
SCEnter();
SigIntId cnt = 0;
SigIntId store_cnt = 0;
DeStateStore *store = NULL;
char umatch = 0;
char uinspect = 0;
char dmatch = 0;
char dinspect = 0;
char appinspect = 0;
char appmatch = 0;
if (f == NULL || alstate == NULL || alproto == ALPROTO_UNKNOWN) {
return 0;
}
SCMutexLock(&f->de_state->m);
if (f->de_state->cnt == 0)
goto end;
/* loop through the stores */
for (store = f->de_state->head; store != NULL; store = store->next)
{
/* loop through the sigs in the stores */
for (store_cnt = 0;
store_cnt < DE_STATE_CHUNK_SIZE && cnt < f->de_state->cnt;
store_cnt++, cnt++)
{
DeStateStoreItem *item = &store->store[store_cnt];
umatch = 0;
uinspect = 0;
dmatch = 0;
dinspect = 0;
appinspect = 0;
appmatch = 0;
SCLogDebug("internal id of signature to inspect: %"PRIuMAX,
(uintmax_t)item->sid);
Signature *s = de_ctx->sig_array[item->sid];
SCLogDebug("id of signature to inspect: %"PRIuMAX,
(uintmax_t)s->id);
PROFILING_START;
/* let's continue detection */
/* first, check uricontent */
if (alproto == ALPROTO_HTTP && (flags & STREAM_TOSERVER)) {
if (s->umatch != NULL) {
if (!(item->flags & DE_STATE_FLAG_URI_MATCH)) {
SCLogDebug("inspecting uri");
uinspect = 1;
if (DetectEngineInspectPacketUris(de_ctx, det_ctx, s,
f, flags, alstate) == 1)
{
SCLogDebug("uri matched");
item->flags |= DE_STATE_FLAG_URI_MATCH;
umatch = 1;
} else {
SCLogDebug("uri inspected but no match");
}
} else {
SCLogDebug("uri already inspected");
}
}
} else if (alproto == ALPROTO_DCERPC) {
if (s->dmatch != NULL) {
if (!(item->flags & DE_STATE_FLAG_DCE_MATCH)) {
SCLogDebug("inspecting dce payload");
dinspect = 1;
if (DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
flags, alstate) == 1)
{
SCLogDebug("dce payload matched");
item->flags |= DE_STATE_FLAG_DCE_MATCH;
dmatch = 1;
} else {
SCLogDebug("dce payload inspected but no match");
}
} else {
SCLogDebug("dce payload already inspected");
}
}
}
appinspect = uinspect + dinspect;
appmatch = umatch + dmatch;
SCLogDebug("appinspect %d, appmatch %d", appinspect, appmatch);
/* next, check the other sig matches */
if (item->nm != NULL) {
SigMatch *sm;
int match = 0;
for (sm = item->nm; sm != NULL; sm = sm->next) {
match = sigmatch_table[sm->type].AppLayerMatch(tv,
det_ctx, f, flags, alstate, s, sm);
/* no match, break out */
if (match == 0) {
item->nm = sm;
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_PARTIAL;
SCLogDebug("state set to %s", DeStateMatchResultToString(DE_STATE_MATCH_PARTIAL));
break;
/* match, and no more sm's */
} else if (sm->next == NULL) {
/* mark the sig as matched */
item->nm = NULL;
if (!appinspect || (appinspect == appmatch)) {
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NEW;
SCLogDebug("state set to %s", DeStateMatchResultToString(DE_STATE_MATCH_NEW));
} else {
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_PARTIAL;
SCLogDebug("state set to %s", DeStateMatchResultToString(DE_STATE_MATCH_PARTIAL));
}
}
}
} else {
if (appinspect > 0 && (appinspect == appmatch)) {
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NEW;
SCLogDebug("state set to %s", DeStateMatchResultToString(DE_STATE_MATCH_NEW));
} else if (uinspect && !umatch) {
det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_PARTIAL;
SCLogDebug("state set to %s", DeStateMatchResultToString(DE_STATE_MATCH_PARTIAL));
}
}
SCLogDebug("signature %"PRIu32" match state %s",
s->id, DeStateMatchResultToString(det_ctx->de_state_sig_array[item->sid]));
RULE_PROFILING_END(s, match);
}
}
end:
SCMutexUnlock(&f->de_state->m);
SCReturnInt(0);
}
/**
* \brief Restart detection as we're going to inspect a new transaction
*/
int DeStateRestartDetection(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, void *alstate, uint16_t alproto)
{
SCEnter();
/* first clear the existing state as it belongs
* to the previous transaction */
SCMutexLock(&f->m);
if (f->de_state != NULL) {
SCMutexLock(&f->de_state->m);
DetectEngineStateReset(f->de_state);
SCMutexUnlock(&f->de_state->m);
}
SCMutexUnlock(&f->m);
SCReturnInt(0);
}
/**
* \retval 1 match
* \retval 0 no match
*/
int DeStateDetectSignature(ThreadVars *tv, DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p, uint8_t flags,
void *alstate, uint16_t alproto)
{
SCEnter();
int r = 0;
if (p->flow == NULL) {
SCReturnInt(0);
}
/* if there is already a state, continue the inspection of this
* signature using that state.
*/
r = DeStateDetectStartDetection(tv, de_ctx, det_ctx, s, p->flow, flags,
alstate, alproto);
SCReturnInt(r);
}
#ifdef UNITTESTS
#include "flow-util.h"
static int DeStateTest01(void) {
SCLogDebug("sizeof(DetectEngineState)\t\t%"PRIuMAX,
(uintmax_t)sizeof(DetectEngineState));
SCLogDebug("sizeof(DeStateStore)\t\t\t%"PRIuMAX,
(uintmax_t)sizeof(DeStateStore));
SCLogDebug("sizeof(DeStateStoreItem)\t\t%"PRIuMAX"",
(uintmax_t)sizeof(DeStateStoreItem));
return 1;
}
static int DeStateTest02(void) {
int result = 0;
DetectEngineState *state = DetectEngineStateAlloc();
if (state == NULL) {
printf("d == NULL: ");
goto end;
}
Signature s;
memset(&s, 0x00, sizeof(s));
s.num = 0;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 11;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 22;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 33;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 44;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 55;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 66;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 77;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 88;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 99;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 100;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 111;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 122;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 133;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 144;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 155;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 166;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
if (state->head == NULL) {
goto end;
}
if (state->head->store[1].sid != 11) {
goto end;
}
if (state->head->next == NULL) {
goto end;
}
if (state->head->store[15].sid != 155) {
goto end;
}
if (state->head->next->store[0].sid != 166) {
goto end;
}
result = 1;
end:
if (state != NULL) {
DetectEngineStateFree(state);
}
return result;
}
static int DeStateTest03(void) {
int result = 0;
DetectEngineState *state = DetectEngineStateAlloc();
if (state == NULL) {
printf("d == NULL: ");
goto end;
}
Signature s;
memset(&s, 0x00, sizeof(s));
s.num = 11;
DeStateSignatureAppend(state, &s, NULL, 0, 0);
s.num = 22;
DeStateSignatureAppend(state, &s, NULL, 1, 0);
if (state->head == NULL) {
goto end;
}
if (state->head->store[0].sid != 11) {
goto end;
}
if (state->head->store[0].flags & DE_STATE_FLAG_URI_MATCH) {
goto end;
}
if (state->head->store[1].sid != 22) {
goto end;
}
if (!(state->head->store[1].flags & DE_STATE_FLAG_URI_MATCH)) {
goto end;
}
result = 1;
end:
if (state != NULL) {
DetectEngineStateFree(state);
}
return result;
}
static int DeStateSigTest01(void) {
int result = 0;
Signature *s = NULL;
DetectEngineThreadCtx *det_ctx = NULL;
ThreadVars th_v;
Flow f;
TcpSession ssn;
Packet p;
uint8_t httpbuf1[] = "POST / HTTP/1.0\r\n";
uint8_t httpbuf2[] = "User-Agent: Mozilla/1.0\r\n";
uint8_t httpbuf3[] = "Cookie: dummy\r\nContent-Length: 10\r\n\r\n";
uint8_t httpbuf4[] = "Http Body!";
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 */
HtpState *http_state = NULL;
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 = NULL;
p.payload_len = 0;
p.proto = IPPROTO_TCP;
FLOW_INITIALIZE(&f);
f.protoctx = (void *)&ssn;
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 = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy\"; http_cookie; sid:1; rev:1;)");
if (s == NULL) {
printf("sig 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("sig 1 didn't alert: ");
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("signature matched, but shouldn't have: ");
goto end;
}
p.alerts.cnt = 0;
result = 1;
end:
if (http_state != NULL) {
HTPStateFree(http_state);
}
if (det_ctx != NULL) {
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
}
if (de_ctx != NULL) {
SigGroupCleanup(de_ctx);
DetectEngineCtxFree(de_ctx);
}
FlowL7DataPtrFree(&f);
StreamTcpFreeConfig(TRUE);
return result;
}
/** \test multiple pipelined http transactions */
static int DeStateSigTest02(void) {
int result = 0;
Signature *s = NULL;
DetectEngineThreadCtx *det_ctx = NULL;
ThreadVars th_v;
Flow f;
TcpSession ssn;
Packet p;
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[] = "Http Body!";
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\nHttp Body!";
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(&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 = NULL;
p.payload_len = 0;
p.proto = 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; 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; 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("sig 1 didn't alert: ");
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("signature matched, but shouldn't have: ");
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;
result = 1;
end:
FLOW_DESTROY(&f);
if (det_ctx != NULL) {
DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
}
if (de_ctx != NULL) {
SigGroupCleanup(de_ctx);
DetectEngineCtxFree(de_ctx);
}
FlowL7DataPtrFree(&f);
StreamTcpFreeConfig(TRUE);
return result;
}
#endif
void DeStateRegisterTests(void) {
#ifdef UNITTESTS
UtRegisterTest("DeStateTest01", DeStateTest01, 1);
UtRegisterTest("DeStateTest02", DeStateTest02, 1);
UtRegisterTest("DeStateTest03", DeStateTest03, 1);
UtRegisterTest("DeStateSigTest01", DeStateSigTest01, 1);
UtRegisterTest("DeStateSigTest02", DeStateSigTest02, 1);
#endif
}