summaryrefslogtreecommitdiffstats
path: root/src/detect-stream_size.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/detect-stream_size.c')
-rw-r--r--src/detect-stream_size.c419
1 files changed, 419 insertions, 0 deletions
diff --git a/src/detect-stream_size.c b/src/detect-stream_size.c
new file mode 100644
index 0000000..50cd15a
--- /dev/null
+++ b/src/detect-stream_size.c
@@ -0,0 +1,419 @@
+/* Copyright (C) 2007-2020 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
+ *
+ * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
+ *
+ * Stream size for the engine.
+ */
+
+#include "suricata-common.h"
+#include "stream-tcp.h"
+#include "util-unittest.h"
+
+#include "detect.h"
+#include "detect-parse.h"
+
+#include "flow.h"
+#include "detect-stream_size.h"
+#include "stream-tcp-private.h"
+#include "detect-engine-prefilter-common.h"
+#include "detect-engine-uint.h"
+#include "util-debug.h"
+#include "util-byte.h"
+
+
+/*prototypes*/
+static int DetectStreamSizeMatch (DetectEngineThreadCtx *, Packet *,
+ const Signature *, const SigMatchCtx *);
+static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, const char *);
+void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *);
+#ifdef UNITTESTS
+static void DetectStreamSizeRegisterTests(void);
+#endif
+static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
+static bool PrefilterStreamSizeIsPrefilterable(const Signature *s);
+
+/**
+ * \brief Registration function for stream_size: keyword
+ */
+
+void DetectStreamSizeRegister(void)
+{
+ sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size";
+ sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream";
+ sigmatch_table[DETECT_STREAM_SIZE].url = "/rules/flow-keywords.html#stream-size";
+ sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch;
+ sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup;
+ sigmatch_table[DETECT_STREAM_SIZE].Free = DetectStreamSizeFree;
+#ifdef UNITTESTS
+ sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests;
+#endif
+ sigmatch_table[DETECT_STREAM_SIZE].SupportsPrefilter = PrefilterStreamSizeIsPrefilterable;
+ sigmatch_table[DETECT_STREAM_SIZE].SetupPrefilter = PrefilterSetupStreamSize;
+}
+
+static int DetectStreamSizeMatchAux(const DetectStreamSizeData *sd, const TcpSession *ssn)
+{
+ int ret = 0;
+ uint32_t csdiff = 0;
+ uint32_t ssdiff = 0;
+
+ if (sd->flags == StreamSizeServer) {
+ /* get the server stream size */
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ ret = DetectU32Match(ssdiff, &sd->du32);
+
+ } else if (sd->flags == StreamSizeClient) {
+ /* get the client stream size */
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+ ret = DetectU32Match(csdiff, &sd->du32);
+
+ } else if (sd->flags == StreamSizeBoth) {
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+
+ if (DetectU32Match(ssdiff, &sd->du32) && DetectU32Match(csdiff, &sd->du32))
+ ret = 1;
+
+ } else if (sd->flags == StreamSizeEither) {
+ ssdiff = ssn->server.next_seq - ssn->server.isn;
+ csdiff = ssn->client.next_seq - ssn->client.isn;
+
+ if (DetectU32Match(ssdiff, &sd->du32) || DetectU32Match(csdiff, &sd->du32))
+ ret = 1;
+ }
+ return ret;
+}
+
+/**
+ * \brief This function is used to match Stream size rule option on a packet with those passed via
+ * stream_size:
+ *
+ * \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 DetectStreamSizeData
+ *
+ * \retval 0 no match
+ * \retval 1 match
+ */
+static int DetectStreamSizeMatch(
+ DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
+{
+
+ const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx;
+
+ if (!(PKT_IS_TCP(p)))
+ return 0;
+ if (p->flow == NULL || p->flow->protoctx == NULL)
+ return 0;
+
+ const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+
+ SCReturnInt(DetectStreamSizeMatchAux(sd, ssn));
+}
+
+/**
+ * \brief this function is used to add the parsed stream size data into the current signature
+ *
+ * \param de_ctx pointer to the Detection Engine Context
+ * \param s pointer to the Current Signature
+ * \param streamstr pointer to the user provided stream size options
+ *
+ * \retval 0 on Success
+ * \retval -1 on Failure
+ */
+static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *streamstr)
+{
+ DetectStreamSizeData *sd = rs_detect_stream_size_parse(streamstr);
+ if (sd == NULL)
+ return -1;
+
+ SigMatch *sm = SigMatchAlloc();
+ if (sm == NULL) {
+ DetectStreamSizeFree(de_ctx, sd);
+ return -1;
+ }
+
+ sm->type = DETECT_STREAM_SIZE;
+ sm->ctx = (SigMatchCtx *)sd;
+
+ SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
+ return 0;
+}
+
+/**
+ * \brief this function will free memory associated with DetectStreamSizeData
+ *
+ * \param ptr pointer to DetectStreamSizeData
+ */
+void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *ptr)
+{
+ rs_detect_stream_size_free(ptr);
+}
+
+/* prefilter code */
+
+static void PrefilterPacketStreamsizeMatch(
+ DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
+{
+ if (!(PKT_IS_TCP(p)) || PKT_IS_PSEUDOPKT(p))
+ return;
+
+ if (p->flow == NULL || p->flow->protoctx == NULL)
+ return;
+
+ /* during setup Suricata will automatically see if there is another
+ * check that can be added: alproto, sport or dport */
+ const PrefilterPacketHeaderCtx *ctx = pectx;
+ if (!PrefilterPacketHeaderExtraMatch(ctx, p))
+ return;
+
+ DetectStreamSizeData dsd;
+ dsd.du32.mode = ctx->v1.u8[0];
+ dsd.flags = ctx->v1.u8[1];
+ dsd.du32.arg1 = ctx->v1.u32[2];
+ const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
+ /* if we match, add all the sigs that use this prefilter. This means
+ * that these will be inspected further */
+ if (DetectStreamSizeMatchAux(&dsd, ssn)) {
+ PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+ }
+}
+
+static void PrefilterPacketStreamSizeSet(PrefilterPacketHeaderValue *v, void *smctx)
+{
+ const DetectStreamSizeData *a = smctx;
+ v->u8[0] = a->du32.mode;
+ v->u8[1] = a->flags;
+ v->u32[2] = a->du32.arg1;
+}
+
+static bool PrefilterPacketStreamSizeCompare(PrefilterPacketHeaderValue v, void *smctx)
+{
+ const DetectStreamSizeData *a = smctx;
+ if (v.u8[0] == a->du32.mode && v.u8[1] == a->flags && v.u32[2] == a->du32.arg1)
+ return true;
+ return false;
+}
+
+static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
+{
+ return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_STREAM_SIZE, PrefilterPacketStreamSizeSet,
+ PrefilterPacketStreamSizeCompare, PrefilterPacketStreamsizeMatch);
+}
+
+static bool PrefilterStreamSizeIsPrefilterable(const Signature *s)
+{
+ const SigMatch *sm;
+ for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
+ switch (sm->type) {
+ case DETECT_STREAM_SIZE:
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef UNITTESTS
+/**
+ * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the
+ * user options correctly, when given valid stream_size options.
+ */
+
+static int DetectStreamSizeParseTest01 (void)
+{
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ sd = rs_detect_stream_size_parse("server,<,6");
+ if (sd != NULL) {
+ if (sd->flags & StreamSizeServer && sd->du32.mode == DETECT_UINT_LT && sd->du32.arg1 == 6)
+ result = 1;
+ DetectStreamSizeFree(NULL, sd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the
+ * invalid stream_size options.
+ */
+
+static int DetectStreamSizeParseTest02 (void)
+{
+ int result = 1;
+ DetectStreamSizeData *sd = NULL;
+ sd = rs_detect_stream_size_parse("invalidoption,<,6");
+ if (sd != NULL) {
+ printf("expected: NULL got 0x%02X %" PRIu32 ": ", sd->flags, sd->du32.arg1);
+ result = 0;
+ DetectStreamSizeFree(NULL, sd);
+ }
+
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest03 is a test to make sure that we match the
+ * packet correctly provided valid stream size.
+ */
+
+static int DetectStreamSizeParseTest03 (void)
+{
+
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+ DetectEngineThreadCtx dtx;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature s;
+ SigMatch sm;
+ TcpStream client;
+ Flow f;
+ TCPHdr tcph;
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
+ memset(&s, 0, sizeof(Signature));
+ memset(&sm, 0, sizeof(SigMatch));
+ memset(&client, 0, sizeof(TcpStream));
+ memset(&f, 0, sizeof(Flow));
+ memset(&tcph, 0, sizeof(TCPHdr));
+
+ sd = rs_detect_stream_size_parse("client,>,8");
+ if (sd != NULL) {
+ if (!(sd->flags & StreamSizeClient)) {
+ printf("sd->flags not STREAM_SIZE_CLIENT: ");
+ DetectStreamSizeFree(NULL, sd);
+ SCFree(p);
+ return 0;
+ }
+
+ if (sd->du32.mode != DETECT_UINT_GT) {
+ printf("sd->mode not DETECTSSIZE_GT: ");
+ DetectStreamSizeFree(NULL, sd);
+ SCFree(p);
+ return 0;
+ }
+
+ if (sd->du32.arg1 != 8) {
+ printf("sd->ssize is %" PRIu32 ", not 8: ", sd->du32.arg1);
+ DetectStreamSizeFree(NULL, sd);
+ SCFree(p);
+ return 0;
+ }
+ } else {
+ printf("sd == NULL: ");
+ SCFree(p);
+ return 0;
+ }
+
+ client.next_seq = 20;
+ client.isn = 10;
+ ssn.client = client;
+ f.protoctx = &ssn;
+ p->flow = &f;
+ p->tcph = &tcph;
+ sm.ctx = (SigMatchCtx*)sd;
+
+ result = DetectStreamSizeMatch(&dtx, p, &s, sm.ctx);
+ if (result == 0) {
+ printf("result 0 != 1: ");
+ }
+ DetectStreamSizeFree(NULL, sd);
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \test DetectStreamSizeParseTest04 is a test to make sure that we match the
+ * stream_size against invalid packet parameters.
+ */
+
+static int DetectStreamSizeParseTest04 (void)
+{
+
+ int result = 0;
+ DetectStreamSizeData *sd = NULL;
+ TcpSession ssn;
+ ThreadVars tv;
+ DetectEngineThreadCtx dtx;
+ Packet *p = PacketGetFromAlloc();
+ if (unlikely(p == NULL))
+ return 0;
+ Signature s;
+ SigMatch sm;
+ TcpStream client;
+ Flow f;
+ IPV4Hdr ip4h;
+
+ memset(&ssn, 0, sizeof(TcpSession));
+ memset(&tv, 0, sizeof(ThreadVars));
+ memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
+ memset(&s, 0, sizeof(Signature));
+ memset(&sm, 0, sizeof(SigMatch));
+ memset(&client, 0, sizeof(TcpStream));
+ memset(&f, 0, sizeof(Flow));
+ memset(&ip4h, 0, sizeof(IPV4Hdr));
+
+ sd = rs_detect_stream_size_parse(" client , > , 8 ");
+ if (sd != NULL) {
+ if (!(sd->flags & StreamSizeClient) && sd->du32.mode != DETECT_UINT_GT &&
+ sd->du32.arg1 != 8) {
+ SCFree(p);
+ return 0;
+ }
+ } else
+ {
+ SCFree(p);
+ return 0;
+ }
+
+ client.next_seq = 20;
+ client.isn = 12;
+ ssn.client = client;
+ f.protoctx = &ssn;
+ p->flow = &f;
+ p->ip4h = &ip4h;
+ sm.ctx = (SigMatchCtx*)sd;
+
+ if (!DetectStreamSizeMatch(&dtx, p, &s, sm.ctx))
+ result = 1;
+
+ SCFree(p);
+ return result;
+}
+
+/**
+ * \brief this function registers unit tests for DetectStreamSize
+ */
+void DetectStreamSizeRegisterTests(void)
+{
+ UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01);
+ UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02);
+ UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03);
+ UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04);
+}
+#endif /* UNITTESTS */