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