aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/suricata/src/detect-engine-state.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/suricata/src/detect-engine-state.c')
-rw-r--r--framework/src/suricata/src/detect-engine-state.c2346
1 files changed, 2346 insertions, 0 deletions
diff --git a/framework/src/suricata/src/detect-engine-state.c b/framework/src/suricata/src/detect-engine-state.c
new file mode 100644
index 00000000..05ca25d5
--- /dev/null
+++ b/framework/src/suricata/src/detect-engine-state.c
@@ -0,0 +1,2346 @@
+/* Copyright (C) 2007-2013 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
+ *
+ * It is possible to do matching on reconstructed applicative flow.
+ * This is done by this code. It uses the ::Flow structure to store
+ * the list of signatures to match on the reconstructed stream.
+ *
+ * The Flow::de_state is a ::DetectEngineState structure. This is
+ * basically a containter 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.
+ *
+ * The state is constructed by DeStateDetectStartDetection() which
+ * also starts the matching. Work is continued by
+ * DeStateDetectContinueDetection().
+ *
+ * Once a transaction has been analysed DeStateRestartDetection()
+ * is used to reset the structures.
+ *
+ * @{
+ */
+
+/**
+ * \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-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 "app-layer-smb.h"
+#include "app-layer-dcerpc-common.h"
+#include "app-layer-dcerpc.h"
+#include "app-layer-dns-common.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
+
+/** The DetectEngineThreadCtx::de_state_sig_array contains 2 separate values:
+ * 1. the first bit tells the prefilter engine to bypass the rule (or not)
+ * 2. the other bits allow 'ContinueDetect' to specify an offset again the
+ * base tx id. This offset will then be used by 'StartDetect' to not
+ * inspect transactions again for the same signature.
+ *
+ * The offset in (2) has a max value due to the limited data type. If it is
+ * set to max the code will fall back to a slower path that validates that
+ * we're not adding duplicate rules to the detection state.
+ */
+#define MAX_STORED_TXID_OFFSET 127
+
+/******** static internal helpers *********/
+
+static inline int StateIsValid(uint16_t alproto, void *alstate)
+{
+ if (alstate != NULL) {
+ if (alproto == ALPROTO_HTTP) {
+ HtpState *htp_state = (HtpState *)alstate;
+ if (htp_state->conn != NULL) {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static inline int TxIsLast(uint64_t tx_id, uint64_t total_txs)
+{
+ if (total_txs - tx_id <= 1)
+ 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;
+}
+static DeStateStoreFlowRules *DeStateStoreFlowRulesAlloc(void)
+{
+ DeStateStoreFlowRules *d = SCMalloc(sizeof(DeStateStoreFlowRules));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DeStateStoreFlowRules));
+
+ return d;
+}
+
+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;
+}
+
+static void DeStateSignatureAppend(DetectEngineState *state, Signature *s, uint32_t inspect_flags, uint8_t direction)
+{
+ int jump = 0;
+ int i = 0;
+ 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->head;
+
+ if (store == NULL) {
+ store = DeStateStoreAlloc();
+ if (store != NULL) {
+ dir_state->head = store;
+ dir_state->tail = store;
+ }
+ } else {
+ jump = dir_state->cnt / DE_STATE_CHUNK_SIZE;
+ for (i = 0; i < jump; i++) {
+ store = store->next;
+ }
+ if (store == NULL) {
+ store = DeStateStoreAlloc();
+ if (store != NULL) {
+ dir_state->tail->next = store;
+ dir_state->tail = store;
+ }
+ }
+ }
+
+ if (store == NULL)
+ return;
+
+ SigIntId idx = dir_state->cnt++ % DE_STATE_CHUNK_SIZE;
+ store->store[idx].sid = s->num;
+ store->store[idx].flags = inspect_flags;
+
+ return;
+}
+
+static void DeStateFlowRuleAppend(DetectEngineStateFlow *state, Signature *s,
+ SigMatch *sm, uint32_t inspect_flags,
+ uint8_t direction)
+{
+ int jump = 0;
+ int i = 0;
+ DetectEngineStateDirectionFlow *dir_state = &state->dir_state[direction & STREAM_TOSERVER ? 0 : 1];
+ DeStateStoreFlowRules *store = dir_state->head;
+
+ if (store == NULL) {
+ store = DeStateStoreFlowRulesAlloc();
+ if (store != NULL) {
+ dir_state->head = store;
+ dir_state->tail = store;
+ }
+ } else {
+ jump = dir_state->cnt / DE_STATE_CHUNK_SIZE;
+ for (i = 0; i < jump; i++) {
+ store = store->next;
+ }
+ if (store == NULL) {
+ store = DeStateStoreFlowRulesAlloc();
+ if (store != NULL) {
+ dir_state->tail->next = store;
+ dir_state->tail = store;
+ }
+ }
+ }
+
+ if (store == NULL)
+ return;
+
+ SigIntId idx = dir_state->cnt++ % DE_STATE_CHUNK_SIZE;
+ store->store[idx].sid = s->num;
+ store->store[idx].flags = inspect_flags;
+ store->store[idx].nm = sm;
+
+ return;
+}
+
+static void DeStateStoreStateVersion(Flow *f,
+ const uint8_t alversion, uint8_t direction)
+{
+ f->detect_alversion[direction & STREAM_TOSERVER ? 0 : 1] = alversion;
+}
+
+static void DeStateStoreFileNoMatchCnt(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 int DeStateStoreFilestoreSigsCantMatch(SigGroupHead *sgh, DetectEngineState *de_state, uint8_t direction)
+{
+ if (de_state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].filestore_cnt == sgh->filestore_cnt)
+ return 1;
+ else
+ return 0;
+}
+
+DetectEngineState *DetectEngineStateAlloc(void)
+{
+ DetectEngineState *d = SCMalloc(sizeof(DetectEngineState));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DetectEngineState));
+
+ return d;
+}
+
+DetectEngineStateFlow *DetectEngineStateFlowAlloc(void)
+{
+ DetectEngineStateFlow *d = SCMalloc(sizeof(DetectEngineStateFlow));
+ if (unlikely(d == NULL))
+ return NULL;
+ memset(d, 0, sizeof(DetectEngineStateFlow));
+
+ 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;
+}
+
+void DetectEngineStateFlowFree(DetectEngineStateFlow *state)
+{
+ DeStateStoreFlowRules *store;
+ DeStateStoreFlowRules *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 int HasStoredSigs(Flow *f, uint8_t flags)
+{
+ if (f->de_state != NULL && f->de_state->dir_state[flags & STREAM_TOSERVER ? 0 : 1].cnt != 0) {
+ SCLogDebug("global sigs present");
+ return 1;
+ }
+
+ if (AppLayerParserProtocolSupportsTxs(f->proto, f->alproto)) {
+ AppProto alproto = f->alproto;
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(f->alproto, alstate)) {
+ return 0;
+ }
+
+ int state = AppLayerParserHasTxDetectState(f->proto, alproto, f->alstate);
+ if (state == -ENOSYS) { /* proto doesn't support this API call */
+ /* fall through */
+ } else if (state == 0) {
+ return 0;
+ }
+ /* if state == 1 we also fall through */
+
+ uint64_t inspect_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ continue;
+ }
+ if (tx_de_state->dir_state[flags & STREAM_TOSERVER ? 0 : 1].cnt != 0) {
+ SCLogDebug("tx %u has sigs present", (uint)inspect_tx_id);
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/** \brief Check if we need to inspect this state
+ *
+ * State needs to be inspected if:
+ * 1. state has been updated
+ * 2. we already have de_state in progress
+ *
+ * \retval 0 no inspectable state
+ * \retval 1 inspectable state
+ * \retval 2 inspectable state, but no update
+ */
+int DeStateFlowHasInspectableState(Flow *f, AppProto alproto,
+ const uint8_t alversion, uint8_t flags)
+{
+ int r = 0;
+
+ FLOWLOCK_WRLOCK(f);
+
+ if (!(flags & STREAM_EOF) && f->de_state &&
+ f->detect_alversion[flags & STREAM_TOSERVER ? 0 : 1] == alversion) {
+ SCLogDebug("unchanged state");
+ r = 2;
+ } else if (HasStoredSigs(f, flags)) {
+ r = 1;
+ } else {
+ r = 0;
+ }
+ FLOWLOCK_UNLOCK(f);
+
+ return r;
+}
+
+static int StoreState(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint8_t alversion,
+ Signature *s, SigMatch *sm, const uint32_t inspect_flags,
+ const uint16_t file_no_match)
+{
+ if (f->de_state == NULL) {
+ f->de_state = DetectEngineStateFlowAlloc();
+ if (f->de_state == NULL) {
+ return 0;
+ }
+ }
+
+ DeStateFlowRuleAppend(f->de_state, s, sm, inspect_flags, flags);
+ DeStateStoreStateVersion(f, alversion, flags);
+ return 1;
+}
+
+static void StoreStateTxHandleFiles(DetectEngineThreadCtx *det_ctx, Flow *f,
+ DetectEngineState *destate, const uint8_t flags,
+ const uint64_t tx_id, const uint16_t file_no_match)
+{
+ DeStateStoreFileNoMatchCnt(destate, file_no_match, flags);
+ if (DeStateStoreFilestoreSigsCantMatch(det_ctx->sgh, destate, flags) == 1) {
+ FileDisableStoringForTransaction(f, flags & (STREAM_TOCLIENT | STREAM_TOSERVER), tx_id);
+ destate->dir_state[flags & STREAM_TOSERVER ? 0 : 1].flags |= DETECT_ENGINE_STATE_FLAG_FILE_STORE_DISABLED;
+ }
+}
+
+static void StoreStateTxFileOnly(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint64_t tx_id, void *tx,
+ const uint16_t file_no_match)
+{
+ if (AppLayerParserSupportsTxDetectState(f->proto, f->alproto)) {
+ DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
+ if (destate == NULL) {
+ destate = DetectEngineStateAlloc();
+ if (destate == NULL)
+ return;
+ if (AppLayerParserSetTxDetectState(f->proto, f->alproto, f->alstate, tx, destate) < 0) {
+ DetectEngineStateFree(destate);
+ return;
+ }
+ SCLogDebug("destate created for %"PRIu64, tx_id);
+ }
+ StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
+ }
+}
+
+/**
+ * \param check_before_add check for duplicates before adding the sig
+ */
+static void StoreStateTx(DetectEngineThreadCtx *det_ctx,
+ Flow *f, const uint8_t flags, const uint8_t alversion,
+ const uint64_t tx_id, void *tx,
+ Signature *s, SigMatch *sm,
+ const uint32_t inspect_flags, const uint16_t file_no_match, int check_before_add)
+{
+ if (AppLayerParserSupportsTxDetectState(f->proto, f->alproto)) {
+ DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
+ if (destate == NULL) {
+ destate = DetectEngineStateAlloc();
+ if (destate == NULL)
+ return;
+ if (AppLayerParserSetTxDetectState(f->proto, f->alproto, f->alstate, tx, destate) < 0) {
+ DetectEngineStateFree(destate);
+ return;
+ }
+ SCLogDebug("destate created for %"PRIu64, tx_id);
+ }
+
+ if (check_before_add == 0 || DeStateSearchState(destate, flags, s->num) == 0)
+ DeStateSignatureAppend(destate, s, inspect_flags, flags);
+ DeStateStoreStateVersion(f, alversion, flags);
+
+ StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
+ }
+ SCLogDebug("Stored for TX %"PRIu64, tx_id);
+}
+
+int DeStateDetectStartDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Signature *s, Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, const uint8_t alversion)
+{
+ SigMatch *sm = NULL;
+ uint16_t file_no_match = 0;
+ uint32_t inspect_flags = 0;
+ uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
+ int alert_cnt = 0;
+ int check_before_add = 0;
+
+ FLOWLOCK_WRLOCK(f);
+ /* TX based matches (inspect engines) */
+ if (AppLayerParserProtocolSupportsTxs(f->proto, alproto)) {
+ uint64_t tx_id = 0;
+ uint64_t total_txs = 0;
+
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ goto end;
+ }
+
+ /* if continue detection already inspected this rule for this tx,
+ * continue with the first not-inspected tx */
+ uint8_t offset = det_ctx->de_state_sig_array[s->num] & 0xef;
+ tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ if (offset > 0) {
+ SCLogDebug("using stored_tx_id %u instead of %u", (uint)tx_id+offset, (uint)tx_id);
+ tx_id += offset;
+ }
+ if (offset == MAX_STORED_TXID_OFFSET) {
+ check_before_add = 1;
+ }
+
+ total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+ SCLogDebug("total_txs %"PRIu64, total_txs);
+
+ SCLogDebug("starting: start tx %u, packet %u", (uint)tx_id, (uint)p->pcap_cnt);
+
+ for (; tx_id < total_txs; tx_id++) {
+ int total_matches = 0;
+ void *tx = AppLayerParserGetTx(f->proto, alproto, alstate, tx_id);
+ if (tx == NULL)
+ continue;
+ det_ctx->tx_id = tx_id;
+ det_ctx->tx_id_set = 1;
+ DetectEngineAppInspectionEngine *engine = app_inspection_engine[f->protomap][alproto][direction];
+ inspect_flags = 0;
+ while (engine != NULL) {
+ if (s->sm_lists[engine->sm_list] != NULL) {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
+ int match = engine->Callback(tv, de_ctx, det_ctx, s, f,
+ flags, alstate,
+ tx, tx_id);
+ if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
+ inspect_flags |= engine->inspect_flags;
+ engine = engine->next;
+ total_matches++;
+ continue;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ file_no_match++;
+ }
+ break;
+ }
+ engine = engine->next;
+ }
+ /* all the engines seem to be exhausted at this point. If we
+ * didn't have a match in one of the engines we would have
+ * broken off and engine wouldn't be NULL. Hence the alert. */
+ if (engine == NULL && total_matches > 0) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, tx_id,
+ PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_TX);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ SCLogDebug("MATCH: tx %u packet %u", (uint)tx_id, (uint)p->pcap_cnt);
+ }
+
+ /* if this is the last tx in our list, and it's incomplete: then
+ * we store the state so that ContinueDetection knows about it */
+ int tx_is_done = (AppLayerParserGetStateProgress(f->proto, alproto, tx, flags) >=
+ AppLayerParserGetStateProgressCompletionStatus(f->proto, alproto, flags));
+ /* see if we need to consider the next tx in our decision to add
+ * a sig to the 'no inspect array'. */
+ int next_tx_no_progress = 0;
+ if (!TxIsLast(tx_id, total_txs)) {
+ void *next_tx = AppLayerParserGetTx(f->proto, alproto, alstate, tx_id+1);
+ if (next_tx != NULL) {
+ int c = AppLayerParserGetStateProgress(f->proto, alproto, next_tx, flags);
+ if (c == 0) {
+ next_tx_no_progress = 1;
+ }
+ }
+ }
+
+ SCLogDebug("tx %u, packet %u, rule %u, alert_cnt %u, last tx %d, tx_is_done %d, next_tx_no_progress %d",
+ (uint)tx_id, (uint)p->pcap_cnt, s->num, alert_cnt,
+ TxIsLast(tx_id, total_txs), tx_is_done, next_tx_no_progress);
+
+ /* if we have something to store (partial match or file store info),
+ * then we do it now. */
+ if (inspect_flags != 0) {
+ if (!(TxIsLast(tx_id, total_txs)) || !tx_is_done) {
+ if (engine == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ /* store */
+ StoreStateTx(det_ctx, f, flags, alversion, tx_id, tx,
+ s, sm, inspect_flags, file_no_match, check_before_add);
+ } else {
+ StoreStateTxFileOnly(det_ctx, f, flags, tx_id, tx, file_no_match);
+ }
+ } else {
+ SCLogDebug("no state to store");
+ }
+ if (next_tx_no_progress)
+ break;
+ } /* for */
+
+ /* DCERPC matches */
+ } else if (s->sm_lists[DETECT_SM_LIST_DMATCH] != NULL &&
+ (alproto == ALPROTO_DCERPC || alproto == ALPROTO_SMB ||
+ alproto == ALPROTO_SMB2))
+ {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ goto end;
+ }
+
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_DMATCH);
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present &&
+ DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
+ flags, &smb_state->dcerpc) == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ } else {
+ if (DetectEngineInspectDcePayload(de_ctx, det_ctx, s, f,
+ flags, alstate) == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ }
+ }
+
+ /* flow based matches */
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_AMATCH);
+ sm = s->sm_lists[DETECT_SM_LIST_AMATCH];
+ if (sm != NULL) {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ goto end;
+ }
+
+ int match = 0;
+ for ( ; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].AppLayerMatch != NULL) {
+ match = 0;
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, &smb_state->dcerpc, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+ } else {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, alstate, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+
+ if (match == 0)
+ break;
+ if (match == 2) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ break;
+ }
+ }
+ }
+
+ if (sm == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ if (match == 1) {
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ alert_cnt = 1;
+ }
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ StoreState(det_ctx, f, flags, alversion,
+ s, sm, inspect_flags, file_no_match);
+ }
+
+ end:
+ FLOWLOCK_UNLOCK(f);
+
+ det_ctx->tx_id = 0;
+ det_ctx->tx_id_set = 0;
+ return alert_cnt ? 1:0;
+}
+
+static int DoInspectItem(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ DeStateStoreItem *item, const uint8_t dir_state_flags,
+ Packet *p, Flow *f, AppProto alproto, uint8_t flags,
+ const uint64_t inspect_tx_id, const uint64_t total_txs,
+
+ uint16_t *file_no_match, int inprogress, // is current tx in progress?
+ const int next_tx_no_progress) // tx after current is still dormant
+{
+ Signature *s = de_ctx->sig_array[item->sid];
+
+ /* check if a sig in state 'full inspect' needs to be reconsidered
+ * as the result of a new file in the existing tx */
+ if (item->flags & DE_STATE_FLAG_FULL_INSPECT) {
+ if (item->flags & (DE_STATE_FLAG_FILE_TC_INSPECT|DE_STATE_FLAG_FILE_TS_INSPECT)) {
+ if ((flags & STREAM_TOCLIENT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TC_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ if ((flags & STREAM_TOSERVER) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TS_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
+ }
+ }
+
+ if (item->flags & DE_STATE_FLAG_FULL_INSPECT) {
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ return 0;
+ }
+ }
+
+ /* check if a sig in state 'cant match' needs to be reconsidered
+ * as the result of a new file in the existing tx */
+ if (item->flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
+ if ((flags & STREAM_TOSERVER) &&
+ (item->flags & DE_STATE_FLAG_FILE_TS_INSPECT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TS_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
+ } else if ((flags & STREAM_TOCLIENT) &&
+ (item->flags & DE_STATE_FLAG_FILE_TC_INSPECT) &&
+ (dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW))
+ {
+ item->flags &= ~DE_STATE_FLAG_FILE_TC_INSPECT;
+ item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
+ } else {
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("skip and bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ SCLogDebug("just skip: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ return 0;
+ }
+ }
+
+ uint8_t alert = 0;
+ uint32_t inspect_flags = 0;
+ int total_matches = 0;
+
+ RULE_PROFILING_START(p);
+
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ RULE_PROFILING_END(det_ctx, s, 0, p);
+ return -1;
+ }
+
+ det_ctx->tx_id = inspect_tx_id;
+ det_ctx->tx_id_set = 1;
+ SCLogDebug("inspecting: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+
+ DetectEngineAppInspectionEngine *engine = app_inspection_engine[f->protomap][alproto][(flags & STREAM_TOSERVER) ? 0 : 1];
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx == NULL) {
+ RULE_PROFILING_END(det_ctx, s, 0, p);
+ return -1;
+ }
+
+ while (engine != NULL) {
+ if (!(item->flags & engine->inspect_flags) &&
+ s->sm_lists[engine->sm_list] != NULL)
+ {
+ KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
+ int match = engine->Callback(tv, de_ctx, det_ctx, s, f,
+ flags, alstate, inspect_tx, inspect_tx_id);
+ if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
+ inspect_flags |= engine->inspect_flags;
+ engine = engine->next;
+ total_matches++;
+ continue;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ } else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ inspect_flags |= engine->inspect_flags;
+ (*file_no_match)++;
+ }
+ break;
+ }
+ engine = engine->next;
+ }
+ if (total_matches > 0 && (engine == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ if (engine == NULL)
+ alert = 1;
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+
+ item->flags |= inspect_flags;
+ /* flag this sig to don't inspect again from the detection loop it if
+ * there is no need for it */
+ if (TxIsLast(inspect_tx_id, total_txs) || inprogress || next_tx_no_progress) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ SCLogDebug("inspected, now bypass: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ } else {
+ /* make sure that if we reinspect this right now from
+ * start detection, we skip this tx we just matched on */
+ uint64_t base_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ uint64_t offset = (inspect_tx_id + 1) - base_tx_id;
+ if (offset > MAX_STORED_TXID_OFFSET)
+ offset = MAX_STORED_TXID_OFFSET;
+ det_ctx->de_state_sig_array[item->sid] = (uint8_t)offset;
+#ifdef DEBUG_VALIDATION
+ BUG_ON(det_ctx->de_state_sig_array[item->sid] & DE_STATE_MATCH_NO_NEW_STATE); // check that we don't set the bit
+#endif
+ SCLogDebug("storing tx_id %u for this sid", (uint)inspect_tx_id + 1);
+ }
+ RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
+
+ if (alert) {
+ det_ctx->flow_locked = 1;
+ SigMatchSignaturesRunPostMatch(tv, de_ctx, det_ctx, p, s);
+ det_ctx->flow_locked = 0;
+
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, inspect_tx_id,
+ PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_TX);
+ } else {
+ PACKET_UPDATE_ACTION(p, s->action);
+ }
+ SCLogDebug("MATCH: tx %u packet %u", (uint)inspect_tx_id, (uint)p->pcap_cnt);
+ }
+
+ DetectFlowvarProcessList(det_ctx, f);
+ return 1;
+}
+
+/** \internal
+ * \brief Continue Detection for a single "flow" rule (AMATCH)
+ */
+static int DoInspectFlowRule(ThreadVars *tv,
+ DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
+ DeStateStoreFlowRule *item, const uint8_t dir_state_flags,
+ Packet *p, Flow *f, AppProto alproto, uint8_t flags)
+{
+ /* flag rules that are either full inspected or unable to match
+ * in the de_state_sig_array so that prefilter filters them out */
+ if (item->flags & (DE_STATE_FLAG_FULL_INSPECT|DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ return 0;
+ }
+
+ uint8_t alert = 0;
+ uint32_t inspect_flags = 0;
+ int total_matches = 0;
+ SigMatch *sm = NULL;
+ Signature *s = de_ctx->sig_array[item->sid];
+
+ RULE_PROFILING_START(p);
+
+ KEYWORD_PROFILING_SET_LIST(det_ctx, DETECT_SM_LIST_AMATCH);
+ if (item->nm != NULL) {
+ void *alstate = FlowGetAppState(f);
+ if (alstate == NULL) {
+ RULE_PROFILING_END(det_ctx, s, 0 /* no match */, p);
+ return -1;
+ }
+
+ for (sm = item->nm; sm != NULL; sm = sm->next) {
+ if (sigmatch_table[sm->type].AppLayerMatch != NULL)
+ {
+ int match = 0;
+ if (alproto == ALPROTO_SMB || alproto == ALPROTO_SMB2) {
+ SMBState *smb_state = (SMBState *)alstate;
+ if (smb_state->dcerpc_present) {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, &smb_state->dcerpc, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+ } else {
+ KEYWORD_PROFILING_START;
+ match = sigmatch_table[sm->type].
+ AppLayerMatch(tv, det_ctx, f, flags, alstate, s, sm);
+ KEYWORD_PROFILING_END(det_ctx, sm->type, (match == 1));
+ }
+
+ if (match == 0)
+ break;
+ else if (match == 2)
+ inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
+ else if (match == 1)
+ total_matches++;
+ }
+ }
+ }
+
+ if (s->sm_lists[DETECT_SM_LIST_AMATCH] != NULL) {
+ if (total_matches > 0 && (sm == NULL || inspect_flags & DE_STATE_FLAG_SIG_CANT_MATCH)) {
+ if (sm == NULL)
+ alert = 1;
+ inspect_flags |= DE_STATE_FLAG_FULL_INSPECT;
+ }
+ /* prevent the rule loop from reinspecting this rule */
+ det_ctx->de_state_sig_array[item->sid] = DE_STATE_MATCH_NO_NEW_STATE;
+ }
+ RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
+
+ /* store the progress in the state */
+ item->flags |= inspect_flags;
+ item->nm = sm;
+
+ if (alert) {
+ det_ctx->flow_locked = 1;
+ SigMatchSignaturesRunPostMatch(tv, de_ctx, det_ctx, p, s);
+ det_ctx->flow_locked = 0;
+
+ if (!(s->flags & SIG_FLAG_NOALERT)) {
+ PacketAlertAppend(det_ctx, s, p, 0,
+ PACKET_ALERT_FLAG_STATE_MATCH);
+ } else {
+ DetectSignatureApplyActions(p, s);
+ }
+ }
+
+ DetectFlowvarProcessList(det_ctx, f);
+ return 1;
+}
+
+void DeStateDetectContinueDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
+ DetectEngineThreadCtx *det_ctx,
+ Packet *p, Flow *f, uint8_t flags,
+ AppProto alproto, const uint8_t alversion)
+{
+ uint16_t file_no_match = 0;
+ SigIntId store_cnt = 0;
+ SigIntId state_cnt = 0;
+ uint64_t inspect_tx_id = 0;
+ uint64_t total_txs = 0;
+ uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
+
+ FLOWLOCK_WRLOCK(f);
+
+ SCLogDebug("starting continue detection for packet %"PRIu64, p->pcap_cnt);
+
+ if (AppLayerParserProtocolSupportsTxs(f->proto, alproto)) {
+ void *alstate = FlowGetAppState(f);
+ if (!StateIsValid(alproto, alstate)) {
+ FLOWLOCK_UNLOCK(f);
+ return;
+ }
+
+ inspect_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
+ total_txs = AppLayerParserGetTxCnt(f->proto, alproto, alstate);
+
+ for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
+ int inspect_tx_inprogress = 0;
+ int next_tx_no_progress = 0;
+ void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
+ if (inspect_tx != NULL) {
+ int a = AppLayerParserGetStateProgress(f->proto, alproto, inspect_tx, flags);
+ int b = AppLayerParserGetStateProgressCompletionStatus(f->proto, alproto, flags);
+ if (a < b) {
+ inspect_tx_inprogress = 1;
+ }
+ SCLogDebug("tx %"PRIu64" (%"PRIu64") => %s", inspect_tx_id, total_txs,
+ inspect_tx_inprogress ? "in progress" : "done");
+
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ SCLogDebug("NO STATE tx %"PRIu64" (%"PRIu64")", inspect_tx_id, total_txs);
+ continue;
+ }
+ DetectEngineStateDirection *tx_dir_state = &tx_de_state->dir_state[direction];
+ DeStateStore *tx_store = tx_dir_state->head;
+
+ /* see if we need to consider the next tx in our decision to add
+ * a sig to the 'no inspect array'. */
+ if (!TxIsLast(inspect_tx_id, total_txs)) {
+ void *next_inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id+1);
+ if (next_inspect_tx != NULL) {
+ int c = AppLayerParserGetStateProgress(f->proto, alproto, next_inspect_tx, flags);
+ if (c == 0) {
+ next_tx_no_progress = 1;
+ }
+ }
+ }
+
+ /* Loop through stored 'items' (stateful rules) and inspect them */
+ 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 < tx_dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreItem *item = &tx_store->store[store_cnt];
+ int r = DoInspectItem(tv, de_ctx, det_ctx,
+ item, tx_dir_state->flags,
+ p, f, alproto, flags,
+ inspect_tx_id, total_txs,
+ &file_no_match, inspect_tx_inprogress, next_tx_no_progress);
+ if (r < 0) {
+ SCLogDebug("failed");
+ goto end;
+ }
+ }
+ }
+ }
+ /* if the current tx is in progress, we won't advance to any newer
+ * tx' just yet. */
+ if (inspect_tx_inprogress) {
+ SCLogDebug("break out");
+ break;
+ }
+ }
+ }
+
+ /* continue on flow based state rules (AMATCH) */
+ if (f->de_state != NULL) {
+ DetectEngineStateDirectionFlow *dir_state = &f->de_state->dir_state[direction];
+ DeStateStoreFlowRules *store = dir_state->head;
+ /* Loop through stored 'items' (stateful rules) and inspect them */
+ for (; store != NULL; store = store->next) {
+ for (store_cnt = 0;
+ store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < dir_state->cnt;
+ store_cnt++, state_cnt++)
+ {
+ DeStateStoreFlowRule *rule = &store->store[store_cnt];
+
+ int r = DoInspectFlowRule(tv, de_ctx, det_ctx,
+ rule, dir_state->flags,
+ p, f, alproto, flags);
+ if (r < 0) {
+ goto end;
+ }
+ }
+ }
+ DeStateStoreStateVersion(f, alversion, flags);
+ }
+
+end:
+ FLOWLOCK_UNLOCK(f);
+ det_ctx->tx_id = 0;
+ det_ctx->tx_id_set = 0;
+ return;
+}
+/** \brief update flow's inspection id's
+ *
+ * \param f unlocked flow
+ * \param flags direction and disruption flags
+ *
+ * \note it is possible that f->alstate, f->alparser are NULL */
+void DeStateUpdateInspectTransactionId(Flow *f, const uint8_t flags)
+{
+ FLOWLOCK_WRLOCK(f);
+ if (f->alparser && f->alstate) {
+ AppLayerParserSetTransactionInspectId(f->alparser, f->proto,
+ f->alproto, f->alstate, flags);
+ }
+ FLOWLOCK_UNLOCK(f);
+
+ return;
+}
+
+void DetectEngineStateReset(DetectEngineStateFlow *state, uint8_t direction)
+{
+ if (state != NULL) {
+ if (direction & STREAM_TOSERVER) {
+ state->dir_state[0].cnt = 0;
+ state->dir_state[0].flags = 0;
+ }
+ if (direction & STREAM_TOCLIENT) {
+ state->dir_state[1].cnt = 0;
+ state->dir_state[1].flags = 0;
+ }
+ }
+
+ return;
+}
+
+/** \brief Reset de state for active tx'
+ * To be used on detect engine reload.
+ * \param f write LOCKED flow
+ */
+void DetectEngineStateResetTxs(Flow *f)
+{
+ if (AppLayerParserProtocolSupportsTxs(f->proto, f->alproto)) {
+ 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->proto, f->alproto, 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) {
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(f->proto, f->alproto, inspect_tx);
+ if (tx_de_state == NULL) {
+ continue;
+ }
+
+ tx_de_state->dir_state[0].cnt = 0;
+ tx_de_state->dir_state[0].filestore_cnt = 0;
+ tx_de_state->dir_state[0].flags = 0;
+
+ tx_de_state->dir_state[1].cnt = 0;
+ tx_de_state->dir_state[1].filestore_cnt = 0;
+ tx_de_state->dir_state[1].flags = 0;
+ }
+ }
+ }
+}
+
+/*********Unittests*********/
+
+#ifdef UNITTESTS
+#include "flow-util.h"
+
+static int DeStateTest01(void)
+{
+ SCLogDebug("sizeof(DetectEngineState)\t\t%"PRIuMAX,
+ (uintmax_t)sizeof(DetectEngineState));
+ SCLogDebug("sizeof(DeStateStore)\t\t\t%"PRIuMAX,
+ (uintmax_t)sizeof(DeStateStore));
+ SCLogDebug("sizeof(DeStateStoreItem)\t\t%"PRIuMAX"",
+ (uintmax_t)sizeof(DeStateStoreItem));
+
+ return 1;
+}
+
+static int DeStateTest02(void)
+{
+ int result = 0;
+
+ DetectEngineState *state = DetectEngineStateAlloc();
+ if (state == NULL) {
+ printf("d == NULL: ");
+ goto end;
+ }
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ uint8_t direction = STREAM_TOSERVER;
+
+ 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);
+ s.num = 144;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 155;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 166;
+ DeStateSignatureAppend(state, &s, 0, direction);
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 11) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[14].sid != 144) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[0].sid != 155) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->next->store[1].sid != 166) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (state != NULL) {
+ DetectEngineStateFree(state);
+ }
+ return result;
+}
+
+static int DeStateTest03(void)
+{
+ int result = 0;
+
+ DetectEngineState *state = DetectEngineStateAlloc();
+ if (state == NULL) {
+ printf("d == NULL: ");
+ goto end;
+ }
+
+ Signature s;
+ memset(&s, 0x00, sizeof(s));
+
+ uint8_t direction = STREAM_TOSERVER;
+
+ s.num = 11;
+ DeStateSignatureAppend(state, &s, 0, direction);
+ s.num = 22;
+ DeStateSignatureAppend(state, &s, DE_STATE_FLAG_URI_INSPECT, direction);
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head == NULL) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].sid != 11) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[0].flags & DE_STATE_FLAG_URI_INSPECT) {
+ goto end;
+ }
+
+ if (state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].sid != 22) {
+ goto end;
+ }
+
+ if (!(state->dir_state[direction & STREAM_TOSERVER ? 0 : 1].head->store[1].flags & DE_STATE_FLAG_URI_INSPECT)) {
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (state != NULL) {
+ DetectEngineStateFree(state);
+ }
+ return result;
+}
+
+static int DeStateSigTest01(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = 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 */
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ 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_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"dummy\"; http_cookie; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("signature matched, but shouldn't have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (http_state != NULL) {
+ HTPStateFree(http_state);
+ }
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+/** \test multiple pipelined http transactions */
+static int DeStateSigTest02(void)
+{
+ int result = 0;
+ Signature *s = NULL;
+ DetectEngineThreadCtx *det_ctx = NULL;
+ ThreadVars th_v;
+ Flow f;
+ TcpSession ssn;
+ Packet *p = 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();
+
+ 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_HTTP;
+
+ StreamTcpInitConfig(TRUE);
+
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ de_ctx->flags |= DE_QUIET;
+
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"POST\"; http_method; content:\"/\"; http_uri; content:\"Mozilla\"; http_header; content:\"dummy\"; http_cookie; content:\"body\"; nocase; http_client_body; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+ s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"GET\"; http_method; content:\"Firefox\"; http_header; content:\"dummy2\"; http_cookie; sid:2; rev:1;)");
+ if (s == NULL) {
+ printf("sig2 parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ SCMutexLock(&f.m);
+ int r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (2): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf3, httplen3);
+ if (r != 0) {
+ printf("toserver chunk 3 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted too early: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ void *tx = AppLayerParserGetTx(IPPROTO_TCP, ALPROTO_HTTP, f.alstate, 0);
+ if (tx == NULL) {
+ printf("no http tx: ");
+ goto end;
+ }
+ DetectEngineState *tx_de_state = AppLayerParserGetTxDetectState(IPPROTO_TCP, ALPROTO_HTTP, tx);
+ if (tx_de_state == NULL || tx_de_state->dir_state[0].cnt != 1 ||
+ tx_de_state->dir_state[0].head->store[0].flags != 0x00000001) {
+ printf("de_state not present or has unexpected content: ");
+ goto end;
+ }
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf4, httplen4);
+ if (r != 0) {
+ printf("toserver chunk 4 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't match: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf5, httplen5);
+ if (r != 0) {
+ printf("toserver chunk 5 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted (5): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf6, httplen6);
+ if (r != 0) {
+ printf("toserver chunk 6 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if ((PacketAlertCheck(p, 1)) || (PacketAlertCheck(p, 2))) {
+ printf("sig 1 alerted (request 2, chunk 6): ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ SCLogDebug("sending data chunk 7");
+
+ SCMutexLock(&f.m);
+ r = AppLayerParserParse(alp_tctx, &f, ALPROTO_HTTP, STREAM_TOSERVER, httpbuf7, httplen7);
+ if (r != 0) {
+ printf("toserver chunk 7 returned %" PRId32 ", expected 0: ", r);
+ SCMutexUnlock(&f.m);
+ goto end;
+ }
+ SCMutexUnlock(&f.m);
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 2))) {
+ printf("signature 2 didn't match, but should have: ");
+ goto end;
+ }
+ p->alerts.cnt = 0;
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+
+ StreamTcpFreeConfig(TRUE);
+ FLOW_DESTROY(&f);
+ UTHFreePacket(p);
+ return result;
+}
+
+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;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ 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; filestore; sid:1; rev:1;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ SigGroupBuild(de_ctx);
+ DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
+
+ f = UTHBuildFlow(AF_INET, "1.2.3.4", "1.2.3.5", 1024, 80);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (!(PacketAlertCheck(p, 1))) {
+ printf("sig 1 didn't alert: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_STORE)) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+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;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ 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;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ 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);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (file->flags & FILE_STORE) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+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;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ 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;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ 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);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_NOSTORE)) {
+ printf("file is not set to \"no store\": ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+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;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ 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;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ 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);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START|STREAM_EOF, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (!(file->flags & FILE_NOSTORE)) {
+ printf("file is not set to \"no store\": ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+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;
+ int result = 0;
+ Flow *f = NULL;
+ Packet *p = NULL;
+ HtpState *http_state = NULL;
+ AppLayerParserThreadCtx *alp_tctx = AppLayerParserThreadCtxAlloc();
+
+ memset(&th_v, 0, sizeof(th_v));
+ memset(&ssn, 0, sizeof(ssn));
+
+ DetectEngineThreadCtx *det_ctx = NULL;
+ DetectEngineCtx *de_ctx = DetectEngineCtxInit();
+ if (de_ctx == NULL) {
+ goto end;
+ }
+
+ 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;)");
+ if (s == NULL) {
+ printf("sig parse failed: ");
+ goto end;
+ }
+
+ 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);
+ if (f == NULL)
+ goto end;
+ f->protoctx = &ssn;
+ f->proto = IPPROTO_TCP;
+ f->alproto = ALPROTO_HTTP;
+
+ p = UTHBuildPacket(NULL, 0, IPPROTO_TCP);
+ if (p == NULL)
+ goto end;
+
+ p->flow = f;
+ p->flags |= PKT_HAS_FLOW|PKT_STREAM_EST;
+ p->flowflags |= FLOW_PKT_TOSERVER;
+ p->flowflags |= FLOW_PKT_ESTABLISHED;
+
+ StreamTcpInitConfig(TRUE);
+
+ SCLogDebug("\n>>>> processing chunk 1 <<<<\n");
+ SCMutexLock(&f->m);
+ int r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_START, httpbuf1, httplen1);
+ if (r != 0) {
+ printf("toserver chunk 1 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ /* do detect */
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ SCLogDebug("\n>>>> processing chunk 2 size %u <<<<\n", httplen2);
+ SCMutexLock(&f->m);
+ r = AppLayerParserParse(alp_tctx, f, ALPROTO_HTTP, STREAM_TOSERVER|STREAM_EOF, httpbuf2, httplen2);
+ if (r != 0) {
+ printf("toserver chunk 2 returned %" PRId32 ", expected 0: ", r);
+ result = 0;
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
+ if (PacketAlertCheck(p, 1)) {
+ printf("sig 1 alerted: ");
+ goto end;
+ }
+
+ http_state = f->alstate;
+ if (http_state == NULL) {
+ printf("no http state: ");
+ result = 0;
+ goto end;
+ }
+
+ if (http_state->files_ts == NULL) {
+ printf("no files in state: ");
+ goto end;
+ }
+
+ SCMutexLock(&f->m);
+ FileContainer *files = AppLayerParserGetFiles(p->flow->proto, p->flow->alproto,
+ p->flow->alstate, STREAM_TOSERVER);
+ if (files == NULL) {
+ printf("no stored files: ");
+ SCMutexUnlock(&f->m);
+ goto end;
+ }
+ SCMutexUnlock(&f->m);
+
+ File *file = files->head;
+ if (file == NULL) {
+ printf("no file: ");
+ goto end;
+ }
+
+ if (file->flags & FILE_STORE) {
+ printf("file is set to store, but sig didn't match: ");
+ goto end;
+ }
+
+ result = 1;
+end:
+ if (alp_tctx != NULL)
+ AppLayerParserThreadCtxFree(alp_tctx);
+ UTHFreeFlow(f);
+
+ if (det_ctx != NULL) {
+ DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
+ }
+ if (de_ctx != NULL) {
+ SigGroupCleanup(de_ctx);
+ DetectEngineCtxFree(de_ctx);
+ }
+ StreamTcpFreeConfig(TRUE);
+ return result;
+}
+
+#endif
+
+void DeStateRegisterTests(void)
+{
+#ifdef UNITTESTS
+ UtRegisterTest("DeStateTest01", DeStateTest01, 1);
+ UtRegisterTest("DeStateTest02", DeStateTest02, 1);
+ UtRegisterTest("DeStateTest03", DeStateTest03, 1);
+ UtRegisterTest("DeStateSigTest01", DeStateSigTest01, 1);
+ UtRegisterTest("DeStateSigTest02", DeStateSigTest02, 1);
+ UtRegisterTest("DeStateSigTest03", DeStateSigTest03, 1);
+ UtRegisterTest("DeStateSigTest04", DeStateSigTest04, 1);
+ UtRegisterTest("DeStateSigTest05", DeStateSigTest05, 1);
+ UtRegisterTest("DeStateSigTest06", DeStateSigTest06, 1);
+ UtRegisterTest("DeStateSigTest07", DeStateSigTest07, 1);
+#endif
+
+ return;
+}
+
+/**
+ * @}
+ */