detect/filemagic: switch to file.magic implementation

Replace implementation of the legacy `filemagic` keyword by the
implementation for the `file.magic` variant. This leads to better
performance and hooks the rules into the detection engine better.

Remove unittests that depended on the old logic.

Ticket: #6194.
pull/9149/head
Victor Julien 2 years ago
parent ee68947460
commit 1f22b51848

@ -1,4 +1,4 @@
/* Copyright (C) 2007-2022 Open Information Security Foundation
/* Copyright (C) 2007-2023 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -84,20 +84,7 @@ typedef struct DetectFilemagicThreadData {
magic_t ctx;
} DetectFilemagicThreadData;
typedef struct DetectFilemagicData {
uint8_t *name; /** name of the file to match */
BmCtx *bm_ctx; /** BM context */
uint16_t len; /** name length */
uint32_t flags;
} DetectFilemagicData;
static int DetectFilemagicMatch (DetectEngineThreadCtx *, Flow *,
uint8_t, File *, const Signature *, const SigMatchCtx *);
static int DetectFilemagicSetup (DetectEngineCtx *, Signature *, const char *);
#ifdef UNITTESTS
static void DetectFilemagicRegisterTests(void);
#endif
static void DetectFilemagicFree(DetectEngineCtx *, void *);
static int DetectFilemagicSetup(DetectEngineCtx *, Signature *, const char *);
static int g_file_match_list_id = 0;
static int DetectFilemagicSetupSticky(DetectEngineCtx *de_ctx, Signature *s, const char *str);
@ -119,12 +106,7 @@ void DetectFilemagicRegister(void)
sigmatch_table[DETECT_FILEMAGIC].name = "filemagic";
sigmatch_table[DETECT_FILEMAGIC].desc = "match on the information libmagic returns about a file";
sigmatch_table[DETECT_FILEMAGIC].url = "/rules/file-keywords.html#filemagic";
sigmatch_table[DETECT_FILEMAGIC].FileMatch = DetectFilemagicMatch;
sigmatch_table[DETECT_FILEMAGIC].Setup = DetectFilemagicSetup;
sigmatch_table[DETECT_FILEMAGIC].Free = DetectFilemagicFree;
#ifdef UNITTESTS
sigmatch_table[DETECT_FILEMAGIC].RegisterTests = DetectFilemagicRegisterTests;
#endif
sigmatch_table[DETECT_FILEMAGIC].flags = SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION;
sigmatch_table[DETECT_FILEMAGIC].alternative = DETECT_FILE_MAGIC;
@ -194,127 +176,6 @@ int FilemagicThreadLookup(magic_t *ctx, File *file)
SCReturnInt(0);
}
/**
* \brief match the specified filemagic
*
* \param t thread local vars
* \param det_ctx pattern matcher thread local data
* \param f *LOCKED* flow
* \param flags direction flags
* \param file file being inspected
* \param s signature being inspected
* \param m sigmatch that we will cast into DetectFilemagicData
*
* \retval 0 no match
* \retval 1 match
*/
static int DetectFilemagicMatch (DetectEngineThreadCtx *det_ctx,
Flow *f, uint8_t flags, File *file, const Signature *s, const SigMatchCtx *m)
{
SCEnter();
int ret = 0;
DetectFilemagicData *filemagic = (DetectFilemagicData *)m;
DetectFilemagicThreadData *tfilemagic =
(DetectFilemagicThreadData *)DetectThreadCtxGetKeywordThreadCtx(det_ctx, g_magic_thread_ctx_id);
if (tfilemagic == NULL) {
SCReturnInt(0);
}
if (file->magic == NULL) {
FilemagicThreadLookup(&tfilemagic->ctx, file);
}
if (file->magic != NULL) {
SCLogDebug("magic %s", file->magic);
/* we include the \0 in the inspection, so patterns can match on the
* end of the string. */
if (BoyerMooreNocase(filemagic->name, filemagic->len, (uint8_t *)file->magic,
strlen(file->magic) + 1, filemagic->bm_ctx) != NULL)
{
#ifdef DEBUG
if (SCLogDebugEnabled()) {
char *name = SCMalloc(filemagic->len + 1);
if (name != NULL) {
memcpy(name, filemagic->name, filemagic->len);
name[filemagic->len] = '\0';
SCLogDebug("will look for filemagic %s", name);
SCFree(name);
}
}
#endif
if (!(filemagic->flags & DETECT_CONTENT_NEGATED)) {
ret = 1;
}
} else if (filemagic->flags & DETECT_CONTENT_NEGATED) {
SCLogDebug("negated match");
ret = 1;
}
}
SCReturnInt(ret);
}
/**
* \brief Parse the filemagic keyword
*
* \param de_ctx Pointer to the detection engine context
* \param idstr Pointer to the user provided option
*
* \retval filemagic pointer to DetectFilemagicData on success
* \retval NULL on failure
*/
static DetectFilemagicData *DetectFilemagicParse (DetectEngineCtx *de_ctx, const char *str, bool negate)
{
DetectFilemagicData *filemagic = NULL;
/* We have a correct filemagic option */
filemagic = SCMalloc(sizeof(DetectFilemagicData));
if (unlikely(filemagic == NULL))
goto error;
memset(filemagic, 0x00, sizeof(DetectFilemagicData));
if (DetectContentDataParse ("filemagic", str, &filemagic->name, &filemagic->len) == -1) {
goto error;
}
filemagic->bm_ctx = BoyerMooreNocaseCtxInit(filemagic->name, filemagic->len);
if (filemagic->bm_ctx == NULL) {
goto error;
}
if (negate) {
filemagic->flags |= DETECT_CONTENT_NEGATED;
}
SCLogDebug("flags %02X", filemagic->flags);
if (filemagic->flags & DETECT_CONTENT_NEGATED) {
SCLogDebug("negated filemagic");
}
#ifdef DEBUG
if (SCLogDebugEnabled()) {
char *name = SCMalloc(filemagic->len + 1);
if (name != NULL) {
memcpy(name, filemagic->name, filemagic->len);
name[filemagic->len] = '\0';
SCLogDebug("will look for filemagic %s", name);
SCFree(name);
}
}
#endif
return filemagic;
error:
if (filemagic != NULL)
DetectFilemagicFree(de_ctx, filemagic);
return NULL;
}
static void *DetectFilemagicThreadInit(void *data /*@unused@*/)
{
DetectFilemagicThreadData *t = SCCalloc(1, sizeof(DetectFilemagicThreadData));
@ -359,54 +220,40 @@ static void DetectFilemagicThreadFree(void *ctx)
*/
static int DetectFilemagicSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
{
SigMatch *sm = NULL;
if (s->init_data->transforms.cnt) {
SCLogError("previous transforms not consumed before 'filemagic'");
SCReturnInt(-1);
}
s->init_data->list = DETECT_SM_LIST_NOTSET;
s->file_flags |= (FILE_SIG_NEED_FILE | FILE_SIG_NEED_MAGIC);
DetectFilemagicData *filemagic = DetectFilemagicParse(de_ctx, str, s->init_data->negated);
if (filemagic == NULL)
if (DetectContentSetup(de_ctx, s, str) < 0) {
return -1;
}
g_magic_thread_ctx_id = DetectRegisterThreadCtxFuncs(
de_ctx, "filemagic", DetectFilemagicThreadInit, NULL, DetectFilemagicThreadFree, 1);
if (g_magic_thread_ctx_id == -1)
goto error;
/* Okay so far so good, lets get this into a SigMatch
* and put it in the Signature. */
sm = SigMatchAlloc();
SigMatch *sm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
if (sm == NULL)
goto error;
sm->type = DETECT_FILEMAGIC;
sm->ctx = (void *)filemagic;
SigMatchAppendSMToList(s, sm, g_file_match_list_id);
s->file_flags |= (FILE_SIG_NEED_FILE|FILE_SIG_NEED_MAGIC);
return 0;
return -1;
error:
DetectFilemagicFree(de_ctx, filemagic);
if (sm != NULL)
SCFree(sm);
return -1;
}
DetectContentData *cd = (DetectContentData *)sm->ctx;
cd->flags |= DETECT_CONTENT_NOCASE;
/* Recreate the context with nocase chars */
SpmDestroyCtx(cd->spm_ctx);
cd->spm_ctx = SpmInitCtx(cd->content, cd->content_len, 1, de_ctx->spm_global_thread_ctx);
if (cd->spm_ctx == NULL) {
return -1;
}
if (DetectEngineContentModifierBufferSetup(
de_ctx, s, NULL, DETECT_FILE_MAGIC, g_file_magic_buffer_id, s->alproto) < 0)
return -1;
/**
* \brief this function will free memory associated with DetectFilemagicData
*
* \param filemagic pointer to DetectFilemagicData
*/
static void DetectFilemagicFree(DetectEngineCtx *de_ctx, void *ptr)
{
if (ptr != NULL) {
DetectFilemagicData *filemagic = (DetectFilemagicData *)ptr;
if (filemagic->bm_ctx != NULL) {
BoyerMooreCtxDeInit(filemagic->bm_ctx);
}
if (filemagic->name != NULL)
SCFree(filemagic->name);
SCFree(filemagic);
if (g_magic_thread_ctx_id == -1) {
g_magic_thread_ctx_id = DetectRegisterThreadCtxFuncs(
de_ctx, "filemagic", DetectFilemagicThreadInit, NULL, DetectFilemagicThreadFree, 1);
if (g_magic_thread_ctx_id == -1)
return -1;
}
return 0;
}
/* file.magic implementation */
@ -577,54 +424,5 @@ static int PrefilterMpmFilemagicRegister(DetectEngineCtx *de_ctx, SigGroupHead *
mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
pectx, PrefilterMpmFilemagicFree, mpm_reg->pname);
}
#ifdef UNITTESTS /* UNITTESTS */
/**
* \test DetectFilemagicTestParse01
*/
static int DetectFilemagicTestParse01 (void)
{
DetectFilemagicData *dnd = DetectFilemagicParse(NULL, "secret.pdf", false);
FAIL_IF_NULL(dnd);
DetectFilemagicFree(NULL, dnd);
PASS;
}
/**
* \test DetectFilemagicTestParse02
*/
static int DetectFilemagicTestParse02 (void)
{
DetectFilemagicData *dnd = DetectFilemagicParse(NULL, "backup.tar.gz", false);
FAIL_IF_NULL(dnd);
FAIL_IF_NOT(dnd->len == 13);
FAIL_IF_NOT(memcmp(dnd->name, "backup.tar.gz", 13) == 0);
DetectFilemagicFree(NULL, dnd);
PASS;
}
/**
* \test DetectFilemagicTestParse03
*/
static int DetectFilemagicTestParse03 (void)
{
DetectFilemagicData *dnd = DetectFilemagicParse(NULL, "cmd.exe", false);
FAIL_IF_NULL(dnd);
FAIL_IF_NOT(dnd->len == 7);
FAIL_IF_NOT(memcmp(dnd->name, "cmd.exe", 7) == 0);
DetectFilemagicFree(NULL, dnd);
PASS;
}
/**
* \brief this function registers unit tests for DetectFilemagic
*/
void DetectFilemagicRegisterTests(void)
{
UtRegisterTest("DetectFilemagicTestParse01", DetectFilemagicTestParse01);
UtRegisterTest("DetectFilemagicTestParse02", DetectFilemagicTestParse02);
UtRegisterTest("DetectFilemagicTestParse03", DetectFilemagicTestParse03);
}
#endif /* UNITTESTS */
#endif /* HAVE_MAGIC */

Loading…
Cancel
Save