detect: clean up signature validate logic

`SigValidate` was doing more than just validation. Break out the
function into validation steps and consolidation steps.
pull/13410/head
Victor Julien 6 months ago committed by Victor Julien
parent 12f2f3f9f1
commit 0de19d61aa

@ -2507,25 +2507,23 @@ static void DetectFirewallRuleSetTable(Signature *s)
s->firewall_table = (uint8_t)table;
}
/**
* \internal
* \brief validate a just parsed signature for internal inconsistencies
*
* \param s just parsed signature
*
* \retval 0 invalid
* \retval 1 valid
*/
static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
static int SigValidateFirewall(const DetectEngineCtx *de_ctx, const Signature *s)
{
SCEnter();
if (s->init_data->firewall_rule) {
if (!DetectFirewallRuleValidate(de_ctx, s))
SCReturnInt(0);
}
SCReturnInt(1);
}
static int SigValidateCheckBuffers(
DetectEngineCtx *de_ctx, const Signature *s, int *ts_excl, int *tc_excl, int *dir_amb)
{
bool has_frame = false;
bool has_app = false;
bool has_pkt = false;
bool has_pmatch = false;
uint32_t sig_flags = 0;
int nlists = 0;
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
nlists = MAX(nlists, (int)s->init_data->buffers[x].id);
@ -2539,11 +2537,6 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
SCReturnInt(0);
}
bool has_frame = false;
bool has_app = false;
bool has_pkt = false;
bool has_pmatch = false;
/* run buffer type validation callbacks if any */
if (s->init_data->smlists[DETECT_SM_LIST_PMATCH]) {
if (!DetectContentPMATCHValidateCallback(s))
@ -2558,9 +2551,6 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
} bufdir[nlists + 1];
memset(&bufdir, 0, (nlists + 1) * sizeof(struct BufferVsDir));
int ts_excl = 0;
int tc_excl = 0;
for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
SignatureInitDataBuffer *b = &s->init_data->buffers[x];
const DetectBufferType *bt = DetectEngineBufferTypeGetById(de_ctx, b->id);
@ -2569,7 +2559,7 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
continue;
}
SCLogDebug("x %u b->id %u name %s", x, b->id, bt->name);
for (SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
for (const SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
SCLogDebug("sm %u %s", sm->type, sigmatch_table[sm->type].name);
}
@ -2601,10 +2591,10 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
if (b->only_tc) {
if (app->dir == 1)
tc_excl++;
(*tc_excl)++;
} else if (b->only_ts) {
if (app->dir == 0)
ts_excl++;
(*ts_excl)++;
} else {
bufdir[b->id].ts += (app->dir == 0);
bufdir[b->id].tc += (app->dir == 1);
@ -2640,17 +2630,46 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
}
}
int dir_amb = 0;
if (has_pmatch && has_frame) {
SCLogError("can't mix pure content and frame inspection");
SCReturnInt(0);
}
if (has_app && has_frame) {
SCLogError("can't mix app-layer buffer and frame inspection");
SCReturnInt(0);
}
if (has_pkt && has_frame) {
SCLogError("can't mix pkt buffer and frame inspection");
SCReturnInt(0);
}
for (int x = 0; x < nlists; x++) {
if (bufdir[x].ts == 0 && bufdir[x].tc == 0)
continue;
ts_excl += (bufdir[x].ts > 0 && bufdir[x].tc == 0);
tc_excl += (bufdir[x].ts == 0 && bufdir[x].tc > 0);
dir_amb += (bufdir[x].ts > 0 && bufdir[x].tc > 0);
(*ts_excl) += (bufdir[x].ts > 0 && bufdir[x].tc == 0);
(*tc_excl) += (bufdir[x].ts == 0 && bufdir[x].tc > 0);
(*dir_amb) += (bufdir[x].ts > 0 && bufdir[x].tc > 0);
SCLogDebug("%s/%d: %d/%d", DetectEngineBufferTypeGetNameById(de_ctx, x), x, bufdir[x].ts,
bufdir[x].tc);
}
SCReturnInt(1);
}
static int SigValidatePacketStream(const Signature *s)
{
if ((s->flags & SIG_FLAG_REQUIRE_PACKET) && (s->flags & SIG_FLAG_REQUIRE_STREAM)) {
SCLogError("can't mix packet keywords with "
"tcp-stream or flow:only_stream. Invalidating signature.");
SCReturnInt(0);
}
SCReturnInt(1);
}
static int SigConsolidateDirection(
Signature *s, const int ts_excl, const int tc_excl, const int dir_amb)
{
if (s->flags & SIG_FLAG_TXBOTHDIR) {
if (!ts_excl || !tc_excl) {
SCLogError("rule %u should use both directions, but does not", s->id);
@ -2683,34 +2702,11 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
} else if (dir_amb) {
SCLogDebug("%u: rule direction cannot be deduced from keywords", s->id);
}
SCReturnInt(1);
}
if ((s->flags & SIG_FLAG_REQUIRE_PACKET) &&
(s->flags & SIG_FLAG_REQUIRE_STREAM)) {
SCLogError("can't mix packet keywords with "
"tcp-stream or flow:only_stream. Invalidating signature.");
SCReturnInt(0);
}
if ((sig_flags & (SIG_FLAG_TOCLIENT | SIG_FLAG_TOSERVER)) == (SIG_FLAG_TOCLIENT | SIG_FLAG_TOSERVER)) {
SCLogError("You seem to have mixed keywords "
"that require inspection in both directions. Atm we only "
"support keywords in one direction within a rule.");
SCReturnInt(0);
}
if (has_pmatch && has_frame) {
SCLogError("can't mix pure content and frame inspection");
SCReturnInt(0);
}
if (has_app && has_frame) {
SCLogError("can't mix app-layer buffer and frame inspection");
SCReturnInt(0);
}
if (has_pkt && has_frame) {
SCLogError("can't mix pkt buffer and frame inspection");
SCReturnInt(0);
}
static void SigConsolidateTcpBuffer(Signature *s)
{
/* TCP: corner cases:
* - pkt vs stream vs depth/offset
* - pkt vs stream vs stream_size
@ -2719,7 +2715,7 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
if (s->init_data->smlists[DETECT_SM_LIST_PMATCH]) {
if (!(s->flags & (SIG_FLAG_REQUIRE_PACKET | SIG_FLAG_REQUIRE_STREAM))) {
s->flags |= SIG_FLAG_REQUIRE_STREAM;
for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_PMATCH]; sm != NULL;
sm = sm->next) {
if (sm->type == DETECT_CONTENT &&
(((DetectContentData *)(sm->ctx))->flags &
@ -2729,7 +2725,7 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
}
}
/* if stream_size is in use, also inspect packets */
for (SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
for (const SigMatch *sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL;
sm = sm->next) {
if (sm->type == DETECT_STREAM_SIZE) {
s->flags |= SIG_FLAG_REQUIRE_PACKET;
@ -2739,42 +2735,116 @@ static int SigValidate(DetectEngineCtx *de_ctx, Signature *s)
}
}
}
}
if ((s->flags & SIG_FLAG_FILESTORE) || s->file_flags != 0 ||
(s->init_data->init_flags & SIG_FLAG_INIT_FILEDATA)) {
if (s->alproto != ALPROTO_UNKNOWN &&
!AppLayerParserSupportsFiles(IPPROTO_TCP, s->alproto))
{
SCLogError("protocol %s doesn't "
"support file matching",
AppProtoToString(s->alproto));
SCReturnInt(0);
} else if (s->init_data->alprotos[0] != ALPROTO_UNKNOWN) {
bool found = false;
for (AppProto i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
break;
}
if (AppLayerParserSupportsFiles(IPPROTO_TCP, s->init_data->alprotos[i])) {
found = true;
break;
}
static bool SigInspectsFiles(const Signature *s)
{
return ((s->flags & SIG_FLAG_FILESTORE) || s->file_flags != 0 ||
(s->init_data->init_flags & SIG_FLAG_INIT_FILEDATA));
}
/** \internal
* \brief validate file handling
* \retval 1 good signature
* \retval 0 bad signature
*/
static int SigValidateFileHandling(const Signature *s)
{
if (!SigInspectsFiles(s)) {
SCReturnInt(1);
}
if (s->alproto != ALPROTO_UNKNOWN && !AppLayerParserSupportsFiles(IPPROTO_TCP, s->alproto)) {
SCLogError("protocol %s doesn't "
"support file matching",
AppProtoToString(s->alproto));
SCReturnInt(0);
}
if (s->init_data->alprotos[0] != ALPROTO_UNKNOWN) {
bool found = false;
for (AppProto i = 0; i < SIG_ALPROTO_MAX; i++) {
if (s->init_data->alprotos[i] == ALPROTO_UNKNOWN) {
break;
}
if (!found) {
SCLogError("No protocol support file matching");
SCReturnInt(0);
if (AppLayerParserSupportsFiles(IPPROTO_TCP, s->init_data->alprotos[i])) {
found = true;
break;
}
}
if (s->alproto == ALPROTO_HTTP2 && (s->file_flags & FILE_SIG_NEED_FILENAME)) {
SCLogError("protocol HTTP2 doesn't support file name matching");
if (!found) {
SCLogError("No protocol support file matching");
SCReturnInt(0);
}
}
if (s->alproto == ALPROTO_HTTP2 && (s->file_flags & FILE_SIG_NEED_FILENAME)) {
SCLogError("protocol HTTP2 doesn't support file name matching");
SCReturnInt(0);
}
SCReturnInt(1);
}
/**
* \internal
* \brief validate and consolidate parsed signature
*
* \param de_ctx detect engine
* \param s signature to validate and consolidate
*
* \retval 0 invalid
* \retval 1 valid
*/
static int SigValidateConsolidate(
DetectEngineCtx *de_ctx, Signature *s, const SignatureParser *parser, const uint8_t dir)
{
SCEnter();
if (SigValidateFirewall(de_ctx, s) == 0)
SCReturnInt(0);
if (SigValidatePacketStream(s) == 0) {
SCReturnInt(0);
}
int ts_excl = 0;
int tc_excl = 0;
int dir_amb = 0;
if (SigValidateCheckBuffers(de_ctx, s, &ts_excl, &tc_excl, &dir_amb) == 0) {
SCReturnInt(0);
}
if (SigConsolidateDirection(s, ts_excl, tc_excl, dir_amb) == 0) {
SCReturnInt(0);
}
SigConsolidateTcpBuffer(s);
SignatureSetType(de_ctx, s);
if (de_ctx->flags & DE_HAS_FIREWALL) {
DetectFirewallRuleSetTable(s);
}
int r = SigValidateFileHandling(s);
if (r == 0) {
SCReturnInt(0);
}
if (SigInspectsFiles(s)) {
if (s->alproto == ALPROTO_HTTP1 || s->alproto == ALPROTO_HTTP) {
AppLayerHtpNeedFileInspection();
}
}
if (DetectRuleValidateTable(s) == false) {
SCReturnInt(0);
}
if (s->type == SIG_TYPE_IPONLY) {
/* For IPOnly */
if (IPOnlySigParseAddress(de_ctx, s, parser->src, SIG_DIREC_SRC ^ dir) < 0)
SCReturnInt(0);
if (IPOnlySigParseAddress(de_ctx, s, parser->dst, SIG_DIREC_DST ^ dir) < 0)
SCReturnInt(0);
}
SCReturnInt(1);
}
@ -2918,28 +2988,10 @@ static Signature *SigInitHelper(
SigSetupPrefilter(de_ctx, sig);
/* validate signature, SigValidate will report the error reason */
if (SigValidate(de_ctx, sig) == 0) {
if (SigValidateConsolidate(de_ctx, sig, &parser, dir) == 0) {
goto error;
}
/* check what the type of this sig is */
SignatureSetType(de_ctx, sig);
if (de_ctx->flags & DE_HAS_FIREWALL) {
DetectFirewallRuleSetTable(sig);
}
if (DetectRuleValidateTable(sig) == false) {
goto error;
}
if (sig->type == SIG_TYPE_IPONLY) {
/* For IPOnly */
if (IPOnlySigParseAddress(de_ctx, sig, parser.src, SIG_DIREC_SRC ^ dir) < 0)
goto error;
if (IPOnlySigParseAddress(de_ctx, sig, parser.dst, SIG_DIREC_DST ^ dir) < 0)
goto error;
}
return sig;
error:

Loading…
Cancel
Save