|
|
|
|
@ -18,23 +18,12 @@
|
|
|
|
|
/**
|
|
|
|
|
* \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
|
|
|
|
|
* State is stored in the ::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.
|
|
|
|
|
*
|
|
|
|
|
* @{
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
@ -81,22 +70,6 @@
|
|
|
|
|
/** convert enum to string */
|
|
|
|
|
#define CASE_CODE(E) case E: return #E
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/** 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 *********/
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static inline int StateIsValid(uint16_t alproto, void *alstate)
|
|
|
|
|
{
|
|
|
|
|
if (alstate != NULL) {
|
|
|
|
|
@ -112,15 +85,6 @@ static inline int StateIsValid(uint16_t alproto, void *alstate)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
static inline int TxIsLast(uint64_t tx_id, uint64_t total_txs)
|
|
|
|
|
{
|
|
|
|
|
if (total_txs - tx_id <= 1)
|
|
|
|
|
return 1;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static DeStateStore *DeStateStoreAlloc(void)
|
|
|
|
|
{
|
|
|
|
|
DeStateStore *d = SCMalloc(sizeof(DeStateStore));
|
|
|
|
|
@ -279,584 +243,6 @@ void DetectRunStoreStateTx(
|
|
|
|
|
SCLogDebug("Stored for TX %"PRIu64, tx_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DetectRunStoreStateTxFileOnly(
|
|
|
|
|
const SigGroupHead *sgh,
|
|
|
|
|
Flow *f, void *tx, uint64_t tx_id,
|
|
|
|
|
const uint8_t flow_flags,
|
|
|
|
|
const uint16_t file_no_match)
|
|
|
|
|
{
|
|
|
|
|
DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
|
|
|
|
|
if (destate == NULL) {
|
|
|
|
|
destate = DetectEngineStateAlloc();
|
|
|
|
|
if (destate == NULL)
|
|
|
|
|
return;
|
|
|
|
|
if (AppLayerParserSetTxDetectState(f, f->alstate, tx, destate) < 0) {
|
|
|
|
|
DetectEngineStateFree(destate);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SCLogDebug("destate created for %"PRIu64, tx_id);
|
|
|
|
|
}
|
|
|
|
|
StoreStateTxHandleFiles(sgh, f, destate, flow_flags, tx_id, file_no_match);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
/**
|
|
|
|
|
* \param check_before_add check for duplicates before adding the sig
|
|
|
|
|
*/
|
|
|
|
|
static void StoreStateTx(DetectEngineThreadCtx *det_ctx,
|
|
|
|
|
Flow *f, const uint8_t flags,
|
|
|
|
|
const uint64_t tx_id, void *tx,
|
|
|
|
|
const Signature *s, const SigMatchData *smd,
|
|
|
|
|
const uint32_t inspect_flags, const uint16_t file_no_match, int check_before_add)
|
|
|
|
|
{
|
|
|
|
|
DetectEngineState *destate = AppLayerParserGetTxDetectState(f->proto, f->alproto, tx);
|
|
|
|
|
if (destate == NULL) {
|
|
|
|
|
destate = DetectEngineStateAlloc();
|
|
|
|
|
if (destate == NULL)
|
|
|
|
|
return;
|
|
|
|
|
if (AppLayerParserSetTxDetectState(f, f->alstate, tx, destate) < 0) {
|
|
|
|
|
DetectEngineStateFree(destate);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
SCLogDebug("destate created for %"PRIu64, tx_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SCLogDebug("file_no_match %u", file_no_match);
|
|
|
|
|
|
|
|
|
|
if (check_before_add == 0 || DeStateSearchState(destate, flags, s->num) == 0)
|
|
|
|
|
DeStateSignatureAppend(destate, s, inspect_flags, flags);
|
|
|
|
|
|
|
|
|
|
StoreStateTxHandleFiles(det_ctx, f, destate, flags, tx_id, file_no_match);
|
|
|
|
|
SCLogDebug("Stored for TX %"PRIu64, tx_id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int HasStoredSigs(const Flow *f, const uint8_t flags)
|
|
|
|
|
{
|
|
|
|
|
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, 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 %"PRIu64" has sigs present", 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
|
|
|
|
|
*/
|
|
|
|
|
int DeStateFlowHasInspectableState(const Flow *f, const uint8_t flags)
|
|
|
|
|
{
|
|
|
|
|
int r = 0;
|
|
|
|
|
|
|
|
|
|
if (HasStoredSigs(f, flags)) {
|
|
|
|
|
r = 1;
|
|
|
|
|
} else {
|
|
|
|
|
r = 0;
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* returns: true match, false no match */
|
|
|
|
|
bool DeStateDetectStartDetection(ThreadVars *tv,
|
|
|
|
|
DetectEngineCtx *de_ctx,
|
|
|
|
|
DetectEngineThreadCtx *det_ctx,
|
|
|
|
|
const Signature *s, Packet *p, Flow *f,
|
|
|
|
|
const uint8_t flags, // direction
|
|
|
|
|
const AppProto alproto)
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("rule %u/%u", s->id, s->num);
|
|
|
|
|
|
|
|
|
|
/* TX based matches (inspect engines) */
|
|
|
|
|
void *alstate = FlowGetAppState(f);
|
|
|
|
|
if (unlikely(!StateIsValid(alproto, alstate))) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SigMatchData *smd = NULL;
|
|
|
|
|
uint16_t file_no_match = 0;
|
|
|
|
|
uint32_t inspect_flags = 0;
|
|
|
|
|
int alert_cnt = 0;
|
|
|
|
|
uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
|
|
|
|
|
int check_before_add = 0;
|
|
|
|
|
|
|
|
|
|
/* 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] & 0x7f;
|
|
|
|
|
uint64_t tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
|
|
|
|
|
if (offset > 0) {
|
|
|
|
|
SCLogDebug("using stored_tx_id %"PRIu64" instead of %"PRIu64, tx_id+offset, tx_id);
|
|
|
|
|
tx_id += offset;
|
|
|
|
|
}
|
|
|
|
|
if (offset == MAX_STORED_TXID_OFFSET) {
|
|
|
|
|
check_before_add = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
|
|
|
|
|
SCLogDebug("total_txs %"PRIu64, total_txs);
|
|
|
|
|
|
|
|
|
|
SCLogDebug("starting: start tx %"PRIu64", packet %"PRIu64, tx_id, p->pcap_cnt);
|
|
|
|
|
|
|
|
|
|
det_ctx->stream_already_inspected = false;
|
|
|
|
|
for (; tx_id < total_txs; tx_id++) {
|
|
|
|
|
int total_matches = 0;
|
|
|
|
|
void *tx = AppLayerParserGetTx(f->proto, alproto, alstate, tx_id);
|
|
|
|
|
SCLogDebug("tx %p", tx);
|
|
|
|
|
if (tx == NULL)
|
|
|
|
|
continue;
|
|
|
|
|
det_ctx->tx_id = tx_id;
|
|
|
|
|
det_ctx->tx_id_set = 1;
|
|
|
|
|
det_ctx->p = p;
|
|
|
|
|
int tx_progress = AppLayerParserGetStateProgress(f->proto, alproto, tx, flags);
|
|
|
|
|
|
|
|
|
|
/* see if we need to consider the next tx in our decision to add
|
|
|
|
|
* a sig to the 'no inspect array'. */
|
|
|
|
|
bool next_tx_no_progress = false;
|
|
|
|
|
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 = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DetectEngineAppInspectionEngine *engine = s->app_inspect;
|
|
|
|
|
SCLogDebug("engine %p", engine);
|
|
|
|
|
inspect_flags = 0;
|
|
|
|
|
while (engine != NULL) {
|
|
|
|
|
SCLogDebug("engine %p", engine);
|
|
|
|
|
SCLogDebug("inspect_flags %x", inspect_flags);
|
|
|
|
|
if (direction == engine->dir) {
|
|
|
|
|
if (tx_progress < engine->progress) {
|
|
|
|
|
SCLogDebug("tx progress %d < engine progress %d",
|
|
|
|
|
tx_progress, engine->progress);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
|
|
|
|
|
int match = engine->Callback(tv, de_ctx, det_ctx,
|
|
|
|
|
s, engine->smd, f, flags, alstate, tx, tx_id);
|
|
|
|
|
SCLogDebug("engine %p match %d", engine, match);
|
|
|
|
|
if ((match == DETECT_ENGINE_INSPECT_SIG_NO_MATCH || match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH)
|
|
|
|
|
&& (engine->mpm)) {
|
|
|
|
|
SCLogDebug("MPM and not matching, so skip the whole TX");
|
|
|
|
|
// TODO
|
|
|
|
|
goto try_next;
|
|
|
|
|
} else
|
|
|
|
|
if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
|
|
|
|
|
inspect_flags |= BIT_U32(engine->id);
|
|
|
|
|
engine = engine->next;
|
|
|
|
|
total_matches++;
|
|
|
|
|
continue;
|
|
|
|
|
} else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
|
|
|
|
|
/* if the file engine matched, but indicated more
|
|
|
|
|
* files are still in progress, we don't set inspect
|
|
|
|
|
* flags as these would end inspection for this tx */
|
|
|
|
|
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 |= BIT_U32(engine->id);
|
|
|
|
|
} else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
|
|
|
|
|
inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
|
|
|
|
|
inspect_flags |= BIT_U32(engine->id);
|
|
|
|
|
file_no_match++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
engine = engine->next;
|
|
|
|
|
}
|
|
|
|
|
SCLogDebug("inspect_flags %x", inspect_flags);
|
|
|
|
|
/* 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,
|
|
|
|
|
PACKET_ALERT_FLAG_STATE_MATCH|PACKET_ALERT_FLAG_TX);
|
|
|
|
|
}
|
|
|
|
|
alert_cnt = 1;
|
|
|
|
|
SCLogDebug("MATCH: tx %"PRIu64" packet %"PRIu64, tx_id, 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 = (tx_progress >=
|
|
|
|
|
AppLayerParserGetStateProgressCompletionStatus(alproto, flags));
|
|
|
|
|
|
|
|
|
|
SCLogDebug("tx %"PRIu64", packet %"PRIu64", rule %u, alert_cnt %u, "
|
|
|
|
|
"last tx %d, tx_is_done %d, next_tx_no_progress %s",
|
|
|
|
|
tx_id, p->pcap_cnt, s->num, alert_cnt,
|
|
|
|
|
TxIsLast(tx_id, total_txs), tx_is_done,
|
|
|
|
|
next_tx_no_progress ? "true" : "false");
|
|
|
|
|
|
|
|
|
|
/* store our state */
|
|
|
|
|
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, tx_id, tx,
|
|
|
|
|
s, smd, inspect_flags, file_no_match, check_before_add);
|
|
|
|
|
} else {
|
|
|
|
|
StoreStateTxFileOnly(det_ctx, f, flags, tx_id, tx, file_no_match);
|
|
|
|
|
}
|
|
|
|
|
try_next:
|
|
|
|
|
if (next_tx_no_progress)
|
|
|
|
|
break;
|
|
|
|
|
} /* for */
|
|
|
|
|
|
|
|
|
|
det_ctx->tx_id = 0;
|
|
|
|
|
det_ctx->tx_id_set = 0;
|
|
|
|
|
det_ctx->p = NULL;
|
|
|
|
|
return (alert_cnt > 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,
|
|
|
|
|
const bool inprogress, // is current tx in progress?
|
|
|
|
|
const bool next_tx_no_progress) // tx after current is still dormant
|
|
|
|
|
{
|
|
|
|
|
Signature *s = de_ctx->sig_array[item->sid];
|
|
|
|
|
det_ctx->stream_already_inspected = false;
|
|
|
|
|
|
|
|
|
|
SCLogDebug("file_no_match %u, sid %u", *file_no_match, s->id);
|
|
|
|
|
|
|
|
|
|
/* 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))
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("~DE_STATE_FLAG_FILE_TC_INSPECT");
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_FILE_TC_INSPECT;
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((flags & STREAM_TOSERVER) &&
|
|
|
|
|
(dir_state_flags & DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW))
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("~DE_STATE_FLAG_FILE_TS_INSPECT");
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_FILE_TS_INSPECT;
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_FULL_INSPECT;
|
|
|
|
|
item->flags &= ~DE_STATE_FLAG_SIG_CANT_MATCH;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 %u: tx %"PRIu64" packet %"PRIu64, s->id, inspect_tx_id, p->pcap_cnt);
|
|
|
|
|
} else {
|
|
|
|
|
SCLogDebug("just skip: tx %"PRIu64" packet %"PRIu64, inspect_tx_id, 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 %"PRIu64" for this sid", 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 */
|
|
|
|
|
SCLogDebug("item->flags %x", item->flags);
|
|
|
|
|
if (item->flags & DE_STATE_FLAG_SIG_CANT_MATCH) {
|
|
|
|
|
SCLogDebug("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))
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("unset ~DE_STATE_FLAG_FILE_TS_INSPECT ~DE_STATE_FLAG_SIG_CANT_MATCH");
|
|
|
|
|
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))
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("unset ~DE_STATE_FLAG_FILE_TC_INSPECT ~DE_STATE_FLAG_SIG_CANT_MATCH");
|
|
|
|
|
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 %"PRIu64" packet %"PRIu64, inspect_tx_id, p->pcap_cnt);
|
|
|
|
|
} else {
|
|
|
|
|
SCLogDebug("just skip: tx %"PRIu64" packet %"PRIu64, inspect_tx_id, 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 %"PRIu64" for this sid", 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;
|
|
|
|
|
det_ctx->p = p;
|
|
|
|
|
SCLogDebug("inspecting: tx %"PRIu64" packet %"PRIu64, inspect_tx_id, p->pcap_cnt);
|
|
|
|
|
|
|
|
|
|
uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
|
|
|
|
|
DetectEngineAppInspectionEngine *engine = s->app_inspect;
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
int tx_progress = AppLayerParserGetStateProgress(f->proto, alproto, inspect_tx, flags);
|
|
|
|
|
|
|
|
|
|
while (engine != NULL) {
|
|
|
|
|
if (!(item->flags & BIT_U32(engine->id)) &&
|
|
|
|
|
direction == engine->dir)
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("inspect_flags %x", inspect_flags);
|
|
|
|
|
|
|
|
|
|
if (tx_progress < engine->progress) {
|
|
|
|
|
SCLogDebug("tx progress %d < engine progress %d",
|
|
|
|
|
tx_progress, engine->progress);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
|
|
|
|
|
int match = engine->Callback(tv, de_ctx, det_ctx,
|
|
|
|
|
s, engine->smd,
|
|
|
|
|
f, flags, alstate, inspect_tx, inspect_tx_id);
|
|
|
|
|
if (match == DETECT_ENGINE_INSPECT_SIG_MATCH) {
|
|
|
|
|
inspect_flags |= BIT_U32(engine->id);
|
|
|
|
|
engine = engine->next;
|
|
|
|
|
total_matches++;
|
|
|
|
|
continue;
|
|
|
|
|
} else if (match == DETECT_ENGINE_INSPECT_SIG_MATCH_MORE_FILES) {
|
|
|
|
|
/* if the file engine matched, but indicated more
|
|
|
|
|
* files are still in progress, we don't set inspect
|
|
|
|
|
* flags as these would end inspection for this tx */
|
|
|
|
|
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 |= BIT_U32(engine->id);
|
|
|
|
|
} else if (match == DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILESTORE) {
|
|
|
|
|
inspect_flags |= DE_STATE_FLAG_SIG_CANT_MATCH;
|
|
|
|
|
inspect_flags |= BIT_U32(engine->id);
|
|
|
|
|
(*file_no_match)++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
engine = engine->next;
|
|
|
|
|
}
|
|
|
|
|
SCLogDebug("inspect_flags %x", inspect_flags);
|
|
|
|
|
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 %"PRIu64" packet %"PRIu64, inspect_tx_id, 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 %"PRIu64" for this sid", inspect_tx_id + 1);
|
|
|
|
|
}
|
|
|
|
|
RULE_PROFILING_END(det_ctx, s, (alert == 1), p);
|
|
|
|
|
|
|
|
|
|
if (alert) {
|
|
|
|
|
SigMatchSignaturesRunPostMatch(tv, de_ctx, det_ctx, p, s);
|
|
|
|
|
|
|
|
|
|
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 %"PRIu64" packet %"PRIu64, inspect_tx_id, p->pcap_cnt);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DetectVarProcessList(det_ctx, f, p);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void DeStateDetectContinueDetection(ThreadVars *tv, DetectEngineCtx *de_ctx,
|
|
|
|
|
DetectEngineThreadCtx *det_ctx,
|
|
|
|
|
Packet *p, Flow *f, const uint8_t flags,
|
|
|
|
|
AppProto alproto)
|
|
|
|
|
{
|
|
|
|
|
SCLogDebug("starting continue detection for packet %"PRIu64, p->pcap_cnt);
|
|
|
|
|
|
|
|
|
|
void *alstate = FlowGetAppState(f);
|
|
|
|
|
if (unlikely(!StateIsValid(alproto, alstate))) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint8_t direction = (flags & STREAM_TOSERVER) ? 0 : 1;
|
|
|
|
|
uint64_t inspect_tx_id = AppLayerParserGetTransactionInspectId(f->alparser, flags);
|
|
|
|
|
const uint64_t total_txs = AppLayerParserGetTxCnt(f, alstate);
|
|
|
|
|
const int end_progress = AppLayerParserGetStateProgressCompletionStatus(alproto, flags);
|
|
|
|
|
|
|
|
|
|
for ( ; inspect_tx_id < total_txs; inspect_tx_id++) {
|
|
|
|
|
bool inspect_tx_inprogress = false;
|
|
|
|
|
void *inspect_tx = AppLayerParserGetTx(f->proto, alproto, alstate, inspect_tx_id);
|
|
|
|
|
if (inspect_tx != NULL) {
|
|
|
|
|
bool next_tx_no_progress = false;
|
|
|
|
|
|
|
|
|
|
const int tx_progress = AppLayerParserGetStateProgress(f->proto, alproto, inspect_tx, flags);
|
|
|
|
|
if (end_progress < tx_progress) {
|
|
|
|
|
inspect_tx_inprogress = true;
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
SCLogDebug("tx_dir_state->filestore_cnt %u", tx_dir_state->filestore_cnt);
|
|
|
|
|
|
|
|
|
|
/* 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 = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Loop through stored 'items' (stateful rules) and inspect them */
|
|
|
|
|
SigIntId state_cnt = 0;
|
|
|
|
|
for (; tx_store != NULL; tx_store = tx_store->next) {
|
|
|
|
|
SCLogDebug("tx_store %p", tx_store);
|
|
|
|
|
|
|
|
|
|
SigIntId store_cnt = 0;
|
|
|
|
|
for (store_cnt = 0;
|
|
|
|
|
store_cnt < DE_STATE_CHUNK_SIZE && state_cnt < tx_dir_state->cnt;
|
|
|
|
|
store_cnt++, state_cnt++)
|
|
|
|
|
{
|
|
|
|
|
uint16_t file_no_match = 0; // TODO looks like we're just ignoring this
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tx_dir_state->flags &=
|
|
|
|
|
~(DETECT_ENGINE_STATE_FLAG_FILE_TS_NEW|DETECT_ENGINE_STATE_FLAG_FILE_TC_NEW);
|
|
|
|
|
}
|
|
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
end:
|
|
|
|
|
det_ctx->p = NULL;
|
|
|
|
|
det_ctx->tx_id = 0;
|
|
|
|
|
det_ctx->tx_id_set = 0;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/** \brief update flow's inspection id's
|
|
|
|
|
*
|
|
|
|
|
* \param f unlocked flow
|
|
|
|
|
|