detect: log the hook for sub-state firewall rules

Alerts and drops for firewall rules hooked at a sub-state (http2
stream/global) had no firewall.hook field: the logger resolved the state
name without the sub-state, so the lookup found nothing.

Keep the rule's sub-state on the Signature next to app_progress_hook,
set it from the hook and for the default policy signatures, resolve the
name in the sub-state table, and fall back to the plain state name when
that lookup fails.

Ticket: #8944.
This commit is contained in:
Victor Julien
2026-10-03 14:09:37 +00:00
parent 9419f15779
commit 2630c6a5d3
4 changed files with 38 additions and 11 deletions
@@ -122,6 +122,11 @@ alert
action in firewall rules. The effect will be the creation of an alert event when the
firewall rule matches.
For application layer transactions the alert event carries a ``firewall`` object with
the resolved ``policy`` and, when the protocol registers a state name callback, the
``hook`` the rule was registered at. Protocols without such a callback (``quic``,
``modbus``, ``rdp``) omit the ``hook`` key; the policy is still reported.
config
~~~~~~
+3
View File
@@ -1575,6 +1575,8 @@ static int SigParseProtoHookApp(
s->init_data->hook.t.app.app_progress);
s->app_progress_hook = s->init_data->hook.t.app.app_progress;
/* the hook parser only accepts known sub states of the protocol */
s->sub_state = s->init_data->hook.t.app.sub_state;
return 0;
}
@@ -4187,6 +4189,7 @@ static int AddAppPolicySignature(struct DetectFirewallAppPolicy *pol)
return -1;
}
s->app_progress_hook = pol->progress;
s->sub_state = pol->sub_state;
s->action = pol->policy.action;
s->action_scope = pol->policy.action_scope;
s->alproto = pol->alproto;
+4
View File
@@ -727,6 +727,10 @@ typedef struct Signature_ {
/** firewall: progress value for this signature */
uint8_t app_progress_hook;
/** firewall: sub state (transaction type) of the hook on protocols with sub
* states (http2 stream/global, DoH2); 0 when the hook has no sub state.
* Signatures are zero initialized, so unused stays 0. */
uint8_t sub_state;
DetectMatchAddressIPv4 *addr_dst_match4;
DetectMatchAddressIPv4 *addr_src_match4;
+26 -11
View File
@@ -662,20 +662,35 @@ static void AlertJsonAddFirewall(SCJsonBuilder *jb, const Signature *s)
const char *hook = NULL;
char hook_string[256];
switch (s->detect_table) {
case DETECT_TABLE_APP_FILTER:
if (s->flags & SIG_FLAG_TOSERVER) {
hook = AppLayerParserGetStateNameById(
IPPROTO_TCP, s->alproto, s->app_progress_hook, STREAM_TOSERVER);
} else {
hook = AppLayerParserGetStateNameById(
IPPROTO_TCP, s->alproto, s->app_progress_hook, STREAM_TOCLIENT);
case DETECT_TABLE_APP_FILTER: {
const uint8_t dir_flag =
(s->flags & SIG_FLAG_TOSERVER) ? STREAM_TOSERVER : STREAM_TOCLIENT;
if (s->sub_state != 0 && AppLayerParserSupportsSubStates(s->alproto)) {
/* sub state rules (http2 stream/global): resolve the state in
* the sub state table and keep the sub state in the name */
const char *state = AppLayerParserGetSubStateProgressName(
s->alproto, s->sub_state, s->app_progress_hook, dir_flag);
const char *sub_state_name =
AppLayerParserGetSubStateName(s->alproto, s->sub_state);
if (state != NULL && sub_state_name != NULL) {
snprintf(hook_string, sizeof(hook_string), "%s:%s:%s",
AppProtoToString(s->alproto), sub_state_name, state);
hook = hook_string;
}
}
if (hook) {
snprintf(hook_string, sizeof(hook_string), "%s:%s", AppProtoToString(s->alproto),
hook);
hook = hook_string;
if (hook == NULL) {
/* no sub state, or the sub state lookup failed: fall back to
* the plain per-protocol state name */
hook = AppLayerParserGetStateNameById(
IPPROTO_TCP, s->alproto, s->app_progress_hook, dir_flag);
if (hook != NULL) {
snprintf(hook_string, sizeof(hook_string), "%s:%s",
AppProtoToString(s->alproto), hook);
hook = hook_string;
}
}
break;
}
case DETECT_TABLE_PACKET_FILTER:
hook = "packet:filter";
break;