mirror of https://github.com/OISF/suricata
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
601 lines
23 KiB
C
601 lines
23 KiB
C
/* Copyright (C) 2007-2022 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 Victor Julien <victor@inliniac.net>
|
|
*
|
|
*/
|
|
|
|
#include "suricata-common.h"
|
|
#include "threads.h"
|
|
#include "decode.h"
|
|
|
|
#include "detect.h"
|
|
#include "detect-parse.h"
|
|
|
|
#include "detect-engine.h"
|
|
#include "detect-engine-buffer.h"
|
|
#include "detect-engine-mpm.h"
|
|
#include "detect-engine-state.h"
|
|
#include "detect-engine-prefilter.h"
|
|
#include "detect-engine-content-inspection.h"
|
|
#include "detect-engine-file.h"
|
|
#include "detect-file-data.h"
|
|
|
|
#include "app-layer.h"
|
|
#include "app-layer-parser.h"
|
|
#include "app-layer-htp.h"
|
|
#include "app-layer-smtp.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 "util-file-decompression.h"
|
|
#include "util-profiling.h"
|
|
|
|
static int DetectFiledataSetup (DetectEngineCtx *, Signature *, const char *);
|
|
#ifdef UNITTESTS
|
|
static void DetectFiledataRegisterTests(void);
|
|
#endif
|
|
static void DetectFiledataSetupCallback(const DetectEngineCtx *de_ctx,
|
|
Signature *s);
|
|
static int g_file_data_buffer_id = 0;
|
|
|
|
/* file API */
|
|
int PrefilterMpmFiledataRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
|
|
const DetectBufferMpmRegistry *mpm_reg, int list_id);
|
|
|
|
// file protocols with common file handling
|
|
typedef struct {
|
|
AppProto alproto;
|
|
int direction;
|
|
int to_client_progress;
|
|
int to_server_progress;
|
|
} DetectFileHandlerProtocol_t;
|
|
|
|
/* Table with all filehandler registrations */
|
|
DetectFileHandlerTableElmt filehandler_table[DETECT_TBLSIZE_STATIC];
|
|
|
|
#define ALPROTO_WITHFILES_MAX 16
|
|
|
|
// file protocols with common file handling
|
|
DetectFileHandlerProtocol_t al_protocols[ALPROTO_WITHFILES_MAX] = {
|
|
{ .alproto = ALPROTO_NFS, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
|
|
{ .alproto = ALPROTO_SMB, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
|
|
{ .alproto = ALPROTO_FTP, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
|
|
{ .alproto = ALPROTO_FTPDATA, .direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT },
|
|
{ .alproto = ALPROTO_HTTP1,
|
|
.direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT,
|
|
.to_client_progress = HTP_RESPONSE_PROGRESS_BODY,
|
|
.to_server_progress = HTP_REQUEST_PROGRESS_BODY },
|
|
{ .alproto = ALPROTO_HTTP2,
|
|
.direction = SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT,
|
|
.to_client_progress = HTTP2StateDataServer,
|
|
.to_server_progress = HTTP2StateDataClient },
|
|
{ .alproto = ALPROTO_SMTP, .direction = SIG_FLAG_TOSERVER }, { .alproto = ALPROTO_UNKNOWN }
|
|
};
|
|
|
|
void DetectFileRegisterProto(
|
|
AppProto alproto, int direction, int to_client_progress, int to_server_progress)
|
|
{
|
|
size_t i = 0;
|
|
while (i < ALPROTO_WITHFILES_MAX && al_protocols[i].alproto != ALPROTO_UNKNOWN) {
|
|
i++;
|
|
}
|
|
if (i == ALPROTO_WITHFILES_MAX) {
|
|
return;
|
|
}
|
|
al_protocols[i].alproto = alproto;
|
|
al_protocols[i].direction = direction;
|
|
al_protocols[i].to_client_progress = to_client_progress;
|
|
al_protocols[i].to_server_progress = to_server_progress;
|
|
if (i + 1 < ALPROTO_WITHFILES_MAX) {
|
|
al_protocols[i + 1].alproto = ALPROTO_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
void DetectFileRegisterFileProtocols(DetectFileHandlerTableElmt *reg)
|
|
{
|
|
for (size_t i = 0; i < g_alproto_max; i++) {
|
|
if (al_protocols[i].alproto == ALPROTO_UNKNOWN) {
|
|
break;
|
|
}
|
|
int direction = al_protocols[i].direction == 0
|
|
? (int)(SIG_FLAG_TOSERVER | SIG_FLAG_TOCLIENT)
|
|
: al_protocols[i].direction;
|
|
|
|
if (direction & SIG_FLAG_TOCLIENT) {
|
|
DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOCLIENT, reg->priority, reg->PrefilterFn,
|
|
reg->GetData, al_protocols[i].alproto, al_protocols[i].to_client_progress);
|
|
DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto,
|
|
SIG_FLAG_TOCLIENT, al_protocols[i].to_client_progress, reg->Callback,
|
|
reg->GetData);
|
|
}
|
|
if (direction & SIG_FLAG_TOSERVER) {
|
|
DetectAppLayerMpmRegister(reg->name, SIG_FLAG_TOSERVER, reg->priority, reg->PrefilterFn,
|
|
reg->GetData, al_protocols[i].alproto, al_protocols[i].to_server_progress);
|
|
DetectAppLayerInspectEngineRegister(reg->name, al_protocols[i].alproto,
|
|
SIG_FLAG_TOSERVER, al_protocols[i].to_server_progress, reg->Callback,
|
|
reg->GetData);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* \brief Registration function for keyword: file_data
|
|
*/
|
|
void DetectFiledataRegister(void)
|
|
{
|
|
sigmatch_table[DETECT_FILE_DATA].name = "file.data";
|
|
sigmatch_table[DETECT_FILE_DATA].alias = "file_data";
|
|
sigmatch_table[DETECT_FILE_DATA].desc = "make content keywords match on file data";
|
|
sigmatch_table[DETECT_FILE_DATA].url = "/rules/file-keywords.html#file-data";
|
|
sigmatch_table[DETECT_FILE_DATA].Setup = DetectFiledataSetup;
|
|
#ifdef UNITTESTS
|
|
sigmatch_table[DETECT_FILE_DATA].RegisterTests = DetectFiledataRegisterTests;
|
|
#endif
|
|
sigmatch_table[DETECT_FILE_DATA].flags = SIGMATCH_OPTIONAL_OPT | SIGMATCH_SUPPORT_DIR;
|
|
|
|
filehandler_table[DETECT_FILE_DATA].name = "file_data";
|
|
filehandler_table[DETECT_FILE_DATA].priority = 2;
|
|
filehandler_table[DETECT_FILE_DATA].PrefilterFn = PrefilterMpmFiledataRegister;
|
|
filehandler_table[DETECT_FILE_DATA].Callback = DetectEngineInspectFiledata;
|
|
|
|
DetectBufferTypeRegisterSetupCallback("file_data", DetectFiledataSetupCallback);
|
|
|
|
DetectBufferTypeSetDescriptionByName("file_data", "data from tracked files");
|
|
DetectBufferTypeSupportsMultiInstance("file_data");
|
|
|
|
g_file_data_buffer_id = DetectBufferTypeGetByName("file_data");
|
|
}
|
|
|
|
static void SetupDetectEngineConfig(DetectEngineCtx *de_ctx) {
|
|
if (de_ctx->filedata_config)
|
|
return;
|
|
|
|
de_ctx->filedata_config = SCMalloc(g_alproto_max * sizeof(DetectFileDataCfg));
|
|
if (unlikely(de_ctx->filedata_config == NULL))
|
|
return;
|
|
/* initialize default */
|
|
for (AppProto i = 0; i < g_alproto_max; i++) {
|
|
de_ctx->filedata_config[i].content_limit = FILEDATA_CONTENT_LIMIT;
|
|
de_ctx->filedata_config[i].content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
|
|
}
|
|
|
|
/* add protocol specific settings here */
|
|
|
|
/* SMTP */
|
|
de_ctx->filedata_config[ALPROTO_SMTP].content_limit = smtp_config.content_limit;
|
|
de_ctx->filedata_config[ALPROTO_SMTP].content_inspect_min_size =
|
|
smtp_config.content_inspect_min_size;
|
|
}
|
|
|
|
/**
|
|
* \brief this function is used to parse filedata 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 "filestore" option
|
|
*
|
|
* \retval 0 on Success
|
|
* \retval -1 on Failure
|
|
*/
|
|
static int DetectFiledataSetup (DetectEngineCtx *de_ctx, Signature *s, const char *str)
|
|
{
|
|
SCEnter();
|
|
|
|
if (!DetectProtoContainsProto(&s->proto, IPPROTO_TCP)) {
|
|
SCLogError("The 'file_data' keyword cannot be used with non-TCP protocols");
|
|
return -1;
|
|
}
|
|
|
|
if (s->alproto != ALPROTO_UNKNOWN && !AppLayerParserSupportsFiles(IPPROTO_TCP, s->alproto)) {
|
|
SCLogError("The 'file_data' keyword cannot be used with TCP protocol %s",
|
|
AppLayerGetProtoName(s->alproto));
|
|
return -1;
|
|
}
|
|
|
|
if (s->alproto == ALPROTO_SMTP && (s->init_data->init_flags & SIG_FLAG_INIT_FLOW) &&
|
|
!(s->flags & SIG_FLAG_TOSERVER) && (s->flags & SIG_FLAG_TOCLIENT)) {
|
|
SCLogError("The 'file-data' keyword cannot be used with SMTP flow:to_client or "
|
|
"flow:from_server.");
|
|
return -1;
|
|
}
|
|
|
|
if (SCDetectBufferSetActiveList(de_ctx, s, DetectBufferTypeGetByName("file_data")) < 0)
|
|
return -1;
|
|
|
|
s->init_data->init_flags |= SIG_FLAG_INIT_FILEDATA;
|
|
if ((s->init_data->init_flags & SIG_FLAG_INIT_FORCE_TOCLIENT) == 0) {
|
|
// we cannot use a transactional rule with a fast pattern to client and this
|
|
if (s->init_data->init_flags & SIG_FLAG_INIT_TXDIR_FAST_TOCLIENT) {
|
|
SCLogError("fast_pattern cannot be used on to_client keyword for "
|
|
"transactional rule with a streaming buffer to server %u",
|
|
s->id);
|
|
return -1;
|
|
}
|
|
s->init_data->init_flags |= SIG_FLAG_INIT_TXDIR_STREAMING_TOSERVER;
|
|
}
|
|
|
|
SetupDetectEngineConfig(de_ctx);
|
|
return 0;
|
|
}
|
|
|
|
static void DetectFiledataSetupCallback(const DetectEngineCtx *de_ctx,
|
|
Signature *s)
|
|
{
|
|
if (s->alproto == ALPROTO_HTTP1 || s->alproto == ALPROTO_UNKNOWN ||
|
|
s->alproto == ALPROTO_HTTP) {
|
|
AppLayerHtpEnableResponseBodyCallback();
|
|
}
|
|
|
|
/* server body needs to be inspected in sync with stream if possible */
|
|
s->init_data->init_flags |= SIG_FLAG_INIT_NEED_FLUSH;
|
|
|
|
SCLogDebug("callback invoked by %u", s->id);
|
|
}
|
|
|
|
/* common */
|
|
|
|
static void PrefilterMpmFiledataFree(void *ptr)
|
|
{
|
|
SCFree(ptr);
|
|
}
|
|
|
|
/* file API based inspection */
|
|
|
|
static inline InspectionBuffer *FiledataWithXformsGetDataCallback(DetectEngineThreadCtx *det_ctx,
|
|
const DetectEngineTransforms *transforms, const int list_id, int local_file_id,
|
|
InspectionBuffer *base_buffer)
|
|
{
|
|
InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, local_file_id);
|
|
if (buffer == NULL) {
|
|
SCLogDebug("list_id: %d: no buffer", list_id);
|
|
return NULL;
|
|
}
|
|
if (buffer->initialized) {
|
|
SCLogDebug("list_id: %d: returning %p", list_id, buffer);
|
|
return buffer;
|
|
}
|
|
|
|
InspectionBufferSetupMulti(
|
|
det_ctx, buffer, transforms, base_buffer->inspect, base_buffer->inspect_len);
|
|
buffer->inspect_offset = base_buffer->inspect_offset;
|
|
SCLogDebug("xformed buffer %p size %u", buffer, buffer->inspect_len);
|
|
SCReturnPtr(buffer, "InspectionBuffer");
|
|
}
|
|
|
|
static InspectionBuffer *FiledataGetDataCallback(DetectEngineThreadCtx *det_ctx,
|
|
const DetectEngineTransforms *transforms, Flow *f, uint8_t flow_flags, File *cur_file,
|
|
const int list_id, const int base_id, int local_file_id, void *txv)
|
|
{
|
|
SCEnter();
|
|
SCLogDebug("starting: list_id %d base_id %d", list_id, base_id);
|
|
|
|
InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, base_id, local_file_id);
|
|
SCLogDebug("base: buffer %p", buffer);
|
|
if (buffer == NULL)
|
|
return NULL;
|
|
if (base_id != list_id && buffer->inspect != NULL) {
|
|
SCLogDebug("handle xform %s", (list_id != base_id) ? "true" : "false");
|
|
return FiledataWithXformsGetDataCallback(
|
|
det_ctx, transforms, list_id, local_file_id, buffer);
|
|
}
|
|
if (buffer->initialized) {
|
|
SCLogDebug("base_id: %d, not first: use %p", base_id, buffer);
|
|
return buffer;
|
|
}
|
|
|
|
const uint64_t file_size = FileDataSize(cur_file);
|
|
const DetectEngineCtx *de_ctx = det_ctx->de_ctx;
|
|
uint32_t content_limit = FILEDATA_CONTENT_LIMIT;
|
|
uint32_t content_inspect_min_size = FILEDATA_CONTENT_INSPECT_MIN_SIZE;
|
|
if (de_ctx->filedata_config) {
|
|
content_limit = de_ctx->filedata_config[f->alproto].content_limit;
|
|
content_inspect_min_size = de_ctx->filedata_config[f->alproto].content_inspect_min_size;
|
|
}
|
|
|
|
SCLogDebug("[list %d] content_limit %u, content_inspect_min_size %u", list_id, content_limit,
|
|
content_inspect_min_size);
|
|
|
|
SCLogDebug("[list %d] file %p size %" PRIu64 ", state %d", list_id, cur_file, file_size,
|
|
cur_file->state);
|
|
|
|
/* no new data */
|
|
if (cur_file->content_inspected == file_size) {
|
|
SCLogDebug("no new data");
|
|
goto empty_return;
|
|
}
|
|
|
|
if (file_size == 0) {
|
|
SCLogDebug("no data to inspect for this transaction");
|
|
goto empty_return;
|
|
}
|
|
|
|
SCLogDebug("offset %" PRIu64, StreamingBufferGetOffset(cur_file->sb));
|
|
SCLogDebug("size %" PRIu64, cur_file->size);
|
|
SCLogDebug("content_inspected %" PRIu64, cur_file->content_inspected);
|
|
SCLogDebug("inspect_window %" PRIu32, cur_file->inspect_window);
|
|
SCLogDebug("inspect_min_size %" PRIu32, cur_file->inspect_min_size);
|
|
|
|
bool ips = false;
|
|
uint64_t offset = 0;
|
|
if (f->alproto == ALPROTO_HTTP1) {
|
|
|
|
htp_tx_t *tx = txv;
|
|
HtpState *htp_state = f->alstate;
|
|
ips = htp_state->cfg->http_body_inline;
|
|
|
|
const bool body_done = AppLayerParserGetStateProgress(IPPROTO_TCP, ALPROTO_HTTP1, tx,
|
|
flow_flags) > HTP_RESPONSE_PROGRESS_BODY;
|
|
|
|
SCLogDebug("response.body_limit %u file_size %" PRIu64
|
|
", cur_file->inspect_min_size %" PRIu32 ", EOF %s, progress > body? %s",
|
|
htp_state->cfg->response.body_limit, file_size, cur_file->inspect_min_size,
|
|
flow_flags & STREAM_EOF ? "true" : "false", BOOL2STR(body_done));
|
|
|
|
if (!htp_state->cfg->http_body_inline) {
|
|
/* inspect the body if the transfer is complete or we have hit
|
|
* our body size limit */
|
|
if ((htp_state->cfg->response.body_limit == 0 ||
|
|
file_size < htp_state->cfg->response.body_limit) &&
|
|
file_size < cur_file->inspect_min_size && !body_done &&
|
|
!(flow_flags & STREAM_EOF)) {
|
|
SCLogDebug("we still haven't seen the entire response body. "
|
|
"Let's defer body inspection till we see the "
|
|
"entire body.");
|
|
goto empty_return;
|
|
}
|
|
SCLogDebug("inline and we're continuing");
|
|
}
|
|
|
|
bool force = (flow_flags & STREAM_EOF) || (cur_file->state > FILE_STATE_OPENED) ||
|
|
body_done || htp_state->cfg->http_body_inline;
|
|
/* get the inspect buffer
|
|
*
|
|
* make sure that we have at least the configured inspect_win size.
|
|
* If we have more, take at least 1/4 of the inspect win size before
|
|
* the new data.
|
|
*/
|
|
if (cur_file->content_inspected == 0) {
|
|
if (!force && file_size < cur_file->inspect_min_size) {
|
|
SCLogDebug("skip as file_size %" PRIu64 " < inspect_min_size %u", file_size,
|
|
cur_file->inspect_min_size);
|
|
goto empty_return;
|
|
}
|
|
} else {
|
|
uint64_t new_data = file_size - cur_file->content_inspected;
|
|
BUG_ON(new_data == 0);
|
|
if (new_data < cur_file->inspect_window) {
|
|
uint64_t inspect_short = cur_file->inspect_window - new_data;
|
|
if (cur_file->content_inspected < inspect_short) {
|
|
offset = 0;
|
|
SCLogDebug("offset %" PRIu64, offset);
|
|
} else {
|
|
offset = cur_file->content_inspected - inspect_short;
|
|
SCLogDebug("offset %" PRIu64, offset);
|
|
}
|
|
} else {
|
|
BUG_ON(cur_file->content_inspected == 0);
|
|
uint32_t margin = cur_file->inspect_window / 4;
|
|
if ((uint64_t)margin <= cur_file->content_inspected) {
|
|
offset = cur_file->content_inspected - (cur_file->inspect_window / 4);
|
|
} else {
|
|
offset = 0;
|
|
}
|
|
SCLogDebug("offset %" PRIu64 " (data from offset %" PRIu64 ")", offset,
|
|
file_size - offset);
|
|
}
|
|
}
|
|
|
|
} else {
|
|
if ((content_limit == 0 || file_size < content_limit) &&
|
|
file_size < content_inspect_min_size && !(flow_flags & STREAM_EOF) &&
|
|
!(cur_file->state > FILE_STATE_OPENED)) {
|
|
SCLogDebug("we still haven't seen the entire content. "
|
|
"Let's defer content inspection till we see the "
|
|
"entire content. We've seen %ld and need at least %d",
|
|
file_size, content_inspect_min_size);
|
|
goto empty_return;
|
|
}
|
|
offset = cur_file->content_inspected;
|
|
}
|
|
|
|
const uint8_t *data;
|
|
uint32_t data_len;
|
|
|
|
SCLogDebug("Fetching data at offset: %ld", offset);
|
|
StreamingBufferGetDataAtOffset(cur_file->sb, &data, &data_len, offset);
|
|
SCLogDebug("data_len %u", data_len);
|
|
/* update inspected tracker */
|
|
buffer->inspect_offset = offset;
|
|
|
|
if (ips && file_size < cur_file->inspect_min_size) {
|
|
// don't update content_inspected yet
|
|
} else {
|
|
SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
|
|
cur_file->content_inspected = MAX(cur_file->content_inspected, offset + data_len);
|
|
SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
|
|
}
|
|
|
|
InspectionBufferSetupMulti(det_ctx, buffer, NULL, data, data_len);
|
|
SCLogDebug("[list %d] [before] buffer offset %" PRIu64 "; buffer len %" PRIu32
|
|
"; data_len %" PRIu32 "; file_size %" PRIu64,
|
|
list_id, buffer->inspect_offset, buffer->inspect_len, data_len, file_size);
|
|
|
|
if (f->alproto == ALPROTO_HTTP1 && flow_flags & STREAM_TOCLIENT) {
|
|
HtpState *htp_state = f->alstate;
|
|
/* built-in 'transformation' */
|
|
if (htp_state->cfg->swf_decompression_enabled) {
|
|
int swf_file_type = FileIsSwfFile(data, data_len);
|
|
if (swf_file_type == FILE_SWF_ZLIB_COMPRESSION ||
|
|
swf_file_type == FILE_SWF_LZMA_COMPRESSION) {
|
|
SCLogDebug("decompressing ...");
|
|
(void)FileSwfDecompression(data, data_len, det_ctx, buffer,
|
|
htp_state->cfg->swf_compression_type, htp_state->cfg->swf_decompress_depth,
|
|
htp_state->cfg->swf_compress_depth);
|
|
SCLogDebug("uncompressed buffer %p size %u; buf: \"%s\"", buffer,
|
|
buffer->inspect_len, (char *)buffer->inspect);
|
|
}
|
|
}
|
|
}
|
|
|
|
SCLogDebug("content inspected: %" PRIu64, cur_file->content_inspected);
|
|
|
|
/* get buffer for the list id if it is different from the base id */
|
|
if (list_id != base_id) {
|
|
SCLogDebug("regular %d has been set up: now handle xforms id %d", base_id, list_id);
|
|
InspectionBuffer *tbuffer = FiledataWithXformsGetDataCallback(
|
|
det_ctx, transforms, list_id, local_file_id, buffer);
|
|
SCReturnPtr(tbuffer, "InspectionBuffer");
|
|
}
|
|
SCReturnPtr(buffer, "InspectionBuffer");
|
|
|
|
empty_return:
|
|
InspectionBufferSetupMultiEmpty(buffer);
|
|
return NULL;
|
|
}
|
|
|
|
uint8_t DetectEngineInspectFiledata(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx,
|
|
const DetectEngineAppInspectionEngine *engine, const Signature *s, Flow *f, uint8_t flags,
|
|
void *alstate, void *txv, uint64_t tx_id)
|
|
{
|
|
const DetectEngineTransforms *transforms = NULL;
|
|
if (!engine->mpm) {
|
|
transforms = engine->v2.transforms;
|
|
}
|
|
|
|
AppLayerGetFileState files = AppLayerParserGetTxFiles(f, txv, flags);
|
|
FileContainer *ffc = files.fc;
|
|
if (ffc == NULL) {
|
|
return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH_FILES;
|
|
}
|
|
if (ffc->head == NULL) {
|
|
const bool eof = (AppLayerParserGetStateProgress(f->proto, f->alproto, txv, flags) >
|
|
engine->progress);
|
|
if (eof && engine->match_on_null) {
|
|
return DETECT_ENGINE_INSPECT_SIG_MATCH;
|
|
}
|
|
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
|
|
}
|
|
|
|
int local_file_id = 0;
|
|
File *file = ffc->head;
|
|
for (; file != NULL; file = file->next) {
|
|
InspectionBuffer *buffer = FiledataGetDataCallback(det_ctx, transforms, f, flags, file,
|
|
engine->sm_list, engine->sm_list_base, local_file_id, txv);
|
|
if (buffer == NULL) {
|
|
local_file_id++;
|
|
continue;
|
|
}
|
|
|
|
bool eof = (file->state == FILE_STATE_CLOSED);
|
|
uint8_t ciflags = eof ? DETECT_CI_FLAGS_END : 0;
|
|
if (buffer->inspect_offset == 0)
|
|
ciflags |= DETECT_CI_FLAGS_START;
|
|
|
|
const bool match = DetectEngineContentInspection(de_ctx, det_ctx, s, engine->smd, NULL, f,
|
|
buffer->inspect, buffer->inspect_len, buffer->inspect_offset, ciflags,
|
|
DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE);
|
|
if (match) {
|
|
return DETECT_ENGINE_INSPECT_SIG_MATCH;
|
|
}
|
|
local_file_id++;
|
|
}
|
|
|
|
return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
|
|
}
|
|
|
|
/** \brief Filedata Filedata Mpm prefilter callback
|
|
*
|
|
* \param det_ctx detection engine thread ctx
|
|
* \param pectx inspection context
|
|
* \param p packet to inspect
|
|
* \param f flow to inspect
|
|
* \param txv tx to inspect
|
|
* \param idx transaction id
|
|
* \param flags STREAM_* flags including direction
|
|
*/
|
|
static void PrefilterTxFiledata(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p,
|
|
Flow *f, void *txv, const uint64_t idx, const AppLayerTxData *txd, const uint8_t flags)
|
|
{
|
|
SCEnter();
|
|
|
|
if (!AppLayerParserHasFilesInDir(txd, flags))
|
|
return;
|
|
|
|
const PrefilterMpmFiledata *ctx = (const PrefilterMpmFiledata *)pectx;
|
|
const MpmCtx *mpm_ctx = ctx->mpm_ctx;
|
|
const int list_id = ctx->list_id;
|
|
|
|
AppLayerGetFileState files = AppLayerParserGetTxFiles(f, txv, flags);
|
|
FileContainer *ffc = files.fc;
|
|
if (ffc != NULL) {
|
|
int local_file_id = 0;
|
|
for (File *file = ffc->head; file != NULL; file = file->next) {
|
|
InspectionBuffer *buffer = FiledataGetDataCallback(det_ctx, ctx->transforms, f, flags,
|
|
file, list_id, ctx->base_list_id, local_file_id, txv);
|
|
if (buffer == NULL) {
|
|
local_file_id++;
|
|
continue;
|
|
}
|
|
SCLogDebug("[%" PRIu64 "] buffer size %u", p->pcap_cnt, buffer->inspect_len);
|
|
|
|
if (buffer->inspect_len >= mpm_ctx->minlen) {
|
|
uint32_t prev_rule_id_array_cnt = det_ctx->pmq.rule_id_array_cnt;
|
|
(void)mpm_table[mpm_ctx->mpm_type].Search(mpm_ctx, &det_ctx->mtc, &det_ctx->pmq,
|
|
buffer->inspect, buffer->inspect_len);
|
|
PREFILTER_PROFILING_ADD_BYTES(det_ctx, buffer->inspect_len);
|
|
|
|
if (det_ctx->pmq.rule_id_array_cnt > prev_rule_id_array_cnt) {
|
|
SCLogDebug(
|
|
"%u matches", det_ctx->pmq.rule_id_array_cnt - prev_rule_id_array_cnt);
|
|
}
|
|
}
|
|
local_file_id++;
|
|
}
|
|
}
|
|
}
|
|
|
|
int PrefilterMpmFiledataRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx,
|
|
const DetectBufferMpmRegistry *mpm_reg, int list_id)
|
|
{
|
|
PrefilterMpmFiledata *pectx = SCCalloc(1, sizeof(*pectx));
|
|
if (pectx == NULL)
|
|
return -1;
|
|
pectx->list_id = list_id;
|
|
pectx->base_list_id = mpm_reg->sm_list_base;
|
|
pectx->mpm_ctx = mpm_ctx;
|
|
pectx->transforms = &mpm_reg->transforms;
|
|
|
|
return PrefilterAppendTxEngine(de_ctx, sgh, PrefilterTxFiledata,
|
|
mpm_reg->app_v2.alproto, mpm_reg->app_v2.tx_min_progress,
|
|
pectx, PrefilterMpmFiledataFree, mpm_reg->pname);
|
|
}
|
|
|
|
#ifdef UNITTESTS
|
|
#include "tests/detect-file-data.c"
|
|
#endif
|