mirror of
https://github.com/OISF/suricata.git
synced 2026-10-12 02:07:10 +00:00
detect/xbits: implement tx bits
Implement `xbits:set,mybit,track tx;` and `xbits:isset,mybit,track tx;`. Store these in the AppLayerTxData. Ticket: #6455.
This commit is contained in:
committed by
Victor Julien
parent
7c8a55de54
commit
10dcc383ae
@@ -97,6 +97,7 @@ exclude = [
|
||||
"AppLayerParserState",
|
||||
"CLuaState",
|
||||
"DetectEngineState",
|
||||
"GenericVar",
|
||||
"Flow",
|
||||
"StreamingBufferConfig",
|
||||
"HttpRangeContainerBlock",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! Parser registration functions and common interface module.
|
||||
|
||||
use std;
|
||||
use crate::core::{self,DetectEngineState,AppLayerEventType};
|
||||
use crate::core::{self,DetectEngineState,AppLayerEventType, GenericVar};
|
||||
use crate::direction::Direction;
|
||||
use crate::filecontainer::FileContainer;
|
||||
use crate::flow::Flow;
|
||||
@@ -147,6 +147,7 @@ pub struct AppLayerTxData {
|
||||
|
||||
de_state: *mut DetectEngineState,
|
||||
pub events: *mut core::AppLayerDecoderEvents,
|
||||
txbits: *mut GenericVar,
|
||||
}
|
||||
|
||||
impl Default for AppLayerTxData {
|
||||
@@ -175,6 +176,9 @@ impl AppLayerTxData {
|
||||
if !self.events.is_null() {
|
||||
core::sc_app_layer_decoder_events_free_events(&mut self.events);
|
||||
}
|
||||
if !self.txbits.is_null() {
|
||||
core::sc_generic_var_free(self.txbits);
|
||||
}
|
||||
}
|
||||
|
||||
/// Create new AppLayerTxData for a transaction that covers both
|
||||
@@ -196,6 +200,7 @@ impl AppLayerTxData {
|
||||
detect_progress_tc: 0,
|
||||
de_state: std::ptr::null_mut(),
|
||||
events: std::ptr::null_mut(),
|
||||
txbits: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,6 +227,7 @@ impl AppLayerTxData {
|
||||
flags,
|
||||
de_state: std::ptr::null_mut(),
|
||||
events: std::ptr::null_mut(),
|
||||
txbits: std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ use crate::flow::Flow;
|
||||
/// Opaque C types.
|
||||
pub enum DetectEngineState {}
|
||||
pub enum AppLayerDecoderEvents {}
|
||||
pub enum GenericVar {}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
@@ -148,6 +149,9 @@ pub type SCFileContainerRecycle = extern "C" fn (
|
||||
file_container: &FileContainer,
|
||||
sbcfg: &StreamingBufferConfig);
|
||||
|
||||
pub type GenericVarFreeFunc =
|
||||
extern "C" fn(gvar: *mut GenericVar);
|
||||
|
||||
// A Suricata context that is passed in from C. This is alternative to
|
||||
// using functions from Suricata directly, so they can be wrapped so
|
||||
// Rust unit tests will still compile when they are not linked
|
||||
@@ -172,6 +176,8 @@ pub struct SuricataContext {
|
||||
pub FileAppendData: SCFileAppendDataById,
|
||||
pub FileAppendGAP: SCFileAppendGAPById,
|
||||
pub FileContainerRecycle: SCFileContainerRecycle,
|
||||
|
||||
GenericVarFree: GenericVarFreeFunc,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
@@ -210,6 +216,16 @@ pub fn sc_detect_engine_state_free(state: *mut DetectEngineState)
|
||||
}
|
||||
}
|
||||
|
||||
/// GenericVarFree wrapper.
|
||||
pub fn sc_generic_var_free(gvar: *mut GenericVar)
|
||||
{
|
||||
unsafe {
|
||||
if let Some(c) = SC {
|
||||
(c.GenericVarFree)(gvar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// AppLayerParserTriggerRawStreamReassembly wrapper
|
||||
pub fn sc_app_layer_parser_trigger_raw_stream_reassembly(flow: *const Flow, direction: i32) {
|
||||
unsafe {
|
||||
|
||||
@@ -464,6 +464,7 @@ noinst_HEADERS = \
|
||||
tm-threads-common.h \
|
||||
tm-threads.h \
|
||||
tree.h \
|
||||
tx-bit.h \
|
||||
interval-tree.h \
|
||||
unix-manager.h \
|
||||
util-action.h \
|
||||
@@ -1036,6 +1037,7 @@ libsuricata_c_a_SOURCES = \
|
||||
tm-queuehandlers.c \
|
||||
tm-queues.c \
|
||||
tm-threads.c \
|
||||
tx-bit.c \
|
||||
unix-manager.c \
|
||||
util-action.c \
|
||||
util-affinity.c \
|
||||
|
||||
+88
-6
@@ -48,6 +48,8 @@
|
||||
#include "flow-bit.h"
|
||||
#include "host-bit.h"
|
||||
#include "ippair-bit.h"
|
||||
#include "tx-bit.h"
|
||||
|
||||
#include "util-var-name.h"
|
||||
#include "util-unittest.h"
|
||||
#include "util-debug.h"
|
||||
@@ -59,6 +61,8 @@
|
||||
#define PARSE_REGEX "^([a-z]+)" "(?:,\\s*([^,]+))?" "(?:,\\s*(?:track\\s+([^,]+)))" "(?:,\\s*(?:expire\\s+([^,]+)))?"
|
||||
static DetectParseRegex parse_regex;
|
||||
|
||||
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s, const SigMatchCtx *ctx);
|
||||
static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
|
||||
static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *);
|
||||
#ifdef UNITTESTS
|
||||
@@ -71,6 +75,7 @@ void DetectXbitsRegister (void)
|
||||
sigmatch_table[DETECT_XBITS].name = "xbits";
|
||||
sigmatch_table[DETECT_XBITS].desc = "operate on bits";
|
||||
sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html";
|
||||
sigmatch_table[DETECT_XBITS].AppLayerTxMatch = DetectXbitTxMatch;
|
||||
sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
|
||||
sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
|
||||
sigmatch_table[DETECT_XBITS].Free = DetectXbitFree;
|
||||
@@ -158,6 +163,31 @@ static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DetectXbitPostMatchTx(
|
||||
DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const DetectXbitsData *xd)
|
||||
{
|
||||
if (p->flow == NULL)
|
||||
return 0;
|
||||
if (!det_ctx->tx_id_set)
|
||||
return 0;
|
||||
Flow *f = p->flow;
|
||||
void *txv = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id);
|
||||
if (txv == NULL)
|
||||
return 0;
|
||||
AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
|
||||
if (txd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (xd->cmd != DETECT_XBITS_CMD_SET)
|
||||
return 0;
|
||||
|
||||
SCLogDebug("sid %u: post-match SET for bit %u on tx:%" PRIu64 ", txd:%p", s->id, xd->idx,
|
||||
det_ctx->tx_id, txd);
|
||||
|
||||
return TxBitSet(txd, xd->idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* returns 0: no match
|
||||
* 1: match
|
||||
@@ -177,12 +207,35 @@ static int DetectXbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p, const Sig
|
||||
case VAR_TYPE_IPPAIR_BIT:
|
||||
return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
|
||||
break;
|
||||
case VAR_TYPE_TX_BIT:
|
||||
// TODO this is for PostMatch only. Can we validate somehow?
|
||||
return DetectXbitPostMatchTx(det_ctx, p, s, fd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
|
||||
void *txv, const Signature *s, const SigMatchCtx *ctx)
|
||||
{
|
||||
const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
|
||||
BUG_ON(xd == NULL);
|
||||
|
||||
AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
|
||||
if (txd == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
SCLogDebug("sid:%u: tx:%" PRIu64 ", txd->txbits:%p", s->id, det_ctx->tx_id, txd->txbits);
|
||||
int r = TxBitIsset(txd, xd->idx);
|
||||
if (r == 1) {
|
||||
return DETECT_ENGINE_INSPECT_SIG_MATCH;
|
||||
}
|
||||
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
|
||||
}
|
||||
|
||||
/** \internal
|
||||
* \brief parse xbits rule options
|
||||
* \retval 0 ok
|
||||
@@ -246,6 +299,9 @@ static int DetectXbitParse(DetectEngineCtx *de_ctx,
|
||||
} else if (strcmp(hb_dir_str, "ip_pair") == 0) {
|
||||
hb_dir = DETECT_XBITS_TRACK_IPPAIR;
|
||||
var_type = VAR_TYPE_IPPAIR_BIT;
|
||||
} else if (strcmp(hb_dir_str, "tx") == 0) {
|
||||
hb_dir = DETECT_XBITS_TRACK_TX;
|
||||
var_type = VAR_TYPE_TX_BIT;
|
||||
} else {
|
||||
// TODO
|
||||
pcre2_match_data_free(match);
|
||||
@@ -317,6 +373,13 @@ static int DetectXbitParse(DetectEngineCtx *de_ctx,
|
||||
break;
|
||||
}
|
||||
|
||||
if (hb_dir == DETECT_XBITS_TRACK_TX) {
|
||||
if (fb_cmd != DETECT_XBITS_CMD_ISSET && fb_cmd != DETECT_XBITS_CMD_SET) {
|
||||
SCLogError("tx xbits only support set and isset");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
cd = SCMalloc(sizeof(DetectXbitsData));
|
||||
if (unlikely(cd == NULL))
|
||||
return -1;
|
||||
@@ -327,8 +390,8 @@ static int DetectXbitParse(DetectEngineCtx *de_ctx,
|
||||
cd->type = var_type;
|
||||
cd->expire = expire;
|
||||
|
||||
SCLogDebug("idx %" PRIu32 ", cmd %s, name %s",
|
||||
cd->idx, fb_cmd_str, strlen(fb_name) ? fb_name : "(none)");
|
||||
SCLogDebug("idx %" PRIu32 ", cmd %s, name %s", cd->idx, fb_cmd_str,
|
||||
strlen(fb_name) ? fb_name : "(none)");
|
||||
|
||||
*cdout = cd;
|
||||
return 0;
|
||||
@@ -352,18 +415,37 @@ int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
|
||||
switch (cd->cmd) {
|
||||
/* case DETECT_XBITS_CMD_NOALERT can't happen here */
|
||||
case DETECT_XBITS_CMD_ISNOTSET:
|
||||
case DETECT_XBITS_CMD_ISSET:
|
||||
case DETECT_XBITS_CMD_ISSET: {
|
||||
int list = DETECT_SM_LIST_MATCH;
|
||||
if (cd->tracker == DETECT_XBITS_TRACK_TX) {
|
||||
SCLogDebug("tx xbit isset");
|
||||
if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_APP) {
|
||||
SCLogError("tx xbits require an explicit rule hook");
|
||||
SCFree(cd);
|
||||
return -1;
|
||||
}
|
||||
list = s->init_data->hook.sm_list;
|
||||
SCLogDebug("setting list %d", list);
|
||||
|
||||
if (list == -1) {
|
||||
SCLogError("tx xbits failed to set up"); // TODO how would we get here?
|
||||
SCFree(cd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
SCLogDebug("adding match/txmatch");
|
||||
/* checks, so packet list */
|
||||
if (SigMatchAppendSMToList(
|
||||
de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, DETECT_SM_LIST_MATCH) == NULL) {
|
||||
if (SigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, list) == NULL) {
|
||||
SCFree(cd);
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
// all other cases
|
||||
// DETECT_XBITS_CMD_SET, DETECT_XBITS_CMD_UNSET, DETECT_XBITS_CMD_TOGGLE:
|
||||
default:
|
||||
SCLogDebug("adding post-match");
|
||||
/* modifiers, only run when entire sig has matched */
|
||||
if (SigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd,
|
||||
DETECT_SM_LIST_POSTMATCH) == NULL) {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#define DETECT_XBITS_TRACK_IPSRC 0
|
||||
#define DETECT_XBITS_TRACK_IPDST 1
|
||||
#define DETECT_XBITS_TRACK_IPPAIR 2
|
||||
#define DETECT_XBITS_TRACK_TX 3
|
||||
|
||||
#define DETECT_XBITS_EXPIRE_DEFAULT 30
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "app-layer-register.h"
|
||||
#include "app-layer-htp-range.h"
|
||||
#include "app-layer-htp-file.h"
|
||||
#include "util-var.h"
|
||||
|
||||
const SuricataContext suricata_context = {
|
||||
SCLogMessage,
|
||||
@@ -37,6 +38,8 @@ const SuricataContext suricata_context = {
|
||||
FileAppendDataById,
|
||||
FileAppendGAPById,
|
||||
FileContainerRecycle,
|
||||
|
||||
GenericVarFree,
|
||||
};
|
||||
|
||||
const SuricataContext *SCGetContext(void)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "util-debug.h"
|
||||
#include "util-file.h"
|
||||
#include "util-var.h"
|
||||
|
||||
// hack for include orders cf SCSha256
|
||||
typedef struct HttpRangeContainerBlock HttpRangeContainerBlock;
|
||||
@@ -56,6 +57,8 @@ typedef struct SuricataContext_ {
|
||||
int (*FileAppendGAPById)(FileContainer *, const StreamingBufferConfig *, uint32_t track_id,
|
||||
const uint8_t *data, uint32_t data_len);
|
||||
void (*FileContainerRecycle)(FileContainer *ffc, const StreamingBufferConfig *);
|
||||
|
||||
void (*GenericVarFree)(GenericVar *);
|
||||
} SuricataContext;
|
||||
|
||||
extern const SuricataContext suricata_context;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/* Copyright (C) 2014-2021 Open Information Security Foundation
|
||||
*
|
||||
* You can copy, redistribute or modify this Program under the terms of
|
||||
* the GNU General Public License version 2 as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* version 2 along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <[email protected]>
|
||||
*
|
||||
* Implements per ippair bits. Actually, not a bit,
|
||||
* but called that way because of Snort's flowbits.
|
||||
* It's a binary storage.
|
||||
*
|
||||
* \todo move away from a linked list implementation
|
||||
* \todo use different datatypes, such as string, int, etc.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "threads.h"
|
||||
#include "tx-bit.h"
|
||||
#include "detect.h"
|
||||
#include "util-var.h"
|
||||
#include "util-debug.h"
|
||||
#include "rust.h"
|
||||
|
||||
static XBit *TxBitGet(AppLayerTxData *txd, uint32_t idx)
|
||||
{
|
||||
for (GenericVar *gv = txd->txbits; gv != NULL; gv = gv->next) {
|
||||
if (gv->type == DETECT_XBITS && gv->idx == idx) {
|
||||
return (XBit *)gv;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int TxBitAdd(AppLayerTxData *txd, uint32_t idx)
|
||||
{
|
||||
XBit *xb = TxBitGet(txd, idx);
|
||||
if (xb == NULL) {
|
||||
xb = SCMalloc(sizeof(XBit));
|
||||
if (unlikely(xb == NULL))
|
||||
return -1;
|
||||
|
||||
xb->type = DETECT_XBITS;
|
||||
xb->idx = idx;
|
||||
xb->next = NULL;
|
||||
xb->expire = 0; // not used by tx bits
|
||||
|
||||
GenericVarAppend(&txd->txbits, (GenericVar *)xb);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TxBitIsset(AppLayerTxData *txd, uint32_t idx)
|
||||
{
|
||||
XBit *xb = TxBitGet(txd, idx);
|
||||
if (xb != NULL) {
|
||||
SCLogDebug("isset %u return 1", idx);
|
||||
return 1;
|
||||
}
|
||||
SCLogDebug("isset %u r 0", idx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TxBitSet(AppLayerTxData *txd, uint32_t idx)
|
||||
{
|
||||
int r = TxBitAdd(txd, idx);
|
||||
SCLogDebug("set %u r %d", idx, r);
|
||||
return (r == 1);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/* Copyright (C) 2007-2025 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* \author Victor Julien <[email protected]>
|
||||
*/
|
||||
|
||||
#ifndef SURICATA_TX_BIT_H
|
||||
#define SURICATA_TX_BIT_H
|
||||
|
||||
#include "rust.h"
|
||||
|
||||
int TxBitIsset(AppLayerTxData *txd, uint32_t idx);
|
||||
int TxBitSet(AppLayerTxData *txd, uint32_t idx);
|
||||
|
||||
#endif /* SURICATA_TX_BIT_H */
|
||||
@@ -44,6 +44,8 @@ enum VarTypes {
|
||||
VAR_TYPE_IPPAIR_BIT,
|
||||
VAR_TYPE_IPPAIR_INT,
|
||||
VAR_TYPE_IPPAIR_VAR,
|
||||
|
||||
VAR_TYPE_TX_BIT,
|
||||
};
|
||||
|
||||
typedef struct GenericVar_ {
|
||||
|
||||
Reference in New Issue
Block a user