]> git.ipfire.org Git - people/ms/suricata.git/blame - src/detect-stream_size.c
core: Remove unneeded consts
[people/ms/suricata.git] / src / detect-stream_size.c
CommitLineData
7f6af10f 1/* Copyright (C) 2007-2020 Open Information Security Foundation
ce019275
WM
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 */
8b302269
VJ
17
18/**
19 * \file
ce019275 20 *
ac53ca5b
GS
21 * \author Gurvinder Singh <gurvindersinghdahiya@gmail.com>
22 *
23 * Stream size for the engine.
24 */
25
ecf86f9c 26#include "suricata-common.h"
ac53ca5b
GS
27#include "stream-tcp.h"
28#include "util-unittest.h"
b259e362 29
ac53ca5b 30#include "detect.h"
b259e362
VJ
31#include "detect-parse.h"
32
ac53ca5b
GS
33#include "flow.h"
34#include "detect-stream_size.h"
aa87f704 35#include "stream-tcp-private.h"
cacbf31a 36#include "util-debug.h"
4ed72add 37#include "util-byte.h"
ac53ca5b 38
df74597a 39/**
ac53ca5b
GS
40 * \brief Regex for parsing our flow options
41 */
d82d83eb 42#define PARSE_REGEX "^\\s*([A-z_]+)\\s*,\\s*([<=>!]+)\\s*,\\s*([0-9]+)\\s*$"
ac53ca5b 43
4b0085b0 44static DetectParseRegex parse_regex;
ac53ca5b 45
df74597a 46/*prototypes*/
14896365 47static int DetectStreamSizeMatch (DetectEngineThreadCtx *, Packet *,
bfd4bc82 48 const Signature *, const SigMatchCtx *);
ab1200fb 49static int DetectStreamSizeSetup (DetectEngineCtx *, Signature *, const char *);
d3a65fe1 50void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *);
6ab323d3
VJ
51#ifdef UNITTESTS
52static void DetectStreamSizeRegisterTests(void);
53#endif
ac53ca5b 54
df74597a
GS
55/**
56 * \brief Registration function for stream_size: keyword
57 */
58
8f1d7503
KS
59void DetectStreamSizeRegister(void)
60{
ac53ca5b 61 sigmatch_table[DETECT_STREAM_SIZE].name = "stream_size";
68425453 62 sigmatch_table[DETECT_STREAM_SIZE].desc = "match on amount of bytes of a stream";
26bcc975 63 sigmatch_table[DETECT_STREAM_SIZE].url = "/rules/flow-keywords.html#stream-size";
ac53ca5b
GS
64 sigmatch_table[DETECT_STREAM_SIZE].Match = DetectStreamSizeMatch;
65 sigmatch_table[DETECT_STREAM_SIZE].Setup = DetectStreamSizeSetup;
66 sigmatch_table[DETECT_STREAM_SIZE].Free = DetectStreamSizeFree;
6ab323d3 67#ifdef UNITTESTS
ac53ca5b 68 sigmatch_table[DETECT_STREAM_SIZE].RegisterTests = DetectStreamSizeRegisterTests;
6ab323d3 69#endif
4b0085b0 70 DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
ac53ca5b
GS
71}
72
df74597a
GS
73/**
74 * \brief Function to comapre the stream size against defined size in the user
75 * options.
76 *
77 * \param diff The stream size of server or client stream.
78 * \param stream_size User defined stream size
79 * \param mode The mode defined by user.
80 *
81 * \retval 1 on success and 0 on failure.
82 */
aa87f704 83
e6a009ae
VJ
84static int DetectStreamSizeCompare (uint32_t diff, uint32_t stream_size, uint8_t mode)
85{
86 SCLogDebug("diff %u stream_size %u mode %u", diff, stream_size, mode);
aa87f704
GS
87
88 int ret = 0;
d883a993
VJ
89 switch (mode) {
90 case DETECTSSIZE_LT:
91 if (diff < stream_size)
92 ret = 1;
93 break;
94 case DETECTSSIZE_LEQ:
95 if (diff <= stream_size)
96 ret = 1;
97 break;
98 case DETECTSSIZE_EQ:
99 if (diff == stream_size)
100 ret = 1;
101 break;
102 case DETECTSSIZE_NEQ:
103 if (diff != stream_size)
104 ret = 1;
105 break;
106 case DETECTSSIZE_GEQ:
107 if (diff >= stream_size)
108 ret = 1;
109 break;
110 case DETECTSSIZE_GT:
d883a993
VJ
111 if (diff > stream_size)
112 ret = 1;
113 break;
aa87f704
GS
114 }
115
e6a009ae 116 SCReturnInt(ret);
aa87f704
GS
117}
118
ac53ca5b
GS
119/**
120 * \brief This function is used to match Stream size rule option on a packet with those passed via stream_size:
121 *
122 * \param t pointer to thread vars
123 * \param det_ctx pointer to the pattern matcher thread
124 * \param p pointer to the current packet
125 * \param m pointer to the sigmatch that we will cast into DetectStreamSizeData
126 *
127 * \retval 0 no match
128 * \retval 1 match
129 */
14896365 130static int DetectStreamSizeMatch (DetectEngineThreadCtx *det_ctx, Packet *p,
bfd4bc82 131 const Signature *s, const SigMatchCtx *ctx)
8f1d7503 132{
ac53ca5b 133
923a77e9 134 const DetectStreamSizeData *sd = (const DetectStreamSizeData *)ctx;
ac53ca5b 135
d883a993 136 if (!(PKT_IS_TCP(p)))
e6a009ae
VJ
137 return 0;
138 if (p->flow == NULL || p->flow->protoctx == NULL)
139 return 0;
df74597a 140
e6a009ae
VJ
141 const TcpSession *ssn = (TcpSession *)p->flow->protoctx;
142 int ret = 0;
aa87f704
GS
143 uint32_t csdiff = 0;
144 uint32_t ssdiff = 0;
df74597a 145
aa87f704 146 if (sd->flags & STREAM_SIZE_SERVER) {
d883a993 147 /* get the server stream size */
df74597a 148 ssdiff = ssn->server.next_seq - ssn->server.isn;
aa87f704 149 ret = DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode);
df74597a 150
aa87f704 151 } else if (sd->flags & STREAM_SIZE_CLIENT) {
d883a993 152 /* get the client stream size */
df74597a 153 csdiff = ssn->client.next_seq - ssn->client.isn;
aa87f704 154 ret = DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode);
df74597a 155
aa87f704 156 } else if (sd->flags & STREAM_SIZE_BOTH) {
df74597a
GS
157 ssdiff = ssn->server.next_seq - ssn->server.isn;
158 csdiff = ssn->client.next_seq - ssn->client.isn;
e6a009ae
VJ
159
160 if (DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode) &&
161 DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode))
aa87f704 162 ret = 1;
df74597a 163
aa87f704 164 } else if (sd->flags & STREAM_SIZE_EITHER) {
df74597a
GS
165 ssdiff = ssn->server.next_seq - ssn->server.isn;
166 csdiff = ssn->client.next_seq - ssn->client.isn;
e6a009ae
VJ
167
168 if (DetectStreamSizeCompare(ssdiff, sd->ssize, sd->mode) ||
169 DetectStreamSizeCompare(csdiff, sd->ssize, sd->mode))
aa87f704 170 ret = 1;
ac53ca5b
GS
171 }
172
e6a009ae 173 SCReturnInt(ret);
ac53ca5b
GS
174}
175
df74597a
GS
176/**
177 * \brief This function is used to parse stream options passed via stream_size: keyword
178 *
d3a65fe1 179 * \param de_ctx Pointer to the detection engine context
df74597a
GS
180 * \param streamstr Pointer to the user provided stream_size options
181 *
182 * \retval sd pointer to DetectStreamSizeData on success
183 * \retval NULL on failure
184 */
d3a65fe1 185static DetectStreamSizeData *DetectStreamSizeParse (DetectEngineCtx *de_ctx, const char *streamstr)
8f1d7503 186{
ac53ca5b
GS
187 DetectStreamSizeData *sd = NULL;
188 char *arg = NULL;
189 char *value = NULL;
190 char *mode = NULL;
fa2b46cd 191 int res = 0;
3de99a21 192 size_t pcre2_len;
ac53ca5b 193
3de99a21 194 int ret = DetectParsePcreExec(&parse_regex, streamstr, 0, 0);
7e5f5e68 195 if (ret != 4) {
ba6d807a 196 SCLogError(SC_ERR_PCRE_MATCH, "pcre_exec parse error, ret %" PRId32 ", string %s", ret, streamstr);
ac53ca5b
GS
197 goto error;
198 }
199
200 const char *str_ptr;
3de99a21 201 res = pcre2_substring_get_bynumber(parse_regex.match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
ac53ca5b 202 if (res < 0) {
3de99a21 203 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
ac53ca5b
GS
204 goto error;
205 }
206 arg = (char *)str_ptr;
207
3de99a21 208 res = pcre2_substring_get_bynumber(parse_regex.match, 2, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
ac53ca5b 209 if (res < 0) {
3de99a21 210 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
ac53ca5b
GS
211 goto error;
212 }
213 mode = (char *)str_ptr;
214
3de99a21 215 res = pcre2_substring_get_bynumber(parse_regex.match, 3, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
ac53ca5b 216 if (res < 0) {
3de99a21 217 SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre2_substring_get_bynumber failed");
ac53ca5b
GS
218 goto error;
219 }
220 value = (char *)str_ptr;
221
25a3a5c6 222 sd = SCMalloc(sizeof(DetectStreamSizeData));
e176be6f 223 if (unlikely(sd == NULL))
fa2b46cd 224 goto error;
ac53ca5b
GS
225 sd->ssize = 0;
226 sd->flags = 0;
227
d883a993
VJ
228 if (strlen(mode) == 0)
229 goto error;
230
cd038419
Z
231 if (mode[0] == '=') {
232 sd->mode = DETECTSSIZE_EQ;
233 } else if (mode[0] == '<') {
014f6224 234 sd->mode = DETECTSSIZE_LT;
cd038419
Z
235 if (strcmp("<=", mode) == 0)
236 sd->mode = DETECTSSIZE_LEQ;
237 } else if (mode[0] == '>') {
014f6224 238 sd->mode = DETECTSSIZE_GT;
cd038419
Z
239 if (strcmp(">=", mode) == 0)
240 sd->mode = DETECTSSIZE_GEQ;
241 } else if (strcmp("!=", mode) == 0) {
014f6224 242 sd->mode = DETECTSSIZE_NEQ;
cd038419 243 } else {
ba6d807a 244 SCLogError(SC_ERR_INVALID_OPERATOR, "Invalid operator");
d883a993
VJ
245 goto error;
246 }
ac53ca5b
GS
247
248 /* set the value */
4ed72add
SB
249 if (StringParseUint32(&sd->ssize, 10, 0, (const char *)value) < 0) {
250 SCLogError(SC_ERR_INVALID_VALUE, "Invalid value for stream size: %s", value);
251 goto error;
252 }
df74597a 253 /* inspect our options and set the flags */
ac53ca5b 254 if (strcmp(arg, "server") == 0) {
aa87f704 255 sd->flags |= STREAM_SIZE_SERVER;
ac53ca5b 256 } else if (strcmp(arg, "client") == 0) {
aa87f704 257 sd->flags |= STREAM_SIZE_CLIENT;
aa87f704 258 } else if ((strcmp(arg, "both") == 0)) {
aa87f704
GS
259 sd->flags |= STREAM_SIZE_BOTH;
260 } else if (strcmp(arg, "either") == 0) {
aa87f704 261 sd->flags |= STREAM_SIZE_EITHER;
7e5f5e68
GS
262 } else {
263 goto error;
ac53ca5b
GS
264 }
265
3de99a21
PA
266 pcre2_substring_free((PCRE2_UCHAR8 *)mode);
267 pcre2_substring_free((PCRE2_UCHAR8 *)arg);
268 pcre2_substring_free((PCRE2_UCHAR8 *)value);
ac53ca5b
GS
269 return sd;
270
271error:
014f6224 272 if (mode != NULL)
3de99a21 273 pcre2_substring_free((PCRE2_UCHAR8 *)mode);
014f6224 274 if (arg != NULL)
3de99a21 275 pcre2_substring_free((PCRE2_UCHAR8 *)arg);
014f6224 276 if (value != NULL)
3de99a21 277 pcre2_substring_free((PCRE2_UCHAR8 *)value);
014f6224 278 if (sd != NULL)
d3a65fe1 279 DetectStreamSizeFree(de_ctx, sd);
ac53ca5b
GS
280 return NULL;
281}
282
283/**
284 * \brief this function is used to add the parsed stream size data into the current signature
285 *
286 * \param de_ctx pointer to the Detection Engine Context
287 * \param s pointer to the Current Signature
ac53ca5b
GS
288 * \param streamstr pointer to the user provided stream size options
289 *
290 * \retval 0 on Success
291 * \retval -1 on Failure
292 */
ab1200fb 293static int DetectStreamSizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *streamstr)
8f1d7503 294{
fa2b46cd 295 DetectStreamSizeData *sd = DetectStreamSizeParse(de_ctx, streamstr);
ac53ca5b 296 if (sd == NULL)
fa2b46cd 297 return -1;
ac53ca5b 298
fa2b46cd
VJ
299 SigMatch *sm = SigMatchAlloc();
300 if (sm == NULL) {
301 DetectStreamSizeFree(de_ctx, sd);
302 return -1;
303 }
ac53ca5b
GS
304
305 sm->type = DETECT_STREAM_SIZE;
923a77e9 306 sm->ctx = (SigMatchCtx *)sd;
ac53ca5b 307
a4638fb0 308 SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH);
ac53ca5b 309 return 0;
ac53ca5b
GS
310}
311
312/**
313 * \brief this function will free memory associated with DetectStreamSizeData
314 *
315 * \param ptr pointer to DetectStreamSizeData
316 */
d3a65fe1 317void DetectStreamSizeFree(DetectEngineCtx *de_ctx, void *ptr)
8f1d7503 318{
ac53ca5b 319 DetectStreamSizeData *sd = (DetectStreamSizeData *)ptr;
25a3a5c6 320 SCFree(sd);
ac53ca5b
GS
321}
322
c43319c3 323#ifdef UNITTESTS
df74597a
GS
324/**
325 * \test DetectStreamSizeParseTest01 is a test to make sure that we parse the
326 * user options correctly, when given valid stream_size options.
327 */
328
8f1d7503
KS
329static int DetectStreamSizeParseTest01 (void)
330{
ac53ca5b
GS
331 int result = 0;
332 DetectStreamSizeData *sd = NULL;
d3a65fe1 333 sd = DetectStreamSizeParse(NULL, "server,<,6");
ac53ca5b 334 if (sd != NULL) {
aa87f704 335 if (sd->flags & STREAM_SIZE_SERVER && sd->mode == DETECTSSIZE_LT && sd->ssize == 6)
ac53ca5b 336 result = 1;
d3a65fe1 337 DetectStreamSizeFree(NULL, sd);
ac53ca5b
GS
338 }
339
340 return result;
341}
342
df74597a
GS
343/**
344 * \test DetectStreamSizeParseTest02 is a test to make sure that we detect the
345 * invalid stream_size options.
346 */
347
8f1d7503
KS
348static int DetectStreamSizeParseTest02 (void)
349{
ac53ca5b
GS
350 int result = 1;
351 DetectStreamSizeData *sd = NULL;
d3a65fe1 352 sd = DetectStreamSizeParse(NULL, "invalidoption,<,6");
ac53ca5b 353 if (sd != NULL) {
4aceaf9f 354 printf("expected: NULL got 0x%02X %" PRIu32 ": ",sd->flags, sd->ssize);
ac53ca5b 355 result = 0;
d3a65fe1 356 DetectStreamSizeFree(NULL, sd);
ac53ca5b
GS
357 }
358
359 return result;
360}
aa87f704 361
df74597a
GS
362/**
363 * \test DetectStreamSizeParseTest03 is a test to make sure that we match the
364 * packet correctly provided valid stream size.
365 */
366
8f1d7503
KS
367static int DetectStreamSizeParseTest03 (void)
368{
aa87f704
GS
369
370 int result = 0;
371 DetectStreamSizeData *sd = NULL;
372 TcpSession ssn;
373 ThreadVars tv;
374 DetectEngineThreadCtx dtx;
9c7b411a 375 Packet *p = PacketGetFromAlloc();
e176be6f 376 if (unlikely(p == NULL))
9c7b411a 377 return 0;
aa87f704
GS
378 Signature s;
379 SigMatch sm;
380 TcpStream client;
381 Flow f;
d883a993 382 TCPHdr tcph;
aa87f704
GS
383
384 memset(&ssn, 0, sizeof(TcpSession));
385 memset(&tv, 0, sizeof(ThreadVars));
386 memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
aa87f704
GS
387 memset(&s, 0, sizeof(Signature));
388 memset(&sm, 0, sizeof(SigMatch));
389 memset(&client, 0, sizeof(TcpStream));
390 memset(&f, 0, sizeof(Flow));
d883a993 391 memset(&tcph, 0, sizeof(TCPHdr));
aa87f704 392
d3a65fe1 393 sd = DetectStreamSizeParse(NULL, "client,>,8");
aa87f704 394 if (sd != NULL) {
d883a993
VJ
395 if (!(sd->flags & STREAM_SIZE_CLIENT)) {
396 printf("sd->flags not STREAM_SIZE_CLIENT: ");
d3a65fe1 397 DetectStreamSizeFree(NULL, sd);
1db4aadd 398 SCFree(p);
aa87f704 399 return 0;
d883a993
VJ
400 }
401
402 if (sd->mode != DETECTSSIZE_GT) {
403 printf("sd->mode not DETECTSSIZE_GT: ");
d3a65fe1 404 DetectStreamSizeFree(NULL, sd);
1db4aadd 405 SCFree(p);
d883a993
VJ
406 return 0;
407 }
408
409 if (sd->ssize != 8) {
410 printf("sd->ssize is %"PRIu32", not 8: ", sd->ssize);
d3a65fe1 411 DetectStreamSizeFree(NULL, sd);
1db4aadd 412 SCFree(p);
d883a993
VJ
413 return 0;
414 }
415 } else {
416 printf("sd == NULL: ");
1db4aadd 417 SCFree(p);
7e5f5e68 418 return 0;
d883a993 419 }
aa87f704 420
df74597a
GS
421 client.next_seq = 20;
422 client.isn = 10;
aa87f704 423 ssn.client = client;
0675b7d7 424 f.protoctx = &ssn;
1db4aadd
EL
425 p->flow = &f;
426 p->tcph = &tcph;
923a77e9 427 sm.ctx = (SigMatchCtx*)sd;
aa87f704 428
14896365 429 result = DetectStreamSizeMatch(&dtx, p, &s, sm.ctx);
d883a993
VJ
430 if (result == 0) {
431 printf("result 0 != 1: ");
432 }
d3a65fe1 433 DetectStreamSizeFree(NULL, sd);
1db4aadd 434 SCFree(p);
aa87f704
GS
435 return result;
436}
437
df74597a
GS
438/**
439 * \test DetectStreamSizeParseTest04 is a test to make sure that we match the
440 * stream_size against invalid packet parameters.
441 */
442
8f1d7503
KS
443static int DetectStreamSizeParseTest04 (void)
444{
aa87f704
GS
445
446 int result = 0;
447 DetectStreamSizeData *sd = NULL;
448 TcpSession ssn;
449 ThreadVars tv;
450 DetectEngineThreadCtx dtx;
9c7b411a 451 Packet *p = PacketGetFromAlloc();
e176be6f 452 if (unlikely(p == NULL))
9c7b411a 453 return 0;
aa87f704
GS
454 Signature s;
455 SigMatch sm;
456 TcpStream client;
457 Flow f;
458 IPV4Hdr ip4h;
459
460 memset(&ssn, 0, sizeof(TcpSession));
461 memset(&tv, 0, sizeof(ThreadVars));
462 memset(&dtx, 0, sizeof(DetectEngineThreadCtx));
aa87f704
GS
463 memset(&s, 0, sizeof(Signature));
464 memset(&sm, 0, sizeof(SigMatch));
465 memset(&client, 0, sizeof(TcpStream));
466 memset(&f, 0, sizeof(Flow));
467 memset(&ip4h, 0, sizeof(IPV4Hdr));
468
d3a65fe1 469 sd = DetectStreamSizeParse(NULL, " client , > , 8 ");
aa87f704 470 if (sd != NULL) {
1db4aadd
EL
471 if (!(sd->flags & STREAM_SIZE_CLIENT) && sd->mode != DETECTSSIZE_GT && sd->ssize != 8) {
472 SCFree(p);
473 return 0;
474 }
7e5f5e68 475 } else
1db4aadd
EL
476 {
477 SCFree(p);
7e5f5e68 478 return 0;
1db4aadd 479 }
aa87f704 480
df74597a
GS
481 client.next_seq = 20;
482 client.isn = 12;
aa87f704 483 ssn.client = client;
0675b7d7 484 f.protoctx = &ssn;
1db4aadd
EL
485 p->flow = &f;
486 p->ip4h = &ip4h;
923a77e9 487 sm.ctx = (SigMatchCtx*)sd;
aa87f704 488
14896365 489 if (!DetectStreamSizeMatch(&dtx, p, &s, sm.ctx))
aa87f704
GS
490 result = 1;
491
1db4aadd 492 SCFree(p);
aa87f704
GS
493 return result;
494}
495
df74597a
GS
496/**
497 * \brief this function registers unit tests for DetectStreamSize
498 */
8f1d7503
KS
499void DetectStreamSizeRegisterTests(void)
500{
796dd522
JI
501 UtRegisterTest("DetectStreamSizeParseTest01", DetectStreamSizeParseTest01);
502 UtRegisterTest("DetectStreamSizeParseTest02", DetectStreamSizeParseTest02);
503 UtRegisterTest("DetectStreamSizeParseTest03", DetectStreamSizeParseTest03);
504 UtRegisterTest("DetectStreamSizeParseTest04", DetectStreamSizeParseTest04);
d883a993 505}
3de99a21 506#endif /* UNITTESTS */