suricata
detect-filemagic.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2014 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  */
24 
25 #include "suricata-common.h"
26 #include "threads.h"
27 #include "debug.h"
28 #include "decode.h"
29 
30 #include "detect.h"
31 #include "detect-parse.h"
32 
33 #include "detect-engine.h"
34 #include "detect-engine-mpm.h"
35 #include "detect-engine-state.h"
36 
37 #include "flow.h"
38 #include "flow-var.h"
39 #include "flow-util.h"
40 
41 #include "util-debug.h"
42 #include "util-spm-bm.h"
43 #include "util-magic.h"
44 #include "util-print.h"
45 
46 #include "util-unittest.h"
47 #include "util-unittest-helper.h"
48 
49 #include "app-layer.h"
50 
51 #include "stream-tcp.h"
52 
53 #include "detect-filemagic.h"
54 
55 #include "conf.h"
56 
57 #ifndef HAVE_MAGIC
58 
59 static int DetectFilemagicSetupNoSupport (DetectEngineCtx *de_ctx, Signature *s, const char *str)
60 {
61  SCLogError(SC_ERR_NO_MAGIC_SUPPORT, "no libmagic support built in, needed for filemagic keyword");
62  return -1;
63 }
64 
65 /**
66  * \brief Registration function for keyword: filemagic
67  */
69 {
70  sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
71  sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
72  sigmatch_table[DETECT_FILEMAGIC].url = DOC_URL DOC_VERSION "/rules/file-keywords.html#filemagic";
73  sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetupNoSupport;
75 }
76 
77 #else /* HAVE_MAGIC */
78 
79 static int DetectFilemagicMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
80  uint8_t, File *, const Signature *, const SigMatchCtx *);
81 static int DetectFilemagicSetup (DetectEngineCtx *, Signature *, const char *);
82 static void DetectFilemagicRegisterTests(void);
83 static void DetectFilemagicFree(void *);
84 static int g_file_match_list_id = 0;
85 
86 /**
87  * \brief Registration function for keyword: filemagic
88  */
89 void DetectFilemagicRegister(void)
90 {
91  sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
92  sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
93  sigmatch_table[DETECT_FILEMAGIC].url = DOC_URL DOC_VERSION "/rules/file-keywords.html#filemagic";
94  sigmatch_table[DETECT_FILEMAGIC].FileMatch = DetectFilemagicMatch;
95  sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetup;
96  sigmatch_table[DETECT_FILEMAGIC].Free = DetectFilemagicFree;
97  sigmatch_table[DETECT_FILEMAGIC].RegisterTests = DetectFilemagicRegisterTests;
99 
100  g_file_match_list_id = DetectBufferTypeRegister("files");
101 
102  SCLogDebug("registering filemagic rule option");
103  return;
104 }
105 
106 #define FILEMAGIC_MIN_SIZE 512
107 
108 /**
109  * \brief run the magic check
110  *
111  * \param file the file
112  *
113  * \retval -1 error
114  * \retval 0 ok
115  */
116 int FilemagicGlobalLookup(File *file)
117 {
118  if (file == NULL || FileDataSize(file) == 0) {
119  SCReturnInt(-1);
120  }
121 
122  const uint8_t *data = NULL;
123  uint32_t data_len = 0;
124  uint64_t offset = 0;
125 
127  &data, &data_len, &offset);
128  if (offset == 0) {
129  if (FileDataSize(file) >= FILEMAGIC_MIN_SIZE) {
130  file->magic = MagicGlobalLookup(data, data_len);
131  } else if (file->state >= FILE_STATE_CLOSED) {
132  file->magic = MagicGlobalLookup(data, data_len);
133  }
134  }
135 
136  SCReturnInt(0);
137 }
138 
139 /**
140  * \brief run the magic check
141  *
142  * \param file the file
143  *
144  * \retval -1 error
145  * \retval 0 ok
146  */
147 static int FilemagicThreadLookup(magic_t *ctx, File *file)
148 {
149  if (ctx == NULL || file == NULL || FileDataSize(file) == 0) {
150  SCReturnInt(-1);
151  }
152 
153  const uint8_t *data = NULL;
154  uint32_t data_len = 0;
155  uint64_t offset = 0;
156 
158  &data, &data_len, &offset);
159  if (offset == 0) {
160  if (FileDataSize(file) >= FILEMAGIC_MIN_SIZE) {
161  file->magic = MagicThreadLookup(ctx, data, data_len);
162  } else if (file->state >= FILE_STATE_CLOSED) {
163  file->magic = MagicThreadLookup(ctx, data, data_len);
164  }
165  }
166  SCReturnInt(0);
167 }
168 
169 /**
170  * \brief match the specified filemagic
171  *
172  * \param t thread local vars
173  * \param det_ctx pattern matcher thread local data
174  * \param f *LOCKED* flow
175  * \param flags direction flags
176  * \param file file being inspected
177  * \param s signature being inspected
178  * \param m sigmatch that we will cast into DetectFilemagicData
179  *
180  * \retval 0 no match
181  * \retval 1 match
182  */
183 static int DetectFilemagicMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
184  Flow *f, uint8_t flags, File *file, const Signature *s, const SigMatchCtx *m)
185 {
186  SCEnter();
187  int ret = 0;
188  DetectFilemagicData *filemagic = (DetectFilemagicData *)m;
189 
190  DetectFilemagicThreadData *tfilemagic = (DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, filemagic->thread_ctx_id);
191  if (tfilemagic == NULL) {
192  SCReturnInt(0);
193  }
194 
195  if (file->magic == NULL) {
196  FilemagicThreadLookup(&tfilemagic->ctx, file);
197  }
198 
199  if (file->magic != NULL) {
200  SCLogDebug("magic %s", file->magic);
201 
202  /* we include the \0 in the inspection, so patterns can match on the
203  * end of the string. */
204  if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic,
205  strlen(file->magic) + 1, filemagic->bm_ctx) != NULL)
206  {
207 #ifdef DEBUG
208  if (SCLogDebugEnabled()) {
209  char *name = SCMalloc(filemagic->len + 1);
210  if (name != NULL) {
211  memcpy(name, filemagic->name, filemagic->len);
212  name[filemagic->len] = '\0';
213  SCLogDebug("will look for filemagic %s", name);
214  SCFree(name);
215  }
216  }
217 #endif
218 
219  if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) {
220  ret = 1;
221  }
222  } else if (filemagic->flags & DETECT_CONTENT_NEGATED) {
223  SCLogDebug("negated match");
224  ret = 1;
225  }
226  }
227 
228  SCReturnInt(ret);
229 }
230 
231 /**
232  * \brief Parse the filemagic keyword
233  *
234  * \param idstr Pointer to the user provided option
235  *
236  * \retval filemagic pointer to DetectFilemagicData on success
237  * \retval NULL on failure
238  */
239 static DetectFilemagicData *DetectFilemagicParse (const char *str, bool negate)
240 {
241  DetectFilemagicData *filemagic = NULL;
242 
243  /* We have a correct filemagic option */
244  filemagic = SCMalloc(sizeof(DetectFilemagicData));
245  if (unlikely(filemagic == NULL))
246  goto error;
247 
248  memset(filemagic, 0x00, sizeof(DetectFilemagicData));
249 
250  if (DetectContentDataParse ("filemagic", str, &filemagic->name, &filemagic->len) == -1) {
251  goto error;
252  }
253 
254  filemagic->bm_ctx = BoyerMooreNocaseCtxInit(filemagic->name, filemagic->len);
255  if (filemagic->bm_ctx == NULL) {
256  goto error;
257  }
258 
259  if (negate) {
260  filemagic->flags |= DETECT_CONTENT_NEGATED;
261  }
262 
263  SCLogDebug("flags %02X", filemagic->flags);
264  if (filemagic->flags & DETECT_CONTENT_NEGATED) {
265  SCLogDebug("negated filemagic");
266  }
267 
268 #ifdef DEBUG
269  if (SCLogDebugEnabled()) {
270  char *name = SCMalloc(filemagic->len + 1);
271  if (name != NULL) {
272  memcpy(name, filemagic->name, filemagic->len);
273  name[filemagic->len] = '\0';
274  SCLogDebug("will look for filemagic %s", name);
275  SCFree(name);
276  }
277  }
278 #endif
279 
280  return filemagic;
281 
282 error:
283  if (filemagic != NULL)
284  DetectFilemagicFree(filemagic);
285  return NULL;
286 }
287 
288 static void *DetectFilemagicThreadInit(void *data)
289 {
290  const char *filename = NULL;
291  FILE *fd = NULL;
292  DetectFilemagicData *filemagic = (DetectFilemagicData *)data;
293  BUG_ON(filemagic == NULL);
294 
295  DetectFilemagicThreadData *t = SCMalloc(sizeof(DetectFilemagicThreadData));
296  if (unlikely(t == NULL)) {
297  SCLogError(SC_ERR_MEM_ALLOC, "couldn't alloc ctx memory");
298  return NULL;
299  }
300  memset(t, 0x00, sizeof(DetectFilemagicThreadData));
301 
302  t->ctx = magic_open(0);
303  if (t->ctx == NULL) {
304  SCLogError(SC_ERR_MAGIC_OPEN, "magic_open failed: %s", magic_error(t->ctx));
305  goto error;
306  }
307 
308  (void)ConfGet("magic-file", &filename);
309  if (filename != NULL) {
310  if (strlen(filename) == 0) {
311  /* set filename to NULL on *nix systems so magic_load uses system default path (see man libmagic) */
312  SCLogInfo("using system default magic-file");
313  filename = NULL;
314  }
315  else {
316  SCLogInfo("using magic-file %s", filename);
317 
318  if ( (fd = fopen(filename, "r")) == NULL) {
319  SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
320  goto error;
321  }
322  fclose(fd);
323  }
324  }
325 
326  if (magic_load(t->ctx, filename) != 0) {
327  SCLogError(SC_ERR_MAGIC_LOAD, "magic_load failed: %s", magic_error(t->ctx));
328  goto error;
329  }
330 
331  return (void *)t;
332 
333 error:
334  if (t->ctx)
335  magic_close(t->ctx);
336  SCFree(t);
337  return NULL;
338 }
339 
340 static void DetectFilemagicThreadFree(void *ctx)
341 {
342  if (ctx != NULL) {
343  DetectFilemagicThreadData *t = (DetectFilemagicThreadData *)ctx;
344  if (t->ctx)
345  magic_close(t->ctx);
346  SCFree(t);
347  }
348 }
349 
350 /**
351  * \brief this function is used to parse filemagic options
352  * \brief into the current signature
353  *
354  * \param de_ctx pointer to the Detection Engine Context
355  * \param s pointer to the Current Signature
356  * \param str pointer to the user provided "filemagic" option
357  *
358  * \retval 0 on Success
359  * \retval -1 on Failure
360  */
361 static int DetectFilemagicSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
362 {
363  DetectFilemagicData *filemagic = NULL;
364  SigMatch *sm = NULL;
365 
366  filemagic = DetectFilemagicParse(str, s->init_data->negated);
367  if (filemagic == NULL)
368  goto error;
369 
370  filemagic->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "filemagic",
371  DetectFilemagicThreadInit, (void *)filemagic,
372  DetectFilemagicThreadFree, 1);
373  if (filemagic->thread_ctx_id == -1)
374  goto error;
375 
376  /* Okay so far so good, lets get this into a SigMatch
377  * and put it in the Signature. */
378  sm = SigMatchAlloc();
379  if (sm == NULL)
380  goto error;
381 
382  sm->type = DETECT_FILEMAGIC;
383  sm->ctx = (void *)filemagic;
384 
385  SigMatchAppendSMToList(s, sm, g_file_match_list_id);
386 
388  return 0;
389 
390 error:
391  if (filemagic != NULL)
392  DetectFilemagicFree(filemagic);
393  if (sm != NULL)
394  SCFree(sm);
395  return -1;
396 }
397 
398 /**
399  * \brief this function will free memory associated with DetectFilemagicData
400  *
401  * \param filemagic pointer to DetectFilemagicData
402  */
403 static void DetectFilemagicFree(void *ptr)
404 {
405  if (ptr != NULL) {
406  DetectFilemagicData *filemagic = (DetectFilemagicData *)ptr;
407  if (filemagic->bm_ctx != NULL) {
408  BoyerMooreCtxDeInit(filemagic->bm_ctx);
409  }
410  if (filemagic->name != NULL)
411  SCFree(filemagic->name);
412  SCFree(filemagic);
413  }
414 }
415 
416 #ifdef UNITTESTS /* UNITTESTS */
417 
418 /**
419  * \test DetectFilemagicTestParse01
420  */
421 static int DetectFilemagicTestParse01 (void)
422 {
423  DetectFilemagicData *dnd = DetectFilemagicParse("secret.pdf", false);
424  if (dnd != NULL) {
425  DetectFilemagicFree(dnd);
426  return 1;
427  }
428  return 0;
429 }
430 
431 /**
432  * \test DetectFilemagicTestParse02
433  */
434 static int DetectFilemagicTestParse02 (void)
435 {
436  int result = 0;
437 
438  DetectFilemagicData *dnd = DetectFilemagicParse("backup.tar.gz", false);
439  if (dnd != NULL) {
440  if (dnd->len == 13 && memcmp(dnd->name, "backup.tar.gz", 13) == 0) {
441  result = 1;
442  }
443 
444  DetectFilemagicFree(dnd);
445  return result;
446  }
447  return 0;
448 }
449 
450 /**
451  * \test DetectFilemagicTestParse03
452  */
453 static int DetectFilemagicTestParse03 (void)
454 {
455  int result = 0;
456 
457  DetectFilemagicData *dnd = DetectFilemagicParse("cmd.exe", false);
458  if (dnd != NULL) {
459  if (dnd->len == 7 && memcmp(dnd->name, "cmd.exe", 7) == 0) {
460  result = 1;
461  }
462 
463  DetectFilemagicFree(dnd);
464  return result;
465  }
466  return 0;
467 }
468 
469 #endif /* UNITTESTS */
470 
471 /**
472  * \brief this function registers unit tests for DetectFilemagic
473  */
474 void DetectFilemagicRegisterTests(void)
475 {
476 #ifdef UNITTESTS /* UNITTESTS */
477  UtRegisterTest("DetectFilemagicTestParse01", DetectFilemagicTestParse01);
478  UtRegisterTest("DetectFilemagicTestParse02", DetectFilemagicTestParse02);
479  UtRegisterTest("DetectFilemagicTestParse03", DetectFilemagicTestParse03);
480 #endif /* UNITTESTS */
481 }
482 
483 #endif /* HAVE_MAGIC */
484 
SigTableElmt sigmatch_table[DETECT_TBLSIZE]
Definition: detect.h:1406
SignatureInitData * init_data
Definition: detect.h:563
uint16_t flags
int(* Setup)(DetectEngineCtx *, Signature *, const char *)
Definition: detect.h:1149
#define SCLogDebug(...)
Definition: util-debug.h:335
#define BUG_ON(x)
#define unlikely(expr)
Definition: util-optimize.h:35
void * DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *det_ctx, int id)
Retrieve thread local keyword ctx by id.
uint64_t FileDataSize(const File *file)
get the size of the file data
Definition: util-file.c:277
int DetectContentDataParse(const char *keyword, const char *contentstr, uint8_t **pstr, uint16_t *plen)
Parse a content string, ie "abc|DE|fgh".
uint64_t offset
const char * name
Definition: detect.h:1163
Signature container.
Definition: detect.h:495
Used to start a pointer to SigMatch context Should never be dereferenced without casting to something...
Definition: detect.h:317
int ConfGet(const char *name, const char **vptr)
Retrieve the value of a configuration node.
Definition: conf.c:331
StreamingBuffer * sb
Definition: util-file.h:68
main detection engine ctx
Definition: detect.h:723
int SCLogDebugEnabled(void)
Returns whether debug messages are enabled to be logged or not.
Definition: util-debug.c:631
uint8_t * BoyerMooreNocase(const uint8_t *x, uint16_t m, const uint8_t *y, uint32_t n, BmCtx *bm_ctx)
Boyer Moore search algorithm Is better as the pattern length increases and for big buffers to search ...
Definition: util-spm-bm.c:357
void BoyerMooreCtxDeInit(BmCtx *bmctx)
Free the memory allocated to Booyer Moore context.
Definition: util-spm-bm.c:125
#define str(s)
int(* FileMatch)(ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t flags, File *, const Signature *, const SigMatchCtx *)
Definition: detect.h:1140
Data structures and function prototypes for keeping state for the detection engine.
void(* Free)(void *)
Definition: detect.h:1154
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
#define FILE_SIG_NEED_MAGIC
Definition: detect.h:288
void UtRegisterTest(const char *name, int(*TestFn)(void))
Register unit test.
#define SIGMATCH_QUOTES_MANDATORY
Definition: detect.h:1347
#define SCEnter(...)
Definition: util-debug.h:337
int StreamingBufferGetData(const StreamingBuffer *sb, const uint8_t **data, uint32_t *data_len, uint64_t *stream_offset)
uint8_t type
Definition: detect.h:323
#define SCReturnInt(x)
Definition: util-debug.h:341
#define SCLogWarning(err_code,...)
Macro used to log WARNING messages.
Definition: util-debug.h:281
#define FILE_SIG_NEED_FILE
Definition: detect.h:286
const char * desc
Definition: detect.h:1165
void SigMatchAppendSMToList(Signature *s, SigMatch *new, int list)
Append a SigMatch to the list type.
Definition: detect-parse.c:288
int DetectBufferTypeRegister(const char *name)
int DetectRegisterThreadCtxFuncs(DetectEngineCtx *de_ctx, const char *name, void *(*InitFunc)(void *), void *data, void(*FreeFunc)(void *), int mode)
Register Thread keyword context Funcs.
SigMatchCtx * ctx
Definition: detect.h:325
#define SCMalloc(a)
Definition: util-mem.h:166
#define SCLogInfo(...)
Macro used to log INFORMATIONAL messages.
Definition: util-debug.h:254
#define DETECT_CONTENT_NEGATED
#define SCFree(a)
Definition: util-mem.h:228
const char * url
Definition: detect.h:1166
void DetectFilemagicRegister(void)
Registration function for keyword: filemagic.
FileState state
Definition: util-file.h:67
SCMutex m
Definition: flow-hash.h:105
BmCtx * BoyerMooreNocaseCtxInit(uint8_t *needle, uint16_t needle_len)
Setup a Booyer Moore context for nocase search.
Definition: util-spm-bm.c:111
#define DOC_URL
Definition: suricata.h:86
SigMatch * SigMatchAlloc(void)
Definition: detect-parse.c:232
Per thread variable structure.
Definition: threadvars.h:57
uint8_t file_flags
Definition: detect.h:509
#define SIGMATCH_HANDLE_NEGATION
Definition: detect.h:1351
#define DOC_VERSION
Definition: suricata.h:91
uint16_t flags
Definition: detect.h:1157
Flow data structure.
Definition: flow.h:325
void(* RegisterTests)(void)
Definition: detect.h:1155
a single match condition for a signature
Definition: detect.h:322