suricata
detect-stream_size.c
Go to the documentation of this file.
1/* Copyright (C) 2007-2020 Open Information Security Foundation
2 *
3 * You can copy, redistribute or modify this Program under the terms of
4 * the GNU General Public License version 2 as published by the Free
5 * Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * version 2 along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18/**
19 * \file
20 *
21 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
22 *
23 * Stream size for the engine.
24 */
25
26#include "suricata-common.h"
27#include "stream-tcp.h"
28#include "util-unittest.h"
30
31#include "detect.h"
32#include "detect-parse.h"
33
34#include "flow.h"
35#include "detect-stream_size.h"
36#include "stream-tcp-private.h"
38#include "detect-engine-uint.h"
39#include "util-debug.h"
40#include "util-byte.h"
41
42
43/*prototypes*/
44static int DetectStreamSizeMatch (DetectEngineThreadCtx *, Packet *,
45 const Signature *, const SigMatchCtx *);
46static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, const char *);
48#ifdef UNITTESTS
49static void DetectStreamSizeRegisterTests(void);
50#endif
51static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh);
52static bool PrefilterStreamSizeIsPrefilterable(const Signature *s);
53
54/**
55 * \brief Registration function for stream_size: keyword
56 */
57
59{
60 sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size";
61 sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream";
62 sigmatch_table[DETECT_STREAM_SIZE].url = "/rules/flow-keywords.html#stream-size";
63 sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch;
64 sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup;
66#ifdef UNITTESTS
67 sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests;
68#endif
69 sigmatch_table[DETECT_STREAM_SIZE].SupportsPrefilter = PrefilterStreamSizeIsPrefilterable;
70 sigmatch_table[DETECT_STREAM_SIZE].SetupPrefilter = PrefilterSetupStreamSize;
71}
72
73static int DetectStreamSizeMatchAux(const DetectStreamSizeData *sd, const TcpSession *ssn)
74{
75 int ret = 0;
76 uint32_t csdiff = 0;
77 uint32_t ssdiff = 0;
78
79 if (sd->flags == StreamSizeServer) {
80 /* get the server stream size */
81 ssdiff = ssn->server.next_seq - ssn->server.isn;
82 ret = DetectU32Match(ssdiff, &sd->du32);
83
84 } else if (sd->flags == StreamSizeClient) {
85 /* get the client stream size */
86 csdiff = ssn->client.next_seq - ssn->client.isn;
87 ret = DetectU32Match(csdiff, &sd->du32);
88
89 } else if (sd->flags == StreamSizeBoth) {
90 ssdiff = ssn->server.next_seq - ssn->server.isn;
91 csdiff = ssn->client.next_seq - ssn->client.isn;
92
93 if (DetectU32Match(ssdiff, &sd->du32) && DetectU32Match(csdiff, &sd->du32))
94 ret = 1;
95
96 } else if (sd->flags == StreamSizeEither) {
97 ssdiff = ssn->server.next_seq - ssn->server.isn;
98 csdiff = ssn->client.next_seq - ssn->client.isn;
99
100 if (DetectU32Match(ssdiff, &sd->du32) || DetectU32Match(csdiff, &sd->du32))
101 ret = 1;
102 }
103 return ret;
104}
105
106/**
107 * \brief This function is used to match Stream size rule option on a packet with those passed via
108 * stream_size:
109 *
110 * \param t pointer to thread vars
111 * \param det_ctx pointer to the pattern matcher thread
112 * \param p pointer to the current packet
113 * \param m pointer to the sigmatch that we will cast into DetectStreamSizeData
114 *
115 * \retval 0 no match
116 * \retval 1 match
117 */
118static int DetectStreamSizeMatch(
119 DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
120{
121 const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx;
122
123 if (!(PacketIsTCP(p)))
124 return 0;
125 if (p->flow == NULL || p->flow->protoctx == NULL)
126 return 0;
127
128 const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
129
130 SCReturnInt(DetectStreamSizeMatchAux(sd, ssn));
131}
132
133/**
134 * \brief this function is used to add the parsed stream size data into the current signature
135 *
136 * \param de_ctx pointer to the Detection Engine Context
137 * \param s pointer to the Current Signature
138 * \param streamstr pointer to the user provided stream size options
139 *
140 * \retval 0 on Success
141 * \retval -1 on Failure
142 */
143static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *streamstr)
144{
145 DetectStreamSizeData *sd = SCDetectStreamSizeParse(streamstr);
146 if (sd == NULL)
147 return -1;
148
152 return -1;
153 }
154 return 0;
155}
156
157/**
158 * \brief this function will free memory associated with DetectStreamSizeData
159 *
160 * \param ptr pointer to DetectStreamSizeData
161 */
163{
164 SCDetectStreamSizeFree(ptr);
165}
166
167/* prefilter code */
168
169static void PrefilterPacketStreamsizeMatch(
170 DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx)
171{
172 if (!(PacketIsTCP(p)))
173 return;
174
175 if (p->flow == NULL || p->flow->protoctx == NULL)
176 return;
177
178 /* during setup Suricata will automatically see if there is another
179 * check that can be added: alproto, sport or dport */
180 const PrefilterPacketHeaderCtx *ctx = pectx;
181 if (!PrefilterPacketHeaderExtraMatch(ctx, p))
182 return;
183
184 DetectStreamSizeData dsd;
185 dsd.du32.mode = ctx->v1.u8[0];
186 dsd.flags = ctx->v1.u8[1];
187 dsd.du32.arg1 = ctx->v1.u32[2];
188 const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
189 /* if we match, add all the sigs that use this prefilter. This means
190 * that these will be inspected further */
191 if (DetectStreamSizeMatchAux(&dsd, ssn)) {
192 PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
193 }
194}
195
196static void PrefilterPacketStreamSizeSet(PrefilterPacketHeaderValue *v, void *smctx)
197{
198 const DetectStreamSizeData *a = smctx;
199 v->u8[0] = a->du32.mode;
200 v->u8[1] = a->flags;
201 v->u32[2] = a->du32.arg1;
202}
203
204static bool PrefilterPacketStreamSizeCompare(PrefilterPacketHeaderValue v, void *smctx)
205{
206 const DetectStreamSizeData *a = smctx;
207 if (v.u8[0] == a->du32.mode && v.u8[1] == a->flags && v.u32[2] == a->du32.arg1)
208 return true;
209 return false;
210}
211
212static int PrefilterSetupStreamSize(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
213{
215 PrefilterPacketStreamSizeSet, PrefilterPacketStreamSizeCompare,
216 PrefilterPacketStreamsizeMatch);
217}
218
219static bool PrefilterStreamSizeIsPrefilterable(const Signature *s)
220{
221 const SigMatch *sm;
222 for (sm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; sm != NULL; sm = sm->next) {
223 switch (sm->type) {
225 return true;
226 }
227 }
228 return false;
229}
230
231#ifdef UNITTESTS
232/**
233 * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the
234 * user options correctly, when given valid stream_size options.
235 */
236
237static int DetectStreamSizeParseTest01 (void)
238{
239 int result = 0;
240 DetectStreamSizeData *sd = NULL;
241 sd = SCDetectStreamSizeParse("server,<,6");
242 if (sd != NULL) {
243 if (sd->flags & StreamSizeServer && sd->du32.mode == DETECT_UINT_LT && sd->du32.arg1 == 6)
244 result = 1;
245 DetectStreamSizeFree(NULL, sd);
246 }
247
248 return result;
249}
250
251/**
252 * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the
253 * invalid stream_size options.
254 */
255
256static int DetectStreamSizeParseTest02 (void)
257{
258 int result = 1;
259 DetectStreamSizeData *sd = NULL;
260 sd = SCDetectStreamSizeParse("invalidoption,<,6");
261 if (sd != NULL) {
262 printf("expected: NULL got 0x%02X %" PRIu32 ": ", sd->flags, sd->du32.arg1);
263 result = 0;
264 DetectStreamSizeFree(NULL, sd);
265 }
266
267 return result;
268}
269
270/**
271 * \test DetectStreamSizeParseTest03 is a test to make sure that we match the
272 * packet correctly provided valid stream size.
273 */
274
275static int DetectStreamSizeParseTest03 (void)
276{
277
278 int result = 0;
279 DetectStreamSizeData *sd = NULL;
280 TcpSession ssn;
284 if (unlikely(p == NULL))
285 return 0;
286 Signature s;
287 SigMatch sm;
288 TcpStream client;
289 Flow f;
290 TCPHdr tcph;
291
292 memset(&ssn, 0, sizeof(TcpSession));
293 memset(&tv, 0, sizeof(ThreadVars));
294 memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
295 memset(&s, 0, sizeof(Signature));
296 memset(&sm, 0, sizeof(SigMatch));
297 memset(&client, 0, sizeof(TcpStream));
298 memset(&f, 0, sizeof(Flow));
299 memset(&tcph, 0, sizeof(TCPHdr));
300
301 sd = SCDetectStreamSizeParse("client,>,8");
302 if (sd != NULL) {
303 if (!(sd->flags & StreamSizeClient)) {
304 printf("sd->flags not STREAM_SIZE_CLIENT: ");
305 DetectStreamSizeFree(NULL, sd);
306 SCFree(p);
307 return 0;
308 }
309
310 if (sd->du32.mode != DETECT_UINT_GT) {
311 printf("sd->mode not DETECTSSIZE_GT: ");
312 DetectStreamSizeFree(NULL, sd);
313 SCFree(p);
314 return 0;
315 }
316
317 if (sd->du32.arg1 != 8) {
318 printf("sd->ssize is %" PRIu32 ", not 8: ", sd->du32.arg1);
319 DetectStreamSizeFree(NULL, sd);
320 SCFree(p);
321 return 0;
322 }
323 } else {
324 printf("sd == NULL: ");
325 SCFree(p);
326 return 0;
327 }
328
329 client.next_seq = 20;
330 client.isn = 10;
331 ssn.client = client;
332 f.protoctx = &ssn;
333 p->flow = &f;
334 PacketSetTCP(p, (uint8_t *)&tcph);
335 sm.ctx = (SigMatchCtx*)sd;
336
337 result = DetectStreamSizeMatch(&dtx, p, &s, sm.ctx);
338 if (result == 0) {
339 printf("result 0 != 1: ");
340 }
341 DetectStreamSizeFree(NULL, sd);
342 SCFree(p);
343 return result;
344}
345
346/**
347 * \test DetectStreamSizeParseTest04 is a test to make sure that we match the
348 * stream_size against invalid packet parameters.
349 */
350
351static int DetectStreamSizeParseTest04 (void)
352{
353
354 int result = 0;
355 DetectStreamSizeData *sd = NULL;
356 TcpSession ssn;
360 if (unlikely(p == NULL))
361 return 0;
362 Signature s;
363 SigMatch sm;
364 TcpStream client;
365 Flow f;
366 IPV4Hdr ip4h;
367
368 memset(&ssn, 0, sizeof(TcpSession));
369 memset(&tv, 0, sizeof(ThreadVars));
370 memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
371 memset(&s, 0, sizeof(Signature));
372 memset(&sm, 0, sizeof(SigMatch));
373 memset(&client, 0, sizeof(TcpStream));
374 memset(&f, 0, sizeof(Flow));
375 memset(&ip4h, 0, sizeof(IPV4Hdr));
376
377 sd = SCDetectStreamSizeParse(" client , > , 8 ");
378 if (sd != NULL) {
379 if (!(sd->flags & StreamSizeClient) && sd->du32.mode != DETECT_UINT_GT &&
380 sd->du32.arg1 != 8) {
381 SCFree(p);
382 return 0;
383 }
384 } else
385 {
386 SCFree(p);
387 return 0;
388 }
389
390 client.next_seq = 20;
391 client.isn = 12;
392 ssn.client = client;
393 f.protoctx = &ssn;
394 p->flow = &f;
395 UTHSetIPV4Hdr(p, &ip4h);
396 sm.ctx = (SigMatchCtx*)sd;
397
398 if (!DetectStreamSizeMatch(&dtx, p, &s, sm.ctx))
399 result = 1;
400
401 SCFree(p);
402 return result;
403}
404
405/**
406 * \brief this function registers unit tests for DetectStreamSize
407 */
408void DetectStreamSizeRegisterTests(void)
409{
410 UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01);
411 UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02);
412 UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03);
413 UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04);
414}
415#endif /* UNITTESTS */
int PrefilterSetupPacketHeader(DetectEngineCtx *de_ctx, SigGroupHead *sgh, int sm_type, SignatureMask mask, void(*Set)(PrefilterPacketHeaderValue *v, void *), bool(*Compare)(PrefilterPacketHeaderValue v, void *), void(*Match)(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx))
@ DETECT_STREAM_SIZE
int DetectU32Match(const uint32_t parg, const DetectUintData_u32 *du32)
#define DETECT_UINT_LT
#define DETECT_UINT_GT
SigMatch * SCSigMatchAppendSMToList(DetectEngineCtx *de_ctx, Signature *s, uint16_t type, SigMatchCtx *ctx, const int list)
Append a SigMatch to the list type.
SigTableElmt * sigmatch_table
void DetectStreamSizeRegister(void)
Registration function for stream_size: keyword.
void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *)
this function will free memory associated with DetectStreamSizeData
#define SIG_MASK_REQUIRE_FLOW
Definition detect.h:312
@ DETECT_SM_LIST_MATCH
Definition detect.h:117
ThreadVars * tv
DetectEngineCtx * de_ctx
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
Packet * PacketGetFromAlloc(void)
Get a malloced packet.
Definition decode.c:258
struct Thresholds ctx
main detection engine ctx
Definition detect.h:932
PrefilterRuleStore pmq
Definition detect.h:1349
Flow data structure.
Definition flow.h:356
void * protoctx
Definition flow.h:441
struct Flow_ * flow
Definition decode.h:546
Container for matching data for a signature group.
Definition detect.h:1629
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition detect.h:351
a single match condition for a signature
Definition detect.h:356
uint16_t type
Definition detect.h:357
struct SigMatch_ * next
Definition detect.h:360
SigMatchCtx * ctx
Definition detect.h:359
const char * url
Definition detect.h:1462
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition detect.h:1441
int(* SetupPrefilter)(DetectEngineCtx *de_ctx, struct SigGroupHead_ *sgh)
Definition detect.h:1444
void(* Free)(DetectEngineCtx *, void *)
Definition detect.h:1446
const char * desc
Definition detect.h:1461
void(* RegisterTests)(void)
Definition detect.h:1448
int(* Match)(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *)
Definition detect.h:1421
const char * name
Definition detect.h:1459
bool(* SupportsPrefilter)(const Signature *s)
Definition detect.h:1443
struct SigMatch_ * smlists[DETECT_SM_LIST_MAX]
Definition detect.h:642
Signature container.
Definition detect.h:668
SignatureInitData * init_data
Definition detect.h:747
Per thread variable structure.
Definition threadvars.h:58
#define SCReturnInt(x)
Definition util-debug.h:281
#define SCFree(p)
Definition util-mem.h:61
#define unlikely(expr)
void UTHSetIPV4Hdr(Packet *p, IPV4Hdr *ip4h)