detect/firewall: configurable default policies

Allow configurable policies, including accept. For app-layer this
requires looping all available hooks to apply the policies.

Support configurable policies for packet-filter, pre-stream, pre-flow.

If there are no rules there is also no rule group (sgh). Make sure
the app hooks policies are correctly handled in this case by allowing
a NULL sgh to be handled as well.

For tx rule match actually apply drop directly. Previously this was
always handled by the default drop:flow policy.

Ticket: #7701.
pull/15398/head
Victor Julien 2 months ago
parent eaacb41aaf
commit 7134592fea

@ -50,6 +50,8 @@
#include <glob.h>
#endif
#include "app-layer-parser.h"
extern int rule_reload;
extern int engine_analysis;
static bool fp_engine_analysis_set = false;
@ -294,6 +296,15 @@ static int ProcessSigFiles(DetectEngineCtx *de_ctx, char *pattern, SigFileLoader
static int LoadFirewallRuleFiles(DetectEngineCtx *de_ctx)
{
if (DetectFirewallInitDefaultPolicies(de_ctx) < 0) {
SCLogError("initializing firewall policies failed");
return -1;
}
if (DetectFirewallLoadDefaultPolicies(de_ctx) < 0) {
SCLogError("loading firewall policies failed");
return -1;
}
if (de_ctx->firewall_rule_file_exclusive) {
int32_t good_sigs = 0;
int32_t bad_sigs = 0;
@ -393,7 +404,7 @@ int SigLoadSignatures(DetectEngineCtx *de_ctx, char *sig_file, bool sig_file_exc
if (EngineModeIsFirewall() || de_ctx->firewall_rule_file_exclusive) {
if (LoadFirewallRuleFiles(de_ctx) < 0) {
if (de_ctx->failure_fatal) {
if (de_ctx->fw_policies == NULL || de_ctx->failure_fatal) {
exit(EXIT_FAILURE);
}
ret = -1;

@ -35,6 +35,9 @@ void RulesDumpTxMatchArray(const DetectEngineThreadCtx *det_ctx, const SigGroupH
const Packet *p, const uint64_t tx_id, const uint32_t rule_cnt,
const uint32_t pkt_prefilter_cnt)
{
if (sgh == NULL)
return;
SCJsonBuilder *js =
CreateEveHeaderWithTxId(p, LOG_DIR_PACKET, "inspectedrules", NULL, tx_id, NULL);
if (js == NULL)
@ -78,6 +81,9 @@ void RulesDumpTxMatchArray(const DetectEngineThreadCtx *det_ctx, const SigGroupH
void RulesDumpMatchArray(const DetectEngineThreadCtx *det_ctx,
const SigGroupHead *sgh, const Packet *p)
{
if (sgh == NULL)
return;
SCJsonBuilder *js = CreateEveHeader(p, LOG_DIR_PACKET, "inspectedrules", NULL, NULL);
if (js == NULL)
return;

@ -2738,6 +2738,7 @@ void DetectEngineCtxFree(DetectEngineCtx *de_ctx)
if (de_ctx->non_pf_engine_names) {
HashTableFree(de_ctx->non_pf_engine_names);
}
SCFree(de_ctx->fw_policies);
SCFree(de_ctx);
//DetectAddressGroupPrintMemory();
//DetectSigGroupPrintMemory();

@ -1548,13 +1548,14 @@ static uint8_t ActionStringToFlags(const char *action)
* to its Signature instance.
*
* \param s Pointer to the Signature instance to which the action belongs.
* \param action Pointer to the action string used by the Signature.
* \param action_in Pointer to the action string used by the Signature.
*
* \retval 0 On successfully parsing the action string and adding it to the
* Signature.
* \retval -1 On failure.
*/
static int SigParseActionDo(Signature *s, const char *action_in, const int idx)
static int SigParseActionDo(const char *action_in, const int idx, const bool fw_rule,
uint8_t *action_out, uint8_t *scope_out)
{
char action[32];
strlcpy(action, action_in, sizeof(action));
@ -1577,7 +1578,7 @@ static int SigParseActionDo(Signature *s, const char *action_in, const int idx)
if (flags == 0)
return -1;
if (s->init_data->firewall_rule) {
if (fw_rule) {
if (idx == 0 &&
!(flags & (ACTION_ACCEPT | ACTION_DROP | ACTION_REJECT_ANY | ACTION_CONFIG))) {
SCLogError("only accept, config, drop and reject actions allowed as primary action "
@ -1638,24 +1639,24 @@ static int SigParseActionDo(Signature *s, const char *action_in, const int idx)
o, action_in);
return -1;
}
if (s->action_scope != 0 && s->action_scope != scope_flags) {
if (*scope_out != 0 && *scope_out != scope_flags) {
SCLogError("multi-action rules cannot use different action scopes");
return -1;
}
s->action_scope = scope_flags;
*scope_out = scope_flags;
}
/* require explicit action scope for fw rules */
if (s->init_data->firewall_rule && s->action_scope == 0) {
if (fw_rule && *scope_out == 0) {
SCLogError("firewall rules require setting an explicit action scope");
return -1;
}
if (!s->init_data->firewall_rule && (flags & ACTION_ACCEPT)) {
if (!fw_rule && (flags & ACTION_ACCEPT)) {
SCLogError("'accept' action only supported for firewall rules");
return -1;
}
s->action |= flags;
*action_out |= flags;
return 0;
}
@ -1663,7 +1664,7 @@ static int SigParseAction(Signature *s, const char *action_in)
{
/* multi-action rules are only supported for firewall rules at this time. */
if (!s->init_data->firewall_rule)
return SigParseActionDo(s, action_in, 0);
return SigParseActionDo(action_in, 0, false, &s->action, &s->action_scope);
int r = 0;
char *copy = SCStrdup(action_in);
@ -1674,7 +1675,7 @@ static int SigParseAction(Signature *s, const char *action_in)
char *xsaveptr = NULL;
char *a = strtok_r(copy, ",", &xsaveptr);
while (a != NULL) {
if (SigParseActionDo(s, a, i) < 0) {
if (SigParseActionDo(a, i, true, &s->action, &s->action_scope) < 0) {
r = -1;
break;
}
@ -3723,6 +3724,200 @@ void DetectSetupParseRegexes(const char *parse_str, DetectParseRegex *detect_par
}
}
static int DoParsePolicy(const char *policy_name, struct DetectFirewallPolicy *pol)
{
SCConfNode *policy_actions = SCConfGetNode(policy_name);
if (policy_actions == NULL) {
SCLogDebug("fw: no policy at %s", policy_name);
return 0;
}
uint8_t action = 0;
uint8_t action_scope = 0;
int idx = 0;
SCConfNode *paction = NULL;
TAILQ_FOREACH (paction, &policy_actions->head, next) {
SCLogNotice("fw: %s => %s", policy_name, paction->val);
if (SigParseActionDo(paction->val, idx, true, &action, &action_scope) < 0)
return -1;
idx++;
}
pol->action = action;
pol->action_scope = action_scope;
return 1;
}
static int DoParseAppPolicy(const char *prefix, const AppProto app_proto, const char *hookname,
const uint8_t state, const uint8_t complete_state, const int direction,
struct DetectFirewallAppPolicy *app_fw_policies)
{
char policy_name[256];
const char *in_name = hookname;
if (hookname == NULL) {
if (state == 0) {
if (direction == STREAM_TOSERVER)
hookname = "request-started";
else
hookname = "response-started";
} else if (state == complete_state) {
if (direction == STREAM_TOSERVER)
hookname = "request-complete";
else
hookname = "response-complete";
}
if (hookname == NULL)
return 0;
}
char *nname = SCStrdup(hookname);
if (nname == NULL)
return -1;
for (int i = 0; nname[i] != '\0'; i++) {
if (nname[i] == '_')
nname[i] = '-';
}
const char *app_name = AppProtoToString(app_proto);
int r = snprintf(policy_name, sizeof(policy_name), "%s.%s.%s", prefix, app_name, nname);
SCFree(nname);
if (r < 0 || (size_t)r >= sizeof(policy_name)) {
FatalError("internal error: failed to assemble firewall policy config string");
}
if (direction == STREAM_TOSERVER)
r = DoParsePolicy(policy_name, &app_fw_policies[app_proto].ts[state]);
else
r = DoParsePolicy(policy_name, &app_fw_policies[app_proto].tc[state]);
if (r == 0 && in_name != NULL) {
if (state == 0) {
if (direction == STREAM_TOSERVER)
hookname = "request-started";
else
hookname = "response-started";
} else if (state == complete_state) {
if (direction == STREAM_TOSERVER)
hookname = "request-complete";
else
hookname = "response-complete";
}
if (hookname == NULL)
return 0;
r = snprintf(policy_name, sizeof(policy_name), "%s.%s.%s", prefix, app_name, hookname);
if (r < 0 || (size_t)r >= sizeof(policy_name)) {
FatalError("internal error: failed to assemble firewall policy config string");
}
if (direction == STREAM_TOSERVER)
return DoParsePolicy(policy_name, &app_fw_policies[app_proto].ts[state]);
else
return DoParsePolicy(policy_name, &app_fw_policies[app_proto].tc[state]);
}
return r;
}
/** \brief allocate and initialize to default values the policies table */
int DetectFirewallInitDefaultPolicies(DetectEngineCtx *de_ctx)
{
struct DetectFirewallPolicies *fw_policies = SCCalloc(
1, sizeof(*fw_policies) + g_alproto_max * sizeof(struct DetectFirewallAppPolicy));
if (fw_policies == NULL)
return -1;
struct DetectFirewallAppPolicy *app_fw_policies = fw_policies->app;
if (app_fw_policies == NULL)
goto error;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PACKET_FILTER].action = ACTION_DROP;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PACKET_FILTER].action_scope = ACTION_SCOPE_PACKET;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_FLOW].action = ACTION_ACCEPT;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_FLOW].action_scope = ACTION_SCOPE_HOOK;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_STREAM].action = ACTION_ACCEPT;
fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_STREAM].action_scope = ACTION_SCOPE_HOOK;
for (AppProto a = 0; a < g_alproto_max; a++) {
for (int i = 0; i < 48; i++) {
app_fw_policies[a].ts[i].action = ACTION_DROP;
app_fw_policies[a].ts[i].action_scope = ACTION_SCOPE_FLOW;
app_fw_policies[a].tc[i].action = ACTION_DROP;
app_fw_policies[a].tc[i].action_scope = ACTION_SCOPE_FLOW;
}
}
de_ctx->fw_policies = fw_policies;
return 0;
error:
SCFree(fw_policies);
return -1;
}
int DetectFirewallLoadDefaultPolicies(DetectEngineCtx *de_ctx)
{
int r;
char policy_name[256];
char prefix[96] = "firewall.policies";
if (strlen(de_ctx->config_prefix) > 0) {
snprintf(prefix, sizeof(prefix), "%s.firewall.policies", de_ctx->config_prefix);
}
struct DetectFirewallPolicies *fw_policies = de_ctx->fw_policies;
if (fw_policies == NULL)
return -1;
struct DetectFirewallAppPolicy *app_fw_policies = fw_policies->app;
if (app_fw_policies == NULL)
return -1;
r = snprintf(policy_name, sizeof(policy_name), "%s.packet-filter", prefix);
if (r < 0 || (size_t)r >= sizeof(policy_name)) {
FatalError("internal error: failed to assemble firewall policy config string");
}
r = DoParsePolicy(policy_name, &fw_policies->pkt[DETECT_FIREWALL_POLICY_PACKET_FILTER]);
if (r < 0)
return -1;
r = snprintf(policy_name, sizeof(policy_name), "%s.packet-pre-flow", prefix);
if (r < 0 || (size_t)r >= sizeof(policy_name)) {
FatalError("internal error: failed to assemble firewall policy config string");
}
r = DoParsePolicy(policy_name, &fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_FLOW]);
if (r < 0)
return -1;
r = snprintf(policy_name, sizeof(policy_name), "%s.packet-pre-stream", prefix);
if (r < 0 || (size_t)r >= sizeof(policy_name)) {
FatalError("internal error: failed to assemble firewall policy config string");
}
r = DoParsePolicy(policy_name, &fw_policies->pkt[DETECT_FIREWALL_POLICY_PRE_STREAM]);
if (r < 0)
return -1;
for (AppProto a = 0; a < g_alproto_max; a++) {
if (!AppProtoIsValid(a))
continue;
const uint8_t complete_state_ts =
(const uint8_t)AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOSERVER);
for (uint8_t state = 0; state <= complete_state_ts; state++) {
const char *name =
AppLayerParserGetStateNameById(IPPROTO_TCP, a, state, STREAM_TOSERVER);
if (DoParseAppPolicy(prefix, a, name, state, complete_state_ts, STREAM_TOSERVER,
app_fw_policies) < 0)
return -1;
}
const uint8_t complete_state_tc =
(const uint8_t)AppLayerParserGetStateProgressCompletionStatus(a, STREAM_TOCLIENT);
for (uint8_t state = 0; state <= complete_state_tc; state++) {
const char *name =
AppLayerParserGetStateNameById(IPPROTO_TCP, a, state, STREAM_TOCLIENT);
if (DoParseAppPolicy(prefix, a, name, state, complete_state_tc, STREAM_TOCLIENT,
app_fw_policies) < 0)
return -1;
}
}
return 0;
}
/*
* TESTS
*/

@ -114,4 +114,7 @@ int SC_Pcre2SubstringGet(pcre2_match_data *match_data, uint32_t number, PCRE2_UC
void DetectRegisterAppLayerHookLists(void);
void DetectListSupportedProtocols(void);
int DetectFirewallInitDefaultPolicies(DetectEngineCtx *);
int DetectFirewallLoadDefaultPolicies(DetectEngineCtx *);
#endif /* SURICATA_DETECT_PARSE_H */

@ -71,20 +71,14 @@ typedef struct DetectRunScratchpad {
const AppProto alproto;
const uint8_t flow_flags; /* flow/state flags: STREAM_* */
const bool app_decoder_events;
/**
* Either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
*
* ACTION_DROP means the default policy of drop:packet is applied
* ACTION_ACCEPT means the default policy of accept:hook is applied
*/
const uint8_t default_action;
const enum DetectFirewallPacketPolicies fw_pkt_policy;
const SigGroupHead *sgh;
} DetectRunScratchpad;
/* prototypes */
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
const uint8_t default_action);
const enum DetectFirewallPacketPolicies fw_pkt_policy);
static void DetectRunInspectIPOnly(ThreadVars *tv, const DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Flow * const pflow, Packet * const p);
static inline void DetectRunGetRuleGroup(const DetectEngineCtx *de_ctx,
@ -122,7 +116,8 @@ static void DetectRun(ThreadVars *th_v,
* Mark as a constant pointer, although the flow itself can change. */
Flow * const pflow = p->flow;
DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_DROP);
DetectRunScratchpad scratch =
DetectRunSetup(de_ctx, det_ctx, p, pflow, DETECT_FIREWALL_POLICY_PACKET_FILTER);
/* run the IPonly engine */
DetectRunInspectIPOnly(th_v, de_ctx, det_ctx, pflow, p);
@ -132,17 +127,22 @@ static void DetectRun(ThreadVars *th_v,
/* if we didn't get a sig group head, we
* have nothing to do.... */
if (scratch.sgh == NULL) {
SCLogDebug("no sgh for this packet, nothing to match against");
goto end;
if (!EngineModeIsFirewall()) {
SCLogDebug("no sgh for this packet, nothing to match against");
goto end;
}
SCLogDebug(
"packet %" PRIu64 ": no sgh, need to apply default policies", PcapPacketCntGet(p));
} else {
/* run the prefilters for packets */
DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
}
/* run the prefilters for packets */
DetectRunPrefilterPkt(th_v, de_ctx, det_ctx, p, &scratch);
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_RULES);
/* inspect the rules against the packet */
const uint8_t pkt_policy = DetectRulePacketRules(th_v, de_ctx, det_ctx, p, pflow, &scratch);
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_RULES);
SCLogDebug("packet %" PRIu64 ": pkt_policy %02x (p->action %02x)", PcapPacketCntGet(p),
pkt_policy, p->action);
/* Only FW rules will already have set the action, IDS rules go through PacketAlertFinalize
*
@ -210,7 +210,8 @@ end:
/** \internal
*/
static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p)
DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p,
enum DetectFirewallPacketPolicies fw_pkt_policy)
{
SCEnter();
SCLogDebug("pcap_cnt %" PRIu64 " direction %s pkt_src %s", PcapPacketCntGet(p),
@ -222,7 +223,7 @@ static void DetectRunPacketHook(ThreadVars *th_v, const DetectEngineCtx *de_ctx,
* Mark as a constant pointer, although the flow itself can change. */
Flow *const pflow = p->flow;
DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, ACTION_ACCEPT);
DetectRunScratchpad scratch = DetectRunSetup(de_ctx, det_ctx, p, pflow, fw_pkt_policy);
scratch.sgh = sgh;
/* if we didn't get a sig group head, we
@ -678,6 +679,56 @@ static inline bool SkipFwRules(const Packet *p)
return false;
}
/**
* \internal
* \brief apply packet default policy
* \param[in] de_ctx detect engine, for looking up the policy
* \param[in] policy policy to apply
* \param[in] p packet to apply policy to
* \param[in] final see if we need to apply accept:hook to the packet
* \retval action action to immediately apply, accept:hook will not set this unless final is true
*
* If this is run from the post-match final check, we need to apply a
* packet:filter accept:hook to the packet as well.
*/
static uint8_t DetectRunApplyPacketPolicy(const DetectEngineCtx *de_ctx,
const enum DetectFirewallPacketPolicies policy, Packet *p, const bool final)
{
DEBUG_VALIDATE_BUG_ON(de_ctx->fw_policies == NULL);
const struct DetectFirewallPolicy *pol = &de_ctx->fw_policies->pkt[policy];
if (pol->action & ACTION_DROP) {
SCLogDebug("packet %" PRIu64 ": drop PKT_DROP_REASON_DEFAULT_PACKET_POLICY",
PcapPacketCntGet(p));
PacketDrop(p, pol->action, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
} else if (pol->action & ACTION_ACCEPT) {
SCLogDebug("packet %" PRIu64 ": accept", PcapPacketCntGet(p));
if (pol->action_scope == ACTION_SCOPE_PACKET) {
p->action |= pol->action;
SCLogDebug("packet %" PRIu64 ": accept scope packet", PcapPacketCntGet(p));
} else if (pol->action_scope == ACTION_SCOPE_HOOK) {
SCLogDebug("packet %" PRIu64 ": accept scope hook", PcapPacketCntGet(p));
if (final) {
p->action |= pol->action;
SCLogDebug("packet %" PRIu64 ": accept scope hook upgraded to packet",
PcapPacketCntGet(p));
}
} else if (pol->action_scope == ACTION_SCOPE_FLOW) {
p->action |= pol->action;
SCLogDebug("packet %" PRIu64 ": accept scope flow", PcapPacketCntGet(p));
if (p->flow) {
p->flow->flags |= FLOW_ACTION_ACCEPT;
}
} else {
/* should be unreachable */
DEBUG_VALIDATE_BUG_ON(1);
}
} else {
/* should be unreachable */
DEBUG_VALIDATE_BUG_ON(1);
}
return p->action;
}
static inline uint8_t DetectRulePacketRules(ThreadVars *const tv,
const DetectEngineCtx *const de_ctx, DetectEngineThreadCtx *const det_ctx, Packet *const p,
Flow *const pflow, const DetectRunScratchpad *scratch)
@ -920,28 +971,25 @@ next:
/* if no rule told us to accept, and no rule explicitly dropped, we invoke the default drop
* policy
*/
if (have_fw_rules && scratch->default_action == ACTION_DROP) {
if (!skip_fw && !fw_verdict) {
DEBUG_VALIDATE_BUG_ON(action & ACTION_DROP);
PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
action |= ACTION_DROP;
} else {
if (have_fw_rules) {
if (skip_fw || fw_verdict) {
/* apply fw action */
p->action |= action;
} else {
DEBUG_VALIDATE_BUG_ON(action & ACTION_DROP);
/* non-final call as we may have to consider app-layer still */
action |= DetectRunApplyPacketPolicy(de_ctx, scratch->fw_pkt_policy, p, false);
}
}
return action;
}
/** \internal
* \param default_action either ACTION_DROP (drop:packet) or ACTION_ACCEPT (accept:hook)
*
* ACTION_DROP means the default policy of drop:packet is applied
* ACTION_ACCEPT means the default policy of accept:hook is applied
* \param fw_pkt_policy policy to apply to packet rules
*/
static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx, Packet *const p, Flow *const pflow,
const uint8_t default_action)
const enum DetectFirewallPacketPolicies fw_pkt_policy)
{
AppProto alproto = ALPROTO_UNKNOWN;
uint8_t flow_flags = 0; /* flow/state flags */
@ -1034,7 +1082,7 @@ static DetectRunScratchpad DetectRunSetup(const DetectEngineCtx *de_ctx,
app_decoder_events = AppLayerParserHasDecoderEvents(pflow->alparser);
}
DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, default_action, NULL };
DetectRunScratchpad pad = { alproto, flow_flags, app_decoder_events, fw_pkt_policy, NULL };
PACKET_PROFILING_DETECT_END(p, PROF_DETECT_SETUP);
return pad;
}
@ -1065,11 +1113,12 @@ static inline void DetectRunPostRules(ThreadVars *tv, const DetectEngineCtx *de_
/* firewall: "fail" closed if we don't have an ACCEPT. This can happen
* if there was no rule group. */
// TODO review packet src types here
if (EngineModeIsFirewall() && !(p->action & ACTION_ACCEPT) && p->pkt_src == PKT_SRC_WIRE &&
scratch->default_action == ACTION_DROP) {
SCLogDebug("packet %" PRIu64 ": droppit as no ACCEPT set %02x (pkt %s)",
if (EngineModeIsFirewall() && ((p->action & (ACTION_ACCEPT | ACTION_DROP)) == 0) &&
p->pkt_src == PKT_SRC_WIRE) {
SCLogDebug("packet %" PRIu64 ": default action as no verdict set %02x (pkt %s)",
PcapPacketCntGet(p), p->action, PktSrcToString(p->pkt_src));
PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_PACKET_POLICY);
(void)DetectRunApplyPacketPolicy(de_ctx, scratch->fw_pkt_policy, p, true);
DEBUG_VALIDATE_BUG_ON((p->action & (ACTION_DROP | ACTION_ACCEPT)) == 0);
}
}
@ -1565,6 +1614,16 @@ static inline void RuleMatchCandidateMergeStateRules(
// and come before any other element later in the list
}
struct DetectFirewallAppTxState {
bool fw_skip_app_filter;
bool tx_fw_verdict;
bool skip_fw_hook;
uint8_t skip_before_progress;
bool fw_last_for_progress;
bool fw_next_progress_missing;
bool last_fw_rule; /**< processing the last fw rule, so we need to eval all hooks after it. */
};
/** \internal
* \brief apply default policy
* \param p packet to apply policy to
@ -1574,11 +1633,92 @@ static inline void RuleMatchCandidateMergeStateRules(
* \note alproto and progress are unused right now, will be used
* to look up configurable default policies later
*/
static void DetectFirewallApplyDefaultPolicy(
Packet *p, const AppProto alproto, const uint8_t progress)
static const struct DetectFirewallPolicy *DetectFirewallApplyDefaultAppPolicy(
const struct DetectFirewallAppPolicy *policies, Packet *p, const AppProto alproto,
const uint8_t direction, const uint8_t progress)
{
PacketDrop(p, ACTION_DROP, PKT_DROP_REASON_DEFAULT_APP_POLICY);
p->flow->flags |= FLOW_ACTION_DROP;
const struct DetectFirewallPolicy *policy;
if (direction & STREAM_TOSERVER) {
policy = &policies[alproto].ts[progress];
SCLogDebug("packet %" PRIu64 ", hook:%u, toserver, policy: action %02x scope %u",
PcapPacketCntGet(p), progress, policy->action, policy->action_scope);
} else {
policy = &policies[alproto].tc[progress];
SCLogDebug("packet %" PRIu64 ", hook:%u, toclient, policy: action %02x scope %u",
PcapPacketCntGet(p), progress, policy->action, policy->action_scope);
}
if (policy->action & ACTION_DROP) {
SCLogDebug("dropping packet PKT_DROP_REASON_DEFAULT_APP_POLICY");
PacketDrop(p, policy->action, PKT_DROP_REASON_DEFAULT_APP_POLICY);
if (policy->action_scope == ACTION_SCOPE_FLOW) {
SCLogDebug("dropping flow");
p->flow->flags |= FLOW_ACTION_DROP;
}
} else if (policy->action & ACTION_ACCEPT) {
if (policy->action_scope == ACTION_SCOPE_FLOW) {
p->flow->flags |= FLOW_ACTION_ACCEPT;
}
SCLogDebug(
"packet %" PRIu64 " hook %u default policy ACCEPT", PcapPacketCntGet(p), progress);
} else {
/* should be unreachable */
DEBUG_VALIDATE_BUG_ON(1);
}
return policy;
}
/** \internal
* \brief run default policies for hook(s)
*
* For a range of hooks look up the policy and apply it.
*
* \param is_last is this tx the last we have? Used to check if an action needs to be applied to
* the packet.
*
* \retval DETECT_TX_FW_FC_BREAK rest of rules shouldn't be inspected
* \retval DETECT_TX_FW_FC_SKIP skip current firewall rule
* \retval DETECT_TX_FW_FC_OK no action needed
*/
static enum DetectTxFirewallFlowControl DetectFirewallApplyDefaultPolicies(
DetectEngineThreadCtx *det_ctx, const struct DetectFirewallAppPolicy *policies,
DetectTransaction *tx, Packet *p, const AppProto alproto, const uint8_t direction,
const uint8_t start_hook, const uint8_t end_hook, const bool is_last)
{
uint8_t actions = 0;
for (uint8_t hook = start_hook; hook <= end_hook; hook++) {
SCLogDebug("%" PRIu64 ": %s default policy for hook %u", PcapPacketCntGet(p),
direction & STREAM_TOSERVER ? "toserver" : "toclient", hook);
const struct DetectFirewallPolicy *policy = DetectFirewallApplyDefaultAppPolicy(
det_ctx->de_ctx->fw_policies->app, p, alproto, direction, hook);
SCLogDebug("fw: hook:%u policy:%02x", hook, policy->action);
actions |= policy->action;
if (policy->action & ACTION_DROP) {
SCLogDebug("fw: action %02x", policy->action);
return DETECT_TX_FW_FC_BREAK;
} else if (policy->action == ACTION_ACCEPT) {
SCLogDebug("fw: accept hook %u", hook);
/* accepting flow, so skip rest of the fw rules */
if (policy->action_scope == ACTION_SCOPE_FLOW) {
DetectRunAppendDefaultAccept(det_ctx, p);
return DETECT_TX_FW_FC_SKIP;
} else if (policy->action_scope == ACTION_SCOPE_TX) {
tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
SCLogDebug("ACTION_SCOPE_TX, setting APP_LAYER_TX_ACCEPT");
if (is_last) {
DetectRunAppendDefaultAccept(det_ctx, p);
SCLogDebug("DetectRunAppendDefaultAccept for last tx");
}
return DETECT_TX_FW_FC_SKIP;
}
}
}
if ((is_last && (actions & (ACTION_ACCEPT | ACTION_DROP)) == ACTION_ACCEPT)) {
DetectRunAppendDefaultAccept(det_ctx, p);
}
return DETECT_TX_FW_FC_OK;
}
/** \internal
@ -1593,12 +1733,13 @@ static void DetectFirewallApplyDefaultPolicy(
* \retval DETECT_TX_FW_FC_SKIP skip this rule
*/
static enum DetectTxFirewallFlowControl DetectRunTxPreCheckFirewallPolicy(
DetectEngineThreadCtx *det_ctx, Packet *p, DetectTransaction *tx, const Signature *s,
const uint32_t can_idx, bool *tx_fw_verdict, const bool last_tx)
DetectEngineThreadCtx *det_ctx, Packet *p, DetectTransaction *tx, const uint8_t direction,
const Signature *s, const uint32_t can_idx, struct DetectFirewallAppTxState *fw_state,
const bool last_tx)
{
if (p->flow->flags & FLOW_ACTION_ACCEPT) {
if ((*tx_fw_verdict) == false) {
*tx_fw_verdict = true;
if (fw_state->tx_fw_verdict == false) {
fw_state->tx_fw_verdict = true;
DetectRunAppendDefaultAccept(det_ctx, p);
}
if (s->flags & SIG_FLAG_FIREWALL) {
@ -1609,13 +1750,13 @@ static enum DetectTxFirewallFlowControl DetectRunTxPreCheckFirewallPolicy(
if (tx->tx_data_ptr->flags & APP_LAYER_TX_ACCEPT) {
/* append a blank accept:packet action for the APP_LAYER_TX_ACCEPT,
* if this is the last tx */
if (!*tx_fw_verdict) {
if (!fw_state->tx_fw_verdict) {
const bool accept_tx_applies_to_packet = last_tx;
if (accept_tx_applies_to_packet) {
SCLogDebug("accept:(tx|hook): should be applied to the packet");
DetectRunAppendDefaultAccept(det_ctx, p);
}
*tx_fw_verdict = true;
fw_state->tx_fw_verdict = true;
}
if (s->flags & SIG_FLAG_FIREWALL) {
@ -1625,18 +1766,22 @@ static enum DetectTxFirewallFlowControl DetectRunTxPreCheckFirewallPolicy(
/* threat detect rules will be inspected */
} else if (s->flags & SIG_FLAG_FIREWALL) {
fw_state->fw_next_progress_missing = false;
fw_state->fw_last_for_progress = false;
/* if our first rule is beyond the starting state, we need to check if
* there are rules missing for states in between. */
// TODO detect_progress_orig already is +1?
if (s->app_progress_hook > tx->detect_progress_orig && can_idx == 0) {
SCLogDebug("missing fw rules at list start: sid %u, progress %u (%u:%u)", s->id,
s->app_progress_hook, tx->detect_progress, tx->detect_progress_orig);
/* if this rule was after the state we expected meaning that there are
* no rules for that state. Invoke the default drop policy. */
DetectFirewallApplyDefaultPolicy(p, s->alproto, tx->detect_progress_orig);
*tx_fw_verdict = true;
SCLogDebug("default app drop");
return DETECT_TX_FW_FC_BREAK;
* no rules for that state. Invoke the default policies. */
enum DetectTxFirewallFlowControl r = DetectFirewallApplyDefaultPolicies(det_ctx,
det_ctx->de_ctx->fw_policies->app, tx, p, s->alproto, direction,
tx->detect_progress_orig, s->app_progress_hook - 1, last_tx);
fw_state->tx_fw_verdict = true;
return r;
}
}
return DETECT_TX_FW_FC_OK;
@ -1664,9 +1809,8 @@ static enum DetectTxFirewallFlowControl DetectRunTxPreCheckFirewallPolicy(
*/
static enum DetectTxFirewallFlowControl DetectRunTxCheckFirewallPolicy(
DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f, DetectTransaction *tx,
const Signature *s, const uint32_t can_idx, const uint32_t can_size, bool *skip_fw_hook,
const uint8_t skip_before_progress, bool *fw_last_for_progress,
bool *fw_next_progress_missing)
const Signature *s, const uint32_t can_idx, const uint32_t can_size,
struct DetectFirewallAppTxState *fw_state)
{
if (s->flags & SIG_FLAG_FIREWALL) {
/* check if the next sig is on the same progress hook. If not, we need to apply our
@ -1685,32 +1829,34 @@ static enum DetectTxFirewallFlowControl DetectRunTxCheckFirewallPolicy(
SCLogDebug("peek: next sid progress %u != current progress %u, so current "
"is last for progress",
next_s->app_progress_hook, s->app_progress_hook);
*fw_last_for_progress = true;
fw_state->fw_last_for_progress = true;
if (next_s->app_progress_hook - s->app_progress_hook > 1) {
SCLogDebug("peek: missing progress, so we'll drop that unless we get a "
"sweeping accept first");
*fw_next_progress_missing = true;
fw_state->fw_next_progress_missing = true;
}
}
} else {
SCLogDebug("peek: next sid not a fw rule, so current is last for progress");
*fw_last_for_progress = true;
fw_state->fw_last_for_progress = true;
fw_state->last_fw_rule = true;
}
} else {
SCLogDebug("peek: no peek beyond last rule");
if (s->app_progress_hook < tx->tx_progress) {
SCLogDebug("peek: there are no rules to allow the state after this rule");
*fw_next_progress_missing = true;
fw_state->fw_next_progress_missing = true;
}
*fw_last_for_progress = true;
fw_state->fw_last_for_progress = true;
fw_state->last_fw_rule = true;
}
if ((*skip_fw_hook) == true) {
if (s->app_progress_hook <= skip_before_progress) {
if (fw_state->skip_fw_hook == true) {
if (s->app_progress_hook <= fw_state->skip_before_progress) {
return DETECT_TX_FW_FC_SKIP;
}
*skip_fw_hook = false;
fw_state->skip_fw_hook = false;
}
} else {
/* fw mode, we skip anything after the fw rules if:
@ -1773,43 +1919,113 @@ static inline bool ApplyAcceptToPacket(
/** \internal
* \retval bool true: break_out_of_app_filter, false: don't break out */
static bool ApplyAccept(Packet *p, const uint8_t flow_flags, const Signature *s,
DetectTransaction *tx, const int tx_end_state, const bool fw_next_progress_missing,
bool *tx_fw_verdict, bool *skip_fw_hook, uint8_t *skip_before_progress)
static bool ApplyAccept(DetectEngineThreadCtx *det_ctx, Packet *p, const uint8_t direction,
const Signature *s, DetectTransaction *tx, const int tx_end_state, const bool is_last,
struct DetectFirewallAppTxState *fw_state)
{
*tx_fw_verdict = true;
fw_state->tx_fw_verdict = true;
const enum ActionScope as = s->action_scope;
/* accept:hook: jump to first rule of next state.
* Implemented as skip until the first rule of next state. */
if (as == ACTION_SCOPE_HOOK) {
*skip_fw_hook = true;
*skip_before_progress = s->app_progress_hook;
fw_state->skip_fw_hook = true;
fw_state->skip_before_progress = s->app_progress_hook;
SCLogDebug("fw match sid:%u hook:%u", s->id, s->app_progress_hook);
SCLogDebug("fw fw_skip_app_filter:%s tx_fw_verdict:%s skip_fw_hook:%s "
"skip_before_progress:%u fw_last_for_progress:%s fw_next_progress_missing:%s",
BOOL2STR(fw_state->fw_skip_app_filter), BOOL2STR(fw_state->tx_fw_verdict),
BOOL2STR(fw_state->skip_fw_hook), fw_state->skip_before_progress,
BOOL2STR(fw_state->fw_last_for_progress),
BOOL2STR(fw_state->fw_next_progress_missing));
/* if there is no fw rule for the next progress value,
* we invoke the default drop policy. */
if (fw_next_progress_missing) {
SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
DetectFirewallApplyDefaultPolicy(p, s->alproto, s->app_progress_hook + 1);
return true;
* we invoke the defaul policies for the remaining available hooks. */
if (fw_state->fw_next_progress_missing) {
const uint8_t last_hook =
fw_state->last_fw_rule
? (uint8_t)tx_end_state
: MIN((uint8_t)tx_end_state, s->app_progress_hook + (uint8_t)1);
enum DetectTxFirewallFlowControl r = DetectFirewallApplyDefaultPolicies(det_ctx,
det_ctx->de_ctx->fw_policies->app, tx, p, s->alproto, direction,
s->app_progress_hook + 1, last_hook, is_last);
if (r == DETECT_TX_FW_FC_BREAK)
return true;
}
return false;
} else if (as == ACTION_SCOPE_TX) {
tx->tx_data_ptr->flags |= APP_LAYER_TX_ACCEPT;
*skip_fw_hook = true;
*skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
SCLogDebug(
"accept:tx applied, skip_fw_hook, skip_before_progress %u", *skip_before_progress);
fw_state->skip_fw_hook = true;
fw_state->skip_before_progress = (uint8_t)tx_end_state + 1; // skip all hooks
SCLogDebug("accept:tx applied, skip_fw_hook, skip_before_progress %u",
fw_state->skip_before_progress);
return false;
} else if (as == ACTION_SCOPE_PACKET) {
return true;
} else if (as == ACTION_SCOPE_FLOW) {
SCLogDebug("ACTION_ACCEPT with ACTION_SCOPE_FLOW");
return true;
}
return false;
}
/**
* \internal
* \brief check if there are no (fw) rules, and apply the default policies if so
*
* \retval 3 policies handled, continue with inspection
* \retval 2 continue with next tx
* \retval 1 done with inspection
* \retval 0 ok, continue as normal. No policies applied.
*/
static int DetectTxFirewallNoRulesApplyPolicies(DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f,
DetectTransaction *tx, const AppProto alproto, const uint8_t flow_flags, const int rule_cnt,
const bool last_tx)
{
/* if there are no rules / rule candidates, handling invoking the default
* policy. */
if (rule_cnt == 0 || (det_ctx->tx_candidates[0].s->flags & SIG_FLAG_FIREWALL) == 0) {
/* if there are no rules, make sure to handle accept:flow and accept:tx */
if (rule_cnt == 0) {
if (f->flags & FLOW_ACTION_ACCEPT) {
DetectRunAppendDefaultAccept(det_ctx, p);
return 1;
}
if (tx->tx_data_ptr->flags & APP_LAYER_TX_ACCEPT) {
/* current tx is the last we have, append a blank accept:packet */
if (last_tx) {
DetectRunAppendDefaultAccept(det_ctx, p);
return 1;
}
return 2;
}
}
/* if there are no fw rules, handle default policies */
if ((f->flags & FLOW_ACTION_ACCEPT) == 0 &&
(tx->tx_data_ptr->flags & APP_LAYER_TX_ACCEPT) == 0) {
/* No rules to eval, so we need to see if there are default policies to apply.
* Start at last inspected progress and check each hook. If all hooks accepted,
* apply the accept to the packet. */
SCLogDebug("tx.detect_progress_orig %u tx.tx_progress %u", tx->detect_progress_orig,
tx->tx_progress);
enum DetectTxFirewallFlowControl r =
DetectFirewallApplyDefaultPolicies(det_ctx, det_ctx->de_ctx->fw_policies->app,
tx, p, alproto, flow_flags & (STREAM_TOSERVER | STREAM_TOCLIENT),
tx->detect_progress_orig, (const uint8_t)tx->tx_progress, last_tx);
SCLogDebug("r %u", r);
if (r == DETECT_TX_FW_FC_BREAK)
return 1;
if (r == DETECT_TX_FW_FC_SKIP)
return 2;
/* continue with TD rules */
SCLogDebug("continue with TD rules");
return 3;
}
}
return 0;
}
static void DetectRunTx(ThreadVars *tv,
DetectEngineCtx *de_ctx,
DetectEngineThreadCtx *det_ctx,
@ -1866,7 +2082,7 @@ static void DetectRunTx(ThreadVars *tv,
total_rules += (tx.de_state ? tx.de_state->cnt : 0);
/* run prefilter engines and merge results into a candidates array */
if (sgh->tx_engines) {
if (sgh && sgh->tx_engines) {
PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX);
DetectRunPrefilterTx(det_ctx, sgh, p, ipproto, flow_flags, alproto,
alstate, &tx);
@ -1960,9 +2176,16 @@ static void DetectRunTx(ThreadVars *tv,
SCLogDebug("%u: sid %u flags %p", i, s->id, can->flags);
}
#endif
bool skip_fw_hook = false;
uint8_t skip_before_progress = 0;
bool fw_next_progress_missing = false;
struct DetectFirewallAppTxState fw_state = {
false,
false,
false,
0,
false,
false,
false,
};
SCLogDebug("%s: tx_progress %u tx %p have_fw_rules %s array_idx %u detect_progress_orig %u "
"cur detect_progress %u",
@ -1970,33 +2193,21 @@ static void DetectRunTx(ThreadVars *tv,
tx.tx_data_ptr, BOOL2STR(have_fw_rules), array_idx, tx.detect_progress_orig,
tx.detect_progress);
/* if there are no rules / rule candidates, handling invoking the default
* policy. */
if (have_fw_rules && array_idx == 0) {
if (f->flags & FLOW_ACTION_ACCEPT) {
if (have_fw_rules) {
/* if there are no firewall rules to consider, handle invoking the default
* policies. */
const int r = DetectTxFirewallNoRulesApplyPolicies(
det_ctx, p, f, &tx, alproto, flow_flags, array_idx, last_tx);
if (r != 0) {
fw_verdicted++;
DetectRunAppendDefaultAccept(det_ctx, p);
return;
}
if (tx.tx_data_ptr->flags & APP_LAYER_TX_ACCEPT) {
fw_verdicted++;
/* current tx is the last we have, append a blank accept:packet */
if (last_tx) {
DetectRunAppendDefaultAccept(det_ctx, p);
if (r == 1) {
SCLogDebug("done");
return;
}
goto next;
} else {
/* use last inspected progress */
DetectFirewallApplyDefaultPolicy(p, alproto, tx.detect_progress_orig);
fw_verdicted++;
break;
} else if (r == 2)
goto next_tx_fw; /* next tx */
}
}
bool fw_skip_app_filter = false; /**< skip the rest of the app filter (fw) rules */
bool tx_fw_verdict = false;
/* run rules: inspect the match candidates */
for (uint32_t i = 0; i < array_idx; i++) {
RuleMatchCandidateTx *can = &det_ctx->tx_candidates[i];
@ -2008,14 +2219,24 @@ static void DetectRunTx(ThreadVars *tv,
tx.detect_progress, tx.detect_progress_orig, s->app_progress_hook);
if (have_fw_rules) {
if ((s->flags & SIG_FLAG_FIREWALL) != 0 && fw_skip_app_filter)
if ((s->flags & SIG_FLAG_FIREWALL) != 0 && fw_state.fw_skip_app_filter) {
continue;
}
const enum DetectTxFirewallFlowControl fw_r = DetectRunTxPreCheckFirewallPolicy(
det_ctx, p, &tx, s, i, &tx_fw_verdict, last_tx);
if (fw_r == DETECT_TX_FW_FC_SKIP)
det_ctx, p, &tx, flow_flags & (STREAM_TOSERVER | STREAM_TOCLIENT), s, i,
&fw_state, last_tx);
SCLogDebug("fw fw_skip_app_filter:%s tx_fw_verdict:%s skip_fw_hook:%s "
"skip_before_progress:%u fw_last_for_progress:%s "
"fw_next_progress_missing:%s",
BOOL2STR(fw_state.fw_skip_app_filter), BOOL2STR(fw_state.tx_fw_verdict),
BOOL2STR(fw_state.skip_fw_hook), fw_state.skip_before_progress,
BOOL2STR(fw_state.fw_last_for_progress),
BOOL2STR(fw_state.fw_next_progress_missing));
if (fw_r == DETECT_TX_FW_FC_SKIP) {
continue;
else if (fw_r == DETECT_TX_FW_FC_BREAK)
} else if (fw_r == DETECT_TX_FW_FC_BREAK) {
break;
}
}
/* deduplicate: rules_array is sorted, but not deduplicated:
@ -2044,9 +2265,8 @@ static void DetectRunTx(ThreadVars *tv,
if (have_fw_rules && (s->flags & SIG_FLAG_FIREWALL) &&
(s->action & ACTION_ACCEPT) && s->app_progress_hook == tx.tx_progress) {
const bool fw_accept_to_packet = ApplyAcceptToPacket(last_tx, &tx, s);
fw_skip_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
&skip_before_progress);
fw_state.fw_skip_app_filter = ApplyAccept(
det_ctx, p, flow_flags, s, &tx, tx_end_state, last_tx, &fw_state);
if (fw_accept_to_packet)
DetectRunAppendDefaultAccept(det_ctx, p);
}
@ -2066,11 +2286,16 @@ static void DetectRunTx(ThreadVars *tv,
SCLogDebug("%p/%"PRIu64" Start sid %u", tx.tx_ptr, tx.tx_id, s->id);
}
bool fw_last_for_progress = false;
if (have_fw_rules) {
const enum DetectTxFirewallFlowControl fw_r = DetectRunTxCheckFirewallPolicy(
det_ctx, p, f, &tx, s, i, array_idx, &skip_fw_hook, skip_before_progress,
&fw_last_for_progress, &fw_next_progress_missing);
det_ctx, p, f, &tx, s, i, array_idx, &fw_state);
SCLogDebug("fw fw_skip_app_filter:%s tx_fw_verdict:%s skip_fw_hook:%s "
"skip_before_progress:%u fw_last_for_progress:%s "
"fw_next_progress_missing:%s",
BOOL2STR(fw_state.fw_skip_app_filter), BOOL2STR(fw_state.tx_fw_verdict),
BOOL2STR(fw_state.skip_fw_hook), fw_state.skip_before_progress,
BOOL2STR(fw_state.fw_last_for_progress),
BOOL2STR(fw_state.fw_next_progress_missing));
if (fw_r == DETECT_TX_FW_FC_SKIP)
continue;
else if (fw_r == DETECT_TX_FW_FC_BREAK)
@ -2089,34 +2314,59 @@ static void DetectRunTx(ThreadVars *tv,
SCLogDebug(
"%p/%" PRIu64 " sig %u (%u) matched", tx.tx_ptr, tx.tx_id, s->id, s->iid);
if ((s->flags & SIG_FLAG_FIREWALL) && (s->action & ACTION_ACCEPT)) {
fw_skip_app_filter = ApplyAccept(p, flow_flags, s, &tx, tx_end_state,
fw_next_progress_missing, &tx_fw_verdict, &skip_fw_hook,
&skip_before_progress);
/* see if we need to apply tx/hook accept to the packet. This can be needed when
* we've completed the inspection so far for an incomplete tx, and an accept:tx
* or accept:hook is the last match.*/
const bool fw_accept_to_packet = ApplyAcceptToPacket(last_tx, &tx, s);
if (fw_accept_to_packet) {
SCLogDebug("accept:(tx|hook): should be applied to the packet");
alert_flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET;
if (s->flags & SIG_FLAG_FIREWALL) {
if (s->action & ACTION_ACCEPT) {
fw_state.fw_skip_app_filter = ApplyAccept(
det_ctx, p, flow_flags, s, &tx, tx_end_state, last_tx, &fw_state);
fw_state.fw_next_progress_missing = false; // reset
/* see if we need to apply tx/hook accept to the packet. This can be needed
* when we've completed the inspection so far for an incomplete tx, and an
* accept:tx or accept:hook is the last match.*/
const bool fw_accept_to_packet = ApplyAcceptToPacket(last_tx, &tx, s);
if (fw_accept_to_packet) {
SCLogDebug("accept:(tx|hook): should be applied to the packet");
alert_flags |= PACKET_ALERT_FLAG_APPLY_ACTION_TO_PACKET;
}
} else if (s->action & ACTION_DROP) {
SCLogDebug("drop packet because of rule with drop action");
PacketDrop(p, s->action, PKT_DROP_REASON_RULES);
if (s->action_scope == ACTION_SCOPE_FLOW) {
SCLogDebug("drop flow because of rule with drop action");
f->flags |= FLOW_ACTION_DROP;
}
}
}
AlertQueueAppend(det_ctx, s, p, tx.tx_id, alert_flags);
} else if (fw_last_for_progress) {
SCLogDebug("sid %u: not a match: %s rule, fw_last_for_progress %s", s->id,
(s->flags & SIG_FLAG_FIREWALL) ? "firewall" : "regular",
BOOL2STR(fw_last_for_progress));
if (s->flags & SIG_FLAG_FIREWALL) {
SCLogDebug("%" PRIu64 ": %s default drop for progress", PcapPacketCntGet(p),
flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
/* if this rule was the last for our progress state, and it didn't match,
* we have to invoke the default drop policy. */
DetectFirewallApplyDefaultPolicy(p, s->alproto, s->app_progress_hook);
fw_skip_app_filter = true;
tx_fw_verdict = true;
} else if (fw_state.fw_last_for_progress && (s->flags & SIG_FLAG_FIREWALL)) {
SCLogDebug("%" PRIu64 ": %s default policy for progress %u", PcapPacketCntGet(p),
flow_flags & STREAM_TOSERVER ? "toserver" : "toclient",
s->app_progress_hook);
/* if this rule was the last for our progress state, and it didn't match,
* we have to invoke the default policy. We only check the current rule hook. */
const struct DetectFirewallPolicy *policy =
DetectFirewallApplyDefaultAppPolicy(det_ctx->de_ctx->fw_policies->app, p,
s->alproto, flow_flags, s->app_progress_hook);
SCLogDebug("fw_last_for_progress policy %02x", policy->action);
if (policy->action & ACTION_DROP) {
fw_state.fw_skip_app_filter = true;
SCLogDebug("packet %02x", p->action);
} else if (policy->action == ACTION_ACCEPT) {
SCLogDebug("accept hook(s)? current hook %u (tx.detect_progress %u) max %u",
s->app_progress_hook, tx.detect_progress, tx.tx_progress);
/* accepting flow, so skip rest of the fw rules */
if (policy->action_scope == ACTION_SCOPE_FLOW) {
fw_state.fw_skip_app_filter = true;
}
/* if this is also the last fw rule we'll inspect we have to issue a default
* accept to the packet */
if (s->app_progress_hook == tx.tx_progress) {
DetectRunAppendDefaultAccept(det_ctx, p);
}
}
fw_state.tx_fw_verdict = true;
}
DetectVarProcessList(det_ctx, p->flow, p);
RULE_PROFILING_END(det_ctx, s, r, p);
@ -2153,7 +2403,7 @@ static void DetectRunTx(ThreadVars *tv,
PMQ_RESET(&det_ctx->pmq);
}
}
if (tx_fw_verdict)
if (fw_state.tx_fw_verdict)
fw_verdicted++;
det_ctx->tx_id = 0;
@ -2184,7 +2434,7 @@ static void DetectRunTx(ThreadVars *tv,
StoreDetectProgress(&tx, flow_flags, tx.detect_progress);
}
next_tx_fw:
InspectionBufferClean(det_ctx);
next:
@ -2192,17 +2442,8 @@ static void DetectRunTx(ThreadVars *tv,
break;
}
/* apply default policy if there were txs to inspect, we have fw rules and non of the rules
* applied a policy. */
SCLogDebug("packet %" PRIu64 ": tx_inspected %u fw_verdicted %u", PcapPacketCntGet(p),
tx_inspected, fw_verdicted);
if (tx_inspected && have_fw_rules && tx_inspected != fw_verdicted) {
SCLogDebug("%" PRIu64 ": %s default drop", PcapPacketCntGet(p),
flow_flags & STREAM_TOSERVER ? "toserver" : "toclient");
DetectFirewallApplyDefaultPolicy(
p, ALPROTO_UNKNOWN, 0); // TODO can we assign this to a hook?
return;
}
/* if all tables have been bypassed, we accept:packet */
if (tx_inspected == 0 && fw_verdicted == 0 && have_fw_rules) {
DetectRunAppendDefaultAccept(det_ctx, p);
@ -2420,7 +2661,7 @@ uint8_t DetectPreFlow(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *p)
const SigGroupHead *sgh = de_ctx->pre_flow_sgh;
SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p, DETECT_FIREWALL_POLICY_PRE_FLOW);
return p->action;
}
@ -2431,7 +2672,7 @@ uint8_t DetectPreStream(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, Packet *
const SigGroupHead *sgh = de_ctx->pre_stream_sgh[direction];
SCLogDebug("thread id: %u, packet %" PRIu64 ", sgh %p", tv->id, PcapPacketCntGet(p), sgh);
DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p);
DetectRunPacketHook(tv, de_ctx, det_ctx, sgh, p, DETECT_FIREWALL_POLICY_PRE_STREAM);
return p->action;
}

@ -910,6 +910,35 @@ enum DetectEngineType
DETECT_ENGINE_TYPE_TENANT = 3,
};
enum DetectFirewallPacketPolicies {
DETECT_FIREWALL_POLICY_PACKET_FILTER,
DETECT_FIREWALL_POLICY_PRE_FLOW,
DETECT_FIREWALL_POLICY_PRE_STREAM,
#define DETECT_FIREWALL_POLICY_SIZE DETECT_FIREWALL_POLICY_PRE_STREAM + 1
};
/** Single Firewall Policy */
struct DetectFirewallPolicy {
uint8_t action; /**< same as Signature::action. Action flags to apply on policy match. */
uint8_t action_scope; /**< same as Signature::action_scope. Scope argument for the action. */
};
/** Application layer firewall policies per hook. */
struct DetectFirewallAppPolicy {
/** policy per hook/progress value (max 48) for toserver direction. */
struct DetectFirewallPolicy ts[48];
/** policy per hook/progress value (max 48) for toclient direction. */
struct DetectFirewallPolicy tc[48];
};
struct DetectFirewallPolicies {
/** policy for packet_filter, pre_flow, pre_stream hooks */
struct DetectFirewallPolicy pkt[DETECT_FIREWALL_POLICY_SIZE];
/** app layer policies, one per alproto */
struct DetectFirewallAppPolicy app[];
};
/* Flow states:
* toserver
* toclient
@ -963,6 +992,9 @@ typedef struct DetectEngineCtx_ {
/* main sigs */
DetectEngineLookupFlow flow_gh[FLOW_STATES];
/** firewall policy table entry point */
struct DetectFirewallPolicies *fw_policies;
/* init phase vars */
HashListTable *sgh_hash_table;

@ -247,7 +247,8 @@ SCProfilingSghDump(DetectEngineCtx *de_ctx)
void
SCProfilingSghUpdateCounter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh)
{
if (det_ctx != NULL && det_ctx->sgh_perf_data != NULL && sgh->id < det_ctx->de_ctx->sgh_array_cnt) {
if (det_ctx != NULL && sgh != NULL && det_ctx->sgh_perf_data != NULL &&
sgh->id < det_ctx->de_ctx->sgh_array_cnt) {
SCProfileSghData *p = &det_ctx->sgh_perf_data[sgh->id];
p->checks++;

@ -2369,7 +2369,6 @@ firewall:
#rule-files:
# - firewall.rules
##
## Include other configs
##

Loading…
Cancel
Save