firewall: beginning of ruleset analyzer

Output a `firewall.json` with a per table list of rules.

Also output a sorted list of the threat detection rules.
pull/12979/head
Victor Julien 2 years ago committed by Victor Julien
parent 31d048ed4b
commit 98372b6599

@ -1959,3 +1959,221 @@ void EngineAnalysisRules(const DetectEngineCtx *de_ctx,
fprintf(fp, "\n");
}
}
#include "app-layer-parser.h"
static void FirewallAddRulesForState(const DetectEngineCtx *de_ctx, const AppProto a,
const uint8_t state, const uint8_t direction, RuleAnalyzer *ctx)
{
uint32_t accept_rules = 0;
SCJbSetString(ctx->js, "policy", "drop:flow");
SCJbOpenArray(ctx->js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) == 0)
break;
if (s->type != SIG_TYPE_APP_TX)
continue;
if (s->alproto != a)
continue;
if (direction == STREAM_TOSERVER) {
if (s->flags & SIG_FLAG_TOCLIENT) {
continue;
}
} else {
if (s->flags & SIG_FLAG_TOSERVER) {
continue;
}
}
if (s->app_progress_hook == state) {
SCJbAppendString(ctx->js, s->sig_str);
accept_rules += ((s->action & ACTION_ACCEPT) != 0);
}
}
SCJbClose(ctx->js);
if (accept_rules == 0) {
AnalyzerWarning(ctx, (char *)"no accept rules for state, default policy will be applied");
}
}
int FirewallAnalyzer(const DetectEngineCtx *de_ctx)
{
RuleAnalyzer ctx = { NULL, NULL, NULL };
ctx.js = SCJbNewObject();
if (ctx.js == NULL)
return -1;
SCJbOpenObject(ctx.js, "tables");
SCJbOpenObject(ctx.js, "packet:filter");
SCJbSetString(ctx.js, "policy", "drop:packet");
SCJbOpenArray(ctx.js, "rules");
uint32_t accept_rules = 0;
uint32_t last_sid = 0;
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) == 0)
break;
if (s->type != SIG_TYPE_PKT)
continue;
/* don't double list <> sigs */
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
accept_rules += ((s->action & ACTION_ACCEPT) != 0);
}
SCJbClose(ctx.js);
if (accept_rules == 0) {
AnalyzerWarning(&ctx,
(char *)"no accept rules for \'packet:filter\', default policy will be applied");
}
if (ctx.js_warnings) {
SCJbClose(ctx.js_warnings);
SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
SCJbFree(ctx.js_warnings);
ctx.js_warnings = NULL;
}
SCJbClose(ctx.js); // packet_filter
for (AppProto a = 0; a < g_alproto_max; a++) {
if (!AppProtoIsValid(a))
continue;
// HACK not all protocols have named states yet
const char *hack = AppLayerParserGetStateNameById(IPPROTO_TCP, a, 0, STREAM_TOSERVER);
if (!hack)
continue;
SCJbOpenObject(ctx.js, AppProtoToString(a));
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);
char table_name[128];
snprintf(table_name, sizeof(table_name), "app:%s:%s", AppProtoToString(a), name);
SCJbOpenObject(ctx.js, table_name);
FirewallAddRulesForState(de_ctx, a, state, STREAM_TOSERVER, &ctx);
if (ctx.js_warnings) {
SCJbClose(ctx.js_warnings);
SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
SCJbFree(ctx.js_warnings);
ctx.js_warnings = NULL;
}
SCJbClose(ctx.js);
}
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);
char table_name[128];
snprintf(table_name, sizeof(table_name), "app:%s:%s", AppProtoToString(a), name);
SCJbOpenObject(ctx.js, table_name);
FirewallAddRulesForState(de_ctx, a, state, STREAM_TOCLIENT, &ctx);
if (ctx.js_warnings) {
SCJbClose(ctx.js_warnings);
SCJbSetObject(ctx.js, "warnings", ctx.js_warnings);
SCJbFree(ctx.js_warnings);
ctx.js_warnings = NULL;
}
SCJbClose(ctx.js);
}
SCJbClose(ctx.js); // app layer
}
SCJbOpenObject(ctx.js, "packet:td");
SCJbSetString(ctx.js, "policy", "accept:hook");
last_sid = 0;
SCJbOpenArray(ctx.js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) != 0)
continue;
if (s->type == SIG_TYPE_APP_TX)
continue;
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
}
SCJbClose(ctx.js); // rules
SCJbClose(ctx.js); // packet:td
SCJbOpenObject(ctx.js, "app:td");
SCJbSetString(ctx.js, "policy", "accept:hook");
last_sid = 0;
SCJbOpenArray(ctx.js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) != 0)
continue;
if (s->type != SIG_TYPE_APP_TX)
continue;
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
}
SCJbClose(ctx.js); // rules
SCJbClose(ctx.js); // app:td
SCJbClose(ctx.js); // tables
SCJbOpenObject(ctx.js, "lists");
SCJbOpenObject(ctx.js, "firewall");
last_sid = 0;
SCJbOpenArray(ctx.js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) == 0)
continue;
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
}
SCJbClose(ctx.js); // rules
SCJbClose(ctx.js); // firewall
SCJbOpenObject(ctx.js, "td");
last_sid = 0;
SCJbOpenArray(ctx.js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if ((s->flags & SIG_FLAG_FIREWALL) != 0)
continue;
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
}
SCJbClose(ctx.js); // rules
SCJbClose(ctx.js); // td
SCJbOpenObject(ctx.js, "all");
last_sid = 0;
SCJbOpenArray(ctx.js, "rules");
for (Signature *s = de_ctx->sig_list; s != NULL; s = s->next) {
if (last_sid == s->id)
continue;
last_sid = s->id;
SCJbAppendString(ctx.js, s->sig_str);
}
SCJbClose(ctx.js); // rules
SCJbClose(ctx.js); // all
SCJbClose(ctx.js); // lists
SCJbClose(ctx.js); // top level object
const char *filename = "firewall.json";
const char *log_dir = SCConfigGetLogDirectory();
char json_path[PATH_MAX] = "";
snprintf(json_path, sizeof(json_path), "%s/%s", log_dir, filename);
SCMutexLock(&g_rules_analyzer_write_m);
FILE *fp = fopen(json_path, "w");
if (fp != NULL) {
fwrite(SCJbPtr(ctx.js), SCJbLen(ctx.js), 1, fp);
fprintf(fp, "\n");
fclose(fp);
}
SCMutexUnlock(&g_rules_analyzer_write_m);
SCJbFree(ctx.js);
return 0;
}

@ -39,4 +39,6 @@ void EngineAnalysisRulesFailure(
void EngineAnalysisRules2(const struct DetectEngineCtx_ *de_ctx, const Signature *s);
int FirewallAnalyzer(const struct DetectEngineCtx_ *de_ctx);
#endif /* SURICATA_DETECT_ENGINE_ANALYZER_H */

@ -2190,6 +2190,10 @@ int SigGroupBuild(DetectEngineCtx *de_ctx)
if (!DetectEngineMultiTenantEnabled()) {
VarNameStoreActivate();
}
if (de_ctx->flags & DE_HAS_FIREWALL) {
FirewallAnalyzer(de_ctx);
}
return 0;
}

Loading…
Cancel
Save