PageRenderTime 56ms CodeModel.GetById 22ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/src/detect-filemagic.c

https://github.com/decanio/suricata-tilera
C | 491 lines | 310 code | 80 blank | 101 comment | 109 complexity | 32db0ca332d03921b03950738690a688 MD5 | raw 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 */
 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#include "util-magic.h"
 57
 58static int DetectFilemagicMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *,
 59        uint8_t, File *, Signature *, SigMatch *);
 60static int DetectFilemagicSetup (DetectEngineCtx *, Signature *, char *);
 61static void DetectFilemagicRegisterTests(void);
 62static void DetectFilemagicFree(void *);
 63
 64/**
 65 * \brief Registration function for keyword: filemagic
 66 */
 67void DetectFilemagicRegister(void) {
 68    sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
 69    sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
 70    sigmatch_table[DETECT_FILEMAGIC].url = "https://redmine.openinfosecfoundation.org/projects/suricata/wiki/File-keywords#filemagic";
 71    sigmatch_table[DETECT_FILEMAGIC].FileMatch = DetectFilemagicMatch;
 72    sigmatch_table[DETECT_FILEMAGIC].alproto = ALPROTO_HTTP;
 73    sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetup;
 74    sigmatch_table[DETECT_FILEMAGIC].Free  = DetectFilemagicFree;
 75    sigmatch_table[DETECT_FILEMAGIC].RegisterTests = DetectFilemagicRegisterTests;
 76
 77	SCLogDebug("registering filemagic rule option");
 78    return;
 79}
 80
 81#define FILEMAGIC_MIN_SIZE  512
 82
 83/**
 84 *  \brief run the magic check
 85 *
 86 *  \param file the file
 87 *
 88 *  \retval -1 error
 89 *  \retval 0 ok
 90 */
 91int FilemagicGlobalLookup(File *file) {
 92    if (file == NULL || file->chunks_head == NULL) {
 93        SCReturnInt(-1);
 94    }
 95
 96    /* initial chunk already matching our requirement */
 97    if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
 98        file->magic = MagicGlobalLookup(file->chunks_head->data, FILEMAGIC_MIN_SIZE);
 99    } else {
100        uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
101        uint32_t size = 0;
102
103        if (likely(buf != NULL)) {
104            FileData *ffd = file->chunks_head;
105
106            for ( ; ffd != NULL; ffd = ffd->next) {
107                uint32_t copy_len = ffd->len;
108                if (size + ffd->len > FILEMAGIC_MIN_SIZE)
109                    copy_len = FILEMAGIC_MIN_SIZE - size;
110
111                memcpy(buf + size, ffd->data, copy_len);
112                size += copy_len;
113
114                if (size >= FILEMAGIC_MIN_SIZE) {
115                    file->magic = MagicGlobalLookup(buf, size);
116                    break;
117                }
118                /* file is done but smaller than FILEMAGIC_MIN_SIZE */
119                if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
120                    file->magic = MagicGlobalLookup(buf, size);
121                    break;
122                }
123            }
124
125            SCFree(buf);
126        }
127    }
128
129    SCReturnInt(0);
130}
131
132/**
133 *  \brief run the magic check
134 *
135 *  \param file the file
136 *
137 *  \retval -1 error
138 *  \retval 0 ok
139 */
140int FilemagicThreadLookup(magic_t *ctx, File *file) {
141    if (ctx == NULL || file == NULL || file->chunks_head == NULL) {
142        SCReturnInt(-1);
143    }
144
145    /* initial chunk already matching our requirement */
146    if (file->chunks_head->len >= FILEMAGIC_MIN_SIZE) {
147        file->magic = MagicThreadLookup(ctx, file->chunks_head->data, FILEMAGIC_MIN_SIZE);
148    } else {
149        uint8_t *buf = SCMalloc(FILEMAGIC_MIN_SIZE);
150        uint32_t size = 0;
151
152        if (likely(buf != NULL)) {
153            FileData *ffd = file->chunks_head;
154
155            for ( ; ffd != NULL; ffd = ffd->next) {
156                uint32_t copy_len = ffd->len;
157                if (size + ffd->len > FILEMAGIC_MIN_SIZE)
158                    copy_len = FILEMAGIC_MIN_SIZE - size;
159
160                memcpy(buf + size, ffd->data, copy_len);
161                size += copy_len;
162
163                if (size >= FILEMAGIC_MIN_SIZE) {
164                    file->magic = MagicThreadLookup(ctx, buf, size);
165                    break;
166                }
167                /* file is done but smaller than FILEMAGIC_MIN_SIZE */
168                if (ffd->next == NULL && file->state >= FILE_STATE_CLOSED) {
169                    file->magic = MagicThreadLookup(ctx, buf, size);
170                    break;
171                }
172            }
173
174            SCFree(buf);
175        }
176    }
177
178    SCReturnInt(0);
179}
180
181/**
182 * \brief match the specified filemagic
183 *
184 * \param t thread local vars
185 * \param det_ctx pattern matcher thread local data
186 * \param f *LOCKED* flow
187 * \param flags direction flags
188 * \param file file being inspected
189 * \param s signature being inspected
190 * \param m sigmatch that we will cast into DetectFilemagicData
191 *
192 * \retval 0 no match
193 * \retval 1 match
194 */
195static int DetectFilemagicMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx,
196        Flow *f, uint8_t flags, File *file, Signature *s, SigMatch *m)
197{
198    SCEnter();
199    int ret = 0;
200    DetectFilemagicData *filemagic = (DetectFilemagicData *)m->ctx;
201
202    if (file->txid < det_ctx->tx_id)
203        SCReturnInt(0);
204
205    if (file->txid > det_ctx->tx_id)
206        SCReturnInt(0);
207
208    DetectFilemagicThreadData *tfilemagic = (DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, filemagic->thread_ctx_id);
209    if (tfilemagic == NULL) {
210        SCReturnInt(0);
211    }
212
213    if (file->magic == NULL) {
214        FilemagicThreadLookup(&tfilemagic->ctx, file);
215    }
216
217    if (file->magic != NULL) {
218        SCLogDebug("magic %s", file->magic);
219
220        /* we include the \0 in the inspection, so patterns can match on the
221         * end of the string. */
222        if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic,
223                    strlen(file->magic) + 1, filemagic->bm_ctx->bmGs,
224                    filemagic->bm_ctx->bmBc) != NULL)
225        {
226#ifdef DEBUG
227            if (SCLogDebugEnabled()) {
228                char *name = SCMalloc(filemagic->len + 1);
229                if (name != NULL) {
230                    memcpy(name, filemagic->name, filemagic->len);
231                    name[filemagic->len] = '\0';
232                    SCLogDebug("will look for filemagic %s", name);
233                }
234            }
235#endif
236
237            if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) {
238                ret = 1;
239            }
240        } else if (filemagic->flags & DETECT_CONTENT_NEGATED) {
241            SCLogDebug("negated match");
242            ret = 1;
243        }
244    }
245
246    SCReturnInt(ret);
247}
248
249/**
250 * \brief Parse the filemagic keyword
251 *
252 * \param idstr Pointer to the user provided option
253 *
254 * \retval filemagic pointer to DetectFilemagicData on success
255 * \retval NULL on failure
256 */
257static DetectFilemagicData *DetectFilemagicParse (char *str)
258{
259    DetectFilemagicData *filemagic = NULL;
260
261    /* We have a correct filemagic option */
262    filemagic = SCMalloc(sizeof(DetectFilemagicData));
263    if (unlikely(filemagic == NULL))
264        goto error;
265
266    memset(filemagic, 0x00, sizeof(DetectFilemagicData));
267
268    if (DetectContentDataParse ("filemagic", str, &filemagic->name, &filemagic->len, &filemagic->flags) == -1) {
269        goto error;
270    }
271
272    filemagic->bm_ctx = BoyerMooreCtxInit(filemagic->name, filemagic->len);
273    if (filemagic->bm_ctx == NULL) {
274        goto error;
275    }
276
277    SCLogDebug("flags %02X", filemagic->flags);
278    if (filemagic->flags & DETECT_CONTENT_NEGATED) {
279        SCLogDebug("negated filemagic");
280    }
281
282    BoyerMooreCtxToNocase(filemagic->bm_ctx, filemagic->name, filemagic->len);
283#ifdef DEBUG
284    if (SCLogDebugEnabled()) {
285        char *name = SCMalloc(filemagic->len + 1);
286        if (name != NULL) {
287            memcpy(name, filemagic->name, filemagic->len);
288            name[filemagic->len] = '\0';
289            SCLogDebug("will look for filemagic %s", name);
290        }
291    }
292#endif
293
294    return filemagic;
295
296error:
297    if (filemagic != NULL)
298        DetectFilemagicFree(filemagic);
299    return NULL;
300}
301
302static void *DetectFilemagicThreadInit(void *data) {
303    char *filename = NULL;
304    FILE *fd = NULL;
305    DetectFilemagicData *filemagic = (DetectFilemagicData *)data;
306    BUG_ON(filemagic == NULL);
307
308    DetectFilemagicThreadData *t = SCMalloc(sizeof(DetectFilemagicThreadData));
309    if (unlikely(t == NULL)) {
310        SCLogError(SC_ERR_LUAJIT_ERROR, "couldn't alloc ctx memory");
311        return NULL;
312    }
313    memset(t, 0x00, sizeof(DetectFilemagicThreadData));
314
315    t->ctx = magic_open(0);
316    if (t->ctx == NULL) {
317        SCLogError(SC_ERR_MAGIC_OPEN, "magic_open failed: %s", magic_error(t->ctx));
318        goto error;
319    }
320
321    (void)ConfGet("magic-file", &filename);
322    if (filename != NULL) {
323        SCLogInfo("using magic-file %s", filename);
324
325        if ( (fd = fopen(filename, "r")) == NULL) {
326            SCLogWarning(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
327            goto error;
328        }
329        fclose(fd);
330    }
331
332    if (magic_load(t->ctx, filename) != 0) {
333        SCLogError(SC_ERR_MAGIC_LOAD, "magic_load failed: %s", magic_error(t->ctx));
334        goto error;
335    }
336
337    return (void *)t;
338
339error:
340    if (t->ctx)
341        magic_close(t->ctx);
342    SCFree(t);
343    return NULL;
344}
345
346static void DetectFilemagicThreadFree(void *ctx) {
347    if (ctx != NULL) {
348        DetectFilemagicThreadData *t = (DetectFilemagicThreadData *)ctx;
349        if (t->ctx)
350            magic_close(t->ctx);
351        SCFree(t);
352    }
353}
354
355/**
356 * \brief this function is used to parse filemagic options
357 * \brief into the current signature
358 *
359 * \param de_ctx pointer to the Detection Engine Context
360 * \param s pointer to the Current Signature
361 * \param str pointer to the user provided "filemagic" option
362 *
363 * \retval 0 on Success
364 * \retval -1 on Failure
365 */
366static int DetectFilemagicSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
367{
368    DetectFilemagicData *filemagic = NULL;
369    SigMatch *sm = NULL;
370
371    filemagic = DetectFilemagicParse(str);
372    if (filemagic == NULL)
373        goto error;
374
375    filemagic->thread_ctx_id = DetectRegisterThreadCtxFuncs(de_ctx, "filemagic",
376            DetectFilemagicThreadInit, (void *)filemagic,
377            DetectFilemagicThreadFree, 1);
378    if (filemagic->thread_ctx_id == -1)
379        goto error;
380
381    /* Okay so far so good, lets get this into a SigMatch
382     * and put it in the Signature. */
383    sm = SigMatchAlloc();
384    if (sm == NULL)
385        goto error;
386
387    sm->type = DETECT_FILEMAGIC;
388    sm->ctx = (void *)filemagic;
389
390    SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_FILEMATCH);
391
392    if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
393        SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
394        goto error;
395    }
396
397    AppLayerHtpNeedFileInspection();
398
399    /** \todo remove this once we support more than http */
400    s->alproto = ALPROTO_HTTP;
401
402    s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_MAGIC);
403    return 0;
404
405error:
406    if (filemagic != NULL)
407        DetectFilemagicFree(filemagic);
408    if (sm != NULL)
409        SCFree(sm);
410    return -1;
411}
412
413/**
414 * \brief this function will free memory associated with DetectFilemagicData
415 *
416 * \param filemagic pointer to DetectFilemagicData
417 */
418static void DetectFilemagicFree(void *ptr) {
419    if (ptr != NULL) {
420        DetectFilemagicData *filemagic = (DetectFilemagicData *)ptr;
421        if (filemagic->bm_ctx != NULL) {
422            BoyerMooreCtxDeInit(filemagic->bm_ctx);
423        }
424        if (filemagic->name != NULL)
425            SCFree(filemagic->name);
426        SCFree(filemagic);
427    }
428}
429
430#ifdef UNITTESTS /* UNITTESTS */
431
432/**
433 * \test DetectFilemagicTestParse01
434 */
435int DetectFilemagicTestParse01 (void) {
436    DetectFilemagicData *dnd = DetectFilemagicParse("\"secret.pdf\"");
437    if (dnd != NULL) {
438        DetectFilemagicFree(dnd);
439        return 1;
440    }
441    return 0;
442}
443
444/**
445 * \test DetectFilemagicTestParse02
446 */
447int DetectFilemagicTestParse02 (void) {
448    int result = 0;
449
450    DetectFilemagicData *dnd = DetectFilemagicParse("\"backup.tar.gz\"");
451    if (dnd != NULL) {
452        if (dnd->len == 13 && memcmp(dnd->name, "backup.tar.gz", 13) == 0) {
453            result = 1;
454        }
455
456        DetectFilemagicFree(dnd);
457        return result;
458    }
459    return 0;
460}
461
462/**
463 * \test DetectFilemagicTestParse03
464 */
465int DetectFilemagicTestParse03 (void) {
466    int result = 0;
467
468    DetectFilemagicData *dnd = DetectFilemagicParse("\"cmd.exe\"");
469    if (dnd != NULL) {
470        if (dnd->len == 7 && memcmp(dnd->name, "cmd.exe", 7) == 0) {
471            result = 1;
472        }
473
474        DetectFilemagicFree(dnd);
475        return result;
476    }
477    return 0;
478}
479
480#endif /* UNITTESTS */
481
482/**
483 * \brief this function registers unit tests for DetectFilemagic
484 */
485void DetectFilemagicRegisterTests(void) {
486#ifdef UNITTESTS /* UNITTESTS */
487    UtRegisterTest("DetectFilemagicTestParse01", DetectFilemagicTestParse01, 1);
488    UtRegisterTest("DetectFilemagicTestParse02", DetectFilemagicTestParse02, 1);
489    UtRegisterTest("DetectFilemagicTestParse03", DetectFilemagicTestParse03, 1);
490#endif /* UNITTESTS */
491}