You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect.h

1764 lines
58 KiB
C

/* 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 <victor@inliniac.net>
*/
#ifndef SURICATA_DETECT_H
#define SURICATA_DETECT_H
#include "suricata-common.h"
#include "flow.h"
#include "detect-engine-proto.h"
16 years ago
#include "detect-reference.h"
#include "detect-metadata.h"
#include "detect-engine-register.h"
#include "detect-engine-inspect-buffer.h"
#include "util-prefilter.h"
#include "util-mpm.h"
#include "util-spm.h"
#include "util-hash.h"
#include "util-hashlist.h"
#include "util-radix4-tree.h"
#include "util-radix6-tree.h"
#include "util-file.h"
#include "reputation.h"
#define DETECT_MAX_RULE_SIZE 8192
#define DETECT_TRANSFORMS_MAX 16
/** default rule priority if not set through priority keyword or via
* classtype. */
#define DETECT_DEFAULT_PRIO 3
// tx_id value to use when there is no transaction
#define PACKET_ALERT_NOTX UINT64_MAX
/* forward declaration for sigorder logic in detect-engine-sigorder.[ch] */
struct SCSigOrderFunc_;
/* Forward declarations for structures from Rust. */
typedef struct SCDetectRequiresStatus SCDetectRequiresStatus;
// rule types documentation tag start: SignatureType
enum SignatureType {
SIG_TYPE_NOT_SET = 0,
SIG_TYPE_IPONLY, // rule is handled by IPONLY engine
SIG_TYPE_LIKE_IPONLY, // rule is handled by pkt engine, has action effect like ip-only
/** Proto detect only signature.
* Inspected once per direction when protocol detection is done. */
SIG_TYPE_PDONLY, // rule is handled by PDONLY engine
SIG_TYPE_DEONLY,
SIG_TYPE_PKT,
SIG_TYPE_PKT_STREAM,
SIG_TYPE_STREAM,
SIG_TYPE_APPLAYER, // app-layer but not tx, e.g. appproto
SIG_TYPE_APP_TX, // rule is handled by TX engine
SIG_TYPE_MAX,
};
// rule types documentation tag end: SignatureType
enum SignaturePropertyFlowAction {
SIG_PROP_FLOW_ACTION_PACKET,
SIG_PROP_FLOW_ACTION_FLOW,
SIG_PROP_FLOW_ACTION_FLOW_IF_STATEFUL,
};
struct SignatureProperties {
enum SignaturePropertyFlowAction flow_action;
};
extern const struct SignatureProperties signature_properties[SIG_TYPE_MAX];
/*
The detection engine groups similar signatures/rules together. Internally a
tree of different types of data is created on initialization. This is it's
global layout:
For TCP/UDP
- Flow direction
-- Protocol
-=- Dst port
For the other protocols
- Flow direction
-- Protocol
*/
/* holds the values for different possible lists in struct Signature.
* These codes are access points to particular lists in the array
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
2 years ago
* Signature->init_data->smlists[DETECT_SM_LIST_MAX]. */
enum DetectSigmatchListEnum {
/* list for non-payload per packet matches, e.g. ttl, flow keyword */
DETECT_SM_LIST_MATCH = 0,
/* list for payload and stream match */
DETECT_SM_LIST_PMATCH,
/* base64_data keyword uses some hardcoded logic so consider
* built-in
* TODO convert to inspect engine */
DETECT_SM_LIST_BASE64_DATA,
/* list for post match actions: flowbit set, flowint increment, etc */
DETECT_SM_LIST_POSTMATCH,
DETECT_SM_LIST_TMATCH, /**< post-detection tagging */
/* lists for alert thresholding and suppression */
DETECT_SM_LIST_SUPPRESS,
DETECT_SM_LIST_THRESHOLD,
DETECT_SM_LIST_MAX,
/* start of dynamically registered lists */
DETECT_SM_LIST_DYNAMIC_START = DETECT_SM_LIST_MAX,
};
/* used for Signature->list, which indicates which list
* we're adding keywords to in cases of sticky buffers like
* file_data */
#define DETECT_SM_LIST_NOTSET INT_MAX
/*
* DETECT ADDRESS
*/
/* a is ... than b */
enum {
ADDRESS_ER = -1, /**< error e.g. compare ipv4 and ipv6 */
ADDRESS_LT, /**< smaller [aaa] [bbb] */
ADDRESS_LE, /**< smaller with overlap [aa[bab]bb] */
ADDRESS_EQ, /**< exactly equal [abababab] */
ADDRESS_ES, /**< within [bb[aaa]bb] and [[abab]bbb] and [bbb[abab]] */
ADDRESS_EB, /**< completely overlaps [aa[bbb]aa] and [[baba]aaa] and [aaa[baba]] */
ADDRESS_GE, /**< bigger with overlap [bb[aba]aa] */
ADDRESS_GT, /**< bigger [bbb] [aaa] */
};
#define ADDRESS_FLAG_NOT 0x01 /**< address is negated */
/** \brief address structure for use in the detection engine.
*
* Contains the address information and matching information.
*/
typedef struct DetectAddress_ {
/** address data for this group */
Address ip;
Address ip2;
/** flags affecting this address */
uint8_t flags;
/** ptr to the previous address in the list */
struct DetectAddress_ *prev;
/** ptr to the next address in the list */
struct DetectAddress_ *next;
} DetectAddress;
/** Address grouping head. IPv4 and IPv6 are split out */
typedef struct DetectAddressHead_ {
DetectAddress *ipv4_head;
DetectAddress *ipv6_head;
} DetectAddressHead;
typedef struct DetectMatchAddressIPv4_ {
uint32_t ip; /**< address in host order, start of range */
uint32_t ip2; /**< address in host order, end of range */
} DetectMatchAddressIPv4;
typedef struct DetectMatchAddressIPv6_ {
uint32_t ip[4];
uint32_t ip2[4];
} DetectMatchAddressIPv6;
/*
* DETECT PORT
*/
/* a is ... than b */
enum {
PORT_ER = -1, /* error */
PORT_LT, /* smaller [aaa] [bbb] */
PORT_LE, /* smaller with overlap [aa[bab]bb] */
PORT_EQ, /* exactly equal [abababab] */
PORT_ES, /* within [bb[aaa]bb] and [[abab]bbb] and [bbb[abab]] */
PORT_EB, /* completely overlaps [aa[bbb]aa] and [[baba]aaa] and [aaa[baba]] */
PORT_GE, /* bigger with overlap [bb[aba]aa] */
PORT_GT, /* bigger [bbb] [aaa] */
};
16 years ago
#define PORT_FLAG_ANY 0x01 /**< 'any' special port */
#define PORT_FLAG_NOT 0x02 /**< negated port */
#define PORT_SIGGROUPHEAD_COPY 0x04 /**< sgh is a ptr copy */
16 years ago
/** \brief Port structure for detection engine */
typedef struct DetectPort_ {
uint16_t port;
uint16_t port2;
uint8_t flags; /**< flags for this port */
/* signatures that belong in this group
*
* If the PORT_SIGGROUPHEAD_COPY flag is set, we don't own this pointer
* (memory is freed elsewhere).
*/
struct SigGroupHead_ *sh;
struct DetectPort_ *prev;
struct DetectPort_ *next;
struct DetectPort_ *last; /* Pointer to the last node in the list */
} DetectPort;
17 years ago
/* Signature flags */
/** \note: additions should be added to the rule analyzer as well */
#define SIG_FLAG_SRC_ANY BIT_U32(0) /**< source is any */
#define SIG_FLAG_DST_ANY BIT_U32(1) /**< destination is any */
#define SIG_FLAG_SP_ANY BIT_U32(2) /**< source port is any */
#define SIG_FLAG_DP_ANY BIT_U32(3) /**< destination port is any */
firewall: start of firewall rules support Config: Firewall rules are like normal rule, with some key differences. They are loaded separate, and first, from: ```yaml firewall-rule-path: /etc/suricata/firewall/ firewall-rule-files: - fw.rules ``` Can also be loaded with --firewall-rules-exclusive: Mostly for QA purposes. Allow -S with --firewall-rules-exclusive, so that firewall and threat detection rules can be tested together. Rules: Differences with regular "threat detection" rules: 1. these rules are evaluated before threat detection rules 2. these rules are evaluated in the order as they appear in the rule file 3. currently only rules specifying an explicit hook at supported a. as a consequence, no rules will be treated as (like) IP-only, PD-only or DE-only Require explicit action scope for firewall rules. Default policy is drop for the firewall tables. Actions: New action "accept" is added to allow traffic in the filter tables. New scope "accept:tx" is added to allow accepting a transaction. Tables: Rulesets are per table. Table processing order: `packet:filter` -> `packet:td` -> `app:*:*` -> `app:td`. Each of the tables has some unique properties: `packet:filter`: - default policy is `drop:packet` - rules are process in order - action scopes are explicit - `drop` or `accept` is immediate - `accept:hook` continues to `packet:td` `packet:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `app:*:*` or `alert/action finalize` `app:*:*`: - default policy is `drop:flow` - rules are process in order - action scopes are explicit - `drop` is immediate - `accept` is conditional on possible `drop` from `packet:td` - `accept:hook` continues to `app:td`, `accept:packet` or `accept:flow` continues to `alert/action finalize` `app:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `alert/action finalize` Implementation: During sigorder, split into packet:filter, app:*:* and general td. Allow fw rules to work when in pass:flow mode. When firewall mode is enabled, `pass:flow` will not skip the detection engine anymore, but instead process the firewall rules and then apply the pass before inspecting threat detect rules.
6 months ago
#define SIG_FLAG_FIREWALL BIT_U32(4) /**< sig is a firewall rule */
#define SIG_FLAG_DSIZE BIT_U32(5) /**< signature has a dsize setting */
#define SIG_FLAG_APPLAYER BIT_U32(6) /**< signature applies to app layer instead of packets */
#define SIG_FLAG_TXBOTHDIR BIT_U32(7) /**< signature needs tx with both directions to match */
// vacancy
#define SIG_FLAG_REQUIRE_PACKET BIT_U32(9) /**< signature is requiring packet match */
#define SIG_FLAG_REQUIRE_STREAM BIT_U32(10) /**< signature is requiring stream match */
#define SIG_FLAG_MPM_NEG BIT_U32(11)
#define SIG_FLAG_FLUSH BIT_U32(12) /**< detection logic needs stream flush notification */
#define SIG_FLAG_REQUIRE_STREAM_ONLY \
BIT_U32(13) /**< signature is requiring stream match. Stream match is not optional, so no \
fallback to packet payload. */
// vacancies
#define SIG_FLAG_REQUIRE_FLOWVAR BIT_U32(17) /**< signature can only match if a flowbit, flowvar or flowint is available. */
#define SIG_FLAG_FILESTORE BIT_U32(18) /**< signature has filestore keyword */
#define SIG_FLAG_TOSERVER BIT_U32(19)
#define SIG_FLAG_TOCLIENT BIT_U32(20)
#define SIG_FLAG_TLSSTORE BIT_U32(21)
#define SIG_FLAG_BYPASS BIT_U32(22)
#define SIG_FLAG_PREFILTER BIT_U32(23) /**< sig is part of a prefilter engine */
// vacancy
/** Info for Source and Target identification */
#define SIG_FLAG_SRC_IS_TARGET BIT_U32(25)
/** Info for Source and Target identification */
#define SIG_FLAG_DEST_IS_TARGET BIT_U32(26)
#define SIG_FLAG_HAS_TARGET (SIG_FLAG_DEST_IS_TARGET|SIG_FLAG_SRC_IS_TARGET)
/* signature init flags */
// available 0
#define SIG_FLAG_INIT_PACKET BIT_U32(1) /**< signature has matches against a packet (as opposed to app layer) */
#define SIG_FLAG_INIT_FLOW BIT_U32(2) /**< signature has a flow setting */
#define SIG_FLAG_INIT_BIDIREC BIT_U32(3) /**< signature has bidirectional operator */
#define SIG_FLAG_INIT_FIRST_IPPROTO_SEEN \
BIT_U32(4) /** < signature has seen the first ip_proto keyword */
#define SIG_FLAG_INIT_STATE_MATCH BIT_U32(6) /**< signature has matches that require stateful inspection */
#define SIG_FLAG_INIT_NEED_FLUSH BIT_U32(7)
#define SIG_FLAG_INIT_PRIO_EXPLICIT \
BIT_U32(8) /**< priority is explicitly set by the priority keyword */
#define SIG_FLAG_INIT_FILEDATA BIT_U32(9) /**< signature has filedata keyword */
#define SIG_FLAG_INIT_FORCE_TOCLIENT BIT_U32(10) /**< signature now takes keywords toclient */
#define SIG_FLAG_INIT_FORCE_TOSERVER BIT_U32(11) /**< signature now takes keywords toserver */
// Two following flags are meant to be mutually exclusive
#define SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER \
BIT_U32(12) /**< transactional signature uses a streaming buffer to server */
#define SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT \
BIT_U32(13) /**< transactional signature uses a fast pattern to client */
/* signature mask flags */
/** \note: additions should be added to the rule analyzer as well */
#define SIG_MASK_REQUIRE_PAYLOAD BIT_U8(0)
#define SIG_MASK_REQUIRE_FLOW BIT_U8(1)
#define SIG_MASK_REQUIRE_FLAGS_INITDEINIT BIT_U8(2) /* SYN, FIN, RST */
#define SIG_MASK_REQUIRE_FLAGS_UNUSUAL BIT_U8(3) /* URG, ECN, CWR */
#define SIG_MASK_REQUIRE_NO_PAYLOAD BIT_U8(4)
#define SIG_MASK_REQUIRE_REAL_PKT BIT_U8(5)
// vacancy 1x
#define SIG_MASK_REQUIRE_ENGINE_EVENT BIT_U8(7)
#define FILE_SIG_NEED_FILE 0x01
#define FILE_SIG_NEED_FILENAME 0x02
#define FILE_SIG_NEED_MAGIC 0x04 /**< need the start of the file */
#define FILE_SIG_NEED_FILECONTENT 0x08
#define FILE_SIG_NEED_MD5 0x10
#define FILE_SIG_NEED_SHA1 0x20
#define FILE_SIG_NEED_SHA256 0x40
#define FILE_SIG_NEED_SIZE 0x80
17 years ago
/* Detection Engine flags */
#define DE_QUIET 0x01 /**< DE is quiet (esp for unittests) */
typedef struct IPOnlyCIDRItem_ {
/* address data for this item */
uint8_t family;
/* netmask in CIDR values (ex. /16 /18 /24..) */
uint8_t netmask;
/* If this host or net is negated for the signum */
uint8_t negated;
uint32_t ip[4];
SigIntId signum; /**< our internal id */
/* linked list, the header should be the biggest network */
struct IPOnlyCIDRItem_ *next;
} IPOnlyCIDRItem;
/** \brief Used to start a pointer to SigMatch context
* Should never be dereferenced without casting to something else.
*/
typedef struct SigMatchCtx_ {
int foo;
} SigMatchCtx;
/** \brief a single match condition for a signature */
typedef struct SigMatch_ {
uint16_t type; /**< match type */
uint16_t idx; /**< position in the signature */
SigMatchCtx *ctx; /**< plugin specific data */
struct SigMatch_ *next;
struct SigMatch_ *prev;
} SigMatch;
/** \brief Data needed for Match() */
typedef struct SigMatchData_ {
uint16_t type; /**< match type */
bool is_last; /**< Last element of the list */
SigMatchCtx *ctx; /**< plugin specific data */
} SigMatchData;
struct DetectEngineThreadCtx_;// DetectEngineThreadCtx;
/* inspection buffers are kept per tx (in det_ctx), but some protocols
* need a bit more. A single TX might have multiple buffers, e.g. files in
* SMTP or DNS queries. Since all prefilters+transforms run before the
* individual rules need the same buffers, we need a place to store the
* transformed data. This array of arrays is that place. */
typedef struct InspectionBufferMultipleForList {
InspectionBuffer *inspection_buffers;
uint32_t size; /**< size in number of elements */
uint32_t max:31; /**< max id in use in this run */
uint32_t init:1; /**< first time used this run. Used for clean logic */
} InspectionBufferMultipleForList;
typedef struct TransformData_ {
int transform;
void *options;
} TransformData;
typedef struct DetectEngineTransforms {
TransformData transforms[DETECT_TRANSFORMS_MAX];
int cnt;
} DetectEngineTransforms;
/** callback for getting the buffer we need to prefilter/inspect */
typedef InspectionBuffer *(*InspectionBufferGetDataPtr)(
struct DetectEngineThreadCtx_ *det_ctx,
const DetectEngineTransforms *transforms,
Flow *f, const uint8_t flow_flags,
void *txv, const int list_id);
typedef bool (*InspectionSingleBufferGetDataPtr)(
const void *txv, const uint8_t flow_flags, const uint8_t **buf, uint32_t *buf_len);
typedef bool (*InspectionMultiBufferGetDataPtr)(struct DetectEngineThreadCtx_ *det_ctx,
const void *txv, const uint8_t flow_flags, uint32_t local_id, const uint8_t **buf,
uint32_t *buf_len);
struct DetectEngineAppInspectionEngine_;
typedef uint8_t (*InspectEngineFuncPtr)(struct DetectEngineCtx_ *de_ctx,
struct DetectEngineThreadCtx_ *det_ctx,
const struct DetectEngineAppInspectionEngine_ *engine, const struct Signature_ *s, Flow *f,
uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
typedef struct DetectEngineAppInspectionEngine_ {
AppProto alproto;
uint8_t dir;
uint8_t id; /**< per sig id used in state keeping */
bool mpm;
bool stream;
/** will match on a NULL buffer (so an absent buffer) */
bool match_on_null;
uint16_t sm_list;
uint16_t sm_list_base; /**< base buffer being transformed */
int16_t progress;
struct {
union {
InspectionBufferGetDataPtr GetData;
InspectionSingleBufferGetDataPtr GetDataSingle;
InspectionMultiBufferGetDataPtr GetMultiData;
};
InspectEngineFuncPtr Callback;
/** pointer to the transforms in the 'DetectBuffer entry for this list */
const DetectEngineTransforms *transforms;
} v2;
SigMatchData *smd;
struct DetectEngineAppInspectionEngine_ *next;
} DetectEngineAppInspectionEngine;
typedef struct TransformIdData_ {
const uint8_t *id_data;
uint32_t id_data_len;
} TransformIdData;
typedef struct DetectBufferType_ {
char name[64];
char description[128];
int id;
int parent_id;
bool mpm;
bool packet; /**< compat to packet matches */
bool frame; /**< is about Frame inspection */
bool supports_transforms;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
2 years ago
bool multi_instance; /**< buffer supports multiple buffer instances per tx */
void (*SetupCallback)(const struct DetectEngineCtx_ *, struct Signature_ *);
bool (*ValidateCallback)(
const struct Signature_ *, const char **sigerror, const struct DetectBufferType_ *);
DetectEngineTransforms transforms;
TransformIdData xform_id[DETECT_TRANSFORMS_MAX];
} DetectBufferType;
struct DetectEnginePktInspectionEngine;
/**
* \param alert_flags[out] for setting PACKET_ALERT_FLAG_*
*/
typedef int (*InspectionBufferPktInspectFunc)(
struct DetectEngineThreadCtx_ *,
const struct DetectEnginePktInspectionEngine *engine,
const struct Signature_ *s,
Packet *p, uint8_t *alert_flags);
/** callback for getting the buffer we need to prefilter/inspect */
typedef InspectionBuffer *(*InspectionBufferGetPktDataPtr)(
struct DetectEngineThreadCtx_ *det_ctx,
const DetectEngineTransforms *transforms,
Packet *p, const int list_id);
typedef struct DetectEnginePktInspectionEngine {
SigMatchData *smd;
bool mpm;
uint16_t sm_list;
uint16_t sm_list_base;
struct {
InspectionBufferGetPktDataPtr GetData;
InspectionBufferPktInspectFunc Callback;
/** pointer to the transforms in the 'DetectBuffer entry for this list */
const DetectEngineTransforms *transforms;
} v1;
struct DetectEnginePktInspectionEngine *next;
} DetectEnginePktInspectionEngine;
struct Frame;
struct Frames;
struct DetectEngineFrameInspectionEngine;
/**
* \param alert_flags[out] for setting PACKET_ALERT_FLAG_*
*/
typedef int (*InspectionBufferFrameInspectFunc)(struct DetectEngineThreadCtx_ *,
const struct DetectEngineFrameInspectionEngine *engine, const struct Signature_ *s,
Packet *p, const struct Frames *frames, const struct Frame *frame);
typedef struct DetectEngineFrameInspectionEngine {
AppProto alproto;
uint8_t dir;
uint8_t type;
bool mpm;
uint16_t sm_list;
uint16_t sm_list_base;
struct {
InspectionBufferFrameInspectFunc Callback;
/** pointer to the transforms in the 'DetectBuffer entry for this list */
const DetectEngineTransforms *transforms;
} v1;
SigMatchData *smd;
struct DetectEngineFrameInspectionEngine *next;
} DetectEngineFrameInspectionEngine;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
2 years ago
typedef struct SignatureInitDataBuffer_ {
uint32_t id; /**< buffer id */
bool sm_init; /**< initialized by sigmatch, which is likely something like `urilen:10; http.uri;
content:"abc";`. These need to be in the same list. Unset once `http.uri` is
set up. */
bool multi_capable; /**< true if we can have multiple instances of this buffer, so e.g. for
http.uri. */
bool only_tc; /**< true if we can only used toclient. */
bool only_ts; /**< true if we can only used toserver. */
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
2 years ago
/* sig match list */
SigMatch *head;
SigMatch *tail;
} SignatureInitDataBuffer;
enum SignatureHookPkt {
SIGNATURE_HOOK_PKT_NOT_SET,
SIGNATURE_HOOK_PKT_FLOW_START,
SIGNATURE_HOOK_PKT_PRE_FLOW,
SIGNATURE_HOOK_PKT_PRE_STREAM,
SIGNATURE_HOOK_PKT_ALL, /**< match each packet */
};
enum SignatureHookType {
SIGNATURE_HOOK_TYPE_NOT_SET,
SIGNATURE_HOOK_TYPE_PKT,
SIGNATURE_HOOK_TYPE_APP,
};
enum DetectTable {
DETECT_TABLE_NOT_SET = 0,
DETECT_TABLE_PACKET_PRE_FLOW,
DETECT_TABLE_PACKET_PRE_STREAM,
DETECT_TABLE_PACKET_FILTER,
DETECT_TABLE_PACKET_TD,
DETECT_TABLE_APP_FILTER,
DETECT_TABLE_APP_TD,
#define DETECT_TABLE_PACKET_PRE_FLOW_FLAG BIT_U8(DETECT_TABLE_PACKET_PRE_FLOW)
#define DETECT_TABLE_PACKET_PRE_STREAM_FLAG BIT_U8(DETECT_TABLE_PACKET_PRE_STREAM)
#define DETECT_TABLE_PACKET_FILTER_FLAG BIT_U8(DETECT_TABLE_PACKET_FILTER)
#define DETECT_TABLE_PACKET_TD_FLAG BIT_U8(DETECT_TABLE_PACKET_TD)
#define DETECT_TABLE_APP_FILTER_FLAG BIT_U8(DETECT_TABLE_APP_FILTER)
#define DETECT_TABLE_APP_TD_FLAG BIT_U8(DETECT_TABLE_APP_TD)
};
// dns:request_complete should add DetectBufferTypeGetByName("dns:request_complete");
// TODO to json
typedef struct SignatureHook_ {
enum SignatureHookType type;
int sm_list; /**< list id for the hook's generic list. e.g. for dns:request_complete:generic */
union {
struct {
AppProto alproto;
/** progress value of the app-layer hook specified in the rule. Sets the app_proto
* specific progress value. */
int app_progress;
} app;
struct {
enum SignatureHookPkt ph;
} pkt;
} t;
} SignatureHook;
#define SIG_ALPROTO_MAX 4
typedef struct SignatureInitData_ {
SignatureHook hook;
/** Number of sigmatches. Used for assigning SigMatch::idx */
uint16_t sm_cnt;
/** option was prefixed with '!'. Only set for sigmatches that
* have the SIGMATCH_HANDLE_NEGATION flag set. */
bool negated;
/* track if we saw any negation in the addresses. If so, we
* skip it for ip-only */
bool src_contains_negation;
bool dst_contains_negation;
/** see if any of the sigmatches supports an enabled prefilter */
bool has_possible_prefilter;
/* used to hold flags that are used during init */
uint32_t init_flags;
/* coccinelle: SignatureInitData:init_flags:SIG_FLAG_INIT_ */
/* alproto mask if multiple protocols are possible */
AppProto alprotos[SIG_ALPROTO_MAX];
/* used at init to determine max dsize */
SigMatch *dsize_sm;
/** netblocks and hosts specified at the sid, in CIDR format */
IPOnlyCIDRItem *cidr_src, *cidr_dst;
/* list id for `mpm_sm`. Should always match `SigMatchListSMBelongsTo(s, mpm_sm)`. */
int mpm_sm_list;
/* the fast pattern added from this signature */
SigMatch *mpm_sm;
/* used to speed up init of prefilter */
SigMatch *prefilter_sm;
/* SigMatch list used for adding content and friends. E.g. file_data; */
int list;
bool list_set;
DetectEngineTransforms transforms;
/** score to influence rule grouping. A higher value leads to a higher
* likelihood of a rulegroup with this sig ending up as a contained
* group. */
int score;
/** address settings for this signature */
const DetectAddressHead *src, *dst;
detect: support multi buffer matching Multi buffer matching is implemented as a way for a rule to match on multiple buffers within the same transaction. Before this patch a rule like: dns.query; content:"example"; dns.query; content:".com"; would be equivalent to: dns.query; content:"example"; content:".com"; If a DNS query would request more than one name, e.g.: DNS: [example.net][something.com] Eeach would be inspected to have both patterns present. Otherwise, it would not be a match. So the rule above would not match, as neither example.net and somthing.com satisfy both conditions at the same time. This patch changes this behavior. Instead of the above, each time the sticky buffer is specified, it creates a separate detection unit. Each buffer is a "multi buffer" sticky buffer will now be evaluated against each "instance" of the sticky buffer. To continue with the above example: DNS: [example.net] <- matches 'dns.query; content:"example";' DNS: [something.com] <- matches 'dns.query; content:".com"' So this would now be a match. To make sure both patterns match in a single query string, the expression 'dns.query; content:"example"; content:".com";' still works for this. This patch doesn't yet enable the behavior for the keywords. That is done in a follow up patch. To be able to implement this the internal storage of parsed rules is changed. Until this patch and array of lists was used, where the index was the buffer id (e.g. http_uri, dns_query). Therefore there was only one list of matches per buffer id. As a side effect this array was always very sparsely populated as many buffers could not be mixed. This patch changes the internal representation. The new array is densely packed: dns.query; content:"1"; dns.query; bsize:1; content:"2"; [type: dns_query][list: content:"1";] [type: dns_query][list: bsize:1; content:"2";] The new scheme allows for multiple instances of the same buffer. These lists are then translated into multiple inspection engines during the final setup of the rule. Ticket: #5784.
2 years ago
/* holds built-in sm lists */
struct SigMatch_ *smlists[DETECT_SM_LIST_MAX];
/* holds built-in sm lists' tails */
struct SigMatch_ *smlists_tail[DETECT_SM_LIST_MAX];
/* Storage for buffers. */
SignatureInitDataBuffer *buffers;
uint32_t buffer_index;
uint32_t buffers_size;
SignatureInitDataBuffer *curbuf;
/* highest list/buffer id which holds a DETECT_CONTENT */
uint32_t max_content_list_id;
/* inter-signature state dependency */
bool is_rule_state_dependant;
uint32_t *rule_state_dependant_sids_array;
uint32_t rule_state_dependant_sids_size;
uint32_t rule_state_dependant_sids_idx;
uint32_t *rule_state_flowbits_ids_array;
uint32_t rule_state_flowbits_ids_size;
firewall: start of firewall rules support Config: Firewall rules are like normal rule, with some key differences. They are loaded separate, and first, from: ```yaml firewall-rule-path: /etc/suricata/firewall/ firewall-rule-files: - fw.rules ``` Can also be loaded with --firewall-rules-exclusive: Mostly for QA purposes. Allow -S with --firewall-rules-exclusive, so that firewall and threat detection rules can be tested together. Rules: Differences with regular "threat detection" rules: 1. these rules are evaluated before threat detection rules 2. these rules are evaluated in the order as they appear in the rule file 3. currently only rules specifying an explicit hook at supported a. as a consequence, no rules will be treated as (like) IP-only, PD-only or DE-only Require explicit action scope for firewall rules. Default policy is drop for the firewall tables. Actions: New action "accept" is added to allow traffic in the filter tables. New scope "accept:tx" is added to allow accepting a transaction. Tables: Rulesets are per table. Table processing order: `packet:filter` -> `packet:td` -> `app:*:*` -> `app:td`. Each of the tables has some unique properties: `packet:filter`: - default policy is `drop:packet` - rules are process in order - action scopes are explicit - `drop` or `accept` is immediate - `accept:hook` continues to `packet:td` `packet:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `app:*:*` or `alert/action finalize` `app:*:*`: - default policy is `drop:flow` - rules are process in order - action scopes are explicit - `drop` is immediate - `accept` is conditional on possible `drop` from `packet:td` - `accept:hook` continues to `app:td`, `accept:packet` or `accept:flow` continues to `alert/action finalize` `app:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `alert/action finalize` Implementation: During sigorder, split into packet:filter, app:*:* and general td. Allow fw rules to work when in pass:flow mode. When firewall mode is enabled, `pass:flow` will not skip the detection engine anymore, but instead process the firewall rules and then apply the pass before inspecting threat detect rules.
6 months ago
/* Signature is a "firewall" rule. */
bool firewall_rule;
} SignatureInitData;
/** \brief Signature container */
typedef struct Signature_ {
uint32_t flags;
/* coccinelle: Signature:flags:SIG_FLAG_ */
enum SignatureType type;
AppProto alproto;
uint16_t dsize_low;
uint16_t dsize_high;
uint8_t dsize_mode;
SignatureMask mask;
SigIntId iid; /**< signature internal id */
/** inline -- action */
uint8_t action;
uint8_t file_flags;
/** addresses, ports and proto this sig matches on */
DetectProto proto;
/* scope setting for the action: enum ActionScope */
uint8_t action_scope;
/** ipv4 match arrays */
uint16_t addr_dst_match4_cnt;
uint16_t addr_src_match4_cnt;
uint16_t addr_dst_match6_cnt;
uint16_t addr_src_match6_cnt;
/** classification id **/
uint16_t class_id;
/** detect: pseudo table this rule is part of (enum DetectTable) */
uint8_t detect_table;
firewall: start of firewall rules support Config: Firewall rules are like normal rule, with some key differences. They are loaded separate, and first, from: ```yaml firewall-rule-path: /etc/suricata/firewall/ firewall-rule-files: - fw.rules ``` Can also be loaded with --firewall-rules-exclusive: Mostly for QA purposes. Allow -S with --firewall-rules-exclusive, so that firewall and threat detection rules can be tested together. Rules: Differences with regular "threat detection" rules: 1. these rules are evaluated before threat detection rules 2. these rules are evaluated in the order as they appear in the rule file 3. currently only rules specifying an explicit hook at supported a. as a consequence, no rules will be treated as (like) IP-only, PD-only or DE-only Require explicit action scope for firewall rules. Default policy is drop for the firewall tables. Actions: New action "accept" is added to allow traffic in the filter tables. New scope "accept:tx" is added to allow accepting a transaction. Tables: Rulesets are per table. Table processing order: `packet:filter` -> `packet:td` -> `app:*:*` -> `app:td`. Each of the tables has some unique properties: `packet:filter`: - default policy is `drop:packet` - rules are process in order - action scopes are explicit - `drop` or `accept` is immediate - `accept:hook` continues to `packet:td` `packet:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `app:*:*` or `alert/action finalize` `app:*:*`: - default policy is `drop:flow` - rules are process in order - action scopes are explicit - `drop` is immediate - `accept` is conditional on possible `drop` from `packet:td` - `accept:hook` continues to `app:td`, `accept:packet` or `accept:flow` continues to `alert/action finalize` `app:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `alert/action finalize` Implementation: During sigorder, split into packet:filter, app:*:* and general td. Allow fw rules to work when in pass:flow mode. When firewall mode is enabled, `pass:flow` will not skip the detection engine anymore, but instead process the firewall rules and then apply the pass before inspecting threat detect rules.
6 months ago
/** firewall: progress value for this signature */
uint8_t app_progress_hook;
DetectMatchAddressIPv4 *addr_dst_match4;
DetectMatchAddressIPv4 *addr_src_match4;
/** ipv6 match arrays */
DetectMatchAddressIPv6 *addr_dst_match6;
DetectMatchAddressIPv6 *addr_src_match6;
uint32_t id; /**< sid, set by the 'sid' rule keyword */
uint32_t gid; /**< generator id */
uint32_t rev;
int prio;
/** port settings for this signature */
DetectPort *sp, *dp;
#ifdef PROFILE_RULES
uint16_t profiling_id;
#endif
DetectEngineAppInspectionEngine *app_inspect;
DetectEnginePktInspectionEngine *pkt_inspect;
DetectEngineFrameInspectionEngine *frame_inspect;
/* Matching structures for the built-ins. The others are in
* their inspect engines. */
SigMatchData *sm_arrays[DETECT_SM_LIST_MAX];
/* memory is still owned by the sm_lists/sm_arrays entry */
const struct DetectFilestoreData_ *filestore_ctx;
char *msg;
/** classification message */
char *class_msg;
/** Reference */
DetectReference *references;
/** Metadata */
DetectMetadataHead *metadata;
char *sig_str;
SignatureInitData *init_data;
/** ptr to the next sig in the list */
struct Signature_ *next;
} Signature;
enum DetectBufferMpmType {
DETECT_BUFFER_MPM_TYPE_PKT,
DETECT_BUFFER_MPM_TYPE_APP,
DETECT_BUFFER_MPM_TYPE_FRAME,
/* must be last */
DETECT_BUFFER_MPM_TYPE_SIZE,
};
/** \brief one time registration of keywords at start up */
typedef struct DetectBufferMpmRegistry_ {
const char *name;
char pname[32]; /**< name used in profiling */
int direction; /**< SIG_FLAG_TOSERVER or SIG_FLAG_TOCLIENT */
int16_t sm_list;
int16_t sm_list_base;
int priority;
int id; /**< index into this array and result arrays */
enum DetectBufferMpmType type;
int sgh_mpm_context;
int (*PrefilterRegisterWithListId)(struct DetectEngineCtx_ *de_ctx, struct SigGroupHead_ *sgh,
MpmCtx *mpm_ctx, const struct DetectBufferMpmRegistry_ *mpm_reg, int list_id);
DetectEngineTransforms transforms;
union {
/* app-layer matching: use if type == DETECT_BUFFER_MPM_TYPE_APP */
struct {
union {
InspectionBufferGetDataPtr GetData;
InspectionSingleBufferGetDataPtr GetDataSingle;
InspectionMultiBufferGetDataPtr GetMultiData;
};
AppProto alproto;
int tx_min_progress;
} app_v2;
/* pkt matching: use if type == DETECT_BUFFER_MPM_TYPE_PKT */
struct {
int (*PrefilterRegisterWithListId)(struct DetectEngineCtx_ *de_ctx,
struct SigGroupHead_ *sgh, MpmCtx *mpm_ctx,
const struct DetectBufferMpmRegistry_ *mpm_reg, int list_id);
InspectionBufferGetPktDataPtr GetData;
} pkt_v1;
/* frame matching: use if type == DETECT_BUFFER_MPM_TYPE_FRAME */
struct {
AppProto alproto;
uint8_t type;
} frame_v1;
};
struct DetectBufferMpmRegistry_ *next;
} DetectBufferMpmRegistry;
/* helper structure to track pattern stats and assign pattern id's. */
typedef struct DetectPatternTracker {
const struct DetectContentData_ *cd;
int sm_list;
uint32_t cnt;
uint32_t mpm;
} DetectPatternTracker;
typedef struct DetectReplaceList_ {
const struct DetectContentData_ *cd;
uint8_t *found;
struct DetectReplaceList_ *next;
} DetectReplaceList;
/** only execute flowvar storage if rule matched */
#define DETECT_VAR_TYPE_FLOW_POSTMATCH 1
#define DETECT_VAR_TYPE_PKT_POSTMATCH 2
/** list for flowvar store candidates, to be stored from
* post-match function */
typedef struct DetectVarList_ {
uint16_t type; /**< type of store candidate POSTMATCH or ALWAYS */
uint8_t pad[2];
uint32_t idx; /**< flowvar name idx */
uint16_t len; /**< data len */
uint16_t key_len;
uint8_t *key;
uint8_t *buffer; /**< alloc'd buffer, may be freed by
post-match, post-non-match */
struct DetectVarList_ *next;
} DetectVarList;
typedef struct SCFPSupportSMList_ {
int list_id;
int priority;
struct SCFPSupportSMList_ *next;
} SCFPSupportSMList;
/** \brief IP only rules matching ctx. */
typedef struct DetectEngineIPOnlyCtx_ {
/* Lookup trees */
SCRadix4Tree tree_ipv4src, tree_ipv4dst;
SCRadix6Tree tree_ipv6src, tree_ipv6dst;
/* Used to build the radix trees */
IPOnlyCIDRItem *ip_src, *ip_dst;
uint32_t max_idx;
/* Used to map large signums to smaller values to compact the bitsets
* stored in the radix trees */
uint32_t *sig_mapping;
uint32_t sig_mapping_size;
} DetectEngineIPOnlyCtx;
typedef struct DetectEngineLookupFlow_ {
DetectPort *tcp;
DetectPort *udp;
struct SigGroupHead_ *sgh[256];
} DetectEngineLookupFlow;
typedef struct SigString_ {
char *filename;
char *sig_str;
char *sig_error;
int line;
TAILQ_ENTRY(SigString_) next;
} SigString;
/** \brief Signature loader statistics */
typedef struct SigFileLoaderStat_ {
TAILQ_HEAD(, SigString_) failed_sigs;
int bad_files;
int total_files;
int good_sigs_total;
int bad_sigs_total;
int skipped_sigs_total;
} SigFileLoaderStat;
typedef struct DetectEngineThreadKeywordCtxItem_ {
void *(*InitFunc)(void *);
void (*FreeFunc)(void *);
void *data;
struct DetectEngineThreadKeywordCtxItem_ *next;
int id;
const char *name; /* keyword name, for error printing */
} DetectEngineThreadKeywordCtxItem;
enum DetectEnginePrefilterSetting
{
DETECT_PREFILTER_MPM = 0, /**< use only mpm / fast_pattern */
DETECT_PREFILTER_AUTO = 1, /**< use mpm + keyword prefilters */
};
enum DetectEngineType
{
DETECT_ENGINE_TYPE_NORMAL = 0,
DETECT_ENGINE_TYPE_DD_STUB = 1, /* delayed detect stub: can be reloaded */
DETECT_ENGINE_TYPE_MT_STUB = 2, /* multi-tenant stub: cannot be reloaded */
DETECT_ENGINE_TYPE_TENANT = 3,
};
/* Flow states:
* toserver
* toclient
*/
#define FLOW_STATES 2
typedef struct {
uint32_t content_limit;
uint32_t content_inspect_min_size;
} DetectFileDataCfg;
typedef uint8_t (*DetectPacketHookFunc)(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p);
/**
* \brief Function type for rate filter callback.
*
* This function should return the new action to be applied. If no change to the
* action is to be made, the callback should return the current action provided
* in the new_action parameter.
*/
typedef uint8_t (*SCDetectRateFilterFunc)(const Packet *p, uint32_t sid, uint32_t gid, uint32_t rev,
uint8_t original_action, uint8_t new_action, void *arg);
16 years ago
/** \brief main detection engine ctx */
typedef struct DetectEngineCtx_ {
bool failure_fatal;
uint8_t flags; /**< only DE_QUIET */
uint8_t mpm_matcher; /**< mpm matcher this ctx uses */
MpmConfig *mpm_cfg;
uint8_t spm_matcher; /**< spm matcher this ctx uses */
uint32_t tenant_id;
Signature *sig_list;
uint32_t sig_cnt;
/* version of the srep data */
uint32_t srep_version;
/* reputation for netblocks */
SRepCIDRTree *srepCIDR_ctx;
Signature **sig_array;
uint32_t sig_array_len; /* size in array members */
uint32_t signum;
/* used by the signature ordering module */
struct SCSigOrderFunc_ *sc_sig_order_funcs;
/* main sigs */
DetectEngineLookupFlow flow_gh[FLOW_STATES];
/* init phase vars */
HashListTable *sgh_hash_table;
HashListTable *mpm_hash_table;
HashListTable *pattern_hash_table;
/* hash table used to cull out duplicate sigs */
HashListTable *dup_sig_hash_table;
DetectEngineIPOnlyCtx io_ctx;
/* maximum recursion depth for content inspection */
int inspection_recursion_limit;
/* maximum number of times a tx will get logged for rules not using app-layer keywords */
uint8_t guess_applayer_log_limit;
/* force app-layer tx finding for alerts with signatures not having app-layer keywords */
bool guess_applayer;
/* registration id for per thread ctx for the filemagic/file.magic keywords */
int filemagic_thread_ctx_id;
/* spm thread context prototype, built as spm matchers are constructed and
* later used to construct thread context for each thread. */
SpmGlobalThreadCtx *spm_global_thread_ctx;
/* Config options */
uint16_t max_uniq_toclient_groups;
uint16_t max_uniq_toserver_groups;
/* max flowbit id that is used */
uint32_t max_fb_id;
MpmCtxFactoryContainer *mpm_ctx_factory_container;
/* array containing all sgh's in use so we can loop
* through it in Stage4. */
struct SigGroupHead_ **sgh_array;
uint32_t sgh_array_cnt;
uint32_t sgh_array_size;
int32_t sgh_mpm_context_proto_tcp_packet;
int32_t sgh_mpm_context_proto_udp_packet;
int32_t sgh_mpm_context_proto_other_packet;
int32_t sgh_mpm_context_stream;
/* the max local id used amongst all sigs */
int32_t byte_extract_max_local_id;
/** version of the detect engine. The version is incremented on reloads */
uint32_t version;
/** sgh for signatures that match against invalid packets. In those cases
* we can't lookup by proto, address, port as we don't have these */
struct SigGroupHead_ *decoder_event_sgh;
/* Maximum size of the buffer for decoded base64 data. */
uint16_t base64_decode_max_len;
/** Store rule file and line so that parsers can use them in errors. */
int rule_line;
const char *rule_file;
const char *sigerror;
bool sigerror_silent;
bool sigerror_ok;
/** The rule errored out due to missing requirements. */
bool sigerror_requires;
/* specify the configuration for mpm context factory */
uint8_t sgh_mpm_ctx_cnf;
int keyword_id;
/** hash list of keywords that need thread local ctxs */
HashListTable *keyword_hash;
DetectFileDataCfg *filedata_config;
#ifdef PROFILE_RULES
struct SCProfileDetectCtx_ *profile_ctx;
#endif
#ifdef PROFILING
struct SCProfileKeywordDetectCtx_ *profile_keyword_ctx;
struct SCProfilePrefilterDetectCtx_ *profile_prefilter_ctx;
struct SCProfileKeywordDetectCtx_ **profile_keyword_ctx_per_list;
struct SCProfileSghDetectCtx_ *profile_sgh_ctx;
uint32_t profile_match_logging_threshold;
#endif
char config_prefix[64];
enum DetectEngineType type;
/** how many de_ctx' are referencing this */
uint32_t ref_cnt;
/** list in master: either active or freelist */
struct DetectEngineCtx_ *next;
/** id of loader thread 'owning' this de_ctx */
int loader_id;
/** are we using just mpm or also other prefilters */
enum DetectEnginePrefilterSetting prefilter_setting;
HashListTable *dport_hash_table;
DetectPort *tcp_priorityports;
DetectPort *udp_priorityports;
/** table for storing the string representation with the parsers result */
HashListTable *address_table;
/** table to store metadata keys and values */
HashTable *metadata_table;
/* hash tables with rule-time buffer registration. Start time registration
* is in detect-engine.c::g_buffer_type_hash */
HashListTable *buffer_type_hash_name;
HashListTable *buffer_type_hash_id;
uint32_t buffer_type_id;
uint32_t app_mpms_list_cnt;
DetectBufferMpmRegistry *app_mpms_list;
/* list with app inspect engines. Both the start-time registered ones and
* the rule-time registered ones. */
DetectEngineAppInspectionEngine *app_inspect_engines;
DetectEnginePktInspectionEngine *pkt_inspect_engines;
DetectBufferMpmRegistry *pkt_mpms_list;
uint32_t pkt_mpms_list_cnt;
DetectEngineFrameInspectionEngine *frame_inspect_engines;
DetectBufferMpmRegistry *frame_mpms_list;
uint32_t frame_mpms_list_cnt;
uint32_t prefilter_id;
HashListTable *prefilter_hash_table;
/** time of last ruleset reload */
struct timeval last_reload;
/** signatures stats */
SigFileLoaderStat sig_stat;
/* list of Fast Pattern registrations. Initially filled using a copy of
* `g_fp_support_smlist_list`, then extended at rule loading time if needed */
SCFPSupportSMList *fp_support_smlist_list;
/** per keyword flag indicating if a prefilter has been
* set for it. If true, the setup function will have to
* run. */
bool *sm_types_prefilter;
bool *sm_types_silent_error;
/* classification config parsing */
/* hash table used for holding the classification config info */
HashTable *class_conf_ht;
pcre2_code *class_conf_regex;
pcre2_match_data *class_conf_regex_match;
/* reference config parsing */
/* hash table used for holding the reference config info */
HashTable *reference_conf_ht;
pcre2_code *reference_conf_regex;
pcre2_match_data *reference_conf_regex_match;
/* --engine-analysis */
struct EngineAnalysisCtx_ *ea;
/* path to the tenant yaml for this engine */
char *tenant_path;
/* Track rule requirements for reporting after loading rules. */
SCDetectRequiresStatus *requirements;
/* number of signatures using filestore, limited as u16 */
uint16_t filestore_cnt;
detect: move non-pf rules into special prefilter engines Instead of having a per detection engine list of rule that couldn't be prefiltered, put those into special "prefilter" engines. For packet and frame rules this doesn't change much, it just removes some hard coded logic from the detect engine. For the packet non-prefilter rules in the "non-prefilter" special prefilter engine, add additional filtering for the packet variant. It can prefilter on alproto, dsize and dest port. The frame non-prefilter rules are added to a single engine, that per rule checks the alproto and the type. For app-layer, there is an engine per progress value, per app-layer protocol and per direction. This hooks app-layer non-prefilter rules into the app inspect logic at the correct "progress" hook. e.g. a rule like dns.query; bsize:1; Negated MPM rules will also fall into this category: dns.query; content:!"abc"; Are part of a special "generic list" app engine for dns, at the same progress hook as `dns.query`. This all results in a lot fewer checks: previous: -------------------------------------------------------------------------- Date: 1/29/2025 -- 10:22:25. Sorted by: number of checks. -------------------------------------------------------------------------- Num Rule Gid Rev Ticks % Checks Matches Max Ticks Avg Ticks Avg Match Avg No Match -------- ------------ -------- -------- ------------ ------ -------- -------- ----------- ----------- ----------- -------------- 1 20 1 0 181919672 11.85 588808 221 60454 308.96 2691.46 308.07 2 50 1 0 223455914 14.56 453104 418 61634 493.17 3902.59 490.02 3 60 1 0 185990683 12.12 453104 418 60950 410.48 1795.40 409.20 4 51 1 0 192436011 12.54 427028 6084 61223 450.64 2749.12 417.42 5 61 1 0 180401533 11.75 427028 6084 61093 422.46 2177.04 397.10 6 70 1 0 153899099 10.03 369836 0 61282 416.13 0.00 416.13 7 71 1 0 123389405 8.04 369836 12833 44921 333.63 2430.23 258.27 8 41 1 0 63889876 4.16 155824 12568 39138 410.01 1981.97 272.10 9 40 1 0 64149724 4.18 155818 210 39792 411.70 4349.57 406.38 10 10 1 0 70848850 4.62 65558 0 39544 1080.70 0.00 1080.70 11 11 1 0 94743878 6.17 65558 32214 60547 1445.19 2616.14 313.92 this commit: -------------------------------------------------------------------------- Date: 1/29/2025 -- 10:15:46. Sorted by: number of checks. -------------------------------------------------------------------------- Num Rule Gid Rev Ticks % Checks Matches Max Ticks Avg Ticks Avg Match Avg No Match -------- ------------ -------- -------- ------------ ------ -------- -------- ----------- ----------- ----------- -------------- 1 50 1 0 138776766 19.23 95920 418 167584 1446.80 3953.11 1435.83 2 60 1 0 97988084 13.58 95920 418 182817 1021.56 1953.63 1017.48 3 51 1 0 105318318 14.60 69838 6084 65649 1508.04 2873.38 1377.74 4 61 1 0 89571260 12.41 69838 6084 164632 1282.56 2208.41 1194.20 5 11 1 0 91132809 12.63 32779 32214 373569 2780.22 2785.58 2474.45 6 10 1 0 66095303 9.16 32779 0 56704 2016.39 0.00 2016.39 7 70 1 0 48107573 6.67 12928 0 42832 3721.19 0.00 3721.19 8 71 1 0 32308792 4.48 12928 12833 39565 2499.13 2510.05 1025.09 9 41 1 0 25546837 3.54 12886 12470 41479 1982.53 1980.84 2033.05 10 40 1 0 26069992 3.61 12886 210 38495 2023.13 4330.05 1984.91 11 20 1 0 639025 0.09 221 221 14750 2891.52 2891.52 0.00
2 years ago
/* name store for non-prefilter engines. Used in profiling but
* part of the API, so hash is always used. */
HashTable *non_pf_engine_names;
firewall: start of firewall rules support Config: Firewall rules are like normal rule, with some key differences. They are loaded separate, and first, from: ```yaml firewall-rule-path: /etc/suricata/firewall/ firewall-rule-files: - fw.rules ``` Can also be loaded with --firewall-rules-exclusive: Mostly for QA purposes. Allow -S with --firewall-rules-exclusive, so that firewall and threat detection rules can be tested together. Rules: Differences with regular "threat detection" rules: 1. these rules are evaluated before threat detection rules 2. these rules are evaluated in the order as they appear in the rule file 3. currently only rules specifying an explicit hook at supported a. as a consequence, no rules will be treated as (like) IP-only, PD-only or DE-only Require explicit action scope for firewall rules. Default policy is drop for the firewall tables. Actions: New action "accept" is added to allow traffic in the filter tables. New scope "accept:tx" is added to allow accepting a transaction. Tables: Rulesets are per table. Table processing order: `packet:filter` -> `packet:td` -> `app:*:*` -> `app:td`. Each of the tables has some unique properties: `packet:filter`: - default policy is `drop:packet` - rules are process in order - action scopes are explicit - `drop` or `accept` is immediate - `accept:hook` continues to `packet:td` `packet:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `app:*:*` or `alert/action finalize` `app:*:*`: - default policy is `drop:flow` - rules are process in order - action scopes are explicit - `drop` is immediate - `accept` is conditional on possible `drop` from `packet:td` - `accept:hook` continues to `app:td`, `accept:packet` or `accept:flow` continues to `alert/action finalize` `app:td`: - default policy is `accept:hook` - rules are ordered by IDS/IPS ordering logic - action scopes are implicit - actions are queued - continues to `alert/action finalize` Implementation: During sigorder, split into packet:filter, app:*:* and general td. Allow fw rules to work when in pass:flow mode. When firewall mode is enabled, `pass:flow` will not skip the detection engine anymore, but instead process the firewall rules and then apply the pass before inspecting threat detect rules.
6 months ago
const char *firewall_rule_file_exclusive;
/* user provided rate filter callbacks. */
SCDetectRateFilterFunc RateFilterCallback;
/* use provided data to be passed to rate_filter_callback. */
void *rate_filter_callback_arg;
/* Hook for pre_stream engine if it is used. */
DetectPacketHookFunc PreStreamHook;
/** TCP pre_stream hook rule groups. One per direction. */
struct SigGroupHead_ *pre_stream_sgh[2];
/* Hook for pre_flow engine if it is used. */
DetectPacketHookFunc PreFlowHook;
/** pre_flow hook rule groups. Before flow we don't know a direction yet. */
struct SigGroupHead_ *pre_flow_sgh;
} DetectEngineCtx;
/**
* \brief Register a callback when a rate_filter has been applied to
* an alert.
*
* This callback is added to the current detection engine and will be
* copied to all future detection engines over rule reloads.
*/
void SCDetectEngineRegisterRateFilterCallback(SCDetectRateFilterFunc cb, void *arg);
/* Engine groups profiles (low, medium, high, custom) */
enum {
ENGINE_PROFILE_UNKNOWN,
ENGINE_PROFILE_LOW,
ENGINE_PROFILE_MEDIUM,
ENGINE_PROFILE_HIGH,
ENGINE_PROFILE_CUSTOM,
};
/* Siggroup mpm context profile */
enum {
ENGINE_SGH_MPM_FACTORY_CONTEXT_FULL = 0,
ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE,
ENGINE_SGH_MPM_FACTORY_CONTEXT_AUTO,
#define ENGINE_SGH_MPM_FACTORY_CONTEXT_START_ID_RANGE (ENGINE_SGH_MPM_FACTORY_CONTEXT_AUTO + 1)
};
#define DETECT_FILESTORE_MAX 15
typedef struct SignatureNonPrefilterStore_ {
SigIntId id;
SignatureMask mask;
AppProto alproto;
} SignatureNonPrefilterStore;
detect: rewrite of the detect engine Use per tx detect_flags to track prefilter. Detect flags are used for 2 things: 1. marking tx as fully inspected 2. tracking already run prefilter (incl mpm) engines This supercedes the MpmIDs API for directionless tracking of the prefilter engines. When we have no SGH we have to flag the txs that are 'complete' as inspected as well. Special handling for the stream engine: If a rule mixes TX inspection and STREAM inspection, we can encounter the case where the rule is evaluated against multiple transactions during a single inspection run. As the stream data is exactly the same for each of those runs, it's wasteful to rerun inspection of the stream portion of the rule. This patch enables caching of the stream 'inspect engine' result in the local 'RuleMatchCandidateTx' array. This is valid only during the live of a single inspection run. Remove stateful inspection from 'mask' (SignatureMask). The mask wasn't used in most cases for those rules anyway, as there we rely on the prefilter. Add a alproto check to catch the remaining cases. When building the active non-mpm/non-prefilter list check not just the mask, but also the alproto. This especially helps stateful rules with negated mpm. Simplify AppLayerParserHasDecoderEvents usage in detection to only return true if protocol detection events are set. Other detection is done in inspect engines. Move rule group lookup and handling into it's own function. Handle 'post lookup' tasks immediately, instead of after the first detect run. The tasks were independent of the initial detection. Many cleanups and much refactoring.
8 years ago
/** array of TX inspect rule candidates */
typedef struct RuleMatchCandidateTx {
SigIntId id; /**< internal signature id */
uint32_t *flags; /**< inspect flags ptr */
union {
struct {
bool stream_stored;
uint8_t stream_result;
};
uint32_t stream_reset;
};
const Signature *s; /**< ptr to sig */
} RuleMatchCandidateTx;
/** Stores a single u32 for a rule match of the type `sm_type`. Used by
* flowbits prefilter to register DETECT_FLOWBITS,<flowbit id> for post
* match handling. */
typedef struct PostRuleMatchWorkQueueItem {
int sm_type; /**< sigmatch type e.g. DETECT_FLOWBITS */
uint32_t value; /**< value to be interpreted by the sm_type
* implementation. E.g. flowbit id. */
#ifdef DEBUG
SigIntId id;
#endif
} PostRuleMatchWorkQueueItem;
/** Array of PostRuleMatchWorkQueueItem's. */
typedef struct PostRuleMatchWorkQueue {
PostRuleMatchWorkQueueItem *q; /**< array pointer */
uint32_t len; /**< number of array elements in use. */
uint32_t size; /**< allocation size in number of elements. */
} PostRuleMatchWorkQueue;
16 years ago
/**
* Detection engine thread data.
*/
typedef struct DetectEngineThreadCtx_ {
/** \note multi-tenant hash lookup code from Detect() *depends*
* on this being the first member */
uint32_t tenant_id;
SC_ATOMIC_DECLARE(int, so_far_used_by_detect);
16 years ago
/* the thread to which this detection engine thread belongs */
ThreadVars *tv;
uint32_t mt_det_ctxs_cnt;
struct DetectEngineThreadCtx_ **mt_det_ctxs;
HashTable *mt_det_ctxs_hash;
struct DetectEngineTenantMapping_ *tenant_array;
uint32_t tenant_array_size;
uint32_t (*TenantGetId)(const void *, const Packet *p);
16 years ago
/* detection engine variables */
uint64_t raw_stream_progress;
/** offset into the payload of the end of the last match by: content, pcre, etc */
uint32_t buffer_offset;
/** used by pcre match function alone: normally in sync with buffer_offset, but
* points to 1 byte after the start of the last pcre match if a pcre match happened. */
uint32_t pcre_match_start_offset;
/** SPM thread context used for scanning. This has been cloned from the
* prototype held by DetectEngineCtx. */
SpmThreadCtx *spm_thread_ctx;
/* byte_* values */
uint64_t *byte_values;
/* counter for the filestore array below -- up here for cache reasons. */
uint16_t filestore_cnt;
/** id for alert counter */
uint16_t counter_alerts;
/** id for discarded alerts counter */
uint16_t counter_alerts_overflow;
/** id for suppressed alerts counter */
uint16_t counter_alerts_suppressed;
#ifdef PROFILING
uint16_t counter_mpm_list;
uint16_t counter_nonmpm_list;
uint16_t counter_fnonmpm_list;
uint16_t counter_match_list;
#endif
struct {
InspectionBuffer *buffers;
uint32_t buffers_size; /**< in number of elements */
uint32_t to_clear_idx;
uint32_t *to_clear_queue;
} inspect;
struct {
/** inspection buffers for more complex case. As we can inspect multiple
* buffers in parallel, we need this extra wrapper struct */
InspectionBufferMultipleForList *buffers;
uint32_t buffers_size; /**< in number of elements */
uint32_t to_clear_idx;
uint32_t *to_clear_queue;
} multi_inspect;
/* true if tx_id is set */
bool tx_id_set;
/** ID of the transaction currently being inspected. */
uint64_t tx_id;
int64_t frame_id;
uint64_t frame_inspect_progress; /**< used to set Frame::inspect_progress after all inspection
on a frame is complete. */
Packet *p;
uint8_t *base64_decoded;
int base64_decoded_len;
uint16_t alert_queue_size;
uint16_t alert_queue_capacity;
PacketAlert *alert_queue;
/** array of signature pointers we're going to inspect in the detection
* loop. */
Signature **match_array;
/** size of the array in items (mem size if * sizeof(Signature *)
* Only used during initialization. */
uint32_t match_array_len;
/** size in use */
SigIntId match_array_cnt;
detect: rewrite of the detect engine Use per tx detect_flags to track prefilter. Detect flags are used for 2 things: 1. marking tx as fully inspected 2. tracking already run prefilter (incl mpm) engines This supercedes the MpmIDs API for directionless tracking of the prefilter engines. When we have no SGH we have to flag the txs that are 'complete' as inspected as well. Special handling for the stream engine: If a rule mixes TX inspection and STREAM inspection, we can encounter the case where the rule is evaluated against multiple transactions during a single inspection run. As the stream data is exactly the same for each of those runs, it's wasteful to rerun inspection of the stream portion of the rule. This patch enables caching of the stream 'inspect engine' result in the local 'RuleMatchCandidateTx' array. This is valid only during the live of a single inspection run. Remove stateful inspection from 'mask' (SignatureMask). The mask wasn't used in most cases for those rules anyway, as there we rely on the prefilter. Add a alproto check to catch the remaining cases. When building the active non-mpm/non-prefilter list check not just the mask, but also the alproto. This especially helps stateful rules with negated mpm. Simplify AppLayerParserHasDecoderEvents usage in detection to only return true if protocol detection events are set. Other detection is done in inspect engines. Move rule group lookup and handling into it's own function. Handle 'post lookup' tasks immediately, instead of after the first detect run. The tasks were independent of the initial detection. Many cleanups and much refactoring.
8 years ago
RuleMatchCandidateTx *tx_candidates;
uint32_t tx_candidates_size;
MpmThreadCtx mtc; /**< thread ctx for the mpm */
/* work queue for post-rule matching affecting prefilter */
PostRuleMatchWorkQueue post_rule_work_queue;
PrefilterRuleStore pmq;
16 years ago
/* string to replace */
DetectReplaceList *replist;
/* vars to store in post match function */
DetectVarList *varlist;
/* Array in which the filestore keyword stores file id and tx id. If the
* full signature matches, these are processed by a post-match filestore
* function to finalize the store. */
struct {
uint32_t file_id;
uint64_t tx_id;
} filestore[DETECT_FILESTORE_MAX];
16 years ago
DetectEngineCtx *de_ctx;
/** store for keyword contexts that need a per thread storage. Per de_ctx. */
void **keyword_ctxs_array;
int keyword_ctxs_size;
/** store for keyword contexts that need a per thread storage. Global. */
int global_keyword_ctxs_size;
void **global_keyword_ctxs_array;
AppLayerDecoderEvents *decoder_events;
uint16_t events;
/** stats id for lua rule errors */
uint16_t lua_rule_errors;
/** stats id for lua blocked function counts */
uint16_t lua_blocked_function_errors;
/** stats if for lua instruction limit errors */
uint16_t lua_instruction_limit_errors;
/** stat of lua memory limit errors. */
uint16_t lua_memory_limit_errors;
#ifdef DEBUG
uint64_t pkt_stream_add_cnt;
uint64_t payload_mpm_cnt;
uint64_t payload_mpm_size;
uint64_t stream_mpm_cnt;
uint64_t stream_mpm_size;
uint64_t payload_persig_cnt;
uint64_t payload_persig_size;
uint64_t stream_persig_cnt;
uint64_t stream_persig_size;
#endif
#ifdef PROFILE_RULES
struct SCProfileData_ *rule_perf_data;
int rule_perf_data_size;
uint32_t rule_perf_last_sync;
#endif
#ifdef PROFILING
struct SCProfileKeywordData_ *keyword_perf_data;
struct SCProfileKeywordData_ **keyword_perf_data_per_list;
int keyword_perf_list; /**< list we're currently inspecting, DETECT_SM_LIST_* */
struct SCProfileSghData_ *sgh_perf_data;
struct SCProfilePrefilterData_ *prefilter_perf_data;
/** bytes inspected by current prefilter callback call */
uint64_t prefilter_bytes;
/** number of times we inspected a buffer */
uint64_t prefilter_bytes_called;
#endif
16 years ago
} DetectEngineThreadCtx;
/** \brief element in sigmatch type table.
*/
typedef struct SigTableElmt_ {
/** Packet match function pointer */
int (*Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
/** AppLayer TX match function pointer */
int (*AppLayerTxMatch)(DetectEngineThreadCtx *, Flow *,
uint8_t flags, void *alstate, void *txv,
const Signature *, const SigMatchCtx *);
/** File match function pointer */
int (*FileMatch)(DetectEngineThreadCtx *,
Flow *, /**< *LOCKED* flow */
uint8_t flags, File *, const Signature *, const SigMatchCtx *);
/** InspectionBuffer transformation callback */
void (*Transform)(DetectEngineThreadCtx *, InspectionBuffer *, void *context);
bool (*TransformValidate)(const uint8_t *content, uint16_t content_len, void *context);
/** Transform identity callback */
void (*TransformId)(const uint8_t **data, uint32_t *length, void *context);
/** keyword setup function pointer */
int (*Setup)(DetectEngineCtx *, Signature *, const char *);
bool (*SupportsPrefilter)(const Signature *s);
int (*SetupPrefilter)(DetectEngineCtx *de_ctx, struct SigGroupHead_ *sgh);
void (*Free)(DetectEngineCtx *, void *);
#ifdef UNITTESTS
void (*RegisterTests)(void);
#endif
uint16_t flags;
/* coccinelle: SigTableElmt:flags:SIGMATCH_ */
/** bitfield of tables supported by this rule: used by DETECT_TABLE_*_FLAG flags. */
uint8_t tables;
/** better keyword to replace the current one */
uint16_t alternative;
const char *name; /**< keyword name alias */
const char *alias; /**< name alias */
const char *desc;
const char *url;
// Cleanup function for freeing rust allocated name or such
void (*Cleanup)(struct SigTableElmt_ *);
} SigTableElmt;
/* event code */
enum {
FILE_DECODER_EVENT_NO_MEM,
FILE_DECODER_EVENT_INVALID_SWF_LENGTH,
FILE_DECODER_EVENT_INVALID_SWF_VERSION,
FILE_DECODER_EVENT_Z_DATA_ERROR,
FILE_DECODER_EVENT_Z_STREAM_ERROR,
FILE_DECODER_EVENT_Z_BUF_ERROR,
FILE_DECODER_EVENT_Z_UNKNOWN_ERROR,
FILE_DECODER_EVENT_LZMA_IO_ERROR,
FILE_DECODER_EVENT_LZMA_HEADER_TOO_SHORT_ERROR,
FILE_DECODER_EVENT_LZMA_DECODER_ERROR,
FILE_DECODER_EVENT_LZMA_MEMLIMIT_ERROR,
FILE_DECODER_EVENT_LZMA_XZ_ERROR,
FILE_DECODER_EVENT_LZMA_UNKNOWN_ERROR,
DETECT_EVENT_TOO_MANY_BUFFERS,
DETECT_EVENT_POST_MATCH_QUEUE_FAILED,
};
#define SIG_GROUP_HEAD_HAVERAWSTREAM BIT_U16(0)
#ifdef HAVE_MAGIC
#define SIG_GROUP_HEAD_HAVEFILEMAGIC BIT_U16(1)
#endif
#define SIG_GROUP_HEAD_HAVEFILEMD5 BIT_U16(2)
#define SIG_GROUP_HEAD_HAVEFILESIZE BIT_U16(3)
#define SIG_GROUP_HEAD_HAVEFILESHA1 BIT_U16(4)
#define SIG_GROUP_HEAD_HAVEFILESHA256 BIT_U16(5)
enum MpmBuiltinBuffers {
MPMB_TCP_PKT_TS,
MPMB_TCP_PKT_TC,
MPMB_TCP_STREAM_TS,
MPMB_TCP_STREAM_TC,
MPMB_UDP_TS,
MPMB_UDP_TC,
MPMB_OTHERIP,
MPMB_MAX,
};
typedef struct MpmStore_ {
uint8_t *sid_array;
uint32_t sid_array_size;
int direction;
enum MpmBuiltinBuffers buffer;
int sm_list;
int32_t sgh_mpm_context;
AppProto alproto;
MpmCtx *mpm_ctx;
} MpmStore;
typedef void (*PrefilterPktFn)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx);
typedef void (*PrefilterFrameFn)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
const struct Frames *frames, const struct Frame *frame);
typedef struct AppLayerTxData AppLayerTxData;
typedef void (*PrefilterTxFn)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f,
void *tx, const uint64_t tx_id, const AppLayerTxData *tx_data, const uint8_t flags);
typedef struct PrefilterEngineList_ {
uint16_t id;
/** App Proto this engine applies to: only used with Tx Engines */
AppProto alproto;
/** Minimal Tx progress we need before running the engine. Only used
* with Tx Engine. Set to -1 for all states. */
int8_t tx_min_progress;
uint8_t frame_type;
SignatureMask pkt_mask; /**< mask for pkt engines */
enum SignatureHookPkt pkt_hook;
/** Context for matching. Might be MpmCtx for MPM engines, other ctx'
* for other engines. */
void *pectx;
PrefilterPktFn Prefilter;
PrefilterTxFn PrefilterTx;
PrefilterFrameFn PrefilterFrame;
void (*PrefilterPostRule)(
DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f);
struct PrefilterEngineList_ *next;
/** Free function for pectx data. If NULL the memory is not freed. */
void (*Free)(void *pectx);
const char *name;
/* global id for this prefilter */
uint32_t gid;
} PrefilterEngineList;
typedef struct PrefilterEngine_ {
uint16_t local_id;
/** App Proto this engine applies to: only used with Tx Engines */
AppProto alproto;
union {
struct {
SignatureMask mask; /**< mask for pkt engines */
uint8_t hook; /**< enum SignatureHookPkt */
} pkt;
/** Minimal Tx progress we need before running the engine. Only used
* with Tx Engine. Set to -1 for all states. */
int8_t tx_min_progress;
uint8_t frame_type;
} ctx;
bool is_last;
bool is_last_for_progress;
/** Context for matching. Might be MpmCtx for MPM engines, other ctx'
* for other engines. */
void *pectx;
union {
PrefilterPktFn Prefilter;
PrefilterTxFn PrefilterTx;
PrefilterFrameFn PrefilterFrame;
void (*PrefilterPostRule)(
DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f);
} cb;
/* global id for this prefilter */
uint32_t gid;
} PrefilterEngine;
typedef struct SigGroupHeadInitData_ {
MpmStore mpm_store[MPMB_MAX];
uint8_t *sig_array; /**< bit array of sig nums (internal id's) */
uint32_t sig_size; /**< size in bytes */
uint8_t protos[256]; /**< proto(s) this sgh is for */
uint32_t direction; /**< set to SIG_FLAG_TOSERVER, SIG_FLAG_TOCLIENT or both */
int score; /**< try to make this group a unique one */
uint32_t max_sig_id; /**< max signature idx for this sgh */
MpmCtx **app_mpms;
MpmCtx **pkt_mpms;
MpmCtx **frame_mpms;
PrefilterEngineList *pkt_engines;
PrefilterEngineList *payload_engines;
PrefilterEngineList *tx_engines;
PrefilterEngineList *frame_engines;
PrefilterEngineList *post_rule_match_engines;
/** number of sigs in this group */
SigIntId sig_cnt;
/** Array with sig ptrs... size is sig_cnt * sizeof(Signature *) */
Signature **match_array;
} SigGroupHeadInitData;
/** \brief Container for matching data for a signature group */
typedef struct SigGroupHead_ {
uint16_t flags;
/* coccinelle: SigGroupHead:flags:SIG_GROUP_HEAD_ */
/** the number of signatures in this sgh that have the filestore keyword
* set. */
uint16_t filestore_cnt;
uint32_t id; /**< unique id used to index sgh_array for stats */
PrefilterEngine *pkt_engines;
PrefilterEngine *payload_engines;
PrefilterEngine *tx_engines;
PrefilterEngine *frame_engines;
PrefilterEngine *post_rule_match_engines; /**< engines to run after rules modified a state */
/* ptr to our init data we only use at... init :) */
SigGroupHeadInitData *init;
} SigGroupHead;
/** sigmatch has no options, so the parser shouldn't expect any */
#define SIGMATCH_NOOPT BIT_U16(0)
/** sigmatch is compatible with a ip only rule */
#define SIGMATCH_IPONLY_COMPAT BIT_U16(1)
/** sigmatch is compatible with a decode event only rule */
#define SIGMATCH_DEONLY_COMPAT BIT_U16(2)
// vacancy
/** sigmatch may have options, so the parser should be ready to
* deal with both cases */
#define SIGMATCH_OPTIONAL_OPT BIT_U16(4)
/** input may be wrapped in double quotes. They will be stripped before
* input data is passed to keyword parser */
#define SIGMATCH_QUOTES_OPTIONAL BIT_U16(5)
/** input MUST be wrapped in double quotes. They will be stripped before
* input data is passed to keyword parser. Missing double quotes lead to
* error and signature invalidation. */
#define SIGMATCH_QUOTES_MANDATORY BIT_U16(6)
/** negation parsing is handled by the rule parser. Signature::init_data::negated
* will be set to true or false prior to calling the keyword parser. Exclamation
* mark is stripped from the input to the keyword parser. */
#define SIGMATCH_HANDLE_NEGATION BIT_U16(7)
/** keyword is a content modifier */
#define SIGMATCH_INFO_CONTENT_MODIFIER BIT_U16(8)
/** keyword is a sticky buffer */
#define SIGMATCH_INFO_STICKY_BUFFER BIT_U16(9)
/** keyword is deprecated: used to suggest an alternative */
#define SIGMATCH_INFO_DEPRECATED BIT_U16(10)
/** strict parsing is enabled */
#define SIGMATCH_STRICT_PARSING BIT_U16(11)
/** keyword supported by firewall rules */
#define SIGMATCH_SUPPORT_FIREWALL BIT_U16(12)
/** keyword supporting setting an optional direction */
#define SIGMATCH_SUPPORT_DIR BIT_U16(13)
enum DetectEngineTenantSelectors
{
TENANT_SELECTOR_UNKNOWN = 0, /**< not set */
TENANT_SELECTOR_DIRECT, /**< method provides direct tenant id */
TENANT_SELECTOR_VLAN, /**< map vlan to tenant id */
TENANT_SELECTOR_LIVEDEV, /**< map livedev to tenant id */
};
typedef struct DetectEngineTenantMapping_ {
uint32_t tenant_id;
/* traffic id that maps to the tenant id */
uint32_t traffic_id;
struct DetectEngineTenantMapping_ *next;
} DetectEngineTenantMapping;
typedef struct DetectEngineMasterCtx_ {
SCMutex lock;
/** enable multi tenant mode */
int multi_tenant_enabled;
/** version, incremented after each 'apply to threads' */
uint32_t version;
/** list of active detection engines. This list is used to generate the
* threads det_ctx's */
DetectEngineCtx *list;
/** free list, containing detection engines that will be removed but may
* still be referenced by det_ctx's. Freed as soon as all references are
* gone. */
DetectEngineCtx *free_list;
enum DetectEngineTenantSelectors tenant_selector;
/** list of tenant mappings. Updated under lock. Used to generate lookup
* structures. */
DetectEngineTenantMapping *tenant_mapping_list;
/** list of keywords that need thread local ctxs,
* only updated by keyword registration at start up. Not
* covered by the lock. */
DetectEngineThreadKeywordCtxItem *keyword_list;
int keyword_id;
} DetectEngineMasterCtx;
/* Table with all SigMatch registrations */
extern SigTableElmt *sigmatch_table;
/** Remember to add the options in SignatureIsIPOnly() at detect.c otherwise it wont be part of a signature group */
/* detection api */
TmEcode Detect(ThreadVars *tv, Packet *p, void *data);
uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p);
uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p);
SigMatch *SigMatchAlloc(void);
Signature *SigFindSignatureBySidGid(DetectEngineCtx *, uint32_t, uint32_t);
void SigMatchFree(DetectEngineCtx *, SigMatch *sm);
void SigRegisterTests(void);
void DisableDetectFlowFileFlags(Flow *f);
char *DetectLoadCompleteSigPath(const DetectEngineCtx *, const char *sig_file);
int SigLoadSignatures(DetectEngineCtx *, char *, bool);
void SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Packet *p);
int SignatureIsIPOnly(DetectEngineCtx *de_ctx, const Signature *s);
const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, const Packet *p);
int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *, void *data, const char *name);
int DetectRegisterThreadCtxFuncs(DetectEngineCtx *, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int);
void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *, int);
void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags);
detect: rewrite of the detect engine Use per tx detect_flags to track prefilter. Detect flags are used for 2 things: 1. marking tx as fully inspected 2. tracking already run prefilter (incl mpm) engines This supercedes the MpmIDs API for directionless tracking of the prefilter engines. When we have no SGH we have to flag the txs that are 'complete' as inspected as well. Special handling for the stream engine: If a rule mixes TX inspection and STREAM inspection, we can encounter the case where the rule is evaluated against multiple transactions during a single inspection run. As the stream data is exactly the same for each of those runs, it's wasteful to rerun inspection of the stream portion of the rule. This patch enables caching of the stream 'inspect engine' result in the local 'RuleMatchCandidateTx' array. This is valid only during the live of a single inspection run. Remove stateful inspection from 'mask' (SignatureMask). The mask wasn't used in most cases for those rules anyway, as there we rely on the prefilter. Add a alproto check to catch the remaining cases. When building the active non-mpm/non-prefilter list check not just the mask, but also the alproto. This especially helps stateful rules with negated mpm. Simplify AppLayerParserHasDecoderEvents usage in detection to only return true if protocol detection events are set. Other detection is done in inspect engines. Move rule group lookup and handling into it's own function. Handle 'post lookup' tasks immediately, instead of after the first detect run. The tasks were independent of the initial detection. Many cleanups and much refactoring.
8 years ago
void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size);
void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx);
int DetectFlowbitsAnalyze(DetectEngineCtx *de_ctx);
int DetectMetadataHashInit(DetectEngineCtx *de_ctx);
void DetectMetadataHashFree(DetectEngineCtx *de_ctx);
/* events */
void DetectEngineSetEvent(DetectEngineThreadCtx *det_ctx, uint8_t e);
void DumpPatterns(DetectEngineCtx *de_ctx);
#endif /* SURICATA_DETECT_H */