diff options
Diffstat (limited to 'framework/src/suricata/src/detect-engine-state.c')
-rw-r--r-- | framework/src/suricata/src/detect-engine-state.c | 2346 |
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; +} + +/** + * @} + */ |