summaryrefslogtreecommitdiffstats
path: root/src/detect-engine-state.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:39:49 +0000
commita0aa2307322cd47bbf416810ac0292925e03be87 (patch)
tree37076262a026c4b48c8a0e84f44ff9187556ca35 /src/detect-engine-state.c
parentInitial commit. (diff)
downloadsuricata-a0aa2307322cd47bbf416810ac0292925e03be87.tar.xz
suricata-a0aa2307322cd47bbf416810ac0292925e03be87.zip
Adding upstream version 1:7.0.3.upstream/1%7.0.3
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/detect-engine-state.c')
-rw-r--r--src/detect-engine-state.c1472
1 files changed, 1472 insertions, 0 deletions
diff --git a/src/detect-engine-state.c b/src/detect-engine-state.c
new file mode 100644
index 0000000..6fd7f96
--- /dev/null
+++ b/src/detect-engine-state.c
@@ -0,0 +1,1472 @@
+/* Copyright (C) 2007-2021 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.
+ */
+
+/**
+ * \defgroup sigstate State support
+ *
+ * State is stored in the ::DetectEngineState structure. This is
+ * basically a container for storage item of type ::DeStateStore.
+ * They contains an array of ::DeStateStoreItem which store the
+ * state of match for an individual signature identified by
+ * DeStateStoreItem::sid.
+ *
+ * @{
+ */
+
+/**
+ * \file
+ *
+ * \author Victor Julien <victor@inliniac.net>
+ * \author Anoop Saldanha <anoopsaldanha@gmail.com>
+ *
+ * \brief State based signature handling.
+ */
+
+#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-dcepayload.h"
+#include "detect-engine-build.h"
+
+#include "detect-flowvar.h"
+
+#include "stream-tcp.h"
+#include "stream-tcp-private.h"
+#include "stream-tcp-reassemble.h"
+
+#include "app-layer.h"
+#include "app-layer-parser.h"
+#include "app-layer-protos.h"
+#include "app-layer-htp.h"
+
+#include "util-unittest.h"
+#include "util-unittest-helper.h"
+#include "util-profiling.h"
+
+#include "flow-util.h"
+
+/** convert enum to string */
+#define CASE_CODE(E) case E: return #E
+
+static inline int StateIsValid(uint16_t alproto, void *alstate)
+{
+ if (alstate != NULL) {
+ if (alproto == ALPROTO_HTTP1) {
+ HtpState *htp_state = (HtpState *)alstate;
+ if (htp_state->conn != NULL) {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static DeStateStore *DeStateStoreAlloc(void)
+{
+ DeStateStore *d = SCMalloc(sizeof(DeStateStore));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DeStateStore));
+
+ return d;
+}
+
+#ifdef DEBUG_VALIDATION
+static int DeStateSearchState(DetectEngineState *state, uint8_t direction, SigIntId num)
+{
+ DetectEngineStateDirection *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
+ DeStateStore *tx_store = dir_state->head;
+ SigIntId store_cnt;
+ SigIntId state_cnt = 0;
+
+ for (; tx_store != NULL; tx_store = tx_store->next) {
+ SCLogDebug("tx_store %p", tx_store);
+ for (store_cnt = 0;
+ store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreItem *item = &tx_store->store[store_cnt];
+ if (item->sid == num) {
+ SCLogDebug("sid %u already in state: %p %p %p %u %u, direction %s",
+ num, state, dir_state, tx_store, state_cnt,
+ store_cnt, direction & STREAM_TOSERVER ? "toserver" : "toclient");
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+#endif
+
+static void DeStateSignatureAppend(DetectEngineState *state,
+ const Signature *s, uint32_t inspect_flags, uint8_t direction)
+{
+ SCEnter();
+
+ DetectEngineStateDirection *dir_state =
+ &state->dir_state[(direction & STREAM_TOSERVER) ? 0 : 1];
+
+#ifdef DEBUG_VALIDATION
+ BUG_ON(DeStateSearchState(state, direction, s->num));
+#endif
+ DeStateStore *store = dir_state->tail;
+ if (store == NULL) {
+ store = DeStateStoreAlloc();
+ dir_state->head = store;
+ dir_state->cur = store;
+ dir_state->tail = store;
+ } else if (dir_state->cur) {
+ store = dir_state->cur;
+ } else {
+ store = DeStateStoreAlloc();
+ if (store != NULL) {
+ dir_state->tail->next = store;
+ dir_state->tail = store;
+ dir_state->cur = store;
+ }
+ }
+ if (store == NULL)
+ SCReturn;
+
+ SigIntId idx = dir_state->cnt % DE_STATE_CHUNK_SIZE;
+ store->store[idx].sid = s->num;
+ store->store[idx].flags = inspect_flags;
+ dir_state->cnt++;
+ /* if current chunk is full, progress cur */
+ if (dir_state->cnt % DE_STATE_CHUNK_SIZE == 0) {
+ dir_state->cur = dir_state->cur->next;
+ }
+
+ SCReturn;
+}
+
+DetectEngineState *DetectEngineStateAlloc(void)
+{
+ DetectEngineState *d = SCMalloc(sizeof(DetectEngineState));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DetectEngineState));
+
+ return d;
+}
+
+void DetectEngineStateFree(DetectEngineState *state)
+{
+ DeStateStore *store;
+ DeStateStore *store_next;
+ int i = 0;
+
+ for (i = 0; i < 2; i++) {
+ store = state->dir_state[i].head;
+ while (store != NULL) {
+ store_next = store->next;
+ SCFree(store);
+ store = store_next;
+ }
+ }
+ SCFree(state);
+
+ return;
+}
+
+static void StoreFileNoMatchCnt(DetectEngineState *de_state, uint16_t file_no_match, uint8_t direction)
+{
+ de_state->dir_state[(direction & STREAM_TOSERVER) ? 0 : 1].filestore_cnt += file_no_match;
+
+ return;
+}
+
+static bool StoreFilestoreSigsCantMatch(const SigGroupHead *sgh, const DetectEngineState *de_state, uint8_t direction)
+{
+ if (de_state->dir_state[(direction & STREAM_TOSERVER) ? 0 : 1].filestore_cnt ==
+ sgh->filestore_cnt)
+ return true;
+ else
+ return false;
+}
+
+static void StoreStateTxHandleFiles(const SigGroupHead *sgh, Flow *f, DetectEngineState *destate,
+ const uint8_t flow_flags, void *tx, const uint64_t tx_id, const uint16_t file_no_match)
+{
+ SCLogDebug("tx %"PRIu64", file_no_match %u", tx_id, file_no_match);
+ StoreFileNoMatchCnt(destate, file_no_match, flow_flags);
+ if (StoreFilestoreSigsCantMatch(sgh, destate, flow_flags)) {
+ SCLogDebug("filestore sigs can't match");
+ FileDisableStoringForTransaction(
+ f, flow_flags & (STREAM_TOCLIENT | STREAM_TOSERVER), tx, tx_id);
+ } else {
+ SCLogDebug("filestore sigs can still match");
+ }
+}
+
+void DetectRunStoreStateTx(
+ const SigGroupHead *sgh,
+ Flow *f, void *tx, uint64_t tx_id,
+ const Signature *s,
+ uint32_t inspect_flags, uint8_t flow_flags,
+ const uint16_t file_no_match)
+{
+ AppLayerTxData *tx_data = AppLayerParserGetTxData(f->proto, f->alproto, tx);
+ BUG_ON(tx_data == NULL);
+ if (tx_data == NULL) {
+ SCLogDebug("No TX data for %" PRIu64, tx_id);
+ return;
+ }
+ if (tx_data->de_state == NULL) {
+ tx_data->de_state = DetectEngineStateAlloc();
+ if (tx_data->de_state == NULL)
+ return;
+ SCLogDebug("destate created for %"PRIu64, tx_id);
+ }
+ DeStateSignatureAppend(tx_data->de_state, s, inspect_flags, flow_flags);
+ StoreStateTxHandleFiles(sgh, f, tx_data->de_state, flow_flags, tx, tx_id, file_no_match);
+
+ SCLogDebug("Stored for TX %"PRIu64, tx_id);
+}
+
+static inline void ResetTxState(DetectEngineState *s)
+{
+ if (s) {
+ s->dir_state[0].cnt = 0;
+ s->dir_state[0].filestore_cnt = 0;
+ s->dir_state[0].flags = 0;
+ /* reset 'cur' back to the list head */
+ s->dir_state[0].cur = s->dir_state[0].head;
+
+ s->dir_state[1].cnt = 0;
+ s->dir_state[1].filestore_cnt = 0;
+ s->dir_state[1].flags = 0;
+ /* reset 'cur' back to the list head */
+ s->dir_state[1].cur = s->dir_state[1].head;
+ }
+}
+
+/** \brief Reset de state for active tx'
+ * To be used on detect engine reload.
+ * \param f write LOCKED flow
+ */
+void DetectEngineStateResetTxs(Flow *f)
+{
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(f->alproto, alstate)) {
+ return;
+ }
+
+ uint64_t inspect_ts = AppLayerParserGetTransactionInspectId(f->alparser, STREAM_TOCLIENT);
+ uint64_t inspect_tc = AppLayerParserGetTransactionInspectId(f->alparser, STREAM_TOSERVER);
+
+ uint64_t inspect_tx_id = MIN(inspect_ts, inspect_tc);
+
+ uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ void *inspect_tx = AppLayerParserGetTx(f->proto, f->alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, inspect_tx);
+ BUG_ON(txd == NULL);
+ if (txd) {
+ ResetTxState(txd->de_state);
+ }
+ }
+ }
+}
+
+/*********Unittests*********/
+
+#ifdef UNITTESTS
+#include "detect-engine-alert.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)
+{
+ uint8_t direction = STREAM_TOSERVER;
+ DetectEngineState *state = DetectEngineStateAlloc();
+ FAIL_IF_NULL(state);
+ FAIL_IF_NOT_NULL(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head);
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ s.num = 0;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 33;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 44;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 55;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 66;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 77;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 88;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 99;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 100;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 111;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 122;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 133;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+
+ s.num = 144;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur == NULL);
+
+ s.num = 155;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].tail ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+
+ s.num = 166;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 11);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next == NULL);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[0].sid != 155);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[1].sid != 166);
+
+ ResetTxState(state);
+
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL);
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+
+ s.num = 0;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 33;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 44;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 55;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 66;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 77;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 88;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 99;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 100;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 111;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 122;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 133;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+ s.num = 144;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+ FAIL_IF_NOT(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].tail ==
+ state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].cur);
+ s.num = 155;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 166;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 11);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next == NULL);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[0].sid != 155);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[1].sid != 166);
+
+ DetectEngineStateFree(state);
+
+ PASS;
+}
+
+static int DeStateTest03(void)
+{
+ DetectEngineState *state = DetectEngineStateAlloc();
+ FAIL_IF_NULL(state);
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ uint8_t direction = STREAM_TOSERVER;
+
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, BIT_U32(DE_STATE_FLAG_BASE), direction);
+
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].sid != 11);
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].flags & BIT_U32(DE_STATE_FLAG_BASE));
+ FAIL_IF(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 22);
+ FAIL_IF(!(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].flags & BIT_U32(DE_STATE_FLAG_BASE)));
+
+ DetectEngineStateFree(state);
+ PASS;
+}
+
+static int DeStateSigTest01(void)
+{
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = NULL;
+ 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 */
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&f, 0, sizeof(f));
+ memset(&ssn, 0, sizeof(ssn));
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+
+ FLOW_INITIALIZE(&f);
+ f.protoctx = (void *)&ssn;
+ f.proto = IPPROTO_TCP;
+ f.flags |= FLOW_IPV4;
+ f.alproto = ALPROTO_HTTP1;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *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;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+ FAIL_IF_NULL(det_ctx);
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1);
+ FAIL_IF_NOT(r == 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
+ FAIL_IF_NOT(r == 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
+ FAIL_IF_NOT(r == 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4);
+ FAIL_IF_NOT(r == 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ PASS;
+}
+
+/** \test multiple pipelined http transactions */
+static int DeStateSigTest02(void)
+{
+ 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[] = "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 */
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ 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.flags |= FLOW_IPV4;
+
+ p->flow = &f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+ f.alproto = ALPROTO_HTTP1;
+
+ StreamTcpInitConfig(true);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:to_server; content:\"POST\"; http_method; content:\"/\"; http_uri; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"body\"; nocase; http_client_body; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (flow:to_server; content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; sid:2; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+ FAIL_IF_NULL(det_ctx);
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f.alstate, 0);
+ FAIL_IF_NULL(tx);
+
+ AppLayerTxData *tx_data = AppLayerParserGetTxData(IPPROTO_TCP, ALPROTO_HTTP1, tx);
+ FAIL_IF_NULL(tx_data);
+ DetectEngineState *tx_de_state = tx_data->de_state;
+ FAIL_IF_NULL(tx_de_state);
+ FAIL_IF(tx_de_state->dir_state[0].cnt != 1);
+ /* http_header(mpm): 5, uri: 3, method: 6, cookie: 7 */
+ uint32_t expected_flags = (BIT_U32(5) | BIT_U32(3) | BIT_U32(6) | BIT_U32(4));
+ FAIL_IF(tx_de_state->dir_state[0].head->store[0].flags != expected_flags);
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf4, httplen4);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(!(PacketAlertCheck(p, 1)));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf5, httplen5);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf6, httplen6);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2)));
+
+ r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf7, httplen7);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(!(PacketAlertCheck(p, 2)));
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ PASS;
+}
+
+static int DeStateSigTest03(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (flow:to_server; content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1,
+ STREAM_TOSERVER | STREAM_START | STREAM_EOF, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(!(PacketAlertCheck(p, 1)));
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+
+ FAIL_IF(!(file->flags & FILE_STORE));
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+static int DeStateSigTest04(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+ FAIL_IF_NULL(det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1,
+ STREAM_TOSERVER | STREAM_START | STREAM_EOF, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+
+ FAIL_IF(file->flags & FILE_STORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+static int DeStateSigTest05(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"nomatch\"; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1,
+ STREAM_TOSERVER | STREAM_START | STREAM_EOF, httpbuf1, httplen1);
+ FAIL_IF_NOT(r == 0);
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF(http_state->state_data.file_flags & FLOWFILE_NO_STORE_TS);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ /* detect will have set FLOWFILE_NO_STORE_TS, but it won't have had
+ * an opportunity to be applied to the file itself yet */
+ FAIL_IF_NOT(http_state->state_data.file_flags & FLOWFILE_NO_STORE_TS);
+ FAIL_IF(file->flags & FILE_NOSTORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+static int DeStateSigTest06(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"nomatch\"; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+ FAIL_IF_NULL(det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ int r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1,
+ STREAM_TOSERVER | STREAM_START | STREAM_EOF, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ /* detect will have set FLOWFILE_NO_STORE_TS, but it won't have had
+ * an opportunity to be applied to the file itself yet */
+ FAIL_IF_NOT(tx_ud->tx_data.file_flags & FLOWFILE_NO_STORE_TS);
+ FAIL_IF(file->flags & FILE_NOSTORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+static int DeStateSigTest07(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 215\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "filecontent\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"GET\"; http_method; content:\"upload.cgi\"; http_uri; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf2, httplen2);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF(file->flags & FILE_STORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+/**
+ * \test multiple files in a tx
+ */
+static int DeStateSigTest08(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 440\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"AAAApicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "file";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "content\r\n"
+ "-----------------------------277531038314945\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"BBBBpicture2.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent2\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"BBBBpicture\"; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ /* HTTP request with 1st part of the multipart body */
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF(file->flags & FILE_STORE);
+
+ /* 2nd multipart body file */
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ fc = files.fc;
+ FAIL_IF_NULL(fc);
+ file = fc->head;
+ FAIL_IF_NULL(file);
+ file = file->next;
+ FAIL_IF_NULL(file);
+ FAIL_IF_NOT(file->flags & FILE_STORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+/**
+ * \test multiple files in a tx. Both files should match
+ */
+static int DeStateSigTest09(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 440\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "file";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "content\r\n"
+ "-----------------------------277531038314945\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent2\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (content:\"POST\"; http_method; content:\"upload.cgi\"; http_uri; filename:\"somepicture\"; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ /* HTTP request with 1st part of the multipart body */
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF_NOT(file->flags & FILE_STORE);
+
+ /* 2nd multipart body file */
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ fc = files.fc;
+ FAIL_IF_NULL(fc);
+ file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF_NOT(file->flags & FILE_STORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+/**
+ * \test multiple files in a tx. Both files should match. No other matches.
+ */
+static int DeStateSigTest10(void)
+{
+ uint8_t httpbuf1[] = "POST /upload.cgi HTTP/1.1\r\n"
+ "Host: www.server.lan\r\n"
+ "Content-Type: multipart/form-data; boundary=---------------------------277531038314945\r\n"
+ "Content-Length: 440\r\n"
+ "\r\n"
+ "-----------------------------277531038314945\r\n"
+ "Content-Disposition: form-data; name=\"uploadfile_0\"; filename=\"somepicture1.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n";
+
+ uint32_t httplen1 = sizeof(httpbuf1) - 1; /* minus the \0 */
+ uint8_t httpbuf2[] = "file";
+ uint32_t httplen2 = sizeof(httpbuf2) - 1; /* minus the \0 */
+ uint8_t httpbuf3[] = "content\r\n"
+ "-----------------------------277531038314945\r\n";
+ uint32_t httplen3 = sizeof(httpbuf3) - 1; /* minus the \0 */
+
+ uint8_t httpbuf4[] = "Content-Disposition: form-data; name=\"uploadfile_1\"; filename=\"somepicture2.jpg\"\r\n"
+ "Content-Type: image/jpeg\r\n"
+ "\r\n"
+ "filecontent2\r\n"
+ "-----------------------------277531038314945--";
+ uint32_t httplen4 = sizeof(httpbuf4) - 1; /* minus the \0 */
+
+ ThreadVars th_v;
+ TcpSession ssn;
+
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+ FAIL_IF_NULL(alp_tctx);
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ FAIL_IF_NULL(de_ctx);
+ de_ctx->flags |= DE_QUIET;
+
+ Signature *s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any (filename:\"somepicture\"; filestore; sid:1; rev:1;)");
+ FAIL_IF_NULL(s);
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ Flow *f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ FAIL_IF_NULL(f);
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP1;
+
+ Packet *p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ FAIL_IF_NULL(p);
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(true);
+
+ /* HTTP request with 1st part of the multipart body */
+
+ int r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_START, httpbuf1, httplen1);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf2, httplen2);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ HtpState *http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ HtpTxUserData *tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ AppLayerGetFileState files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ FileContainer *fc = files.fc;
+ FAIL_IF_NULL(fc);
+ File *file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF_NOT(file->flags & FILE_STORE);
+
+ /* 2nd multipart body file */
+
+ r = AppLayerParserParse(NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER, httpbuf3, httplen3);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF(PacketAlertCheck(p, 1));
+
+ r = AppLayerParserParse(
+ NULL, alp_tctx, f, ALPROTO_HTTP1, STREAM_TOSERVER | STREAM_EOF, httpbuf4, httplen4);
+ FAIL_IF(r != 0);
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ FAIL_IF_NOT(PacketAlertCheck(p, 1));
+
+ http_state = f->alstate;
+ FAIL_IF_NULL(http_state);
+ tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP1, f->alstate, 0);
+ FAIL_IF_NULL(tx);
+ tx_ud = htp_tx_get_user_data(tx);
+ FAIL_IF_NULL(tx_ud);
+
+ files = AppLayerParserGetTxFiles(p->flow, http_state, tx, STREAM_TOSERVER);
+ fc = files.fc;
+ FAIL_IF_NULL(fc);
+ file = fc->head;
+ FAIL_IF_NULL(file);
+ FAIL_IF_NOT(file->flags & FILE_STORE);
+
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ DetectEngineCtxFree(de_ctx);
+ StreamTcpFreeConfig(true);
+ PASS;
+}
+
+#endif
+
+void DeStateRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DeStateTest01", DeStateTest01);
+ UtRegisterTest("DeStateTest02", DeStateTest02);
+ UtRegisterTest("DeStateTest03", DeStateTest03);
+ UtRegisterTest("DeStateSigTest01", DeStateSigTest01);
+ UtRegisterTest("DeStateSigTest02", DeStateSigTest02);
+ UtRegisterTest("DeStateSigTest03", DeStateSigTest03);
+ UtRegisterTest("DeStateSigTest04", DeStateSigTest04);
+ UtRegisterTest("DeStateSigTest05", DeStateSigTest05);
+ UtRegisterTest("DeStateSigTest06", DeStateSigTest06);
+ UtRegisterTest("DeStateSigTest07", DeStateSigTest07);
+ UtRegisterTest("DeStateSigTest08", DeStateSigTest08);
+ UtRegisterTest("DeStateSigTest09", DeStateSigTest09);
+ UtRegisterTest("DeStateSigTest10", DeStateSigTest10);
+#endif
+
+ return;
+}
+
+/**
+ * @}
+ */