From eed0ef6e6980a70a5de1a9dbaa2f290f67746e14 Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Fri, 18 Jun 2010 23:22:37 +0200 Subject: [PATCH] Adding tag keyword support --- src/Makefile.am | 1 + src/decode.h | 1 + src/detect-engine-alert.c | 98 ++++ src/detect-engine-alert.h | 1 + src/detect-engine-tag.c | 755 ++++++++++++++++++++++++++++++ src/detect-engine-tag.h | 58 +++ src/detect-engine-threshold.c | 34 -- src/detect-engine.c | 1 + src/detect-parse.c | 27 ++ src/detect-parse.h | 1 + src/detect-tag.c | 859 +++++++++++++++++++++++++++++++++- src/detect-tag.h | 71 +++ src/detect-tls-version.c | 8 +- src/detect-uricontent.c | 6 +- src/detect.c | 1 + src/detect.h | 12 + src/flow-util.h | 3 + src/flow.h | 4 + src/suricata.c | 6 + src/util-unittest-helper.c | 4 + 20 files changed, 1905 insertions(+), 46 deletions(-) create mode 100644 src/detect-engine-tag.c create mode 100644 src/detect-engine-tag.h diff --git a/src/Makefile.am b/src/Makefile.am index 042ae0597e..846bedc2a9 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -50,6 +50,7 @@ detect-window.c detect-window.h \ detect-ftpbounce.c detect-ftpbounce.h \ detect-engine-address.c detect-engine-address.h \ detect-engine-threshold.c detect-engine-threshold.h \ +detect-engine-tag.c detect-engine-tag.h \ detect-engine-address-ipv4.c detect-engine-address-ipv4.h \ detect-engine-address-ipv6.c detect-engine-address-ipv6.h \ detect-engine-proto.c detect-engine-proto.h \ diff --git a/src/decode.h b/src/decode.h index be22220655..c366b2262f 100644 --- a/src/decode.h +++ b/src/decode.h @@ -671,6 +671,7 @@ void AddressDebugPrint(Address *); #define PKT_NOPACKET_INSPECTION 0x01 /**< Flag to indicate that packet header or contents should not be inspected*/ #define PKT_NOPAYLOAD_INSPECTION 0x02 /**< Flag to indicate that packet contents should not be inspected*/ #define PKT_ALLOC 0x04 /**< Packet was alloc'd this run, needs to be freed */ +#define PKT_HAS_TAG 0x08 /**< Packet has matched a tag */ #endif /* __DECODE_H__ */ diff --git a/src/detect-engine-alert.c b/src/detect-engine-alert.c index c49c9440b8..26faf1254f 100644 --- a/src/detect-engine-alert.c +++ b/src/detect-engine-alert.c @@ -20,12 +20,47 @@ #include "detect.h" #include "detect-engine-alert.h" #include "detect-engine-threshold.h" +#include "detect-engine-tag.h" #include "decode.h" #include "flow.h" #include "flow-private.h" +/** + * \brief Handle a packet and check if needs a threshold logic + * + * \param de_ctx Detection Context + * \param sig Signature pointer + * \param p Packet structure + * + */ +int PacketAlertHandle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + Signature *s, Packet *p, uint16_t pos) +{ + SCEnter(); + int ret = 0; + + /* retrieve the sig match data */ + DetectThresholdData *td = SigGetThresholdType(s,p); + + SCLogDebug("td %p", td); + + /* if have none just alert, otherwise handle thresholding */ + if (td == NULL) { + /* Already inserted so get out */ + ret = 1; + } else { + ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s); + if (ret == 0) { + /* It doesn't match threshold, remove it */ + PacketAlertRemove(p, pos); + } + } + + SCReturnInt(ret); +} + /** * \brief Check if a certain sid alerted, this is used in the test functions @@ -132,6 +167,54 @@ int PacketAlertAppend(DetectEngineThreadCtx *det_ctx, Signature *s, Packet *p) return 0; } +int PacketAlertAppendTag(DetectEngineThreadCtx *det_ctx, Packet *p) +{ + int i = 0; + + if (p->alerts.cnt == PACKET_ALERT_MAX) + return 0; + + /* It should be usually the last, so check it before iterating */ + if (p->alerts.cnt == 0) { + p->alerts.alerts[p->alerts.cnt].sid = TAG_SIG_ID; + p->alerts.alerts[p->alerts.cnt].gid = TAG_SIG_GEN; + + p->alerts.alerts[p->alerts.cnt].num = TAG_SIG_ID; + p->alerts.alerts[p->alerts.cnt].order_id = 1000; + p->alerts.alerts[p->alerts.cnt].action = ACTION_ALERT; + p->alerts.alerts[p->alerts.cnt].rev = 1; + p->alerts.alerts[p->alerts.cnt].prio = 2; + p->alerts.alerts[p->alerts.cnt].msg = NULL; + p->alerts.alerts[p->alerts.cnt].class = 0; + p->alerts.alerts[p->alerts.cnt].class_msg = NULL; + p->alerts.alerts[p->alerts.cnt].references = NULL; + } else { + /* We need to make room for this s->num + (a bit ugly with mamcpy but we are planning changes here)*/ + for (i = p->alerts.cnt - 1; i >= 0; i--) { + memcpy(&p->alerts.alerts[i + 1], &p->alerts.alerts[i], sizeof(PacketAlert)); + } + + i++; /* The right place to store the alert */ + + p->alerts.alerts[p->alerts.cnt].sid = TAG_SIG_ID; + p->alerts.alerts[p->alerts.cnt].gid = TAG_SIG_GEN; + + p->alerts.alerts[p->alerts.cnt].num = TAG_SIG_ID; + p->alerts.alerts[p->alerts.cnt].order_id = 1000; + p->alerts.alerts[p->alerts.cnt].action = ACTION_ALERT; + p->alerts.alerts[p->alerts.cnt].rev = 1; + p->alerts.alerts[p->alerts.cnt].prio = 2; + p->alerts.alerts[p->alerts.cnt].msg = NULL; + p->alerts.alerts[p->alerts.cnt].class = 0; + p->alerts.alerts[p->alerts.cnt].class_msg = NULL; + p->alerts.alerts[p->alerts.cnt].references = NULL; + } + p->alerts.cnt++; + + return 0; +} + /** * \brief Check the threshold of the sigs that match, set actions, break on pass action * This function iterate the packet alerts array, removing those that didn't match @@ -146,6 +229,7 @@ void PacketAlertFinalize(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx int i = 0; Signature *s = NULL; + SigMatch *sm = NULL; for (i = 0; i < p->alerts.cnt; i++) { SCLogDebug("Sig->num: %"PRIu16, p->alerts.alerts[i].num); @@ -156,6 +240,15 @@ void PacketAlertFinalize(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx if (res == 0) { i--; } else { + /* Now, if we have an alert, we have to check if we want + * to tag this session or src/dst host */ + sm = s->tmatch; + while (sm) { + /* tags are set only for alerts */ + sigmatch_table[sm->type].Match(NULL, det_ctx, p, s, sm); + sm = sm->next; + } + if (s->flags & SIG_FLAG_IPONLY) { if ((p->flowflags & FLOW_PKT_TOSERVER && !(p->flowflags & FLOW_PKT_TOSERVER_IPONLY_SET)) || (p->flowflags & FLOW_PKT_TOCLIENT && !(p->flowflags & FLOW_PKT_TOCLIENT_IPONLY_SET))) { @@ -194,5 +287,10 @@ void PacketAlertFinalize(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx * have compacted the array and decreased cnt by one, so * process again the same position (with different alert now) */ } + + /* At this point, we should have all the new alerts. Now check the tag + * keyword context for sessions and hosts */ + TagHandlePacket(de_ctx, det_ctx, p); } + diff --git a/src/detect-engine-alert.h b/src/detect-engine-alert.h index fb8ebeffc4..7627b5d8de 100644 --- a/src/detect-engine-alert.h +++ b/src/detect-engine-alert.h @@ -29,6 +29,7 @@ void PacketAlertFinalize(DetectEngineCtx *, DetectEngineThreadCtx *, Packet *); int PacketAlertAppend(DetectEngineThreadCtx *, Signature *, Packet *); +int PacketAlertAppendTag(DetectEngineThreadCtx *, Packet *); int PacketAlertCheck(Packet *, uint32_t); int PacketAlertRemove(Packet *, uint16_t); diff --git a/src/detect-engine-tag.c b/src/detect-engine-tag.c new file mode 100644 index 0000000000..b8f794bb59 --- /dev/null +++ b/src/detect-engine-tag.c @@ -0,0 +1,755 @@ +/* Copyright (C) 2007-2010 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file detect-engine-tag.c + * + * \author Pablo Rincon Crespo + * + * Implements a global context to store data related to hosts flagged + * tag keyword + */ + +#include "suricata-common.h" +#include "util-hash.h" +#include "util-time.h" +#include "util-hashlist.h" +#include "detect-engine-tag.h" +#include "detect-tag.h" + +static void TagTimeoutRemove(DetectTagHostCtx *tag_ctx, struct timeval *tv); + +/* Global Ctx for tagging hosts */ +DetectTagHostCtx *tag_ctx = NULL; + +void TagFreeFunc(void *data) +{ + DetectTagDataListFree(data); + return; +} + +/** + * \brief Compare elements into the hash table + * + * \param data1 First element to compare + * \param len1 length of first element + * \param data2 Second element to compare + * \param len2 length of second element + * + * \retval 1 Match or 0 No Match + */ +char TagCompareFunc(void *data1, uint16_t len1, void *data2,uint16_t len2) +{ + SCEnter(); + + DetectTagDataEntryList *a = (DetectTagDataEntryList *)data1; + DetectTagDataEntryList *b = (DetectTagDataEntryList *)data2; + + if (CMP_ADDR(&a->addr,&b->addr)) { + SCReturnInt(1); + } + + SCReturnInt(0); +} + +/** + * \brief Create the hash for tag tables + * + * \param ht Hash Table + * \param data DataEntry that will be used to create the hash + * \param datalen DataEntry length + * + * \retval hash the hash + */ +uint32_t TagHashFunc(HashListTable *ht, void *data, uint16_t datalen) +{ + SCEnter(); + + if (data == NULL) return 0; + DetectTagDataEntryList *dt = (DetectTagDataEntryList *)data; + uint32_t hash = 0; + + if (dt->ipv == 4) + hash = (dt->addr.addr_data32[0]); + else if (dt->ipv == 6) + hash = (dt->addr.addr_data32[0] + + dt->addr.addr_data32[1] + + dt->addr.addr_data32[2] + + dt->addr.addr_data32[3]); + else { + SCLogDebug("no dt->ipv"); + } + + SCReturnInt(hash % TAG_HASH_SIZE); +} + +void TagInitCtx(void) { + tag_ctx = SCMalloc(sizeof(DetectTagHostCtx)); + if (tag_ctx == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for the tagging context"); + exit(EXIT_FAILURE); + } + + TimeGet(&tag_ctx->last_ts); + + if (SCMutexInit(&tag_ctx->lock, NULL) != 0) { + SCLogError(SC_ERR_MEM_ALLOC, + "Tag: Failed to initialize hash table mutex."); + exit(EXIT_FAILURE); + } + + TagHashInit(tag_ctx); +} + +/** \brief Reset the tagging engine context + */ +void TagRestartCtx() { + TagDestroyCtx(); + TagInitCtx(); +} + +/** + * \brief Init tag context hash tables + * + * \param det_ctx Dectection Thread Context + * + */ +void TagHashInit(DetectTagHostCtx *tag_ctx) +{ + if (tag_ctx->tag_hash_table_ipv4 == NULL || + tag_ctx->tag_hash_table_ipv6 == NULL) { + + tag_ctx->tag_hash_table_ipv4 = HashListTableInit(TAG_HASH_SIZE, TagHashFunc, TagCompareFunc, TagFreeFunc); + if(tag_ctx->tag_hash_table_ipv4 == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, + "Tag: Failed to initialize ipv4 dst hash table."); + exit(EXIT_FAILURE); + } + + tag_ctx->tag_hash_table_ipv6 = HashListTableInit(TAG_HASH_SIZE, TagHashFunc, TagCompareFunc, TagFreeFunc); + if(tag_ctx->tag_hash_table_ipv6 == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, + "Tag: Failed to initialize ipv4 src hash table."); + exit(EXIT_FAILURE); + } + } +} + +/** + * \brief Search for a tag data into tag hash table + * + * \param de_ctx Dectection Context + * \param dtde Tag element + * \param p Packet structure + * + * \retval lookup_tde Return the tag element + */ +DetectTagDataEntryList *TagHashSearch(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, + Packet *p) +{ + SCEnter(); + + DetectTagDataEntryList *lookup_tde = NULL; + + if (PKT_IS_IPV4(p)) { + SCLogDebug("ipv4 search"); + lookup_tde = HashListTableLookup(tag_ctx->tag_hash_table_ipv4, dtde, sizeof(DetectTagDataEntryList)); + } else if (PKT_IS_IPV6(p)) { + SCLogDebug("ipv6 search"); + lookup_tde = HashListTableLookup(tag_ctx->tag_hash_table_ipv6, dtde, sizeof(DetectTagDataEntryList)); + } + + SCReturnPtr(lookup_tde, "DetectTagDataEntryList"); +} + +/** + * \brief Add tag element into hash table + * + * \param de_ctx Dectection Context + * \param dtde Tag element + * \param p Packet structure + * + */ +void TagHashAdd(DetectTagHostCtx *tag_ctx, DetectTagDataEntryList *dtde, Packet *p) +{ + SCEnter(); + + int ret = 0; + + if (PKT_IS_IPV4(p)) { + dtde->ipv = 4; + ret = HashListTableAdd(tag_ctx->tag_hash_table_ipv4, + dtde, sizeof(DetectTagDataEntry)); + } else if (PKT_IS_IPV6(p)) { + dtde->ipv = 6; + ret = HashListTableAdd(tag_ctx->tag_hash_table_ipv6, + dtde, sizeof(DetectTagDataEntry)); + } + + SCReturn; +} + +/** + * \brief Add a tag entry for a host. If it already exist, update it. + * + * \param tag_ctx Tag context for hosts + * \param tde Tag data + * \param p packet + * + * \retval 0 if it was added, 1 if it was updated + */ +int TagHashAddTag(DetectTagHostCtx *tag_ctx, DetectTagDataEntry *tde, Packet *p) +{ + uint8_t updated = 0; + uint16_t num_tags = 0; + + /* local, just for searching */ + DetectTagDataEntryList tdl; + + tdl.header_entry = NULL; + tdl.header_entry = tde; + + SCEnter(); + SCMutexLock(&tag_ctx->lock); + + /* first search if we already have an entry of this host */ + DetectTagDataEntryList *entry = NULL; + if (PKT_IS_IPV4(p)) { + tdl.ipv = 4; + if (tde->td->direction == DETECT_TAG_DIR_SRC) { + SET_IPV4_SRC_ADDR(p, &tdl.addr); + } else if (tde->td->direction == DETECT_TAG_DIR_DST) { + SET_IPV4_DST_ADDR(p, &tdl.addr); + } + } else if (PKT_IS_IPV6(p)) { + tdl.ipv = 6; + if (tde->td->direction == DETECT_TAG_DIR_SRC) { + SET_IPV6_SRC_ADDR(p, &tdl.addr); + } else if (tde->td->direction == DETECT_TAG_DIR_DST) { + SET_IPV6_DST_ADDR(p, &tdl.addr); + } + } + + entry = TagHashSearch(tag_ctx, &tdl, p); + if (entry == NULL) { + DetectTagDataEntryList *new = SCMalloc(sizeof(DetectTagDataEntryList)); + if (new == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Failed to allocate a new session"); + } + memcpy(new, &tdl, sizeof(DetectTagDataEntryList)); + TagHashAdd(tag_ctx, new, p); + } else { + /* Append the tag to the list of this host */ + + /* First iterate installed entries searching a duplicated sid/gid */ + DetectTagDataEntry *iter = NULL; + + for (iter = entry->header_entry; iter != NULL; iter = iter->next) { + num_tags++; + if (iter->sid == tde->sid && iter->gid == tde->gid) { + iter->cnt_match++; + /* If so, update data, unless the maximum MATCH limit is + * reached. This prevents possible DOS attacks */ + if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) { + /* Reset time and counters */ + iter->first_ts.tv_sec = iter->last_ts.tv_sec = tde->first_ts.tv_sec; + iter->packets = 0; + iter->bytes = 0; + } + updated = 1; + break; + } + } + + /* If there was no entry of this rule, append the new tde */ + if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) { + tde->next = entry->header_entry; + entry->header_entry = tde; + } else if (num_tags == DETECT_TAG_MAX_TAGS) { + SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags); + } + + } + + SCMutexUnlock(&tag_ctx->lock); + SCReturnInt(updated); +} + +/** + * \brief Destroy tag context hash tables + * + * \param tag_ctx Tag Context + * + */ +void TagDestroyCtx(void) +{ + HashListTableFree(tag_ctx->tag_hash_table_ipv4); + tag_ctx->tag_hash_table_ipv4 = NULL; + + HashListTableFree(tag_ctx->tag_hash_table_ipv6); + tag_ctx->tag_hash_table_ipv6 = NULL; + + SCMutexDestroy(&tag_ctx->lock); + + SCFree(tag_ctx); + tag_ctx = NULL; +} + +/** + * \brief Search tags for src and dst. Update entries of the tag, remove if necessary + * + * \param de_ctx Detect context + * \param det_ctx Detect thread context + * \param p packet + * + */ +void TagHandlePacket(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + Packet *p) { + + DetectTagDataEntry *tde = NULL; + DetectTagDataEntry *prev = NULL; + DetectTagDataEntry *iter = NULL; + DetectTagDataEntryList tdl; + + struct timeval ts; + TimeGet(&ts); + uint8_t flag_added = 0; + + /* Check for timeout tags if we reached the interval for checking it */ + if (ts.tv_sec - tag_ctx->last_ts.tv_sec > TAG_TIMEOUT_CHECK_INTERVAL) { + TagTimeoutRemove(tag_ctx, &ts); + tag_ctx->last_ts.tv_sec = ts.tv_sec; + } + + /* First update and get session tags */ + if (p->flow != NULL) { + SCMutexLock(&p->flow->m); + if (p->flow->tag_list != NULL) { + iter = p->flow->tag_list->header_entry; + prev = NULL; + while (iter != NULL) { + /* update counters */ + iter->last_ts.tv_sec = ts.tv_sec; + iter->packets++; + iter->bytes += p->pktlen; + + /* If this packet triggered the rule with tag, we dont need + * to log it (the alert will log it) */ + if (iter->first_time++ > 0) { + /* Update metrics; remove if tag expired; and set alerts */ + switch (iter->td->metric) { + case DETECT_TAG_METRIC_PACKET: + if (iter->packets > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + SCFree(tde); + continue; + } else { + p->flow->tag_list->header_entry = iter->next; + tde = iter; + iter = iter->next; + SCFree(tde); + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_BYTES: + if (iter->bytes > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + SCFree(tde); + continue; + } else { + p->flow->tag_list->header_entry = iter->next; + tde = iter; + iter = iter->next; + SCFree(tde); + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_SECONDS: + /* last_ts handles this metric, but also a generic time based + * expiration to prevent dead sessions/hosts */ + if (iter->last_ts.tv_sec - iter->first_ts.tv_sec > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + SCFree(tde); + continue; + } else { + p->flow->tag_list->header_entry = iter->next; + tde = iter; + iter = iter->next; + SCFree(tde); + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + } + + } + + prev = iter; + iter = iter->next; + } + } + SCMutexUnlock(&p->flow->m); + } + + /* Then search the src and dst hosts at the ctx */ + SCMutexLock(&tag_ctx->lock); + + DetectTagDataEntryList *tde_src = NULL; + DetectTagDataEntryList *tde_dst = NULL; + + if (PKT_IS_IPV4(p)) { + tdl.ipv = 4; + /* search tags for source */ + SET_IPV4_SRC_ADDR(p, &tdl.addr); + tde_src = TagHashSearch(tag_ctx, &tdl, p); + + /* search tags for dest */ + SET_IPV4_DST_ADDR(p, &tdl.addr); + tde_dst = TagHashSearch(tag_ctx, &tdl, p); + } else if (PKT_IS_IPV6(p)) { + tdl.ipv = 6; + /* search tags for source */ + SET_IPV6_SRC_ADDR(p, &tdl.addr); + tde_src = TagHashSearch(tag_ctx, &tdl, p); + + /* search tags for dest */ + SET_IPV6_DST_ADDR(p, &tdl.addr); + tde_dst = TagHashSearch(tag_ctx, &tdl, p); + } + + if (tde_src != NULL) + iter = tde_src->header_entry; + prev = NULL; + while (iter != NULL) { + /* update counters */ + iter->last_ts.tv_sec = ts.tv_sec; + iter->packets++; + iter->bytes += p->pktlen; + + /* If this packet triggered the rule with tag, we dont need + * to log it (the alert will log it) */ + if (iter->first_time++ > 0 && iter->td != NULL) { + /* Update metrics; remove if tag expired; and set alerts */ + switch (iter->td->metric) { + case DETECT_TAG_METRIC_PACKET: + if (iter->packets > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_src->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_BYTES: + if (iter->bytes > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_src->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_SECONDS: + /* last_ts handles this metric, but also a generic time based + * expiration to prevent dead sessions/hosts */ + if (iter->last_ts.tv_sec - iter->first_ts.tv_sec > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_src->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + } + + } + prev = iter; + iter = iter->next; + } + + if (tde_dst != NULL) + iter = tde_dst->header_entry; + prev = NULL; + while (iter != NULL) { + /* update counters */ + iter->last_ts.tv_sec = ts.tv_sec; + iter->packets++; + iter->bytes += p->pktlen; + + /* If this packet triggered the rule with tag, we dont need + * to log it (the alert will log it) */ + if (iter->first_time++ > 0 && iter->td != NULL) { + /* Update metrics; remove if tag expired; and set alerts */ + switch (iter->td->metric) { + case DETECT_TAG_METRIC_PACKET: + if (iter->packets > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_dst->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_BYTES: + if (iter->bytes > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_dst->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + case DETECT_TAG_METRIC_SECONDS: + /* last_ts handles this metric, but also a generic time based + * expiration to prevent dead sessions/hosts */ + if (iter->last_ts.tv_sec - iter->first_ts.tv_sec > iter->td->count) { + /* tag expired */ + if (prev != NULL) { + tde = iter; + prev->next = iter->next; + iter = iter->next; + DetectTagDataEntryFree(tde); + continue; + } else { + tde = iter; + iter = iter->next; + SCFree(tde); + tde_dst->header_entry = NULL; + continue; + } + } else if (flag_added == 0) { + /* It's matching the tag. Add it to be logged and + * update "flag_added" to add the packet once. */ + PacketAlertAppendTag(det_ctx, p); + p->flags |= PKT_HAS_TAG; + flag_added++; + } + break; + } + } + + prev = iter; + iter = iter->next; + } + + SCMutexUnlock(&tag_ctx->lock); +} + +/** + * \brief Removes the entries exceding the max timeout value + * + * \param tag_ctx Tag context + * \param ts the current time + * + */ +static void TagTimeoutRemove(DetectTagHostCtx *tag_ctx, struct timeval *tv) +{ + HashListTableBucket *next = NULL; + HashListTableBucket *buck = NULL; + + DetectTagDataEntry *tde= NULL; + DetectTagDataEntry *tmp = NULL; + DetectTagDataEntry *prev= NULL; + + DetectTagDataEntryList *tdl = NULL; + + buck = HashListTableGetListHead(tag_ctx->tag_hash_table_ipv4); + + while (buck != NULL) { + /* get the next before we free "buck" */ + next = HashListTableGetListNext(buck); + tdl = HashListTableGetListData(buck); + + if (tdl != NULL && tdl->header_entry != NULL) { + tmp = tdl->header_entry; + + prev = NULL; + while (tmp != NULL) { + + if ((tv->tv_sec - tmp->last_ts.tv_sec) <= TAG_MAX_LAST_TIME_SEEN) { + tmp = tmp->next; + continue; + } + + if (prev != NULL) { + prev->next = tmp->next; + + tde = tmp; + tmp = tmp->next; + + SCFree(tde); + } else { + tdl->header_entry = tmp->next; + + tde = tmp; + tmp = tmp->next; + + SCFree(tde); + } + } + } + buck = next; + } + + buck = HashListTableGetListHead(tag_ctx->tag_hash_table_ipv6); + + while (buck != NULL) { + /* get the next before we free "buck" */ + next = HashListTableGetListNext(buck); + tdl = HashListTableGetListData(buck); + + if (tdl != NULL && tdl->header_entry != NULL) { + tmp = tdl->header_entry; + + prev = NULL; + while (tmp != NULL) { + + if ((tv->tv_sec - tmp->last_ts.tv_sec) <= TAG_MAX_LAST_TIME_SEEN) { + tmp = tmp->next; + continue; + } + + if (prev != NULL) { + prev->next = tmp->next; + + tde = tmp; + tmp = tmp->next; + + SCFree(tde); + } else { + tdl->header_entry = tmp->next; + + tde = tmp; + tmp = tmp->next; + + SCFree(tde); + } + } + } + buck = next; + } + + return; +} + diff --git a/src/detect-engine-tag.h b/src/detect-engine-tag.h new file mode 100644 index 0000000000..58044245a8 --- /dev/null +++ b/src/detect-engine-tag.h @@ -0,0 +1,58 @@ +/* Copyright (C) 2007-2010 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file detect-engine-tag.h + * + * \author Pablo Rincon Crespo + * + * Implements a global context to store data related to hosts flagged + * tag keyword + */ + +#ifndef __DETECT_ENGINE_TAG_H__ +#define __DETECT_ENGINE_TAG_H__ + +#include "detect.h" + +#define TAG_HASH_SIZE 0xffff + +/* This limit should be overwriten/predefined at the config file + * to limit the options to prevent possible DOS situations. We should also + * create a limit for bytes and a limit for number of packets */ +#define TAG_MAX_LAST_TIME_SEEN 600 + +#define TAG_TIMEOUT_CHECK_INTERVAL 60 + +/* Used for tagged data (sid and gid of the packets that + * follow the one that triggered the rule with tag option) */ +#define TAG_SIG_GEN 2 +#define TAG_SIG_ID 1 + +void TagHashInit(DetectTagHostCtx *); +int TagHashAddTag(DetectTagHostCtx *, DetectTagDataEntry *, Packet *); +void TagContextDestroy(DetectTagHostCtx *); +void TagHandlePacket(DetectEngineCtx *, DetectEngineThreadCtx *, + Packet *); + +void TagInitCtx(void); +void TagDestroyCtx(void); +void TagRestartCtx(void); + +#endif /* __DETECT_ENGINE_TAG_H__ */ + + diff --git a/src/detect-engine-threshold.c b/src/detect-engine-threshold.c index bce26d8692..9bab130183 100644 --- a/src/detect-engine-threshold.c +++ b/src/detect-engine-threshold.c @@ -52,40 +52,6 @@ #include "util-var-name.h" #include "tm-modules.h" -/** - * \brief Handle a packet and check if needs a threshold logic - * - * \param de_ctx Detection Context - * \param sig Signature pointer - * \param p Packet structure - * - */ -int PacketAlertHandle(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, - Signature *s, Packet *p, uint16_t pos) -{ - SCEnter(); - int ret = 0; - - /* retrieve the sig match data */ - DetectThresholdData *td = SigGetThresholdType(s,p); - - SCLogDebug("td %p", td); - - /* if have none just alert, otherwise handle thresholding */ - if (td == NULL) { - /* Already inserted so get out */ - ret = 1; - } else { - ret = PacketAlertThreshold(de_ctx, det_ctx, td, p, s); - if (ret == 0) { - /* It doesn't match threshold, remove it */ - PacketAlertRemove(p, pos); - } - } - - SCReturnInt(ret); -} - /** * \brief Check if a certain signature has threshold option * diff --git a/src/detect-engine.c b/src/detect-engine.c index c9547276c2..e6a7408434 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -35,6 +35,7 @@ #include "detect-engine-port.h" #include "detect-engine-mpm.h" #include "detect-engine-iponly.h" +#include "detect-engine-tag.h" #include "detect-engine.h" diff --git a/src/detect-parse.c b/src/detect-parse.c index a3c96c7c3c..506df7031c 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -216,6 +216,33 @@ void SigMatchAppendDcePayload(Signature *s, SigMatch *new) { return; } +/** \brief Append a sig match to the signatures tag match list + * This is done on other list because the tag keyword + * should be always the last inspected (never ordered) + * + * \param s signature + * \param new sigmatch to append + */ +void SigMatchAppendTag(Signature *s, SigMatch *new) { + if (s->tmatch == NULL) { + s->tmatch = new; + s->tmatch_tail = new; + new->next = NULL; + new->prev = NULL; + } else { + SigMatch *cur = s->tmatch_tail; + cur->next = new; + new->prev = cur; + new->next = NULL; + s->tmatch_tail = new; + } + + new->idx = s->sm_cnt; + s->sm_cnt++; + + return; +} + /** \brief Append a sig match to the signatures non-payload match list * * \param s signature diff --git a/src/detect-parse.h b/src/detect-parse.h index 98027e5ca1..7644233450 100644 --- a/src/detect-parse.h +++ b/src/detect-parse.h @@ -60,6 +60,7 @@ void SigMatchAppendDcePayload(Signature *, SigMatch *); void SigMatchAppendPacket(Signature *, SigMatch *); void SigMatchAppendUricontent(Signature *, SigMatch *); void SigMatchAppendAppLayer(Signature *, SigMatch *); +void SigMatchAppendTag(Signature *, SigMatch *); #endif /* __DETECT_PARSE_H__ */ diff --git a/src/detect-tag.c b/src/detect-tag.c index aa4df9d15b..03c4093290 100644 --- a/src/detect-tag.c +++ b/src/detect-tag.c @@ -16,30 +16,875 @@ */ /** - * \file + * \file detect-tag.c * + * \author Pablo Rincon * \author Victor Julien * * Implements the tag keyword * - * \todo Actually implement support */ #include "suricata-common.h" #include "detect.h" +#include "detect-parse.h" +#include "detect-tag.h" +#include "detect-engine-tag.h" +#include "detect-engine.h" -static int DetectTagSetup(DetectEngineCtx *, Signature *, char *); +#include "debug.h" +#include "decode.h" +#include "flow.h" +#include "flow-var.h" +#include "stream-tcp-private.h" + +#include "util-time.h" +#include "util-byte.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" +#include "util-debug.h" + +extern DetectTagHostCtx *tag_ctx; + +/* format: tag: , , , [direction]; */ +#define PARSE_REGEX "^\\s*(host|session)\\s*(,\\s*(\\d+)\\s*,\\s*(packets|bytes|seconds)\\s*(,\\s*(src|dst))?\\s*)?$" + +static pcre *parse_regex; +static pcre_extra *parse_regex_study; + +int DetectTagMatch (ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, SigMatch *); +static int DetectTagSetup (DetectEngineCtx *, Signature *, char *); +void DetectTagRegisterTests(void); +void DetectTagDataFree(void *); + +/** + * \brief Registration function for keyword tag + */ void DetectTagRegister (void) { sigmatch_table[DETECT_TAG].name = "tag"; - sigmatch_table[DETECT_TAG].Match = NULL; + sigmatch_table[DETECT_TAG].Match = DetectTagMatch; sigmatch_table[DETECT_TAG].Setup = DetectTagSetup; - sigmatch_table[DETECT_TAG].Free = NULL; - sigmatch_table[DETECT_TAG].RegisterTests = NULL; + sigmatch_table[DETECT_TAG].Free = DetectTagDataFree; + sigmatch_table[DETECT_TAG].RegisterTests = DetectTagRegisterTests; + + const char *eb; + int eo; + int opts = 0; + + parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL); + if(parse_regex == NULL) + { + SCLogError(SC_ERR_PCRE_COMPILE, "pcre compile of \"%s\" failed at offset %" PRId32 ": %s", PARSE_REGEX, eo, eb); + goto error; + } + + parse_regex_study = pcre_study(parse_regex, 0, &eb); + if(eb != NULL) + { + SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb); + goto error; + } + return; + +error: + /* XXX */ + return; +} + +/** + * \brief This function is used to add a tag to a session (type session) + * or update it if it's already installed. The number of times to + * allow an update is limited by DETECT_TAG_MATCH_LIMIT. This way + * repetitive matches to the same rule are limited of setting tags, + * to avoid DOS attacks + * + * \param p pointer to the current packet + * \param tde pointer to the new DetectTagDataEntry + * + * \retval 0 if the tde was added succesfuly + * \retval 1 if an entry of this sid/gid already exist and was updated + */ +int DetectTagFlowAdd(Packet *p, DetectTagDataEntry *tde) { + uint8_t updated = 0; + uint16_t num_tags = 0; + DetectTagDataEntry *iter = NULL; + + SCMutexLock(&p->flow->m); + + if (p->flow->tag_list == NULL) { + p->flow->tag_list = SCMalloc(sizeof(DetectTagDataEntryList)); + memset(p->flow->tag_list, 0, sizeof(DetectTagDataEntryList)); + } else { + iter = p->flow->tag_list->header_entry; + } + + /* First iterate installed entries searching a duplicated sid/gid */ + for (; iter != NULL; iter = iter->next) { + num_tags++; + if (iter->sid == tde->sid && iter->gid == tde->gid) { + iter->cnt_match++; + /* If so, update data, unless the maximum MATCH limit is + * reached. This prevents possible DOS attacks */ + if (iter->cnt_match < DETECT_TAG_MATCH_LIMIT) { + /* Reset time and counters */ + iter->first_ts.tv_sec = iter->last_ts.tv_sec = tde->first_ts.tv_sec; + iter->packets = 0; + iter->bytes = 0; + } + updated = 1; + break; + } + } + + /* If there was no entry of this rule, prepend the new tde */ + if (updated == 0 && num_tags < DETECT_TAG_MAX_TAGS) { + tde->next = p->flow->tag_list->header_entry; + p->flow->tag_list->header_entry = tde; + } else if (num_tags == DETECT_TAG_MAX_TAGS) { + SCLogDebug("Max tags for sessions reached (%"PRIu16")", num_tags); + } + + SCMutexUnlock(&p->flow->m); + + return updated; +} + +/** + * \brief This function is used to setup a tag for session/host + * + * \param t pointer to thread vars + * \param det_ctx pointer to the pattern matcher thread + * \param p pointer to the current packet + * \param m pointer to the sigmatch that we will cast into DetectTagData + * + * \retval 0 no match + * \retval 1 match + */ +int DetectTagMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, SigMatch *m) +{ + DetectTagData *td = (DetectTagData *) m->ctx; + DetectTagDataEntry *tde = NULL; + tde = SCMalloc(sizeof(DetectTagDataEntry)); + if (tde == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Allocation failed for tag entry. The rule will alert, but no session/host will be taged"); + return 1; + } + memset(tde, 0, sizeof(DetectTagDataEntry)); + + tde->sid = s->id; + tde->gid = s->gid; + + tde->td = td; + TimeGet(&tde->first_ts); + tde->last_ts.tv_sec = tde->first_ts.tv_sec; + + switch (td->type) { + case DETECT_TAG_TYPE_HOST: + if (td->direction == DETECT_TAG_DIR_SRC || td->direction == DETECT_TAG_DIR_DST) { + SCLogDebug("Tagging Host with sid %"PRIu32":%"PRIu32"", s->id, s->gid); + if (TagHashAddTag(tag_ctx, tde, p) == 1) + DetectTagDataEntryFree(tde); + + } else { + SCLogError(SC_ERR_INVALID_VALUE, "Error on direction of a tag keyword (not src nor dst)"); + } + break; + case DETECT_TAG_TYPE_SESSION: + if (p->flow != NULL) { + /* If it already exists it will be updated */ + if (DetectTagFlowAdd(p, tde) == 1) + DetectTagDataEntryFree(tde); + } else { + SCLogDebug("No flow to append the session tag"); + } + break; + default: + SCLogError(SC_ERR_INVALID_VALUE, "Error on type of a tag keyword (not session nor host)"); + break; + } + + return 1; +} + +/** + * \brief This function is used to parse tag options passed to tag keyword + * + * \param tagstr Pointer to the user provided tag options + * + * \retval td pointer to DetectTagData on success + * \retval NULL on failure + */ +DetectTagData *DetectTagParse (char *tagstr) +{ + DetectTagData td; +#define MAX_SUBSTRINGS 30 + int ret = 0, res = 0; + int ov[MAX_SUBSTRINGS]; + + ret = pcre_exec(parse_regex, parse_regex_study, tagstr, strlen(tagstr), 0, 0, ov, MAX_SUBSTRINGS); + if (ret < 1) { + SCLogError(SC_ERR_PCRE_MATCH, "parse error, ret %" PRId32 ", string %s", ret, tagstr); + goto error; + } + + const char *str_ptr; + res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 1, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* Type */ + if (strcasecmp("session", str_ptr) == 0) { + td.type = DETECT_TAG_TYPE_SESSION; + } else if (strcasecmp("host", str_ptr) == 0) { + td.type = DETECT_TAG_TYPE_HOST; + } else { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument type. Must be session or host (%s)", tagstr); + goto error; + } + + /* default tag is 256 packets from session or dst host */ + td.count = DETECT_TAG_MAX_PKTS; + td.metric = DETECT_TAG_METRIC_PACKET; + td.direction = DETECT_TAG_DIR_DST; + + if (ret > 4) { + res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 3, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* count */ + if (ByteExtractStringUint32(&td.count, 10, strlen(str_ptr), + str_ptr) <= 0) { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument for count. Must be a value in the range of 0 to %"PRIu32" (%s)", UINT32_MAX, tagstr); + goto error; + } + + res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 4, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* metric */ + if (strcasecmp("packets", str_ptr) == 0) { + td.metric = DETECT_TAG_METRIC_PACKET; + if (DETECT_TAG_MAX_PKTS > 0 && td.count > DETECT_TAG_MAX_PKTS) + td.count = DETECT_TAG_MAX_PKTS; + /* TODO: load DETECT_TAG_MAX_PKTS from config */ + } else if (strcasecmp("seconds", str_ptr) == 0) { + td.metric = DETECT_TAG_METRIC_SECONDS; + } else if (strcasecmp("bytes", str_ptr) == 0) { + td.metric = DETECT_TAG_METRIC_BYTES; + } else { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument metric. Must be one of \"seconds\", \"packets\" or \"bytes\" (%s)", tagstr); + goto error; + } + + /* if specified, overwrite it */ + if (ret == 7) { + res = pcre_get_substring((char *)tagstr, ov, MAX_SUBSTRINGS, 6, &str_ptr); + if (res < 0) { + SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); + goto error; + } + + /* metric */ + if (strcasecmp("src", str_ptr) == 0) { + td.direction = DETECT_TAG_DIR_SRC; + } else if (strcasecmp("dst", str_ptr) == 0) { + td.direction = DETECT_TAG_DIR_DST; + } else { + SCLogError(SC_ERR_INVALID_VALUE, "Invalid argument direction. Must be one of \"src\" or \"dst\" (only valid for tag host type, not sessions) (%s)", tagstr); + goto error; + } + + if (td.type != DETECT_TAG_TYPE_HOST) { + SCLogWarning(SC_ERR_INVALID_VALUE, "Argument direction doesn't make sense for type \"session\" (%s [%"PRIu8"])", tagstr, td.type); + } + } + } + + DetectTagData *real_td = SCMalloc(sizeof(DetectTagData)); + if (real_td == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + goto error; + } + + memcpy(real_td, &td, sizeof(DetectTagData)); + return real_td; + +error: + return NULL; } -static int DetectTagSetup (DetectEngineCtx *de_ctx, Signature *s, char *rawstr) +/** + * \brief this function is used to add the parsed tag data into the current signature + * + * \param de_ctx pointer to the Detection Engine Context + * \param s pointer to the Current Signature + * \param tagstr pointer to the user provided tag options + * + * \retval 0 on Success + * \retval -1 on Failure + */ +int DetectTagSetup (DetectEngineCtx *de_ctx, Signature *s, char *tagstr) { + DetectTagData *td = NULL; + SigMatch *sm = NULL; + + td = DetectTagParse(tagstr); + if (td == NULL) goto error; + + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_TAG; + sm->ctx = (void *)td; + + /* Append it to the list of tags */ + SigMatchAppendTag(s, sm); + return 0; + +error: + if (td != NULL) DetectTagDataFree(td); + if (sm != NULL) SCFree(sm); + return -1; + +} + + +/** + * \brief this function will free all the entries of a list + * DetectTagDataEntry + * + * \param td pointer to DetectTagDataEntryList + */ +void DetectTagDataListFree(void *ptr) { + if (ptr != NULL) { + DetectTagDataEntryList *list = (DetectTagDataEntryList *)ptr; + DetectTagDataEntryFree(list->header_entry); + SCFree(ptr); + } +} +/** + * \brief this function will free memory associated with + * DetectTagDataEntry + * + * \param td pointer to DetectTagDataEntry + */ +void DetectTagDataEntryFree(void *ptr) { + if (ptr != NULL) { + DetectTagDataEntry *dte = (DetectTagDataEntry *)ptr; + if (dte->next != NULL) + DetectTagDataEntryFree(dte->next); + SCFree(ptr); + } +} + +/** + * \brief this function will free memory associated with DetectTagData + * + * \param td pointer to DetectTagData + */ +void DetectTagDataFree(void *ptr) { + DetectTagData *td = (DetectTagData *)ptr; + SCFree(td); +} + +#ifdef UNITTESTS + +/** + * \test DetectTagTestParse01 is a test to make sure that we return "something" + * when given valid tag opt + */ +int DetectTagTestParse01 (void) { + int result = 0; + DetectTagData *td = NULL; + td = DetectTagParse("session, 123, packets"); + if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION + && td->count == 123 + && td->metric == DETECT_TAG_METRIC_PACKET) { + DetectTagDataFree(td); + result = 1; + } + + return result; +} + +/** + * \test DetectTagTestParse02 is a test to check that we parse tag correctly + */ +int DetectTagTestParse02 (void) { + int result = 0; + DetectTagData *td = NULL; + td = DetectTagParse("host, 200, bytes, src"); + if (td != NULL && td->type == DETECT_TAG_TYPE_HOST + && td->count == 200 + && td->metric == DETECT_TAG_METRIC_BYTES + && td->direction == DETECT_TAG_DIR_SRC) { + result = 1; + DetectTagDataFree(td); + } + + return result; +} + +/** + * \test DetectTagTestParse03 is a test for setting the stateless tag opt + */ +int DetectTagTestParse03 (void) { + int result = 0; + DetectTagData *td = NULL; + td = DetectTagParse("host, 200, bytes, dst"); + if (td != NULL && td->type == DETECT_TAG_TYPE_HOST + && td->count == 200 + && td->metric == DETECT_TAG_METRIC_BYTES + && td->direction == DETECT_TAG_DIR_DST) { + result = 1; + DetectTagDataFree(td); + } + + return result; +} + +/** + * \test DetectTagTestParse04 is a test for default opts + */ +int DetectTagTestParse04 (void) { + int result = 0; + DetectTagData *td = NULL; + td = DetectTagParse("session"); + if (td != NULL && td->type == DETECT_TAG_TYPE_SESSION + && td->count == DETECT_TAG_MAX_PKTS + && td->metric == DETECT_TAG_METRIC_PACKET) { + result = 1; + DetectTagDataFree(td); + } + + return result; +} + +/** + * \test DetectTagTestParse05 is a test for default opts + */ +int DetectTagTestParse05 (void) { + int result = 0; + DetectTagData *td = NULL; + td = DetectTagParse("host"); + if (td != NULL && td->type == DETECT_TAG_TYPE_HOST + && td->count == DETECT_TAG_MAX_PKTS + && td->metric == DETECT_TAG_METRIC_PACKET + && td->direction == DETECT_TAG_DIR_DST) { + result = 1; + DetectTagDataFree(td); + } + + return result; +} + + +/** + * \test DetectTagTestPacket01 is a test to check tagged hosts + */ +int DetectTagTestPacket01 (void) { + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint8_t *buf2 = (uint8_t *)"lalala!"; + uint16_t buf_len = strlen((char *)buf); + uint16_t buf_len2 = strlen((char *)buf2); + + Packet *p[7]; + p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.9", + 41424, 80); + p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.11", + 41424, 80); + p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.11", + 41424, 80); + + char *sigs[5]; + sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host, 3, packets, src; sid:4;)"; + sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host, 4, packets, dst; sid:5;)"; + sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)"; + sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:6;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:2;)"; + + /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */ + uint32_t sid[5] = {1,2,3,4,5}; + + int32_t results[7][5] = { + {0, 0, 0, 1, 1}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }; + result = UTHGenericTest(p, 7, sigs, sid, (uint32_t *) results, 5); + + UTHFreePackets(p, 7); + + TagRestartCtx(); + return result; +} + +/** + * \test DetectTagTestPacket02 is a test to check tagged hosts + */ +int DetectTagTestPacket02 (void) { + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint8_t *buf2 = (uint8_t *)"lalala!"; + uint16_t buf_len = strlen((char *)buf); + uint16_t buf_len2 = strlen((char *)buf2); + + DecodeThreadVars dtv; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&th_v, 0, sizeof(th_v)); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + Packet *p[7]; + p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.9", + 41424, 80); + p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.11", + 41424, 80); + p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.11", + 41424, 80); + + char *sigs[5]; + sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host, 3, seconds, src; sid:4;)"; + sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host, 4, seconds, dst; sid:5;)"; + sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)"; + sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:6;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:2;)"; + + /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */ + uint32_t sid[5] = {1,2,3,4,5}; + int numsigs = 5; + + if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0) + goto cleanup; + + //de_ctx->flags |= DE_QUIET; + + int32_t results[7][5] = { + {0, 0, 0, 1, 1}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }; + + int num_packets = 7; + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + int i = 0; + for (; i < num_packets; i++) { + SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]); + if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0) + goto cleanup; + if (i == 3) + TimeSetIncrementTime(10); + } + + result = 1; + +cleanup: + UTHFreePackets(p, 7); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } +end: + TagRestartCtx(); + return result; +} + +/** + * \test DetectTagTestPacket03 is a test to check tagged hosts + */ +int DetectTagTestPacket03 (void) { + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint8_t *buf2 = (uint8_t *)"lalala!"; + uint16_t buf_len = strlen((char *)buf); + uint16_t buf_len2 = strlen((char *)buf2); + + DecodeThreadVars dtv; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&th_v, 0, sizeof(th_v)); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + Packet *p[7]; + p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.9", + 41424, 80); + p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.9", + 41424, 80); + p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.11", + 41424, 80); + p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.11", + 41424, 80); + + char *sigs[5]; + sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:host, 150, bytes, src; sid:4;)"; + sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"Hi all\"; tag:host, 150, bytes, dst; sid:5;)"; + sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)"; + sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:6;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:2;)"; + + /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */ + uint32_t sid[5] = {1,2,3,4,5}; + int numsigs = 5; + + if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0) + goto cleanup; + + //de_ctx->flags |= DE_QUIET; + + int32_t results[7][5] = { + {0, 0, 0, 1, 1}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }; + + int num_packets = 7; + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + uint32_t sum = 0; + int i = 0; + for (; i < num_packets; i++) { + SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]); + sum += p[i]->pktlen; + //printf("Sum %"PRIu32"\n", sum); + if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0) + goto cleanup; + } + + result = 1; + +cleanup: + UTHFreePackets(p, 7); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } +end: + TagRestartCtx(); + return result; +} + +/** + * \test DetectTagTestPacket04 is a test to check tagged hosts + */ +int DetectTagTestPacket04 (void) { + int result = 0; + uint8_t *buf = (uint8_t *)"Hi all!"; + uint8_t *buf2 = (uint8_t *)"lalala!"; + uint16_t buf_len = strlen((char *)buf); + uint16_t buf_len2 = strlen((char *)buf2); + + Flow f; + TcpSession ssn; + + memset(&f, 0, sizeof(f)); + memset(&ssn, 0, sizeof(ssn)); + + f.protoctx = (void *)&ssn; + f.dst.family = AF_INET; + inet_pton(f.src.family, "192.168.1.5", f.src.addr_data32); + inet_pton(f.dst.family, "192.168.1.1", f.dst.addr_data32); + + DecodeThreadVars dtv; + ThreadVars th_v; + DetectEngineThreadCtx *det_ctx = NULL; + memset(&dtv, 0, sizeof(DecodeThreadVars)); + memset(&th_v, 0, sizeof(th_v)); + + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + if (de_ctx == NULL) { + goto end; + } + de_ctx->flags |= DE_QUIET; + + Packet *p[7]; + p[0] = UTHBuildPacketReal(buf, buf_len, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[1] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[2] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[3] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 41424, 80); + p[4] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[5] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.1", "192.168.1.5", + 80, 41424); + p[6] = UTHBuildPacketReal(buf2, buf_len2, IPPROTO_TCP, + "192.168.1.5", "192.168.1.1", + 80, 41424); + + char *sigs[5]; + sigs[0]= "alert tcp any any -> any any (msg:\"Testing tag 1\"; content:\"Hi all\"; tag:session, 5, packets; sid:4;)"; + sigs[1]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"blahblah\"; sid:5;)"; + sigs[2]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:3;)"; + sigs[3]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:6;)"; + sigs[4]= "alert tcp any any -> any any (msg:\"Testing tag 2\"; content:\"no match\"; sid:2;)"; + + /* Please, Notice that tagged data goes with sig_id = 1 and tag sig generator = 2 */ + uint32_t sid[5] = {1,2,3,4,5}; + int numsigs = 5; + + if (UTHAppendSigs(de_ctx, sigs, numsigs) == 0) + goto cleanup; + + //de_ctx->flags |= DE_QUIET; + + int32_t results[7][5] = { + {0, 0, 0, 1, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {1, 0, 0, 0, 0}, + {0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0} + }; + + int num_packets = 7; + SigGroupBuild(de_ctx); + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + + uint32_t sum = 0; + int i = 0; + for (; i < num_packets; i++) { + p[i]->flow = &f; + p[i]->flow->protoctx = &ssn; + SigMatchSignatures(&th_v, de_ctx, det_ctx, p[i]); + sum += p[i]->pktlen; + //printf("Sum %"PRIu32"\n", sum); + if (UTHCheckPacketMatchResults(p[i], sid, (uint32_t *)&results[i][0], numsigs) == 0) + goto cleanup; + } + + result = 1; + +cleanup: + UTHFreePackets(p, 7); + if (det_ctx != NULL) + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + + if (de_ctx != NULL) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } +end: + TagRestartCtx(); + return result; +} + +#endif /* UNITTESTS */ + +/** + * \brief this function registers unit tests for DetectTag + */ +void DetectTagRegisterTests(void) { +#ifdef UNITTESTS + UtRegisterTest("DetectTagTestParse01", DetectTagTestParse01, 1); + UtRegisterTest("DetectTagTestParse02", DetectTagTestParse02, 1); + UtRegisterTest("DetectTagTestParse03", DetectTagTestParse03, 1); + UtRegisterTest("DetectTagTestParse04", DetectTagTestParse04, 1); + UtRegisterTest("DetectTagTestParse05", DetectTagTestParse05, 1); + + UtRegisterTest("DetectTagTestPacket01", DetectTagTestPacket01, 1); + UtRegisterTest("DetectTagTestPacket02", DetectTagTestPacket02, 1); + UtRegisterTest("DetectTagTestPacket03", DetectTagTestPacket03, 1); + UtRegisterTest("DetectTagTestPacket04", DetectTagTestPacket04, 1); +#endif /* UNITTESTS */ } diff --git a/src/detect-tag.h b/src/detect-tag.h index f74accf1ef..8e948f4aa6 100644 --- a/src/detect-tag.h +++ b/src/detect-tag.h @@ -18,14 +18,85 @@ /** * \file * + * \author Pablo Rincon * \author Victor Julien */ #ifndef __DETECT_TAG_H__ #define __DETECT_TAG_H__ +#include "suricata-common.h" +#include "suricata.h" +#include "util-time.h" + +/* Limit the number of times a session can be tagged by the + * same rule without finishing older tags */ +#define DETECT_TAG_MATCH_LIMIT 10 + +/* Limit the number of tags that a session can have */ +#define DETECT_TAG_MAX_TAGS 50 + +/* Limit the number of pkts to capture. Change this to + * zero to make it unlimited + * TODO: load it from config (var tagged_packet_limit) */ +#define DETECT_TAG_MAX_PKTS 256 + +/* Type of tag: session or host */ +enum { + DETECT_TAG_TYPE_SESSION, + DETECT_TAG_TYPE_HOST, + DETECT_TAG_TYPE_MAX +}; + +enum { + DETECT_TAG_DIR_SRC, + DETECT_TAG_DIR_DST, + DETECT_TAG_DIR_MAX +}; + +enum { + DETECT_TAG_METRIC_PACKET, + DETECT_TAG_METRIC_SECONDS, + DETECT_TAG_METRIC_BYTES, + DETECT_TAG_METRIC_MAX +}; + +/** This will be the rule options/parameters */ +typedef struct DetectTagData_ { + uint8_t type; /**< tag type */ + uint32_t count; /**< count */ + uint32_t metric; /**< metric */ + uint8_t direction; /**< host direction */ +} DetectTagData; + +/** This is the installed data at the session/global or host table */ +typedef struct DetectTagDataEntry_ { + DetectTagData *td; /**< Pointer referencing the tag parameters */ + uint32_t sid; /**< sid originating the tag */ + uint32_t gid; /**< gid originating the tag */ + uint32_t packets; /**< number of packets */ + uint32_t bytes; /**< number of bytes */ + struct timeval first_ts; /**< First time seen (for metric = seconds) */ + struct timeval last_ts; /**< Last time seen (to prune old sessions) */ + struct DetectTagDataEntry_ *next; /**< Pointer to the next tag of this + * session/src_host/dst_host (if any from other rule) */ + uint16_t cnt_match; /**< number of times this tag was reset/updated */ + uint8_t first_time; /**< Used at unified output. The first packet write the + header with the data of the sig. The next packets use + gid/sid/rev of the tagging engine */ +} DetectTagDataEntry; + +typedef struct DetectTagDataEntryList_ { + DetectTagDataEntry *header_entry; + Address addr; /**< Var used to store dst or src addr */ + uint8_t ipv; /**< IP Version */ +}DetectTagDataEntryList; + /* prototypes */ void DetectTagRegister (void); +void DetectTagDataFree(void *ptr); +void DetectTagDataEntryFree(void *ptr); +void DetectTagDataListFree(void *ptr); #endif /* __DETECT_TAG_H__ */ diff --git a/src/detect-tls-version.c b/src/detect-tls-version.c index 3c2ddd1445..2b6d525f98 100644 --- a/src/detect-tls-version.c +++ b/src/detect-tls-version.c @@ -659,11 +659,13 @@ static int DetectTlsVersionTestDetect03(void) { result = 1; end: - SigGroupCleanup(de_ctx); - SigCleanSignatures(de_ctx); + if (de_ctx) { + SigGroupCleanup(de_ctx); + SigCleanSignatures(de_ctx); + DetectEngineCtxFree(de_ctx); + } DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); - DetectEngineCtxFree(de_ctx); FlowL7DataPtrFree(&f); StreamTcpFreeConfig(TRUE); diff --git a/src/detect-uricontent.c b/src/detect-uricontent.c index b99c5bce49..ec837c0770 100644 --- a/src/detect-uricontent.c +++ b/src/detect-uricontent.c @@ -1235,6 +1235,7 @@ end: * match */ static int DetectUriSigTest05(void) { + DetectEngineCtx *de_ctx = NULL; int result = 0; Flow f; HtpState *http_state = NULL; @@ -1283,7 +1284,7 @@ static int DetectUriSigTest05(void) { ssn.toserver_smsg_head = stream_msg; ssn.toserver_smsg_tail = stream_msg; - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } @@ -1361,6 +1362,7 @@ end: * match */ static int DetectUriSigTest06(void) { + DetectEngineCtx *de_ctx = NULL; int result = 0; Flow f; HtpState *http_state = NULL; @@ -1409,7 +1411,7 @@ static int DetectUriSigTest06(void) { ssn.toserver_smsg_head = stream_msg; ssn.toserver_smsg_tail = stream_msg; - DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto end; } diff --git a/src/detect.c b/src/detect.c index 7af6048578..6061317e77 100644 --- a/src/detect.c +++ b/src/detect.c @@ -931,6 +931,7 @@ int SigMatchSignatures(ThreadVars *th_v, DetectEngineCtx *de_ctx, DetectEngineTh end: /* so now let's iterate the alerts and remove the ones after a pass rule * matched (if any). This is done inside PacketAlertFinalize() */ + /* PR: installed "tag" keywords are handled after the threshold inspection */ PacketAlertFinalize(de_ctx, det_ctx, p); if (p->alerts.cnt > 0) { SCPerfCounterAddUI64(det_ctx->counter_alerts, det_ctx->tv->sc_perf_pca, (uint64_t)p->alerts.cnt); diff --git a/src/detect.h b/src/detect.h index b0e263bff9..35eb828216 100644 --- a/src/detect.h +++ b/src/detect.h @@ -40,6 +40,7 @@ #include "util-radix-tree.h" #include "detect-threshold.h" +//#include "detect-engine-tag.h" #define COUNTER_DETECT_ALERTS 1 @@ -272,6 +273,8 @@ typedef struct Signature_ { struct SigMatch_ *amatch_tail; /* general app layer matches, tail of the list */ struct SigMatch_ *dmatch; /* dce app layer matches */ struct SigMatch_ *dmatch_tail; /* dce app layer matches, tail of the list */ + struct SigMatch_ *tmatch; /* list of tags matches */ + struct SigMatch_ *tmatch_tail; /* tag matches, tail of the list */ /** ptr to the next sig in the list */ struct Signature_ *next; @@ -381,6 +384,15 @@ typedef struct ThresholdCtx_ { uint32_t th_size; } ThresholdCtx; +/** \brief tag ctx */ +typedef struct DetectTagHostCtx_ { + HashListTable *tag_hash_table_ipv4; /**< Ipv4 hash table */ + HashListTable *tag_hash_table_ipv6; /**< Ipv6 hash table */ + SCMutex lock; /**< Mutex for the ctx */ + struct timeval last_ts; /**< Last time the ctx was pruned */ +} DetectTagHostCtx; + + /** \brief main detection engine ctx */ typedef struct DetectEngineCtx_ { uint8_t flags; diff --git a/src/flow-util.h b/src/flow-util.h index aef5011ad3..f2c16aa75c 100644 --- a/src/flow-util.h +++ b/src/flow-util.h @@ -49,6 +49,7 @@ (f)->aldata = NULL; \ (f)->alflags = 0; \ (f)->alproto = 0; \ + (f)->tag_list = NULL; \ } while (0) #define FLOW_RECYCLE(f) do { \ @@ -78,6 +79,7 @@ (f)->aldata = NULL; \ (f)->alflags = 0; \ (f)->alproto = 0; \ + DetectTagDataListFree((f)->tag_list); \ } while(0) #define FLOW_DESTROY(f) do { \ @@ -94,6 +96,7 @@ (f)->aldata = NULL; \ (f)->alflags = 0; \ (f)->alproto = 0; \ + DetectTagDataListFree((f)->tag_list); \ } while(0) Flow *FlowAlloc(void); diff --git a/src/flow.h b/src/flow.h index 264d1165e8..281fc9bd2e 100644 --- a/src/flow.h +++ b/src/flow.h @@ -27,6 +27,7 @@ #include "decode.h" #include "util-var.h" #include "util-atomic.h" +#include "detect-tag.h" #define FLOW_QUIET TRUE #define FLOW_VERBOSE FALSE @@ -183,6 +184,9 @@ typedef struct Flow_ SCMutex m; + /** List of tags of this flow (from "tag" keyword of type "session") */ + DetectTagDataEntryList *tag_list; + /* list flow ptrs * NOTE!!! These are NOT protected by the * above mutex, but by the FlowQ's */ diff --git a/src/suricata.c b/src/suricata.c index f1460a81c2..5f4af8ea2b 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -52,6 +52,7 @@ #include "detect-engine-payload.h" #include "detect-engine-dcepayload.h" #include "detect-engine-state.h" +#include "detect-engine-tag.h" #include "tm-queuehandlers.h" #include "tm-queues.h" @@ -759,6 +760,8 @@ int main(int argc, char **argv) #endif /* PROFILING */ SCReputationInitCtx(); + TagInitCtx(); + TmModuleReceiveNFQRegister(); TmModuleVerdictNFQRegister(); TmModuleDecodeNFQRegister(); @@ -1168,10 +1171,13 @@ int main(int argc, char **argv) } } #endif + SigCleanSignatures(de_ctx); DetectEngineCtxFree(de_ctx); AlpProtoDestroy(); + TagDestroyCtx(); + RunModeShutDown(); OutputDeregisterAll(); TimeDeinit(); diff --git a/src/util-unittest-helper.c b/src/util-unittest-helper.c index 56e1de50ec..25452ad673 100644 --- a/src/util-unittest-helper.c +++ b/src/util-unittest-helper.c @@ -97,6 +97,8 @@ Packet *UTHBuildPacketIPV6Real(uint8_t *payload, uint16_t payload_len, memset(p->tcph, 0, sizeof(TCPHdr)); p->tcph->th_sport = sport; p->tcph->th_dport = dport; + + p->pktlen = sizeof(IPV6Hdr) + sizeof(TCPHdr) + payload_len; return p; } @@ -159,6 +161,7 @@ Packet *UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len, memset(p->udph, 0, sizeof(UDPHdr)); p->udph->uh_sport = sport; p->udph->uh_dport = dport; + p->pktlen = sizeof(IPV4Hdr) + sizeof(UDPHdr) + payload_len; break; case IPPROTO_TCP: p->tcph = SCMalloc(sizeof(TCPHdr)); @@ -167,6 +170,7 @@ Packet *UTHBuildPacketReal(uint8_t *payload, uint16_t payload_len, memset(p->tcph, 0, sizeof(TCPHdr)); p->tcph->th_sport = sport; p->tcph->th_dport = dport; + p->pktlen = sizeof(IPV4Hdr) + sizeof(TCPHdr) + payload_len; break; case IPPROTO_ICMP: default: