filename and fileext keywords

remotes/origin/master-1.2.x
Pablo Rincon 15 years ago committed by Victor Julien
parent 06b1d71032
commit 6d60b3a747

@ -47,6 +47,7 @@ flow-manager.c flow-manager.h \
flow-queue.c flow-queue.h \
flow-hash.c flow-hash.h \
flow-util.c flow-util.h \
flow-file.c flow-file.h \
util-mem.h \
flow-var.c flow-var.h \
flow-bit.c flow-bit.h \
@ -149,6 +150,8 @@ detect-detection-filter.c detect-detection-filter.h \
detect-http-client-body.c detect-http-client-body.h \
detect-http-stat-msg.c detect-http-stat-msg.h \
detect-asn1.c detect-asn1.h \
detect-filename.c detect-filename.h \
detect-fileext.c detect-fileext.h \
detect-http-stat-code.c detect-http-stat-code.h \
detect-ssl-version.c detect-ssl-version.h \
detect-ssl-state.c detect-ssl-state.h \

@ -59,6 +59,7 @@
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "flow-util.h"
#include "flow-file.h"
#include "detect-engine.h"
#include "detect-engine-state.h"
@ -87,6 +88,7 @@ static uint64_t htp_state_memcnt = 0;
#endif
static uint8_t need_htp_request_body = 0;
static uint8_t need_htp_file_inspection = 0;
#ifdef DEBUG
/**
@ -283,6 +285,19 @@ void AppLayerHtpEnableRequestBodyCallback(void)
SCReturn;
}
/**
* \brief Sets a flag that informs the HTP app layer that some module in the
* engine needs the http request body data.
* \initonly
*/
void AppLayerHtpNeedFileInspection(void)
{
SCEnter();
need_htp_request_body = 1;
need_htp_file_inspection = 1;
SCReturn;
}
/**
* \brief Function to handle the reassembled data from client and feed it to
@ -310,6 +325,7 @@ static int HTPHandleRequestData(Flow *f, void *htp_state,
//PrintRawDataFp(stdout, input, input_len);
HtpState *hstate = (HtpState *)htp_state;
hstate->f = f;
/* On the first invocation, create the connection parser structure to
* be used by HTP library. This is looked up via IP in the radix
@ -462,6 +478,7 @@ static int HTPHandleResponseData(Flow *f, void *htp_state,
int ret = 1;
HtpState *hstate = (HtpState *)htp_state;
hstate->f = f;
if (hstate->connp == NULL) {
SCLogError(SC_ERR_ALPARSER, "HTP state has no connp");
SCReturnInt(-1);
@ -638,7 +655,7 @@ error:
*/
void HtpBodyPrint(HtpBody *body)
{
if (SCLogDebugEnabled()) {
if (SCLogDebugEnabled()||1) {
SCEnter();
if (body->nchunks == 0)
@ -646,9 +663,12 @@ void HtpBodyPrint(HtpBody *body)
HtpBodyChunk *cur = NULL;
SCLogDebug("--- Start body chunks at %p ---", body);
printf("--- Start body chunks at %p ---\n", body);
for (cur = body->first; cur != NULL; cur = cur->next) {
SCLogDebug("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
body, cur->id, cur->data, (uint32_t)cur->len);
printf("Body %p; Chunk id: %"PRIu32", data %p, len %"PRIu32"\n",
body, cur->id, cur->data, (uint32_t)cur->len);
PrintRawDataFp(stdout, (uint8_t*)cur->data, cur->len);
}
SCLogDebug("--- End body chunks at %p ---", body);
@ -724,6 +744,9 @@ static int HTPCallbackRequestUriNormalize(htp_connp_t *c)
int HTPCallbackRequestBodyData(htp_tx_data_t *d)
{
SCEnter();
const char *GROUP_DELIM = ";\r\n";
const char *FIELD_DELIM = ":= ";
HtpState *hstate = (HtpState *)d->tx->connp->user_data;
SCLogDebug("New response body data available at %p -> %p -> %p, bodylen "
"%"PRIu32"", hstate, d, d->data, (uint32_t)d->len);
@ -742,6 +765,52 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
if (cl != NULL)
htud->content_len = htp_parse_content_length(cl->value);
htp_header_t *h = (htp_header_t *)table_getc(d->tx->request_headers,
"Content-Type");
if (h != NULL && bstr_len(h->value) > 0) {
uint8_t *content_type = (uint8_t*) SCMalloc(bstr_len(h->value));
if (content_type == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Could not allocate memory for content type");
exit(EXIT_FAILURE);
}
memcpy(content_type, (uint8_t*) bstr_ptr(h->value), bstr_len(h->value));
char *tok = NULL;
char *ftok = NULL;
char *lctx1 = NULL;
char *lctx2 = NULL;
tok = strtok_r((char *) content_type, GROUP_DELIM, &lctx1);
if (tok == NULL) {
SCLogWarning(SC_ERR_INVALID_VALUE, "Malformed content-type field (null)");
} else {
htud->contenttype = SCStrdup(tok);
htud->contenttype_len = strlen(tok);
if (strcasecmp("multipart/form-data", tok) == 0) {
/* We have a form, let's see the boundary */
tok = strtok_r(NULL, GROUP_DELIM, &lctx1);
while (tok != NULL) {
ftok = strtok_r(tok, FIELD_DELIM, &lctx2);
if (ftok == NULL) {
continue;
}
if (strcasecmp("boundary", ftok) == 0) {
ftok = strtok_r(NULL, FIELD_DELIM, &lctx2);
if (ftok == NULL) {
continue;
}
htud->boundary = SCStrdup(ftok);
htud->boundary_len = strlen(ftok);
htud->flags |= HTP_BOUNDARY_SET;
}
tok = strtok_r(NULL, GROUP_DELIM, &lctx1);
}
}
}
SCFree(content_type);
}
/* Set the user data for handling body chunks on this transaction */
htp_tx_set_user_data(d->tx, htud);
}
@ -776,11 +845,179 @@ int HTPCallbackRequestBodyData(htp_tx_data_t *d)
htud->flags |= HTP_BODY_COMPLETE;
}
if (htud->flags & HTP_BOUNDARY_SET) {
/* Checkout the boundaries */
if ( !(htud->flags & HTP_BOUNDARY_OPEN)) {
HtpBodyChunk *cur = htud->body.first;
uint8_t *chunks_buffer = NULL;
int32_t chunks_buffer_len = 0;
//TODO: Now that we are concatenating chunks here, free the list
//TODO: of chunks and append only 1 big chunk
while (cur != NULL) {
chunks_buffer_len += cur->len;
if ( (chunks_buffer = SCRealloc(chunks_buffer, chunks_buffer_len)) == NULL) {
goto end;
}
memcpy(chunks_buffer + chunks_buffer_len - cur->len, cur->data, cur->len);
cur = cur->next;
}
//PrintRawDataFp(stdout, chunks_buffer, chunks_buffer_len);
if (chunks_buffer != NULL) {
char *expected_boundary = (char*)SCMalloc(htud->boundary_len + 4);
if (expected_boundary == NULL) {
goto end;
}
expected_boundary[0]='-';
expected_boundary[1]='-';
strncat(expected_boundary + 2, (char *)htud->boundary, htud->boundary_len + 4);
char *expected_boundary_end = (char*)SCMalloc(htud->boundary_len + 6);
if (expected_boundary_end == NULL) {
goto end;
}
expected_boundary_end[0]='-';
expected_boundary_end[1]='-';
strncat(expected_boundary_end + 2, (char *)htud->boundary, htud->boundary_len + 4);
strncat(expected_boundary_end, "--", htud->boundary_len + 6);
uint8_t *filename = NULL;
uint8_t *filetype = NULL;
uint8_t *header_start = Bs2bmSearch(chunks_buffer, chunks_buffer_len, (uint8_t *) expected_boundary, strlen(expected_boundary));
uint8_t *header_end = Bs2bmSearch(chunks_buffer, chunks_buffer_len, (uint8_t *)"\r\n\r\n", strlen("\r\n\r\n"));
uint8_t *form_end = Bs2bmSearch(chunks_buffer, chunks_buffer_len, (uint8_t *) expected_boundary_end, strlen(expected_boundary_end));
SCLogDebug("Expected boundary: %s", expected_boundary);
SCLogDebug("Expected boundary_end: %s", expected_boundary_end);
uint8_t *header = NULL;
while (header_start != NULL && header_end != NULL &&
(header_end != form_end) &&
header_start < chunks_buffer + chunks_buffer_len &&
header_end < chunks_buffer + chunks_buffer_len && header_start < header_end) {
header = SCMalloc(sizeof(header_end - header_start) + 4);
memcpy(header, header_start, header_end - header_start);
header[header_end - header_start] = '\0';
header[1 + header_end - header_start] = '\0';
/*
printf("HEADER START: \n");
PrintRawDataFp(stdout, header, header_end - header_start);
printf("HEADER END: \n");
*/
char *ftok2 = NULL;
char *tok2 = NULL;
char *ctx1 = NULL;
char *ctx2 = NULL;
char *field = NULL;
tok2 = strtok_r((char *) header, GROUP_DELIM, &ctx1);
if (tok2 == NULL) {
SCLogWarning(SC_ERR_INVALID_VALUE, "Expecting Boundary headers");
} else {
SCLogDebug("Token: %s", tok2);
if (tok2 != NULL && strcasecmp(expected_boundary, tok2) == 0) {
tok2 = strtok_r(NULL, GROUP_DELIM, &ctx1);
/* We have the boundary! Extract filename, type, and so on */
while (tok2 != NULL && strlen(tok2) > 0) {
SCLogDebug("TOKEN: %s", tok2);
field = SCStrdup(tok2);
ftok2 = strtok_r(field, FIELD_DELIM, &ctx2);
while (ftok2 != NULL && strlen(ftok2) > 0) {
if(strcasecmp("filename", ftok2) == 0) {
ftok2 = strtok_r(NULL, FIELD_DELIM, &ctx2);
if (ftok2 != NULL) {
filename = SCStrdup(ftok2);
SCLogDebug("got filename %s", filename);
}
} else if(strcasecmp("Content-Type", ftok2) == 0) {
ftok2 = strtok_r(NULL, FIELD_DELIM, &ctx2);
if (ftok2 != NULL) {
filetype = SCStrdup(ftok2);
SCLogDebug("got proto filetype %s", filetype);
}
} else {
SCLogDebug("-> ftok2 %s len %d", ftok2, (int)strlen(ftok2));
}
ftok2 = strtok_r(NULL, FIELD_DELIM, &ctx2);
}
//SCFree(field);
tok2 = strtok_r(NULL, GROUP_DELIM, &ctx1);
}
}
//exit(EXIT_FAILURE);
}
if (filename != NULL) {
int i = 0;
SCMutexLock(&hstate->f->files_m);
FlowFileContainer *ffc = NULL;
/* We have to add/update the file */
SCLogDebug("Adding file entry to flow: %s", filename);
FlowFile *cur_file = NULL;
if (hstate->f->files == NULL) {
ffc = FlowFileContainerAlloc();
if (ffc == NULL) {
SCMutexUnlock(&hstate->f->files_m);
goto end;
}
hstate->f->files = ffc;
} else {
/* TODO: else append chunk, update things...*/
ffc = hstate->f->files;
}
cur_file = FlowFileContainerRetrieve(ffc, filename, ALPROTO_HTTP, filetype);
if (cur_file == NULL) {
cur_file = FlowFileAlloc();
cur_file->name = filename;
cur_file->name_len = strlen((char *)filename);
for (i = strlen((char *)filename) - 1; i >= 0 && filename[i] != '.'; i--);
if (filename[i] == '.') {
cur_file->ext = SCStrdup((char *)&filename[i]);
printf("File ext: %s\n", cur_file->ext);
}
if (filetype != NULL) {
cur_file->proto_type = filetype;
cur_file->proto_type_len = strlen((char *)filetype);
}
cur_file->alproto = ALPROTO_HTTP;
SCLogDebug("Added");
FlowFileContainerAdd(ffc, cur_file);
/* cur_file->ext = get extension */
}
SCMutexUnlock(&hstate->f->files_m);
} else if (filetype != NULL) {
/* We got a filetype but not the name of file, so removing */
SCFree(filetype);
}
filename = NULL;
filetype = NULL;
/* Search next boundary entry after the start of body */
if ( (form_end == NULL && header_end + 4 < chunks_buffer + chunks_buffer_len) || (form_end != NULL && header_end != NULL && header_end + 4 < form_end)) {
uint32_t cursizeread = header_end - chunks_buffer;
header_start = Bs2bmSearch(header_end + 4, chunks_buffer_len - (cursizeread + 4), (uint8_t *) expected_boundary, strlen(expected_boundary));
header_end = Bs2bmSearch(header_end + 4, chunks_buffer_len - (cursizeread + 4), (uint8_t *) "\r\n\r\n", strlen("\r\n\r\n"));
}
}
if (header != NULL) {
SCFree(header);
header = NULL;
}
}
}
}
end:
/* set the new chunk flag */
hstate->flags |= HTP_FLAG_NEW_BODY_SET;
//if (SCLogDebugEnabled()) {
// HtpBodyPrint(&htud->body);
//HtpBodyPrint(&htud->body);
//}
}

@ -85,8 +85,11 @@ typedef struct HtpBody_ {
or a response */
} HtpBody;
#define HTP_BODY_COMPLETE 0x01 /* body is complete or limit is reached,
either way, this is it. */
#define HTP_BODY_COMPLETE 0x01 /* body is complete or limit is reached,
either way, this is it. */
#define HTP_CONTENTTYPE_SET 0x02 /* We have the content type */
#define HTP_BOUNDARY_SET 0x04 /* We have a boundary string */
#define HTP_BOUNDARY_OPEN 0x08 /* We have a boundary string */
/** Now the Body Chunks will be stored per transaction, at
* the tx user data */
@ -97,6 +100,13 @@ typedef struct SCHtpTxUserData_ {
uint32_t content_len;
/* Holds the length of the htp request body seen so far */
uint32_t content_len_so_far;
/* Holds the boundary identificator string if any (used on multipart/form-data only) */
uint8_t *boundary;
uint32_t boundary_len;
/* Holds the content-type */
uint8_t *contenttype;
uint32_t contenttype_len;
uint8_t flags;
} SCHtpTxUserData;
@ -104,6 +114,7 @@ typedef struct HtpState_ {
htp_connp_t *connp; /**< Connection parser structure for
each connection */
Flow *f; /**< Needed to retrieve the original flow when usin HTPLib callbacks */
uint8_t flags;
uint16_t transaction_cnt;
uint16_t transaction_done;
@ -125,6 +136,7 @@ void AppLayerHtpRegisterExtraCallbacks(void);
/* To free the state from unittests using app-layer-htp */
void HTPStateFree(void *);
void AppLayerHtpEnableRequestBodyCallback(void);
void AppLayerHtpNeedFileInspection(void);
void AppLayerHtpPrintStats(void);
#endif /* __APP_LAYER_HTP_H__ */

@ -0,0 +1,298 @@
/* Copyright (C) 2007-2010 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
*/
#include "suricata-common.h"
#include "threads.h"
#include "debug.h"
#include "decode.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "detect-engine-state.h"
#include "flow.h"
#include "flow-var.h"
#include "flow-util.h"
#include "util-debug.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "util-spm-bm.h"
#include "app-layer.h"
#include "stream-tcp.h"
#include "detect-fileext.h"
/**
* \brief Regex for parsing the fileext string
*/
#define PARSE_REGEX "^\\s*\"\\s*(.+)\\s*\"\\s*$"
static pcre *parse_regex;
static pcre_extra *parse_regex_study;
int DetectFileextMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
static int DetectFileextSetup (DetectEngineCtx *, Signature *, char *);
void DetectFileextRegisterTests(void);
void DetectFileextFree(void *);
/**
* \brief Registration function for keyword: fileext
*/
void DetectFileextRegister(void) {
sigmatch_table[DETECT_FILEEXT].name = "fileext";
sigmatch_table[DETECT_FILEEXT].Match = NULL;
sigmatch_table[DETECT_FILEEXT].AppLayerMatch = DetectFileextMatch;
sigmatch_table[DETECT_FILEEXT].alproto = ALPROTO_HTTP;
sigmatch_table[DETECT_FILEEXT].Setup = DetectFileextSetup;
sigmatch_table[DETECT_FILEEXT].Free = DetectFileextFree;
sigmatch_table[DETECT_FILEEXT].RegisterTests = DetectFileextRegisterTests;
const char *eb;
int eo;
int opts = 0;
SCLogDebug("registering fileext rule option");
parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
if (parse_regex == NULL) {
SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
PARSE_REGEX, eo, eb);
goto error;
}
parse_regex_study = pcre_study(parse_regex, 0, &eb);
if (eb != NULL) {
SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
goto error;
}
return;
error:
return;
}
/**
* \brief match the specified file extension
*
* \param t pointer to thread vars
* \param det_ctx pointer to the pattern matcher thread
* \param p pointer to the current packet
* \param m pointer to the sigmatch that we will cast into DetectFileextData
*
* \retval 0 no match
* \retval 1 match
*/
int DetectFileextMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
{
SCEnter();
int ret = 0;
DetectFileextData *fileext = m->ctx;
SCMutexLock(&f->files_m);
if (f->files != NULL && f->files->cnt > 0) {
FlowFile *file = f->files->start;
for (; file != NULL; file = file->next) {
if (file != NULL && file->ext != NULL &&
BoyerMooreNocase(fileext->ext, fileext->len, file->ext,
file->ext_len, fileext->bm_ctx->bmGs, fileext->bm_ctx->bmBc) != NULL)
{
ret = 1;
SCLogDebug("File ext %s found", file->ext);
/* Stop searching */
break;
}
}
}
SCMutexUnlock(&f->files_m);
SCReturnInt(ret);
}
/**
* \brief This function is used to parse fileet
*
* \param str Pointer to the fileext value string
*
* \retval pointer to DetectFileextData on success
* \retval NULL on failure
*/
DetectFileextData *DetectFileextParse (char *str)
{
DetectFileextData *fileext = NULL;
#define MAX_SUBSTRINGS 30
int ret = 0, res = 0;
int ov[MAX_SUBSTRINGS];
ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
ov, MAX_SUBSTRINGS);
if (ret < 1 || ret > 3) {
SCLogError(SC_ERR_PCRE_MATCH, "invalid fileext option");
goto error;
}
if (ret > 1) {
const char *str_ptr;
res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
if (res < 0) {
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
goto error;
}
fileext = SCMalloc(sizeof(DetectFileextData));
if (fileext == NULL)
goto error;
memset(fileext, 0x00, sizeof(DetectFileextData));
/* Remove quotes if any and copy the filename */
if (str_ptr[0] == '"') {
fileext->ext = (uint8_t *)SCStrdup((char*)str_ptr + 1);
fileext->ext[strlen(str_ptr - 1)] = '\0';
} else {
fileext->ext = (uint8_t *)SCStrdup((char*)str_ptr);
}
if (fileext->ext == NULL) {
goto error;
}
fileext->len = strlen((char *) fileext->ext);
fileext->bm_ctx = BoyerMooreCtxInit(fileext->ext, fileext->len);
BoyerMooreCtxToNocase(fileext->bm_ctx, fileext->ext, fileext->len);
SCLogDebug("will look for fileext %s", fileext->ext);
}
return fileext;
error:
if (fileext != NULL)
DetectFileextFree(fileext);
return NULL;
}
/**
* \brief this function is used to add the parsed "id" option
* \brief into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param idstr pointer to the user provided "id" option
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectFileextSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
{
DetectFileextData *fileext= NULL;
SigMatch *sm = NULL;
fileext = DetectFileextParse(str);
if (fileext == NULL)
goto error;
/* Okay so far so good, lets get this into a SigMatch
* and put it in the Signature. */
sm = SigMatchAlloc();
if (sm == NULL)
goto error;
sm->type = DETECT_FILEEXT;
sm->ctx = (void *)fileext;
SigMatchAppendAppLayer(s, sm);
if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
goto error;
}
AppLayerHtpNeedFileInspection();
s->alproto = ALPROTO_HTTP;
return 0;
error:
if (fileext != NULL)
DetectFileextFree(fileext);
if (sm != NULL)
SCFree(sm);
return -1;
}
/**
* \brief this function will free memory associated with DetectFileextData
*
* \param fileext pointer to DetectFileextData
*/
void DetectFileextFree(void *ptr) {
DetectFileextData *fileext = (DetectFileextData *)ptr;
BoyerMooreCtxDeInit(fileext->bm_ctx);
SCFree(fileext);
}
#ifdef UNITTESTS /* UNITTESTS */
/**
* \test DetectFileextTestParse01
*/
int DetectFileextTestParse01 (void) {
return 0;
}
/**
* \test DetectFileextTestParse02
*/
int DetectFileextTestParse02 (void) {
return 0;
}
/**
* \test DetectFileextTestParse03
*/
int DetectFileextTestParse03 (void) {
return 1;
}
#include "stream-tcp-reassemble.h"
#endif /* UNITTESTS */
/**
* \brief this function registers unit tests for DetectFileext
*/
void DetectFileextRegisterTests(void) {
#ifdef UNITTESTS /* UNITTESTS */
UtRegisterTest("DetectFileextTestParse01", DetectFileextTestParse01, 1);
UtRegisterTest("DetectFileextTestParse02", DetectFileextTestParse02, 1);
UtRegisterTest("DetectFileextTestParse03", DetectFileextTestParse03, 1);
#endif /* UNITTESTS */
}

