From 8ce38ac8fe0cda143dfdbf3dddddc836e39e959a Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Thu, 19 Sep 2013 16:16:50 +0200 Subject: [PATCH] Split Thresholds and Suppression Thresholds and suppression can be handled independently. Suppression only suppresses output, and is not related to Threshold state tracking. This simplifies mixing suppression and thresholding rules. Part of the Bug #425 effort. --- src/detect-engine-alert.c | 50 +- src/detect-engine-threshold.c | 18 +- src/detect-engine-threshold.h | 3 +- src/detect.h | 3 +- src/util-threshold-config.c | 1212 ++++++++++++++++++++------------- 5 files changed, 798 insertions(+), 488 deletions(-) diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c index bc37347626..e22bb2d292 100644 --- a/src/detect-engine-alert.c +++ b/src/detect-engine-alert.c @@ -69,27 +69,49 @@ static int PacketAlertHandle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det SCEnter(); int ret = 1; DetectThresholdData *td = NULL; - SigMatch *sm = NULL; + SigMatch *sm; if (!(PKT_IS_IPV4(p) || PKT_IS_IPV6(p))) { SCReturnInt(1); } - do { - td = SigGetThresholdTypeIter(s, p, &sm); - if (td != NULL) { - SCLogDebug("td %p", td); - - /* PacketAlertThreshold returns 2 if the alert is suppressed but - * we do need to apply rule actions to the packet. */ - ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s); - if (ret == 0 || ret == 2) { - /* It doesn't match threshold, remove it */ - SCReturnInt(ret); + /* handle suppressions first */ + if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] != NULL) { + sm = NULL; + do { + td = SigGetThresholdTypeIter(s, p, &sm, DETECT_SM_LIST_SUPPRESS); + if (td != NULL) { + SCLogDebug("td %p", td); + + /* PacketAlertThreshold returns 2 if the alert is suppressed but + * we do need to apply rule actions to the packet. */ + ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s); + if (ret == 0 || ret == 2) { + /* It doesn't match threshold, remove it */ + SCReturnInt(ret); + } } - } - } while (sm != NULL); + } while (sm != NULL); + } + /* if we're still here, consider thresholding */ + if (s->sm_lists[DETECT_SM_LIST_THRESHOLD] != NULL) { + sm = NULL; + do { + td = SigGetThresholdTypeIter(s, p, &sm, DETECT_SM_LIST_THRESHOLD); + if (td != NULL) { + SCLogDebug("td %p", td); + + /* PacketAlertThreshold returns 2 if the alert is suppressed but + * we do need to apply rule actions to the packet. */ + ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s); + if (ret == 0 || ret == 2) { + /* It doesn't match threshold, remove it */ + SCReturnInt(ret); + } + } + } while (sm != NULL); + } SCReturnInt(1); } diff --git a/src/detect-engine-threshold.c b/src/detect-engine-threshold.c index 1c0bb4c10f..ca715a3ce5 100644 --- a/src/detect-engine-threshold.c +++ b/src/detect-engine-threshold.c @@ -95,7 +95,7 @@ int ThresholdHostHasThreshold(Host *host) { * * */ -DetectThresholdData *SigGetThresholdTypeIter(Signature *sig, Packet *p, SigMatch **psm) +DetectThresholdData *SigGetThresholdTypeIter(Signature *sig, Packet *p, SigMatch **psm, int list) { SigMatch *sm = NULL; DetectThresholdData *tsh = NULL; @@ -104,7 +104,7 @@ DetectThresholdData *SigGetThresholdTypeIter(Signature *sig, Packet *p, SigMatch return NULL; if (*psm == NULL) { - sm = sig->sm_lists_tail[DETECT_SM_LIST_THRESHOLD]; + sm = sig->sm_lists_tail[list]; } else { /* Iteration in progress, using provided value */ sm = *psm; @@ -127,20 +127,6 @@ DetectThresholdData *SigGetThresholdTypeIter(Signature *sig, Packet *p, SigMatch return NULL; } -/** - * \brief Check if a certain signature has threshold option - * - * \param sig Signature pointer - * \param p Packet structure - * - * \retval tsh Return the threshold data from signature or NULL if not found - */ -DetectThresholdData *SigGetThresholdType(Signature *sig, Packet *p) -{ - SigMatch *psm = NULL; - return SigGetThresholdTypeIter(sig, p, &psm); -} - /** * \brief Remove timeout threshold hash elements * diff --git a/src/detect-engine-threshold.h b/src/detect-engine-threshold.h index 4ecc4f94aa..141d510a6e 100644 --- a/src/detect-engine-threshold.h +++ b/src/detect-engine-threshold.h @@ -31,8 +31,7 @@ int ThresholdHostStorageId(void); int ThresholdHostHasThreshold(Host *); -DetectThresholdData *SigGetThresholdType(Signature *, Packet *); -DetectThresholdData *SigGetThresholdTypeIter(Signature *, Packet *, SigMatch **); +DetectThresholdData *SigGetThresholdTypeIter(Signature *, Packet *, SigMatch **, int list); int PacketAlertThreshold(DetectEngineCtx *, DetectEngineThreadCtx *, DetectThresholdData *, Packet *, Signature *); diff --git a/src/detect.h b/src/detect.h index 9645f7e7a8..6733d5eaf0 100644 --- a/src/detect.h +++ b/src/detect.h @@ -122,7 +122,8 @@ enum { /* list for post match actions: flowbit set, flowint increment, etc */ DETECT_SM_LIST_POSTMATCH, - /* list for alert thresholding */ + /* lists for alert thresholding and suppression */ + DETECT_SM_LIST_SUPPRESS, DETECT_SM_LIST_THRESHOLD, DETECT_SM_LIST_MAX, diff --git a/src/util-threshold-config.c b/src/util-threshold-config.c index e064509056..2b17ba2a1d 100644 --- a/src/util-threshold-config.c +++ b/src/util-threshold-config.c @@ -255,339 +255,376 @@ void SCThresholdConfDeInitContext(DetectEngineCtx *de_ctx, FILE *fd) return; } -/** - * \brief Parses a line from the threshold file and adds it to Thresholdtype - * - * \param rawstr Pointer to the string to be parsed. - * \param de_ctx Pointer to the Detection Engine Context. - * - * \retval 0 On success. - * \retval -1 On failure. +/** \internal + * \brief setup suppress rules + * \retval 0 ok + * \retval -1 error */ -int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx) +static int SetupSuppressRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid, + uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count, + uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action, + const char *th_ip) { - const char *th_rule_type = NULL; - const char *th_gid = NULL; - const char *th_sid = NULL; - const char *th_type = NULL; - const char *th_track = NULL; - const char *th_count = NULL; - const char *th_seconds = NULL; - const char *th_new_action= NULL; - const char *th_timeout = NULL; - const char *th_ip = NULL; - const char *rule_extend = NULL; - - uint8_t parsed_type = 0; - uint8_t parsed_track = 0; - uint8_t parsed_new_action = 0; - uint32_t parsed_count = 0; - uint32_t parsed_seconds = 0; - uint32_t parsed_timeout = 0; - - Signature *sig = NULL; - Signature *s = NULL, *ns = NULL; - DetectThresholdData *de = NULL; + Signature *s = NULL; SigMatch *sm = NULL; - SigMatch *m = NULL; -#define MAX_SUBSTRINGS 30 - int ret = 0; - int ov[MAX_SUBSTRINGS]; - uint32_t id = 0, gid = 0; - ThresholdRuleType rule_type; - int fret = -1; - - if (de_ctx == NULL) - return -1; - - ret = pcre_exec(regex_base, regex_base_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS); - if (ret < 4) { - SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); - goto error; - } - - /* retrieve the classtype name */ - ret = pcre_get_substring((char *)rawstr, ov, 30, 1, &th_rule_type); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - - /* retrieve the classtype name */ - ret = pcre_get_substring((char *)rawstr, ov, 30, 2, &th_gid); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - - ret = pcre_get_substring((char *)rawstr, ov, 30, 3, &th_sid); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + DetectThresholdData *de = NULL; - ret = pcre_get_substring((char *)rawstr, ov, 30, 4, &rule_extend); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + BUG_ON(parsed_type != TYPE_SUPPRESS); - /* get type of rule */ - if (strncasecmp(th_rule_type,"event_filter",strlen("event_filter")) == 0) { - rule_type = THRESHOLD_TYPE_EVENT_FILTER; - } else if (strncasecmp(th_rule_type,"threshold",strlen("threshold")) == 0) { - rule_type = THRESHOLD_TYPE_THRESHOLD; - } else if (strncasecmp(th_rule_type,"rate",strlen("rate")) == 0) { - rule_type = THRESHOLD_TYPE_RATE; - } else if (strncasecmp(th_rule_type,"suppress",strlen("suppress")) == 0) { - rule_type = THRESHOLD_TYPE_SUPPRESS; - } else { - SCLogError(SC_ERR_INVALID_VALUE, "rule type %s is unknown", th_rule_type); - goto error; - } + /* Install it */ + if (id == 0 && gid == 0) { + if (parsed_track == TRACK_RULE) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules"); + } - /* get end of rule */ - switch(rule_type) { - case THRESHOLD_TYPE_EVENT_FILTER: - case THRESHOLD_TYPE_THRESHOLD: - if (strlen(rule_extend) > 0) { - ret = pcre_exec(regex_threshold, regex_threshold_study, - rule_extend, strlen(rule_extend), - 0, 0, ov, MAX_SUBSTRINGS); - if (ret < 4) { - SCLogError(SC_ERR_PCRE_MATCH, - "pcre_exec parse error, ret %" PRId32 ", string %s", - ret, rule_extend); - goto error; - } + /* update each sig with our suppress info */ + for (s = de_ctx->sig_list; s != NULL; s = s->next) { + /* tag the rule as noalert */ + if (parsed_track == TRACK_RULE) { + s->flags |= SIG_FLAG_NOALERT; + continue; + } - ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_type); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + de = SCMalloc(sizeof(DetectThresholdData)); + if (unlikely(de == NULL)) + goto error; + memset(de,0,sizeof(DetectThresholdData)); - ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_track); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + de->type = TYPE_SUPPRESS; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + de->new_action = parsed_new_action; + de->timeout = parsed_timeout; + de->addr = NULL; - ret = pcre_get_substring((char *)rule_extend, ov, 30, 3, &th_count); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + if (parsed_track != TRACK_RULE) { + de->addr = DetectAddressInit(); + if (de->addr == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); goto error; } - - ret = pcre_get_substring((char *)rule_extend, ov, 30, 4, &th_seconds); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { + SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); goto error; } + } - if (strcasecmp(th_type,"limit") == 0) - parsed_type = TYPE_LIMIT; - else if (strcasecmp(th_type,"both") == 0) - parsed_type = TYPE_BOTH; - else if (strcasecmp(th_type,"threshold") == 0) - parsed_type = TYPE_THRESHOLD; - else { - SCLogError(SC_ERR_INVALID_ARGUMENTS, "limit type not supported: %s", th_type); - goto error; - } - } else { - SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr); + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); goto error; } - break; - case THRESHOLD_TYPE_SUPPRESS: - if (strlen(rule_extend) > 0) { - ret = pcre_exec(regex_suppress, regex_suppress_study, - rule_extend, strlen(rule_extend), - 0, 0, ov, MAX_SUBSTRINGS); - if (ret < 2) { - SCLogError(SC_ERR_PCRE_MATCH, - "pcre_exec parse error, ret %" PRId32 ", string %s", - ret, rule_extend); - goto error; - } - /* retrieve the track mode */ - ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_track); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - /* retrieve the IP */ - ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_ip); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } - } else { - parsed_track = TRACK_RULE; + + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS); + } + } else if (id == 0 && gid > 0) { + if (parsed_track == TRACK_RULE) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "suppressing all rules with gid %"PRIu32, gid); + } + /* set up suppression for each signature with a matching gid */ + for (s = de_ctx->sig_list; s != NULL; s = s->next) { + if (s->gid != gid) + continue; + + /* tag the rule as noalert */ + if (parsed_track == TRACK_RULE) { + s->flags |= SIG_FLAG_NOALERT; + continue; } - parsed_type = TYPE_SUPPRESS; - break; - case THRESHOLD_TYPE_RATE: - if (strlen(rule_extend) > 0) { - ret = pcre_exec(regex_rate, regex_rate_study, - rule_extend, strlen(rule_extend), - 0, 0, ov, MAX_SUBSTRINGS); - if (ret < 5) { - SCLogError(SC_ERR_PCRE_MATCH, - "pcre_exec parse error, ret %" PRId32 ", string %s", - ret, rule_extend); - goto error; - } - ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 1, &th_track); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + de = SCMalloc(sizeof(DetectThresholdData)); + if (unlikely(de == NULL)) + goto error; - ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 2, &th_count); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + memset(de,0,sizeof(DetectThresholdData)); - ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 3, &th_seconds); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); - goto error; - } + de->type = TYPE_SUPPRESS; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + de->new_action = parsed_new_action; + de->timeout = parsed_timeout; + de->addr = NULL; - ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 4, &th_new_action); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + if (parsed_track != TRACK_RULE) { + de->addr = DetectAddressInit(); + if (de->addr == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); goto error; } - - ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 5, &th_timeout); - if (ret < 0) { - SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { + SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); goto error; } + } - /* TODO: implement option "apply_to" */ + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } - if (ByteExtractStringUint32(&parsed_timeout, 10, strlen(th_timeout), th_timeout) <= 0) { - goto error; - } + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; - /* Get the new action to take */ - if (strcasecmp(th_new_action, "alert") == 0) - parsed_new_action = TH_ACTION_ALERT; - if (strcasecmp(th_new_action, "drop") == 0) - parsed_new_action = TH_ACTION_DROP; - if (strcasecmp(th_new_action, "pass") == 0) - parsed_new_action = TH_ACTION_PASS; - if (strcasecmp(th_new_action, "reject") == 0) - parsed_new_action = TH_ACTION_REJECT; - if (strcasecmp(th_new_action, "log") == 0) { - SCLogInfo("log action for rate_filter not supported yet"); - parsed_new_action = TH_ACTION_LOG; - } - if (strcasecmp(th_new_action, "sdrop") == 0) { - SCLogInfo("sdrop action for rate_filter not supported yet"); - parsed_new_action = TH_ACTION_SDROP; - } - parsed_type = TYPE_RATE; - } else { - SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr); - goto error; + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS); + } + } else if (id > 0 && gid == 0) { + SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has " + "sid > 0 and gid == 0. Please fix this " + "in your threshold.conf file"); + goto error; + } else { + s = SigFindSignatureBySidGid(de_ctx, id, gid); + if (s == NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid " + "%"PRIu32", gid %"PRIu32": unknown rule", id, gid); + } else { + if (parsed_track == TRACK_RULE) { + s->flags |= SIG_FLAG_NOALERT; + goto end; } - break; - default: - SCLogError(SC_ERR_PCRE_MATCH, "unable to find rule type for string %s", rawstr); - goto error; - } - switch (rule_type) { - /* This part is common to threshold/event_filter/rate_filter */ - case THRESHOLD_TYPE_EVENT_FILTER: - case THRESHOLD_TYPE_THRESHOLD: - case THRESHOLD_TYPE_RATE: - if (strcasecmp(th_track,"by_dst") == 0) - parsed_track = TRACK_DST; - else if (strcasecmp(th_track,"by_src") == 0) - parsed_track = TRACK_SRC; - else if (strcasecmp(th_track,"by_rule") == 0) - parsed_track = TRACK_RULE; - else { - SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rawstr); + de = SCMalloc(sizeof(DetectThresholdData)); + if (unlikely(de == NULL)) goto error; - } + memset(de,0,sizeof(DetectThresholdData)); - if (ByteExtractStringUint32(&parsed_count, 10, strlen(th_count), th_count) <= 0) { + de->type = TYPE_SUPPRESS; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + de->new_action = parsed_new_action; + de->timeout = parsed_timeout; + + de->addr = DetectAddressInit(); + if (de->addr == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); goto error; } - if (parsed_count == 0) { - SCLogError(SC_ERR_INVALID_VALUE, "rate filter count should be > 0"); + if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { + SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); goto error; } - if (ByteExtractStringUint32(&parsed_seconds, 10, strlen(th_seconds), th_seconds) <= 0) { + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); goto error; } - break; - case THRESHOLD_TYPE_SUPPRESS: - /* need to get IP if extension is provided */ - if (th_track != NULL) { - if (strcasecmp(th_track,"by_dst") == 0) - parsed_track = TRACK_DST; - else if (strcasecmp(th_track,"by_src") == 0) - parsed_track = TRACK_SRC; - else { - SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rule_extend); - goto error; - } - } - break; - } + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; - if (ByteExtractStringUint32(&id, 10, strlen(th_sid), th_sid) <= 0) { - goto error; + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_SUPPRESS); + } } - if (ByteExtractStringUint32(&gid, 10, strlen(th_gid), th_gid) <= 0) { - goto error; +end: + return 0; +error: + if (de != NULL) { + if (de->addr != NULL) + DetectAddressFree(de->addr); + SCFree(de); } + return -1; +} + +/** \internal + * \brief setup suppress rules + * \retval 0 ok + * \retval -1 error + */ +static int SetupThresholdRule(DetectEngineCtx *de_ctx, uint32_t id, uint32_t gid, + uint8_t parsed_type, uint8_t parsed_track, uint32_t parsed_count, + uint32_t parsed_seconds, uint32_t parsed_timeout, uint8_t parsed_new_action, + const char *th_ip) +{ + Signature *s = NULL; + SigMatch *sm = NULL; + DetectThresholdData *de = NULL; + + BUG_ON(parsed_type == TYPE_SUPPRESS); /* Install it */ if (id == 0 && gid == 0) { - for (s = de_ctx->sig_list; s != NULL;) { - ns = s->next; - if (parsed_type != TYPE_SUPPRESS) { - m = SigMatchGetLastSMFromLists(s, 2, + for (s = de_ctx->sig_list; s != NULL; s = s->next) { + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + if (sm != NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " + "an event var set. The signature event var is " + "given precedence over the threshold.conf one. " + "We'll change this in the future though.", s->id); + goto end; + } + + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + if (sm != NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " + "an event var set. The signature event var is " + "given precedence over the threshold.conf one. " + "We'll change this in the future though.", s->id); + goto end; + } + + de = SCMalloc(sizeof(DetectThresholdData)); + if (unlikely(de == NULL)) + goto error; + memset(de,0,sizeof(DetectThresholdData)); + + de->type = parsed_type; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + de->new_action = parsed_new_action; + de->timeout = parsed_timeout; + de->addr = NULL; + + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } + + if (parsed_type == TYPE_RATE) + sm->type = DETECT_DETECTION_FILTER; + else + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + + if (parsed_track == TRACK_RULE) { + de_ctx->ths_ctx.th_entry = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *)); + if (de_ctx->ths_ctx.th_entry == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config" + " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1); + } else { + de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL; + de_ctx->ths_ctx.th_size++; + } + } + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD); + } + + } else if (id == 0 && gid > 0) { + for (s = de_ctx->sig_list; s != NULL; s = s->next) { + if (s->gid == gid) { + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + if (sm != NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " + "an event var set. The signature event var is " + "given precedence over the threshold.conf one. " + "We'll change this in the future though.", id); + goto end; + } + + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + if (sm != NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " + "an event var set. The signature event var is " + "given precedence over the threshold.conf one. " + "We'll change this in the future though.", id); + goto end; + } + + de = SCMalloc(sizeof(DetectThresholdData)); + if (unlikely(de == NULL)) + goto error; + memset(de,0,sizeof(DetectThresholdData)); + + de->type = parsed_type; + de->track = parsed_track; + de->count = parsed_count; + de->seconds = parsed_seconds; + de->new_action = parsed_new_action; + de->timeout = parsed_timeout; + de->addr = NULL; + + sm = SigMatchAlloc(); + if (sm == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + goto error; + } + + if (parsed_type == TYPE_RATE) + sm->type = DETECT_DETECTION_FILTER; + else + sm->type = DETECT_THRESHOLD; + sm->ctx = (void *)de; + + if (parsed_track == TRACK_RULE) { + de_ctx->ths_ctx.th_entry = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *)); + if (de_ctx->ths_ctx.th_entry == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config" + " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1); + } else { + de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL; + de_ctx->ths_ctx.th_size++; + } + } + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD); + } + } + } else if (id > 0 && gid == 0) { + SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has " + "sid > 0 and gid == 0. Please fix this " + "in your threshold.conf file"); + } else { + s = SigFindSignatureBySidGid(de_ctx, id, gid); + if (s == NULL) { + SCLogWarning(SC_ERR_EVENT_ENGINE, "can't suppress sid " + "%"PRIu32", gid %"PRIu32": unknown rule", id, gid); + } else { + if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD && + parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT) + { + sm = SigMatchGetLastSMFromLists(s, 2, DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); - - if (m != NULL) { + if (sm != NULL) { SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "an event var set. The signature event var is " - "given precedence over the threshold.conf one. " - "We'll change this in the future though.", s->id); + "a threshold set. The signature event var is " + "given precedence over the threshold.conf one. " + "Bug #425.", s->id); goto end; } - m = SigMatchGetLastSMFromLists(s, 2, + sm = SigMatchGetLastSMFromLists(s, 2, DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); - - if (m != NULL) { + if (sm != NULL) { SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "an event var set. The signature event var is " - "given precedence over the threshold.conf one. " - "We'll change this in the future though.", s->id); + "a detection_filter set. The signature event var is " + "given precedence over the threshold.conf one. " + "Bug #425.", s->id); goto end; } + + /* replace threshold on sig if we have a global override for it */ +#if 1 + } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) { + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + if (sm == NULL) { + sm = SigMatchGetLastSMFromLists(s, 2, + DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + } + if (sm != NULL) { + SigMatchRemoveSMFromList(s, sm, DETECT_SM_LIST_THRESHOLD); + SigMatchFree(sm); + sm = NULL; + } +#endif } de = SCMalloc(sizeof(DetectThresholdData)); if (unlikely(de == NULL)) goto error; - memset(de,0,sizeof(DetectThresholdData)); de->type = parsed_type; @@ -598,18 +635,6 @@ int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx) de->timeout = parsed_timeout; de->addr = NULL; - if ((parsed_type == TYPE_SUPPRESS) && (parsed_track != TRACK_RULE)) { - de->addr = DetectAddressInit(); - if (de->addr == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); - goto error; - } - if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { - SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); - goto error; - } - } - sm = SigMatchAlloc(); if (sm == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); @@ -632,215 +657,351 @@ int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx) de_ctx->ths_ctx.th_size++; } } + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD); - s = ns; } + } +end: + return 0; +error: + if (de != NULL) { + if (de->addr != NULL) + DetectAddressFree(de->addr); + SCFree(de); + } + return -1; +} - } else if (id == 0 && gid > 0) { - for (s = de_ctx->sig_list; s != NULL;) { - ns = s->next; - - if(s->gid == gid) { - if (parsed_type != TYPE_SUPPRESS) { - m = SigMatchGetLastSMFromLists(s, 2, - DETECT_THRESHOLD, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); - - if (m != NULL) { - SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "an event var set. The signature event var is " - "given precedence over the threshold.conf one. " - "We'll change this in the future though.", id); - goto end; - } +/** + * \brief Parses a line from the threshold file and adds it to Thresholdtype + * + * \param rawstr Pointer to the string to be parsed. + * \param de_ctx Pointer to the Detection Engine Context. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int SCThresholdConfAddThresholdtype(char *rawstr, DetectEngineCtx *de_ctx) +{ + const char *th_rule_type = NULL; + const char *th_gid = NULL; + const char *th_sid = NULL; + const char *th_type = NULL; + const char *th_track = NULL; + const char *th_count = NULL; + const char *th_seconds = NULL; + const char *th_new_action= NULL; + const char *th_timeout = NULL; + const char *th_ip = NULL; + const char *rule_extend = NULL; - m = SigMatchGetLastSMFromLists(s, 2, - DETECT_DETECTION_FILTER, s->sm_lists[DETECT_SM_LIST_THRESHOLD]); + uint8_t parsed_type = 0; + uint8_t parsed_track = 0; + uint8_t parsed_new_action = 0; + uint32_t parsed_count = 0; + uint32_t parsed_seconds = 0; + uint32_t parsed_timeout = 0; - if (m != NULL) { - SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "an event var set. The signature event var is " - "given precedence over the threshold.conf one. " - "We'll change this in the future though.", id); - goto end; - } - } +#define MAX_SUBSTRINGS 30 + int ret = 0; + int ov[MAX_SUBSTRINGS]; + uint32_t id = 0, gid = 0; + ThresholdRuleType rule_type; + int fret = -1; - de = SCMalloc(sizeof(DetectThresholdData)); - if (unlikely(de == NULL)) - goto error; + if (de_ctx == NULL) + return -1; - memset(de,0,sizeof(DetectThresholdData)); + ret = pcre_exec(regex_base, regex_base_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS); + if (ret < 4) { + SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, rawstr); + goto error; + } - de->type = parsed_type; - de->track = parsed_track; - de->count = parsed_count; - de->seconds = parsed_seconds; - de->new_action = parsed_new_action; - de->timeout = parsed_timeout; - de->addr = NULL; + /* retrieve the classtype name */ + ret = pcre_get_substring((char *)rawstr, ov, 30, 1, &th_rule_type); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - if ((parsed_type == TYPE_SUPPRESS) && (parsed_track != TRACK_RULE)) { - de->addr = DetectAddressInit(); - if (de->addr == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); - goto error; - } - if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { - SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); - goto error; - } - } + /* retrieve the classtype name */ + ret = pcre_get_substring((char *)rawstr, ov, 30, 2, &th_gid); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - sm = SigMatchAlloc(); - if (sm == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + ret = pcre_get_substring((char *)rawstr, ov, 30, 3, &th_sid); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + ret = pcre_get_substring((char *)rawstr, ov, 30, 4, &rule_extend); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* get type of rule */ + if (strncasecmp(th_rule_type,"event_filter",strlen("event_filter")) == 0) { + rule_type = THRESHOLD_TYPE_EVENT_FILTER; + } else if (strncasecmp(th_rule_type,"threshold",strlen("threshold")) == 0) { + rule_type = THRESHOLD_TYPE_THRESHOLD; + } else if (strncasecmp(th_rule_type,"rate",strlen("rate")) == 0) { + rule_type = THRESHOLD_TYPE_RATE; + } else if (strncasecmp(th_rule_type,"suppress",strlen("suppress")) == 0) { + rule_type = THRESHOLD_TYPE_SUPPRESS; + } else { + SCLogError(SC_ERR_INVALID_VALUE, "rule type %s is unknown", th_rule_type); + goto error; + } + + /* get end of rule */ + switch(rule_type) { + case THRESHOLD_TYPE_EVENT_FILTER: + case THRESHOLD_TYPE_THRESHOLD: + if (strlen(rule_extend) > 0) { + ret = pcre_exec(regex_threshold, regex_threshold_study, + rule_extend, strlen(rule_extend), + 0, 0, ov, MAX_SUBSTRINGS); + if (ret < 4) { + SCLogError(SC_ERR_PCRE_MATCH, + "pcre_exec parse error, ret %" PRId32 ", string %s", + ret, rule_extend); goto error; } - if (parsed_type == TYPE_RATE) - sm->type = DETECT_DETECTION_FILTER; - else - sm->type = DETECT_THRESHOLD; - sm->ctx = (void *)de; + ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_type); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - if (parsed_track == TRACK_RULE) { - de_ctx->ths_ctx.th_entry = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *)); - if (de_ctx->ths_ctx.th_entry == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config" - " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1); - } else { - de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL; - de_ctx->ths_ctx.th_size++; - } + ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_track); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; } - SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_THRESHOLD); - } - s = ns; - } - } else if (id > 0 && gid == 0) { - SCLogError(SC_ERR_INVALID_VALUE, "Can't use a event config that has " - "sid > 0 and gid == 0. Please fix this " - "in your threshold.conf file"); - } else { - sig = SigFindSignatureBySidGid(de_ctx,id,gid); - if(sig != NULL) { - if ((parsed_type == TYPE_SUPPRESS) && (parsed_track == TRACK_RULE)) { - sig->flags |= SIG_FLAG_NOALERT; - goto end; - } + ret = pcre_get_substring((char *)rule_extend, ov, 30, 3, &th_count); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - if (parsed_type != TYPE_SUPPRESS && parsed_type != TYPE_THRESHOLD && - parsed_type != TYPE_BOTH && parsed_type != TYPE_LIMIT) - { - m = SigMatchGetLastSMFromLists(sig, 2, - DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]); + ret = pcre_get_substring((char *)rule_extend, ov, 30, 4, &th_seconds); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - if (m != NULL) { - SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "a threshold set. The signature event var is " - "given precedence over the threshold.conf one. " - "Bug #425.", sig->id); - goto end; + if (strcasecmp(th_type,"limit") == 0) + parsed_type = TYPE_LIMIT; + else if (strcasecmp(th_type,"both") == 0) + parsed_type = TYPE_BOTH; + else if (strcasecmp(th_type,"threshold") == 0) + parsed_type = TYPE_THRESHOLD; + else { + SCLogError(SC_ERR_INVALID_ARGUMENTS, "limit type not supported: %s", th_type); + goto error; + } + } else { + SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr); + goto error; + } + break; + case THRESHOLD_TYPE_SUPPRESS: + if (strlen(rule_extend) > 0) { + ret = pcre_exec(regex_suppress, regex_suppress_study, + rule_extend, strlen(rule_extend), + 0, 0, ov, MAX_SUBSTRINGS); + if (ret < 2) { + SCLogError(SC_ERR_PCRE_MATCH, + "pcre_exec parse error, ret %" PRId32 ", string %s", + ret, rule_extend); + goto error; + } + /* retrieve the track mode */ + ret = pcre_get_substring((char *)rule_extend, ov, 30, 1, &th_track); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + /* retrieve the IP */ + ret = pcre_get_substring((char *)rule_extend, ov, 30, 2, &th_ip); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + } else { + parsed_track = TRACK_RULE; + } + parsed_type = TYPE_SUPPRESS; + break; + case THRESHOLD_TYPE_RATE: + if (strlen(rule_extend) > 0) { + ret = pcre_exec(regex_rate, regex_rate_study, + rule_extend, strlen(rule_extend), + 0, 0, ov, MAX_SUBSTRINGS); + if (ret < 5) { + SCLogError(SC_ERR_PCRE_MATCH, + "pcre_exec parse error, ret %" PRId32 ", string %s", + ret, rule_extend); + goto error; } - m = SigMatchGetLastSMFromLists(sig, 2, - DETECT_DETECTION_FILTER, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]); - - if (m != NULL) { - SCLogWarning(SC_ERR_EVENT_ENGINE, "signature sid:%"PRIu32 " has " - "a detection_filter set. The signature event var is " - "given precedence over the threshold.conf one. " - "Bug #425.", sig->id); - goto end; + ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 1, &th_track); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; } - /* replace threshold on sig if we have a global override for it */ - } else if (parsed_type == TYPE_THRESHOLD || parsed_type == TYPE_BOTH || parsed_type == TYPE_LIMIT) { - m = SigMatchGetLastSMFromLists(sig, 2, - DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]); - if (m == NULL) { - m = SigMatchGetLastSMFromLists(sig, 2, - DETECT_DETECTION_FILTER, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]); + ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 2, &th_count); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; } - if (m != NULL) { - SigMatchRemoveSMFromList(sig, m, DETECT_SM_LIST_THRESHOLD); - SigMatchFree(m); - m = NULL; + + ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 3, &th_seconds); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; } - } - de = SCMalloc(sizeof(DetectThresholdData)); - if (unlikely(de == NULL)) - goto error; + ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 4, &th_new_action); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - memset(de,0,sizeof(DetectThresholdData)); + ret = pcre_get_substring((char *)rule_extend, ov, MAX_SUBSTRINGS, 5, &th_timeout); + if (ret < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } - de->type = parsed_type; - de->track = parsed_track; - de->count = parsed_count; - de->seconds = parsed_seconds; - de->new_action = parsed_new_action; - de->timeout = parsed_timeout; - de->addr = NULL; + /* TODO: implement option "apply_to" */ - if ((parsed_type == TYPE_SUPPRESS) && (parsed_track != TRACK_RULE)) { - de->addr = DetectAddressInit(); - if (de->addr == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Can't init DetectAddress"); + if (ByteExtractStringUint32(&parsed_timeout, 10, strlen(th_timeout), th_timeout) <= 0) { goto error; } - if (DetectAddressParseString(de->addr, (char *)th_ip) < 0) { - SCLogError(SC_ERR_INVALID_IP_NETBLOCK, "Can't add %s to address group", th_ip); - goto error; + + /* Get the new action to take */ + if (strcasecmp(th_new_action, "alert") == 0) + parsed_new_action = TH_ACTION_ALERT; + if (strcasecmp(th_new_action, "drop") == 0) + parsed_new_action = TH_ACTION_DROP; + if (strcasecmp(th_new_action, "pass") == 0) + parsed_new_action = TH_ACTION_PASS; + if (strcasecmp(th_new_action, "reject") == 0) + parsed_new_action = TH_ACTION_REJECT; + if (strcasecmp(th_new_action, "log") == 0) { + SCLogInfo("log action for rate_filter not supported yet"); + parsed_new_action = TH_ACTION_LOG; + } + if (strcasecmp(th_new_action, "sdrop") == 0) { + SCLogInfo("sdrop action for rate_filter not supported yet"); + parsed_new_action = TH_ACTION_SDROP; } + parsed_type = TYPE_RATE; + } else { + SCLogError(SC_ERR_INVALID_ARGUMENTS, "rule invalid: %s", rawstr); + goto error; } + break; + default: + SCLogError(SC_ERR_PCRE_MATCH, "unable to find rule type for string %s", rawstr); + goto error; + } - sm = SigMatchAlloc(); - if (sm == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Error allocating SigMatch"); + switch (rule_type) { + /* This part is common to threshold/event_filter/rate_filter */ + case THRESHOLD_TYPE_EVENT_FILTER: + case THRESHOLD_TYPE_THRESHOLD: + case THRESHOLD_TYPE_RATE: + if (strcasecmp(th_track,"by_dst") == 0) + parsed_track = TRACK_DST; + else if (strcasecmp(th_track,"by_src") == 0) + parsed_track = TRACK_SRC; + else if (strcasecmp(th_track,"by_rule") == 0) + parsed_track = TRACK_RULE; + else { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rawstr); goto error; } - if (parsed_type == TYPE_RATE) - sm->type = DETECT_DETECTION_FILTER; - else - sm->type = DETECT_THRESHOLD; - sm->ctx = (void *)de; + if (ByteExtractStringUint32(&parsed_count, 10, strlen(th_count), th_count) <= 0) { + goto error; + } + if (parsed_count == 0) { + SCLogError(SC_ERR_INVALID_VALUE, "rate filter count should be > 0"); + goto error; + } - if (parsed_track == TRACK_RULE) { - de_ctx->ths_ctx.th_entry = SCRealloc(de_ctx->ths_ctx.th_entry, (de_ctx->ths_ctx.th_size + 1) * sizeof(DetectThresholdEntry *)); - if (de_ctx->ths_ctx.th_entry == NULL) { - SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for threshold config" - " (tried to allocate %"PRIu32"th_entrys for rule tracking with rate_filter)", de_ctx->ths_ctx.th_size + 1); - } else { - de_ctx->ths_ctx.th_entry[de_ctx->ths_ctx.th_size] = NULL; - de_ctx->ths_ctx.th_size++; + if (ByteExtractStringUint32(&parsed_seconds, 10, strlen(th_seconds), th_seconds) <= 0) { + goto error; + } + + break; + case THRESHOLD_TYPE_SUPPRESS: + /* need to get IP if extension is provided */ + if (th_track != NULL) { + if (strcasecmp(th_track,"by_dst") == 0) + parsed_track = TRACK_DST; + else if (strcasecmp(th_track,"by_src") == 0) + parsed_track = TRACK_SRC; + else { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid track parameter %s in %s", th_track, rule_extend); + goto error; } } + break; + } - SigMatchAppendSMToList(sig, sm, DETECT_SM_LIST_THRESHOLD); - } + if (ByteExtractStringUint32(&id, 10, strlen(th_sid), th_sid) <= 0) { + goto error; + } + + if (ByteExtractStringUint32(&gid, 10, strlen(th_gid), th_gid) <= 0) { + goto error; + } + + int r = 0; + if (parsed_type == TYPE_SUPPRESS) { + r = SetupSuppressRule(de_ctx, id, gid, parsed_type, parsed_track, + parsed_count, parsed_seconds, parsed_timeout, parsed_new_action, + th_ip); + } else { + r = SetupThresholdRule(de_ctx, id, gid, parsed_type, parsed_track, + parsed_count, parsed_seconds, parsed_timeout, parsed_new_action, + th_ip); + } + if (r < 0) { + goto error; } -end: fret = 0; error: - if (fret == -1) { - if (de != NULL) { - if (de->addr != NULL) DetectAddressFree(de->addr); - SCFree(de); - } - } - if(th_rule_type != NULL) SCFree((char *)th_rule_type); - if(th_sid != NULL) SCFree((char *)th_sid); - if(th_gid != NULL) SCFree((char *)th_gid); - if(th_track != NULL) SCFree((char *)th_track); - if(th_count != NULL) SCFree((char *)th_count); - if(th_seconds != NULL) SCFree((char *)th_seconds); - if(th_type != NULL) SCFree((char *)th_type); - if(th_ip != NULL) SCFree((char *)th_ip); - if(rule_extend != NULL) SCFree((char *)rule_extend); + if (th_rule_type != NULL) + SCFree((char *)th_rule_type); + if (th_sid != NULL) + SCFree((char *)th_sid); + if (th_gid != NULL) + SCFree((char *)th_gid); + if (th_track != NULL) + SCFree((char *)th_track); + if (th_count != NULL) + SCFree((char *)th_count); + if (th_seconds != NULL) + SCFree((char *)th_seconds); + if (th_type != NULL) + SCFree((char *)th_type); + if (th_ip != NULL) + SCFree((char *)th_ip); + if (rule_extend != NULL) + SCFree((char *)rule_extend); return fret; } @@ -2092,9 +2253,9 @@ int SCThresholdConfTest13(void) SCThresholdConfInitContext(de_ctx,fd); m = SigMatchGetLastSMFromLists(sig, 2, - DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_THRESHOLD]); + DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]); - if(m != NULL) { + if (m != NULL) { de = (DetectThresholdData *)m->ctx; if(de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) result = 1; @@ -2429,11 +2590,11 @@ static int SCThresholdConfTest18(void) SCThresholdConfInitContext(de_ctx,fd); SigGroupBuild(de_ctx); - if (s->sm_lists[DETECT_SM_LIST_THRESHOLD] == NULL) { + if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] == NULL) { printf("no thresholds: "); goto end; } - sm = s->sm_lists[DETECT_SM_LIST_THRESHOLD]; + sm = s->sm_lists[DETECT_SM_LIST_SUPPRESS]; if (sm == NULL) { printf("no sm: "); goto end; @@ -2504,11 +2665,11 @@ static int SCThresholdConfTest19(void) SCThresholdConfInitContext(de_ctx,fd); SigGroupBuild(de_ctx); - if (s->sm_lists[DETECT_SM_LIST_THRESHOLD] == NULL) { + if (s->sm_lists[DETECT_SM_LIST_SUPPRESS] == NULL) { printf("no thresholds: "); goto end; } - sm = s->sm_lists[DETECT_SM_LIST_THRESHOLD]; + sm = s->sm_lists[DETECT_SM_LIST_SUPPRESS]; if (sm == NULL) { printf("no sm: "); goto end; @@ -2531,6 +2692,145 @@ end: return result; } +/** + * \brief Creates a dummy threshold file, with all valid options, for testing purposes. + * + * \retval fd Pointer to file descriptor. + */ +FILE *SCThresholdConfGenerateValidDummyFD20() +{ + FILE *fd = NULL; + const char *buffer = + "suppress gen_id 1, sig_id 1000, track by_src, ip 2.2.3.4\n" + "suppress gen_id 1, sig_id 1000, track by_src, ip 1.2.3.4\n" + "suppress gen_id 1, sig_id 1000, track by_src, ip 192.168.1.1\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Threshold Config test code"); + + return fd; +} + +/** + * \test Check if the threshold file is loaded and well parsed + * + * \retval 1 on succces + * \retval 0 on failure + */ +static int SCThresholdConfTest20(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + HostInitConfig(HOST_QUIET); + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; sid:1000;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD20(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSMFromLists(sig, 2, + DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]); + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + m = m->next; + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + m = m->next; + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + result = 1; + } + } + } + } + } + } +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + HostShutdown(); + return result; +} + +/** + * \test Check if the threshold file is loaded and well parsed, and applied + * correctly to a rule with thresholding + * + * \retval 1 on succces + * \retval 0 on failure + */ +static int SCThresholdConfTest21(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + DetectThresholdData *de = NULL; + Signature *sig = NULL; + SigMatch *m = NULL; + int result = 0; + FILE *fd = NULL; + + HostInitConfig(HOST_QUIET); + + if (de_ctx == NULL) + return result; + + de_ctx->flags |= DE_QUIET; + + sig = de_ctx->sig_list = SigInit(de_ctx,"alert tcp any any -> any any (msg:\"Threshold limit\"; content:\"abc\"; threshold: type limit, track by_dst, count 5, seconds 60; sid:1000;)"); + if (sig == NULL) { + goto end; + } + + fd = SCThresholdConfGenerateValidDummyFD20(); + SCThresholdConfInitContext(de_ctx,fd); + + m = SigMatchGetLastSMFromLists(sig, 2, + DETECT_THRESHOLD, sig->sm_lists[DETECT_SM_LIST_SUPPRESS]); + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + m = m->next; + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + m = m->next; + if (m != NULL) { + de = (DetectThresholdData *)m->ctx; + if (de != NULL && (de->type == TYPE_SUPPRESS && de->track == TRACK_SRC)) { + result = 1; + } + } + } + } + } + } +end: + SigGroupBuild(de_ctx); + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + HostShutdown(); + return result; +} + #endif /* UNITTESTS */ /** @@ -2559,6 +2859,8 @@ void SCThresholdConfRegisterTests(void) UtRegisterTest("SCThresholdConfTest18 - suppress parsing", SCThresholdConfTest18, 1); UtRegisterTest("SCThresholdConfTest19 - suppress parsing", SCThresholdConfTest19, 1); + UtRegisterTest("SCThresholdConfTest20 - suppress parsing", SCThresholdConfTest20, 1); + UtRegisterTest("SCThresholdConfTest21 - suppress parsing", SCThresholdConfTest21, 1); #endif /* UNITTESTS */ }