suricata
detect-filesize.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2012 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 Victor Julien <victor@inliniac.net>
22  *
23  * Implements the filesize keyword
24  */
25 
26 #include "suricata-common.h"
27 #include "app-layer-protos.h"
28 #include "app-layer-htp.h"
29 #include "util-unittest.h"
30 #include "util-unittest-helper.h"
31 #include "util-misc.h"
32 
33 #include "detect.h"
34 #include "detect-parse.h"
35 #include "detect-engine.h"
36 #include "detect-engine-state.h"
37 
38 #include "detect-filesize.h"
39 #include "util-debug.h"
40 #include "util-byte.h"
41 #include "flow-util.h"
42 #include "stream-tcp.h"
43 
44 /**
45  * \brief Regex for parsing our filesize
46  */
47 #define PARSE_REGEX "^(?:\\s*)(<|>)?(?:\\s*)([0-9]{1,23}[a-zA-Z]{0,2})(?:\\s*)(?:(<>)(?:\\s*)([0-9]{1,23}[a-zA-Z]{0,2}))?\\s*$"
48 
49 static pcre *parse_regex;
50 static pcre_extra *parse_regex_study;
51 
52 /*prototypes*/
53 static int DetectFilesizeMatch (DetectEngineThreadCtx *det_ctx, Flow *f,
54  uint8_t flags, File *file, const Signature *s, const SigMatchCtx *m);
55 static int DetectFilesizeSetup (DetectEngineCtx *, Signature *, const char *);
56 static void DetectFilesizeFree (void *);
57 static void DetectFilesizeRegisterTests (void);
58 static int g_file_match_list_id = 0;
59 
60 /**
61  * \brief Registration function for filesize: keyword
62  */
63 
65 {
66  sigmatch_table[DETECT_FILESIZE].name = "filesize";
67  sigmatch_table[DETECT_FILESIZE].desc = "match on the size of the file as it is being transferred";
68  sigmatch_table[DETECT_FILESIZE].url = DOC_URL DOC_VERSION "/rules/file-keywords.html#filesize";
69  sigmatch_table[DETECT_FILESIZE].FileMatch = DetectFilesizeMatch;
70  sigmatch_table[DETECT_FILESIZE].Setup = DetectFilesizeSetup;
71  sigmatch_table[DETECT_FILESIZE].Free = DetectFilesizeFree;
72  sigmatch_table[DETECT_FILESIZE].RegisterTests = DetectFilesizeRegisterTests;
73 
74  DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
75 
76  g_file_match_list_id = DetectBufferTypeRegister("files");
77 }
78 
79 /**
80  * \brief This function is used to match filesize rule option.
81  *
82  * \param t thread local vars
83  * \param det_ctx pattern matcher thread local data
84  * \param f *LOCKED* flow
85  * \param flags direction flags
86  * \param file file being inspected
87  * \param s signature being inspected
88  * \param m sigmatch that we will cast into DetectFilesizeData
89  *
90  * \retval 0 no match
91  * \retval 1 match
92  */
93 static int DetectFilesizeMatch (DetectEngineThreadCtx *det_ctx, Flow *f,
94  uint8_t flags, File *file, const Signature *s, const SigMatchCtx *m)
95 {
96  SCEnter();
97 
99  int ret = 0;
100  uint64_t file_size = FileTrackedSize(file);
101 
102  SCLogDebug("file size %"PRIu64", check %"PRIu64, file_size, fsd->size1);
103 
104  if (file->state == FILE_STATE_CLOSED) {
105  switch (fsd->mode) {
106  case DETECT_FILESIZE_EQ:
107  if (file_size == fsd->size1)
108  ret = 1;
109  break;
110  case DETECT_FILESIZE_LT:
111  if (file_size < fsd->size1)
112  ret = 1;
113  break;
114  case DETECT_FILESIZE_GT:
115  if (file_size > fsd->size1)
116  ret = 1;
117  break;
118  case DETECT_FILESIZE_RA:
119  if (file_size > fsd->size1 && file_size < fsd->size2)
120  ret = 1;
121  break;
122  }
123  /* truncated, error: only see if what we have meets the GT condition */
124  } else if (file->state > FILE_STATE_CLOSED) {
125  if (fsd->mode == DETECT_FILESIZE_GT && file_size > fsd->size1)
126  ret = 1;
127  }
128  SCReturnInt(ret);
129 }
130 
131 /**
132  * \brief parse filesize options
133  *
134  * \param str pointer to the user provided filesize
135  *
136  * \retval fsd pointer to DetectFilesizeData on success
137  * \retval NULL on failure
138  */
139 static DetectFilesizeData *DetectFilesizeParse (const char *str)
140 {
141 
142  DetectFilesizeData *fsd = NULL;
143  char *arg1 = NULL;
144  char *arg2 = NULL;
145  char *arg3 = NULL;
146  char *arg4 = NULL;
147 #define MAX_SUBSTRINGS 30
148  int ret = 0, res = 0;
149  int ov[MAX_SUBSTRINGS];
150 
151  ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str),
152  0, 0, ov, MAX_SUBSTRINGS);
153  if (ret < 3 || ret > 5) {
154  SCLogError(SC_ERR_PCRE_PARSE, "filesize option pcre parse error: \"%s\"", str);
155  goto error;
156  }
157  const char *str_ptr;
158 
159  SCLogDebug("ret %d", ret);
160 
161  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
162  if (res < 0) {
163  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
164  goto error;
165  }
166  arg1 = (char *) str_ptr;
167  SCLogDebug("Arg1 \"%s\"", arg1);
168 
169  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 2, &str_ptr);
170  if (res < 0) {
171  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
172  goto error;
173  }
174  arg2 = (char *) str_ptr;
175  SCLogDebug("Arg2 \"%s\"", arg2);
176 
177  if (ret > 3) {
178  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 3, &str_ptr);
179  if (res < 0) {
180  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
181  goto error;
182  }
183  arg3 = (char *) str_ptr;
184  SCLogDebug("Arg3 \"%s\"", arg3);
185 
186  if (ret > 4) {
187  res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 4, &str_ptr);
188  if (res < 0) {
189  SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
190  goto error;
191  }
192  arg4 = (char *) str_ptr;
193  SCLogDebug("Arg4 \"%s\"", arg4);
194  }
195  }
196 
197  fsd = SCMalloc(sizeof (DetectFilesizeData));
198  if (unlikely(fsd == NULL))
199  goto error;
200  memset(fsd, 0, sizeof(DetectFilesizeData));
201 
202  if (arg1[0] == '<')
203  fsd->mode = DETECT_FILESIZE_LT;
204  else if (arg1[0] == '>')
205  fsd->mode = DETECT_FILESIZE_GT;
206  else
207  fsd->mode = DETECT_FILESIZE_EQ;
208 
209  if (arg3 != NULL && strcmp("<>", arg3) == 0) {
210  if (strlen(arg1) != 0) {
211  SCLogError(SC_ERR_INVALID_ARGUMENT,"Range specified but mode also set");
212  goto error;
213  }
214  fsd->mode = DETECT_FILESIZE_RA;
215  }
216 
217  /** set the first value */
218  if (ParseSizeStringU64(arg2, &fsd->size1) < 0) {
219  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing filesize value - %s", arg2);
220  goto error;
221  }
222 
223  /** set the second value if specified */
224  if (arg4 != NULL && strlen(arg4) > 0) {
225  if (fsd->mode != DETECT_FILESIZE_RA) {
226  SCLogError(SC_ERR_INVALID_ARGUMENT,"Multiple filesize values specified"
227  " but mode is not range");
228  goto error;
229  }
230 
231  if (ParseSizeStringU64(arg4, &fsd->size2) < 0) {
232  SCLogError(SC_ERR_SIZE_PARSE, "Error parsing filesize value - %s", arg4);
233  goto error;
234  }
235 
236  if (fsd->size2 <= fsd->size1){
237  SCLogError(SC_ERR_INVALID_ARGUMENT,"filesize2:%"PRIu64" <= filesize:"
238  "%"PRIu64"",fsd->size2,fsd->size1);
239  goto error;
240  }
241  }
242 
243  pcre_free_substring(arg1);
244  pcre_free_substring(arg2);
245  if (arg3 != NULL)
246  pcre_free_substring(arg3);
247  if (arg4 != NULL)
248  pcre_free_substring(arg4);
249  return fsd;
250 
251 error:
252  if (fsd)
253  SCFree(fsd);
254  if (arg1 != NULL)
255  SCFree(arg1);
256  if (arg2 != NULL)
257  SCFree(arg2);
258  if (arg3 != NULL)
259  SCFree(arg3);
260  if (arg4 != NULL)
261  SCFree(arg4);
262  return NULL;
263 }
264 
265 /**
266  * \brief this function is used to parse filesize data into the current signature
267  *
268  * \param de_ctx pointer to the Detection Engine Context
269  * \param s pointer to the Current Signature
270  * \param str pointer to the user provided options
271  *
272  * \retval 0 on Success
273  * \retval -1 on Failure
274  */
275 static int DetectFilesizeSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
276 {
277  SCEnter();
278  DetectFilesizeData *fsd = NULL;
279  SigMatch *sm = NULL;
280 
281  fsd = DetectFilesizeParse(str);
282  if (fsd == NULL)
283  goto error;
284 
285  sm = SigMatchAlloc();
286  if (sm == NULL)
287  goto error;
288 
289  sm->type = DETECT_FILESIZE;
290  sm->ctx = (SigMatchCtx *)fsd;
291 
292  SigMatchAppendSMToList(s, sm, g_file_match_list_id);
293 
295  SCReturnInt(0);
296 
297 error:
298  if (fsd != NULL)
299  DetectFilesizeFree(fsd);
300  if (sm != NULL)
301  SCFree(sm);
302  SCReturnInt(-1);
303 }
304 
305 /**
306  * \brief this function will free memory associated with DetectFilesizeData
307  *
308  * \param ptr pointer to DetectFilesizeData
309  */
310 static void DetectFilesizeFree(void *ptr)
311 {
313  SCFree(fsd);
314 }
315 
316 #ifdef UNITTESTS
317 #include "stream.h"
318 #include "stream-tcp-private.h"
319 #include "stream-tcp-reassemble.h"
320 #include "detect-engine.h"
321 #include "detect-engine-mpm.h"
322 #include "app-layer-parser.h"
323 
324 /** \test Test the Filesize keyword setup */
325 static int DetectFilesizeParseTest01(void)
326 {
327  int ret = 0;
328  DetectFilesizeData *fsd = NULL;
329 
330  fsd = DetectFilesizeParse("10");
331  if (fsd != NULL) {
332  if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_EQ)
333  ret = 1;
334 
335  DetectFilesizeFree(fsd);
336  }
337  return ret;
338 }
339 
340 /** \test Test the Filesize keyword setup */
341 static int DetectFilesizeParseTest02(void)
342 {
343  int ret = 0;
344  DetectFilesizeData *fsd = NULL;
345 
346  fsd = DetectFilesizeParse(" < 10 ");
347  if (fsd != NULL) {
348  if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_LT)
349  ret = 1;
350 
351  DetectFilesizeFree(fsd);
352  }
353  return ret;
354 }
355 
356 /** \test Test the Filesize keyword setup */
357 static int DetectFilesizeParseTest03(void)
358 {
359  int ret = 0;
360  DetectFilesizeData *fsd = NULL;
361 
362  fsd = DetectFilesizeParse(" > 10 ");
363  if (fsd != NULL) {
364  if (fsd->size1 == 10 && fsd->mode == DETECT_FILESIZE_GT)
365  ret = 1;
366 
367  DetectFilesizeFree(fsd);
368  }
369  return ret;
370 }
371 
372 /** \test Test the Filesize keyword setup */
373 static int DetectFilesizeParseTest04(void)
374 {
375  int ret = 0;
376  DetectFilesizeData *fsd = NULL;
377 
378  fsd = DetectFilesizeParse(" 5 <> 10 ");
379  if (fsd != NULL) {
380  if (fsd->size1 == 5 && fsd->size2 == 10 &&
381  fsd->mode == DETECT_FILESIZE_RA)
382  ret = 1;
383 
384  DetectFilesizeFree(fsd);
385  }
386  return ret;
387 }
388 
389 /** \test Test the Filesize keyword setup */
390 static int DetectFilesizeParseTest05(void)
391 {
392  int ret = 0;
393  DetectFilesizeData *fsd = NULL;
394 
395  fsd = DetectFilesizeParse("5<>10");
396  if (fsd != NULL) {
397  if (fsd->size1 == 5 && fsd->size2 == 10 &&
398  fsd->mode == DETECT_FILESIZE_RA)
399  ret = 1;
400 
401  DetectFilesizeFree(fsd);
402  }
403  return ret;
404 }
405 
406 /**
407  * \brief this function is used to initialize the detection engine context and
408  * setup the signature with passed values.
409  *
410  */
411 
412 static int DetectFilesizeInitTest(DetectEngineCtx **de_ctx, Signature **sig,
413  DetectFilesizeData **fsd, const char *str)
414 {
415  char fullstr[1024];
416  int result = 0;
417 
418  *de_ctx = NULL;
419  *sig = NULL;
420 
421  if (snprintf(fullstr, 1024, "alert http any any -> any any (msg:\"Filesize "
422  "test\"; filesize:%s; sid:1;)", str) >= 1024) {
423  goto end;
424  }
425 
426  *de_ctx = DetectEngineCtxInit();
427  if (*de_ctx == NULL) {
428  goto end;
429  }
430 
431  (*de_ctx)->flags |= DE_QUIET;
432 
433  (*de_ctx)->sig_list = SigInit(*de_ctx, fullstr);
434  if ((*de_ctx)->sig_list == NULL) {
435  goto end;
436  }
437 
438  *sig = (*de_ctx)->sig_list;
439 
440  *fsd = DetectFilesizeParse(str);
441 
442  result = 1;
443 
444 end:
445  return result;
446 }
447 
448 /**
449  * \test DetectFilesizeSetpTest01 is a test for setting up an valid filesize values
450  * with valid "<>" operator and include spaces arround the given values.
451  * In the test the values are setup with initializing the detection engine
452  * context and setting up the signature itself.
453  */
454 
455 static int DetectFilesizeSetpTest01(void)
456 {
457 
458  DetectFilesizeData *fsd = NULL;
459  uint8_t res = 0;
460  Signature *sig = NULL;
461  DetectEngineCtx *de_ctx = NULL;
462 
463  res = DetectFilesizeInitTest(&de_ctx, &sig, &fsd, "1 <> 2 ");
464  if (res == 0) {
465  goto end;
466  }
467 
468  if(fsd == NULL)
469  goto cleanup;
470 
471  if (fsd != NULL) {
472  if (fsd->size1 == 1 && fsd->size2 == 2 &&
473  fsd->mode == DETECT_FILESIZE_RA)
474  res = 1;
475  }
476 
477 cleanup:
478  if (fsd)
479  SCFree(fsd);
480  SigGroupCleanup(de_ctx);
481  SigCleanSignatures(de_ctx);
482  DetectEngineCtxFree(de_ctx);
483 end:
484  return res;
485 }
486 
487 #endif /* UNITTESTS */
488 
489 /**
490  * \brief this function registers unit tests for DetectFilesize
491  */
492 void DetectFilesizeRegisterTests(void)
493 {
494 #ifdef UNITTESTS
495  UtRegisterTest("DetectFilesizeParseTest01", DetectFilesizeParseTest01);
496  UtRegisterTest("DetectFilesizeParseTest02", DetectFilesizeParseTest02);
497  UtRegisterTest("DetectFilesizeParseTest03", DetectFilesizeParseTest03);
498  UtRegisterTest("DetectFilesizeParseTest04", DetectFilesizeParseTest04);
499  UtRegisterTest("DetectFilesizeParseTest05", DetectFilesizeParseTest05);
500  UtRegisterTest("DetectFilesizeSetpTest01", DetectFilesizeSetpTest01);
501 #endif /* UNITTESTS */
502 }
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1448
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1186
#define SCLogDebug(...)
Definition: util-debug.h:335
#define unlikely(expr)
Definition: util-optimize.h:35
Signature * SigInit(DetectEngineCtx *, const char *)
Parses a signature and adds it to the Detection Engine Context.
void SigCleanSignatures(DetectEngineCtx *de_ctx)
const char * name
Definition: detect.h:1200
Signature container.
Definition: detect.h:522
#define DETECT_FILESIZE_GT
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:313
main detection engine ctx
Definition: detect.h:761
int ParseSizeStringU64(const char *size, uint64_t *res)
Definition: util-misc.c:203
#define DE_QUIET
Definition: detect.h:292
#define str(s)
#define DETECT_FILESIZE_EQ
#define DETECT_FILESIZE_LT
int(* FileMatch)(DetectEngineThreadCtx *, Flow *, uint8_t flags, File *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1178
void DetectFilesizeRegister(void)
Registration function for filesize: keyword.
Data structures and function prototypes for keeping state for the detection engine.
void(* Free)(void *)
Definition: detect.h:1191
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
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)
#define SCEnter(...)
Definition: util-debug.h:337
#define DETECT_FILESIZE_RA
int SigGroupCleanup(DetectEngineCtx *de_ctx)
uint8_t type
Definition: detect.h:319
#define SCReturnInt(x)
Definition: util-debug.h:341
#define FILE_SIG_NEED_FILE
Definition: detect.h:282
const char * desc
Definition: detect.h:1202
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:346
#define PARSE_REGEX
Regex for parsing our filesize.
int DetectBufferTypeRegister(const char *name)
SigMatchCtx * ctx
Definition: detect.h:321
#define SCMalloc(a)
Definition: util-mem.h:222
#define SCFree(a)
Definition: util-mem.h:322
PoolThreadReserved res
const char * url
Definition: detect.h:1203
FileState state
Definition: util-file.h:67
SCMutex m
Definition: flow-hash.h:105
#define DOC_URL
Definition: suricata.h:86
uint64_t FileTrackedSize(const File *file)
get the size of the file
Definition: util-file.c:294
#define MAX_SUBSTRINGS
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
uint8_t file_flags
Definition: detect.h:536
#define DOC_VERSION
Definition: suricata.h:91
void DetectEngineCtxFree(DetectEngineCtx *)
Free a DetectEngineCtx::
Flow data structure.
Definition: flow.h:325
void(* RegisterTests)(void)
Definition: detect.h:1192
a single match condition for a signature
Definition: detect.h:318
DetectEngineCtx * DetectEngineCtxInit(void)
#define FILE_SIG_NEED_SIZE
Definition: detect.h:289