detect/firewall: policy for substates

For the most part hard coded for HTTP/2 for now.
pull/15846/head
Victor Julien 4 weeks ago
parent 63b8143577
commit 116a060092

@ -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;
}

@ -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;

@ -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[];
};

Loading…
Cancel
Save