@ -0,0 +1,39 @@
/* Copyright (C) 2007-2010 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*/
#ifndef __DETECT_FILEEXT_H__
#define __DETECT_FILEEXT_H__
#include "util-spm-bm.h"
typedef struct DetectFileextData_ {
uint8_t *ext; /** file extension to match */
uint16_t len; /** length of the file */
BmCtx *bm_ctx;
uint8_t flags;
} DetectFileextData;
/* prototypes */
void DetectFileextRegister (void);
#endif /* __DETECT_FILEEXT_H__ */

@ -0,0 +1,299 @@
/* Copyright (C) 2007-2010 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*
*/
#include "suricata-common.h"
#include "threads.h"
#include "debug.h"
#include "decode.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "detect-engine-state.h"
#include "flow.h"
#include "flow-var.h"
#include "flow-util.h"
#include "util-debug.h"
#include "util-spm-bm.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "app-layer.h"
#include "stream-tcp.h"
#include "detect-filename.h"
/**
* \brief Regex for parsing the protoversion string
*/
#define PARSE_REGEX "^\\s*\"\\s*(.+)\\s*\"\\s*$"
static pcre *parse_regex;
static pcre_extra *parse_regex_study;
int DetectFilenameMatch (ThreadVars *, DetectEngineThreadCtx *, Flow *, uint8_t, void *, Signature *, SigMatch *);
static int DetectFilenameSetup (DetectEngineCtx *, Signature *, char *);
void DetectFilenameRegisterTests(void);
void DetectFilenameFree(void *);
/**
* \brief Registration function for keyword: filename
*/
void DetectFilenameRegister(void) {
sigmatch_table[DETECT_FILENAME].name = "filename";
sigmatch_table[DETECT_FILENAME].Match = NULL;
sigmatch_table[DETECT_FILENAME].AppLayerMatch = DetectFilenameMatch;
sigmatch_table[DETECT_FILENAME].alproto = ALPROTO_HTTP;
sigmatch_table[DETECT_FILENAME].Setup = DetectFilenameSetup;
sigmatch_table[DETECT_FILENAME].Free = DetectFilenameFree;
sigmatch_table[DETECT_FILENAME].RegisterTests = DetectFilenameRegisterTests;
const char *eb;
int eo;
int opts = 0;
SCLogDebug("registering filename rule option");
parse_regex = pcre_compile(PARSE_REGEX, opts, &eb, &eo, NULL);
if (parse_regex == NULL) {
SCLogError(SC_ERR_PCRE_COMPILE, "Compile of \"%s\" failed at offset %" PRId32 ": %s",
PARSE_REGEX, eo, eb);
goto error;
}
parse_regex_study = pcre_study(parse_regex, 0, &eb);
if (eb != NULL) {
SCLogError(SC_ERR_PCRE_STUDY, "pcre study failed: %s", eb);
goto error;
}
return;
error:
return;
}
/**
* \brief match the specified filename
*
* \param t pointer to thread vars
* \param det_ctx pointer to the pattern matcher thread
* \param p pointer to the current packet
* \param m pointer to the sigmatch that we will cast into DetectFilenameData
*
* \retval 0 no match
* \retval 1 match
*/
int DetectFilenameMatch (ThreadVars *t, DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, Signature *s, SigMatch *m)
{
SCEnter();
int ret = 0;
DetectFilenameData *filename = m->ctx;
SCMutexLock(&f->files_m);
if (f->files != NULL && f->files->cnt > 0) {
FlowFile *file = f->files->start;
for (; file != NULL; file = file->next) {
if (file != NULL && file->name != NULL &&
BoyerMooreNocase(filename->name, filename->len, file->name,
file->name_len, filename->bm_ctx->bmGs, filename->bm_ctx->bmBc) != NULL)
{
ret = 1;
SCLogDebug("File %s found", file->name);
/* Stop searching */
break;
}
}
}
SCMutexUnlock(&f->files_m);
SCReturnInt(ret);
}
/**
* \brief This function is used to parse IPV4 ip_id passed via keyword: "id"
*
* \param idstr Pointer to the user provided id option
*
* \retval filename pointer to DetectFilenameData on success
* \retval NULL on failure
*/
DetectFilenameData *DetectFilenameParse (char *str)
{
DetectFilenameData *filename = NULL;
#define MAX_SUBSTRINGS 30
int ret = 0, res = 0;
int ov[MAX_SUBSTRINGS];
ret = pcre_exec(parse_regex, parse_regex_study, str, strlen(str), 0, 0,
ov, MAX_SUBSTRINGS);
if (ret < 1 || ret > 3) {
SCLogError(SC_ERR_PCRE_MATCH, "invalid filename option");
goto error;
}
if (ret > 1) {
const char *str_ptr;
res = pcre_get_substring((char *)str, ov, MAX_SUBSTRINGS, 1, &str_ptr);
if (res < 0) {
SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed");
goto error;
}
/* We have a correct filename option */
filename = SCMalloc(sizeof(DetectFilenameData));
if (filename == NULL)
goto error;
memset(filename, 0x00, sizeof(DetectFilenameData));
if (str_ptr[0] == '"') {
filename->name = (uint8_t *)SCStrdup((char*)str_ptr + 1);
filename->name[strlen(str_ptr - 1)] = '\0';
} else {
filename->name = (uint8_t *)SCStrdup((char*)str_ptr);
}
if (filename->name == NULL) {
goto error;
}
filename->len = strlen((char *) filename->name);
filename->bm_ctx = BoyerMooreCtxInit(filename->name, filename->len);
BoyerMooreCtxToNocase(filename->bm_ctx, filename->name, filename->len);
SCLogDebug("will look for filename %s", filename->name);
}
return filename;
error:
if (filename != NULL)
DetectFilenameFree(filename);
return NULL;
}
/**
* \brief this function is used to parse filename options
* \brief into the current signature
*
* \param de_ctx pointer to the Detection Engine Context
* \param s pointer to the Current Signature
* \param str pointer to the user provided "filename" option
*
* \retval 0 on Success
* \retval -1 on Failure
*/
static int DetectFilenameSetup (DetectEngineCtx *de_ctx, Signature *s, char *str)
{
DetectFilenameData *filename = NULL;
SigMatch *sm = NULL;
filename = DetectFilenameParse(str);
if (filename == NULL)
goto error;
/* Okay so far so good, lets get this into a SigMatch
* and put it in the Signature. */
sm = SigMatchAlloc();
if (sm == NULL)
goto error;
sm->type = DETECT_FILENAME;
sm->ctx = (void *)filename;
SigMatchAppendAppLayer(s, sm);
if (s->alproto != ALPROTO_UNKNOWN && s->alproto != ALPROTO_HTTP) {
SCLogError(SC_ERR_CONFLICTING_RULE_KEYWORDS, "rule contains conflicting keywords.");
goto error;
}
AppLayerHtpNeedFileInspection();
s->alproto = ALPROTO_HTTP;
return 0;
error:
if (filename != NULL)
DetectFilenameFree(filename);
if (sm != NULL)
SCFree(sm);
return -1;
}
/**
* \brief this function will free memory associated with DetectFilenameData
*
* \param filename pointer to DetectFilenameData
*/
void DetectFilenameFree(void *ptr) {
DetectFilenameData *filename = (DetectFilenameData *)ptr;
BoyerMooreCtxDeInit(filename->bm_ctx);
SCFree(filename);
}
#ifdef UNITTESTS /* UNITTESTS */
/**
* \test DetectFilenameTestParse01
*/
int DetectFilenameTestParse01 (void) {
return 0;
}
/**
* \test DetectFilenameTestParse02
*/
int DetectFilenameTestParse02 (void) {
return 0;
}
/**
* \test DetectFilenameTestParse03
*/
int DetectFilenameTestParse03 (void) {
return 1;
}
#include "stream-tcp-reassemble.h"
#endif /* UNITTESTS */
/**
* \brief this function registers unit tests for DetectFilename
*/
void DetectFilenameRegisterTests(void) {
#ifdef UNITTESTS /* UNITTESTS */
UtRegisterTest("DetectFilenameTestParse01", DetectFilenameTestParse01, 1);
UtRegisterTest("DetectFilenameTestParse02", DetectFilenameTestParse02, 1);
UtRegisterTest("DetectFilenameTestParse03", DetectFilenameTestParse03, 1);
#endif /* UNITTESTS */
}

