diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:39:49 +0000 |
commit | a0aa2307322cd47bbf416810ac0292925e03be87 (patch) | |
tree | 37076262a026c4b48c8a0e84f44ff9187556ca35 /src/detect-engine-state.c | |
parent | Initial commit. (diff) | |
download | suricata-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.c | 1472 |
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; +} + +/** + * @} + */ |