diff --git a/src/detect-parse.c b/src/detect-parse.c index 95c0d4c70c..88af468d62 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -109,6 +109,23 @@ typedef struct SignatureParser_ { char opts[DETECT_MAX_RULE_SIZE]; } SignatureParser; +/** Valid action scopes per firewall hook class. */ +enum DetectFirewallPolicyClass { + DETECT_FIREWALL_POLICY_CLASS_PACKET, + DETECT_FIREWALL_POLICY_CLASS_APP +}; + +static const uint8_t fw_packet_hook_scopes[] = { + ACTION_SCOPE_PACKET, + ACTION_SCOPE_HOOK, + ACTION_SCOPE_FLOW, +}; +static const uint8_t fw_app_hook_scopes[] = { + ACTION_SCOPE_FLOW, + ACTION_SCOPE_TX, + ACTION_SCOPE_HOOK, +}; + /** \brief max length of a firewall.policies YAML config path */ #define FW_POLICY_YAML_PATH_MAX 320 /** \brief max length of a single YAML path leaf segment (a hook name) */ @@ -3930,6 +3947,59 @@ static int DoParsePolicy(const char *policy_name, struct DetectFirewallPolicy *p return 1; } +static bool FirewallScopeValidForClass(uint8_t scope, enum DetectFirewallPolicyClass pol_class) +{ + const uint8_t *set = NULL; + size_t n = 0; + switch (pol_class) { + case DETECT_FIREWALL_POLICY_CLASS_PACKET: + set = fw_packet_hook_scopes; + n = ARRAY_SIZE(fw_packet_hook_scopes); + break; + case DETECT_FIREWALL_POLICY_CLASS_APP: + set = fw_app_hook_scopes; + n = ARRAY_SIZE(fw_app_hook_scopes); + break; + default: + FatalError("Invalid firewall policy class %u", (unsigned)pol_class); + } + for (size_t i = 0; i < n; i++) { + if (set[i] == scope) { + return true; + } + } + return false; +} + +/** + * \brief Render the valid scopes for a hook class to a string. + */ +static void FirewallScopeHintForClass( + enum DetectFirewallPolicyClass pol_class, char *out, size_t out_size) +{ + const uint8_t *set = NULL; + size_t n = 0; + switch (pol_class) { + case DETECT_FIREWALL_POLICY_CLASS_PACKET: + set = fw_packet_hook_scopes; + n = ARRAY_SIZE(fw_packet_hook_scopes); + break; + case DETECT_FIREWALL_POLICY_CLASS_APP: + set = fw_app_hook_scopes; + n = ARRAY_SIZE(fw_app_hook_scopes); + break; + default: + FatalError("Invalid firewall policy class %u", (unsigned)pol_class); + } + out[0] = '\0'; + for (size_t i = 0; i < n; i++) { + if ((i > 0 && strlcat(out, "/", out_size) >= out_size) || + strlcat(out, ActionScopeToString((enum ActionScope)set[i]), out_size) >= out_size) { + FatalError("firewall policy scope hint too long"); + } + } +} + /** * \brief Append a unique inheritance tier to the chain of firewall policies to query. */ @@ -3962,13 +4032,16 @@ static void ATTR_FMT_PRINTF(2, 3) /** * \brief Resolve a firewall policy from its config path chain. * - * The first path in the chain that has a policy configured wins. + * The first path in the chain that has a policy configured wins, with its + * action scope validated against the target hook class. * * \retval 1 a config source was used and stored in \p out * \retval 0 no source present, \p out is unmodified - * \retval -1 parse error, e.g. an empty policy + * \retval -1 parse error, e.g. an empty policy, or invalid scope for the target class */ -static int ResolveFirewallPolicy(struct DetectFirewallPolicy *out, const FirewallPolicyChain *chain) + +static int ResolveFirewallPolicy(struct DetectFirewallPolicy *out, + enum DetectFirewallPolicyClass pol_class, const FirewallPolicyChain *chain) { for (uint8_t i = 0; i < chain->len; i++) { const char *path = chain->path[i]; @@ -3982,6 +4055,13 @@ static int ResolveFirewallPolicy(struct DetectFirewallPolicy *out, const Firewal SCLogError("%s: policy is set but empty", path); return -1; } + if (!FirewallScopeValidForClass(tmp.action_scope, pol_class)) { + char hint[32]; // space to combine ActionScopeToString results + FirewallScopeHintForClass(pol_class, hint, sizeof(hint)); + SCLogError("%s: action scope (\"%s\") is not valid. Valid scopes: %s", path, + ActionScopeToString(tmp.action_scope), hint); + return -1; + } *out = tmp; return 1; } @@ -4046,7 +4126,7 @@ static int DoParseAppPolicy(const char *prefix, const AppProto app_proto, const app_pol->action = ACTION_DROP; app_pol->action_scope = ACTION_SCOPE_FLOW; - int r = ResolveFirewallPolicy(app_pol, &chain); + int r = ResolveFirewallPolicy(app_pol, DETECT_FIREWALL_POLICY_CLASS_APP, &chain); if (r < 0) { return -1; } @@ -4116,7 +4196,7 @@ static int DetectFirewallLoadPacketPolicy(struct DetectFirewallPolicies *fw_poli FirewallPolicyChainAdd(&chain, "%s.default-policy", prefix); struct DetectFirewallPolicy *pol = &fw_policies->pkt[id]; // built-in default - int r = ResolveFirewallPolicy(pol, &chain); + int r = ResolveFirewallPolicy(pol, DETECT_FIREWALL_POLICY_CLASS_PACKET, &chain); if (r < 0) { return -1; }