diff --git a/src/detect-engine-loader.c b/src/detect-engine-loader.c index a97ebd6d27..01775d73ce 100644 --- a/src/detect-engine-loader.c +++ b/src/detect-engine-loader.c @@ -50,6 +50,8 @@ #include #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; diff --git a/src/detect-engine-profile.c b/src/detect-engine-profile.c index ab17a59a9f..79186ee1fd 100644 --- a/src/detect-engine-profile.c +++ b/src/detect-engine-profile.c @@ -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; diff --git a/src/detect-engine.c b/src/detect-engine.c index e229d0137b..ffda5beea6 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -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(); diff --git a/src/detect-parse.c b/src/detect-parse.c index 9e2b4cac0b..82822ba8bf 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -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 */ diff --git a/src/detect-parse.h b/src/detect-parse.h index c3519984dd..72d72cf19e 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -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 */ diff --git a/src/detect.c b/src/detect.c index 36bafd9261..d358431026 100644 --- a/src/detect.c +++ b/src/detect.c @@ -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; } diff --git a/src/detect.h b/src/detect.h index 5c6eab3204..75cdd016ea 100644 --- a/src/detect.h +++ b/src/detect.h @@ -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; diff --git a/src/util-profiling-rulegroups.c b/src/util-profiling-rulegroups.c index 6db97a81e2..86f6089209 100644 --- a/src/util-profiling-rulegroups.c +++ b/src/util-profiling-rulegroups.c @@ -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++; diff --git a/suricata.yaml.in b/suricata.yaml.in index e747d124b1..f43483a534 100644 --- a/suricata.yaml.in +++ b/suricata.yaml.in @@ -2369,7 +2369,6 @@ firewall: #rule-files: # - firewall.rules - ## ## Include other configs ##