@ -0,0 +1,38 @@
/* Copyright (C) 2007-2010 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
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Pablo Rincon <pablo.rincon.crespo@gmail.com>
*/
#ifndef __DETECT_FILENAME_H__
#define __DETECT_FILENAME_H__
#include "util-spm-bm.h"
typedef struct DetectFilenameData {
uint8_t *name; /** name of the file to match */
BmCtx *bm_ctx; /** BM context */
uint16_t len; /** name length */
uint8_t flags;
} DetectFilenameData;
/* prototypes */
void DetectFilenameRegister (void);
#endif /* __DETECT_FILENAME_H__ */

@ -90,6 +90,8 @@
#include "detect-id.h"
#include "detect-rpc.h"
#include "detect-asn1.h"
#include "detect-filename.h"
#include "detect-fileext.h"
#include "detect-dsize.h"
#include "detect-flowvar.h"
#include "detect-flowint.h"
@ -4294,6 +4296,8 @@ void SigTableSetup(void) {
DetectHttpStatCodeRegister();
DetectSslVersionRegister();
DetectByteExtractRegister();
DetectFilenameRegister();
DetectFileextRegister();
uint8_t i = 0;
for (i = 0; i < DETECT_TBLSIZE; i++) {

@ -1023,6 +1023,9 @@ enum {
DETECT_ENGINE_EVENT,
DETECT_STREAM_EVENT,
DETECT_FILENAME,
DETECT_FILEEXT,
/* make sure this stays last */
DETECT_TBLSIZE,
};

@ -62,6 +62,8 @@
(f)->lnext = NULL; \
(f)->lprev = NULL; \
RESET_COUNTERS((f)); \
(f)->files = NULL; \
SCMutexInit(&(f)->files_m, NULL); \
} while (0)
/** \brief macro to recycle a flow before it goes into the spare queue for reuse.
@ -92,6 +94,10 @@
GenericVarFree((f)->flowvar); \
(f)->flowvar = NULL; \
RESET_COUNTERS((f)); \
SCMutexLock(&(f)->files_m); \
if ((f)->files != NULL) \
FlowFileContainerRecycle((f)->files); \
SCMutexUnlock(&(f)->files_m); \
} while(0)
#define FLOW_DESTROY(f) do { \
@ -105,6 +111,14 @@
DetectTagDataListFree((f)->tag_list); \
GenericVarFree((f)->flowvar); \
SCMutexDestroy(&(f)->de_state_m); \
(f)->tag_list = NULL; \
SCMutexLock(&(f)->files_m); \
if ((f)->files != NULL) {\
FlowFileContainerFree((f)->files); \
SCMutexDestroy(&(f)->files_m); \
(f)->files = NULL; \
} \
SCMutexUnlock(&(f)->files_m); \
} while(0)
Flow *FlowAlloc(void);

@ -41,6 +41,7 @@
#include "flow-queue.h"
#include "flow-hash.h"
#include "flow-util.h"
#include "flow-file.h"
#include "flow-var.h"
#include "flow-private.h"
#include "flow-timeout.h"

@ -28,6 +28,7 @@
#include "util-var.h"
#include "util-atomic.h"
#include "detect-tag.h"
#include "flow-file.h"
#define FLOW_QUIET TRUE
#define FLOW_VERBOSE FALSE
@ -311,6 +312,10 @@ typedef struct Flow_
uint32_t tosrcpktcnt;
uint64_t bytecnt;
#endif
FlowFileContainer *files;
SCMutex files_m;
} Flow;
enum {

@ -1262,6 +1262,8 @@ int main(int argc, char **argv)
TmModuleDecodeErfDagRegister();
TmModuleDebugList();
AppLayerHtpNeedFileInspection();
/** \todo we need an api for these */
AppLayerDetectProtoThreadInit();
AppLayerParsersInitPostProcess();
@ -1279,6 +1281,7 @@ int main(int argc, char **argv)
}
AppLayerHtpEnableRequestBodyCallback();
AppLayerHtpNeedFileInspection();
AppLayerHtpRegisterExtraCallbacks();
UtInitialize();

@ -34,7 +34,7 @@
typedef struct BmCtx_ {
int32_t bmBc[ALPHABET_SIZE];
int32_t *bmGs; // = SCMalloc(sizeof(int32_t)*(needlelen + 1));
}BmCtx;
} BmCtx;
/** Prepare and return a Boyer Moore context */
BmCtx *BoyerMooreCtxInit(uint8_t *needle, uint32_t needle_len);

Loading…
Cancel
Save