diff --git a/src/detect-parse.c b/src/detect-parse.c index 4b3821a382..bf3c8b88e5 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -4017,6 +4017,49 @@ static int DoParsePolicy(const char *policy_name, struct DetectFirewallPolicy *p return 1; } +static int DoParseAppSubStatePolicy(const char *prefix, const AppProto app_proto, + const uint8_t sub_state, const char *sub_state_name, const uint8_t state, + const char *hookname, const uint8_t complete_state, const int direction, + struct DetectFirewallPolicies *fw_policies, struct DetectFirewallAppPolicy *app_fw_policies) +{ + char policy_name[256]; + BUG_ON(sub_state_name == NULL); + BUG_ON(hookname == NULL); + + 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.%s", prefix, app_name, + sub_state_name, nname); + SCLogDebug("policy_name %s", policy_name); + SCFree(nname); + if (r < 0 || (size_t)r >= sizeof(policy_name)) { + FatalError("internal error: failed to assemble firewall policy config string"); + } + + struct DetectFirewallPolicy *pol; + if (direction == STREAM_TOSERVER) + pol = &fw_policies->http2_substates[sub_state - 1].ts[state]; + else + pol = &fw_policies->http2_substates[sub_state - 1].tc[state]; + + r = DoParsePolicy(policy_name, pol); + /* for policies with an alert action, create a policy sig */ + if (r == 1 && pol->action & ACTION_ALERT) { + SCLogDebug("adding policy signature"); + return AddAppPolicySignature(fw_policies->policy_signatures, direction, app_proto, app_name, + state, hookname, pol); + } + SCLogDebug("r %d", r); + return r; +} + 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 DetectFirewallPolicies *fw_policies, struct DetectFirewallAppPolicy *app_fw_policies) @@ -4122,6 +4165,17 @@ int DetectFirewallInitDefaultPolicies(DetectEngineCtx *de_ctx) app_fw_policies[a].tc[i].action_scope = ACTION_SCOPE_FLOW; } } + + /* TODO hard coded for HTTP/2 for now */ + for (int s = 0; s < 2; s++) { + for (int i = 0; i < 48; i++) { + fw_policies->http2_substates[s].ts[i].action = ACTION_DROP; + fw_policies->http2_substates[s].ts[i].action_scope = ACTION_SCOPE_FLOW; + fw_policies->http2_substates[s].tc[i].action = ACTION_DROP; + fw_policies->http2_substates[s].tc[i].action_scope = ACTION_SCOPE_FLOW; + } + } + de_ctx->fw_policies = fw_policies; return 0; @@ -4187,27 +4241,69 @@ int DetectFirewallLoadDefaultPolicies(DetectEngineCtx *de_ctx) 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, - fw_policies, app_fw_policies) < 0) - return -1; - } + if (AppLayerParserSupportsSubStates(a)) { + uint8_t max_sub_state = AppLayerParserGetMaxSubState(a); + SCLogDebug("%s: max sub state for %u is %u", AppProtoToString(a), a, max_sub_state); + for (uint8_t s = 1; s <= max_sub_state; s++) { + SCLogDebug("%s: checking sub state %u", AppProtoToString(a), s); - 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, - fw_policies, app_fw_policies) < 0) - return -1; + const char *sub_state_name = AppLayerParserGetSubStateName(a, s); + if (sub_state_name == NULL) + continue; + + // iterate the states belonging to the sub state + const uint8_t max_state = AppLayerParserGetSubStateCompletion( + a, s); // TODO allow different completion per direction? + /* to_server */ + for (uint8_t state = 0; state <= max_state; state++) { + SCLogDebug("protocol %s: sub state:%s state:%u", AppProtoToString(a), + sub_state_name, state); + const char *state_name = + AppLayerParserGetSubStateProgressName(a, s, state, STREAM_TOSERVER); + BUG_ON(state_name == NULL); + SCLogDebug("protocol %s: sub state:%s state:%s", AppProtoToString(a), + sub_state_name, state_name); + if (DoParseAppSubStatePolicy(prefix, a, s, sub_state_name, state, state_name, + max_state, STREAM_TOSERVER, fw_policies, app_fw_policies) < 0) + return -1; + } + /* to_client */ + for (uint8_t state = 0; state <= max_state; state++) { + SCLogDebug("protocol %s: to_client: sub state:%s state:%u", AppProtoToString(a), + sub_state_name, state); + const char *state_name = + AppLayerParserGetSubStateProgressName(a, s, state, STREAM_TOCLIENT); + BUG_ON(state_name == NULL); + SCLogDebug("protocol %s: to_client: sub state:%s state:%s", AppProtoToString(a), + sub_state_name, state_name); + if (DoParseAppSubStatePolicy(prefix, a, s, sub_state_name, state, state_name, + max_state, STREAM_TOCLIENT, fw_policies, app_fw_policies) < 0) + return -1; + } + } + } else { + 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, + fw_policies, 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, + fw_policies, app_fw_policies) < 0) + return -1; + } } } - return 0; } diff --git a/src/detect.c b/src/detect.c index 7bdecd0d69..465e82ed2b 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1741,19 +1741,39 @@ static inline void DetectRunAppendDefaultAppPolicyAlert(DetectEngineThreadCtx *d * to look up configurable default policies later */ static const struct DetectFirewallPolicy *DetectFirewallApplyDefaultAppPolicy( - DetectEngineThreadCtx *det_ctx, const struct DetectFirewallAppPolicy *policies, + DetectEngineThreadCtx *det_ctx, const struct DetectFirewallPolicies *policies, const DetectTransaction *tx, Packet *p, const AppProto alproto, const uint8_t direction, const uint8_t progress) { 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); + SCLogDebug("packet %" PRIu64 ": tx type %u", PcapPacketCntGet(p), tx->tx_type); + if (tx->tx_type != 0) { + // TODO hard coded to HTTP/2 for now + BUG_ON(alproto != ALPROTO_HTTP2); + if (direction & STREAM_TOSERVER) { + policy = &policies->http2_substates[tx->tx_type - 1].ts[progress]; + SCLogDebug("packet %" PRIu64 + ", sub_state:%u, hook:%u, toserver, policy: action %02x scope %u", + PcapPacketCntGet(p), tx->tx_type, progress, policy->action, + policy->action_scope); + } else { + policy = &policies->http2_substates[tx->tx_type - 1].tc[progress]; + SCLogDebug("packet %" PRIu64 + ", sub_state:%u, hook:%u, toclient, policy: action %02x scope %u", + PcapPacketCntGet(p), tx->tx_type, 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 (direction & STREAM_TOSERVER) { + policy = &policies->app[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->app[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) { @@ -1825,7 +1845,7 @@ static const struct DetectFirewallPolicy *DetectFirewallApplyDefaultAppPolicy( * \retval DETECT_TX_FW_FC_OK no action needed */ static enum DetectTxFirewallFlowControl DetectFirewallApplyDefaultPolicies( - DetectEngineThreadCtx *det_ctx, const struct DetectFirewallAppPolicy *policies, + DetectEngineThreadCtx *det_ctx, const struct DetectFirewallPolicies *policies, DetectTransaction *tx, Packet *p, const AppProto alproto, const uint8_t direction, const uint8_t start_hook, const uint8_t end_hook) { @@ -1846,7 +1866,7 @@ static enum DetectTxFirewallFlowControl DetectFirewallApplyDefaultPolicies( BOOL2STR(apply_to_packet)); const struct DetectFirewallPolicy *policy = DetectFirewallApplyDefaultAppPolicy( - det_ctx, det_ctx->de_ctx->fw_policies->app, tx, p, alproto, direction, hook); + det_ctx, policies, tx, p, alproto, direction, hook); SCLogDebug("fw: hook:%u policy:%02x apply_to_packet:%s", hook, policy->action, BOOL2STR(apply_to_packet)); if (policy->action & ACTION_DROP) { @@ -1961,9 +1981,9 @@ static enum DetectTxFirewallFlowControl DetectRunTxPreCheckFirewallPolicy( 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 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); + enum DetectTxFirewallFlowControl r = + DetectFirewallApplyDefaultPolicies(det_ctx, det_ctx->de_ctx->fw_policies, tx, p, + s->alproto, direction, tx->detect_progress_orig, s->app_progress_hook - 1); if (r != DETECT_TX_FW_FC_OK) { /* both SKIP and BREAK mean: no more fw rules to inspect. * SKIP applies to just this TX. @@ -2124,8 +2144,8 @@ static void DetectRunTxFirewallApplyAccept(DetectEngineThreadCtx *det_ctx, Packe ? tx->tx_end_state : MIN(tx->tx_end_state, s->app_progress_hook + 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); + DetectFirewallApplyDefaultPolicies(det_ctx, det_ctx->de_ctx->fw_policies, tx, p, + s->alproto, direction, s->app_progress_hook + 1, last_hook); if (r == DETECT_TX_FW_FC_BREAK) { fw_state->fw_skip_app_filter = true; return; @@ -2187,8 +2207,8 @@ static int DetectTxFirewallNoRulesApplyPolicies(DetectEngineThreadCtx *det_ctx, 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), + DetectFirewallApplyDefaultPolicies(det_ctx, det_ctx->de_ctx->fw_policies, tx, p, + alproto, flow_flags & (STREAM_TOSERVER | STREAM_TOCLIENT), tx->detect_progress_orig, tx->tx_progress); SCLogDebug("r %u", r); if (r == DETECT_TX_FW_FC_BREAK) @@ -2299,9 +2319,8 @@ static int DetectRunTxFirewallRuleNoMatch(DetectEngineThreadCtx *det_ctx, const * we have to invoke the default policy. We only check the current rule hook. * DROP is immediate, flow control for various accept options is handled by * the DetectRunTxPreCheckFirewallPolicy function for the next rule. */ - const struct DetectFirewallPolicy *policy = - DetectFirewallApplyDefaultAppPolicy(det_ctx, det_ctx->de_ctx->fw_policies->app, tx, - p, s->alproto, flow_flags, s->app_progress_hook); + const struct DetectFirewallPolicy *policy = DetectFirewallApplyDefaultAppPolicy(det_ctx, + det_ctx->de_ctx->fw_policies, tx, p, s->alproto, flow_flags, s->app_progress_hook); SCLogDebug("fw_last_for_progress policy %02x", policy->action); if (policy->action & ACTION_DROP) { return 1; diff --git a/src/detect.h b/src/detect.h index d0f4456701..29ff23b2b1 100644 --- a/src/detect.h +++ b/src/detect.h @@ -950,6 +950,9 @@ struct DetectFirewallPolicies { /* hash table with a Signature object per default policy that has `alert` enabled. */ HashTable *policy_signatures; + /* hard coded for now: http2 substates. Index at substate - 1. */ + struct DetectFirewallAppPolicy http2_substates[2]; + /** app layer policies, one per alproto */ struct DetectFirewallAppPolicy app[]; };