From: Victor Julien Date: Fri, 3 Dec 2021 06:53:32 +0000 (+0100) Subject: detect/engine: support frames X-Git-Tag: suricata-7.0.0-beta1~1039 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f6f124f2837022c4a0258a9d07f1d618dca93a02;p=thirdparty%2Fsuricata.git detect/engine: support frames Implement the low level detect engine support for inspecting frames, including MPM, transforms and inspect API's. --- diff --git a/src/Makefile.am b/src/Makefile.am index 32dd12a2d6..3ac3f07b06 100755 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -139,6 +139,7 @@ noinst_HEADERS = \ detect-engine-enip.h \ detect-engine-event.h \ detect-engine-file.h \ + detect-engine-frame.h \ detect-engine.h \ detect-engine-iponly.h \ detect-engine-loader.h \ @@ -725,6 +726,7 @@ libsuricata_c_a_SOURCES = \ detect-engine-enip.c \ detect-engine-event.c \ detect-engine-file.c \ + detect-engine-frame.c \ detect-engine-iponly.c \ detect-engine-loader.c \ detect-engine-mpm.c \ diff --git a/src/detect-engine-build.c b/src/detect-engine-build.c index 9c9f7992a2..108514c962 100644 --- a/src/detect-engine-build.c +++ b/src/detect-engine-build.c @@ -1968,6 +1968,7 @@ int SigGroupBuild(DetectEngineCtx *de_ctx) int r = DetectMpmPrepareBuiltinMpms(de_ctx); r |= DetectMpmPrepareAppMpms(de_ctx); r |= DetectMpmPreparePktMpms(de_ctx); + r |= DetectMpmPrepareFrameMpms(de_ctx); if (r != 0) { FatalError(SC_ERR_FATAL, "initializing the detection engine failed"); } diff --git a/src/detect-engine-content-inspection.h b/src/detect-engine-content-inspection.h index 6d9f87d98b..149109e60e 100644 --- a/src/detect-engine-content-inspection.h +++ b/src/detect-engine-content-inspection.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2011 Open Information Security Foundation +/* Copyright (C) 2007-2021 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 @@ -29,9 +29,10 @@ * we're inspecting */ enum { - DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD = 0, /* enables 'replace' logic */ + DETECT_ENGINE_CONTENT_INSPECTION_MODE_PAYLOAD = 0, /* enables 'replace' logic */ DETECT_ENGINE_CONTENT_INSPECTION_MODE_HEADER, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STREAM, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME, DETECT_ENGINE_CONTENT_INSPECTION_MODE_STATE, }; diff --git a/src/detect-engine-frame.c b/src/detect-engine-frame.c new file mode 100644 index 0000000000..5ca268aa43 --- /dev/null +++ b/src/detect-engine-frame.c @@ -0,0 +1,357 @@ +/* Copyright (C) 2021 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 + * + */ + +#include "suricata-common.h" +#include "suricata.h" + +#include "app-layer-parser.h" +#include "app-layer-frames.h" + +#include "detect-engine.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-content-inspection.h" +#include "detect-engine-mpm.h" +#include "detect-engine-frame.h" + +#include "stream-tcp.h" + +#include "util-profiling.h" +#include "util-validate.h" +#include "util-print.h" + +void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, + const Frames *frames, const Frame *frame, const AppProto alproto, const uint32_t idx) +{ + SCLogDebug("pcap_cnt %" PRIu64, p->pcap_cnt); + PrefilterEngine *engine = sgh->frame_engines; + do { + BUG_ON(engine->alproto == ALPROTO_UNKNOWN); + if (engine->alproto == alproto && engine->ctx.frame_type == frame->type) { + SCLogDebug("frame %p engine %p", frame, engine); + PREFILTER_PROFILING_START; + engine->cb.PrefilterFrame(det_ctx, engine->pectx, p, frames, frame, idx); + PREFILTER_PROFILING_END(det_ctx, engine->gid); + } + if (engine->is_last) + break; + engine++; + } while (1); +} + +/* generic mpm for frame engines */ + +// TODO same as Generic? +typedef struct PrefilterMpmFrameCtx { + int list_id; + const MpmCtx *mpm_ctx; + const DetectEngineTransforms *transforms; +} PrefilterMpmFrameCtx; + +/** \brief Generic Mpm prefilter callback + * + * \param det_ctx detection engine thread ctx + * \param frames container for the frames + * \param frame frame to inspect + * \param pectx inspection context + */ +static void PrefilterMpmFrame(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, + const Frames *frames, const Frame *frame, const uint32_t idx) +{ + SCEnter(); + + const PrefilterMpmFrameCtx *ctx = (const PrefilterMpmFrameCtx *)pectx; + const MpmCtx *mpm_ctx = ctx->mpm_ctx; + SCLogDebug("running on list %d -> frame field type %u", ctx->list_id, frame->type); + // BUG_ON(frame->type != ctx->type); + + InspectionBuffer *buffer = DetectFrame2InspectBuffer( + det_ctx, ctx->transforms, p, frames, frame, ctx->list_id, idx, true); + if (buffer == NULL) + return; + + const uint32_t data_len = buffer->inspect_len; + const uint8_t *data = buffer->inspect; + + SCLogDebug("mpm'ing buffer:"); + // SCLogDebug("frame: %p", frame); + // PrintRawDataFp(stdout, data, MIN(32, data_len)); + + if (data != NULL && data_len >= mpm_ctx->minlen) { + (void)mpm_table[mpm_ctx->mpm_type].Search( + mpm_ctx, &det_ctx->mtcu, &det_ctx->pmq, data, data_len); + SCLogDebug("det_ctx->pmq.rule_id_array_cnt %u", det_ctx->pmq.rule_id_array_cnt); + } +} + +static void PrefilterMpmFrameFree(void *ptr) +{ + SCFree(ptr); +} + +int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id) +{ + SCEnter(); + PrefilterMpmFrameCtx *pectx = SCCalloc(1, sizeof(*pectx)); + if (pectx == NULL) + return -1; + pectx->list_id = list_id; + BUG_ON(mpm_reg->frame_v1.alproto == ALPROTO_UNKNOWN); + pectx->mpm_ctx = mpm_ctx; + pectx->transforms = &mpm_reg->transforms; + + int r = PrefilterAppendFrameEngine(de_ctx, sgh, PrefilterMpmFrame, mpm_reg->frame_v1.alproto, + mpm_reg->frame_v1.type, pectx, PrefilterMpmFrameFree, mpm_reg->pname); + if (r != 0) { + SCFree(pectx); + } + return r; +} + +int DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s, + Flow *f, Packet *p, const Frames *frames, const Frame *frame, const uint32_t idx) +{ + BUG_ON(s->frame_inspect == NULL); + + SCLogDebug("inspecting rule %u against frame %p/%" PRIi64 "/%s", s->id, frame, frame->id, + AppLayerParserGetFrameNameById(f->proto, f->alproto, frame->type)); + + for (DetectEngineFrameInspectionEngine *e = s->frame_inspect; e != NULL; e = e->next) { + if (frame->type == e->type) { + // TODO check alproto, direction? + + // TODO there should be only one inspect engine for this frame, ever? + + if (e->v1.Callback(det_ctx, e, s, p, frames, frame, idx) == true) { + SCLogDebug("sid %u: e %p Callback returned true", s->id, e); + return true; + } + SCLogDebug("sid %u: e %p Callback returned false", s->id, e); + } else { + SCLogDebug( + "sid %u: e %p not for frame type %u (want %u)", s->id, e, frame->type, e->type); + } + } + return false; +} + +InspectionBuffer *DetectFrame2InspectBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Packet *p, const Frames *frames, + const Frame *frame, const int list_id, const uint32_t idx, const bool first) +{ + // TODO do we really need multiple buffer support here? + InspectionBuffer *buffer = InspectionBufferMultipleForListGet(det_ctx, list_id, idx); + if (buffer == NULL) + return NULL; + if (!first && buffer->inspect != NULL) + return buffer; + + BUG_ON(p->flow == NULL); + BUG_ON(p->flow->protoctx == NULL); + TcpSession *ssn = p->flow->protoctx; + TcpStream *stream; + if (PKT_IS_TOSERVER(p)) { + stream = &ssn->client; + } else { + stream = &ssn->server; + } + + /* + stream: [s ] + frame: [r ] + progress: |>p + rel_offset: 10, len 100 + progress: 20 + avail: 90 (complete) + + stream: [s ] + frame: [r ] + progress: |>p + stream: 0, len 59 + rel_offset: 10, len 100 + progress: 20 + avail: 30 (incomplete) + + stream: [s ] + frame: [r ] + progress: |>p + stream: 0, len 200 + rel_offset: -30, len 100 + progress: 20 + avail: 50 (complete) + */ + + SCLogDebug("frame %" PRIi64 ", len %" PRIi64, frame->id, frame->len); + + uint32_t data_len = 0; + const uint8_t *data = NULL; + + uint64_t offset = STREAM_BASE_OFFSET(stream); + if (frame->rel_offset > 0 || frames->progress_rel) { + uint64_t frame_offset = 0; + if (frame->rel_offset >= 0) { + frame_offset = MAX((uint64_t)frame->rel_offset, (uint64_t)frames->progress_rel); + } else { + frame_offset = (uint64_t)frames->progress_rel; + } + offset += frame_offset; + } + + const bool eof = ssn->state == TCP_CLOSED || PKT_IS_PSEUDOPKT(p); + + const uint64_t usable = StreamTcpGetUsable(stream, eof); + if (usable <= offset) + return NULL; + + // TODO GAP handling + if (StreamingBufferGetDataAtOffset(&stream->sb, &data, &data_len, offset) == 0) { + return NULL; + } + + const uint64_t data_right_edge = offset + data_len; + if (data_right_edge > usable) + data_len = usable - offset; + + const int64_t frame_start_abs_offset = frame->rel_offset + (int64_t)STREAM_BASE_OFFSET(stream); + const uint64_t usable_right_edge = MIN(data_right_edge, usable); + + bool have_end = false; + + if (frame->len > 0) { + const int64_t frame_avail_data_abs = (int64_t)usable_right_edge; + const int64_t frame_end_abs_offset = frame_start_abs_offset + frame->len; + have_end = (int64_t)usable_right_edge >= frame_end_abs_offset; + + SCLogDebug("frame_end_abs_offset %" PRIi64 ", usable_right_edge %" PRIu64, + frame_end_abs_offset, usable_right_edge); + + const int64_t avail_from_frame = MIN(frame_end_abs_offset, frame_avail_data_abs) - offset; + if (avail_from_frame < (int64_t)data_len) { + SCLogDebug("adjusted data len from %u to %" PRIi64, data_len, avail_from_frame); + data_len = (uint32_t)avail_from_frame; + } + } + const bool have_start = frame_start_abs_offset == (int64_t)offset; + + if (data == NULL || data_len == 0) { + return NULL; + } + + // TODO use eof too? + SCLogDebug("stream->min_inspect_depth %u", stream->min_inspect_depth); + if (data_len < frame->len && data_len < stream->min_inspect_depth) { + if (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED || ssn->state == TCP_CLOSED || + stream->flags & STREAMTCP_STREAM_FLAG_DEPTH_REACHED) { + SCLogDebug("EOF use available data: %u bytes", data_len); + } else { + SCLogDebug("not enough data to inspect now: have %u, want %u", data_len, + stream->min_inspect_depth); + return NULL; + } + } + + const uint8_t ci_flags = + (have_start ? DETECT_CI_FLAGS_START : 0) | (have_end ? DETECT_CI_FLAGS_END : 0); + SCLogDebug("packet %" PRIu64 " -> frame %p/%" PRIi64 "/%s rel_offset %" PRIi64 + " type %u len %" PRIi64 " ci_flags %02x (start:%s, end:%s)", + p->pcap_cnt, frame, frame->id, + AppLayerParserGetFrameNameById(p->flow->proto, p->flow->alproto, frame->type), + frame->rel_offset, frame->type, frame->len, ci_flags, + (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false", + (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false"); + // PrintRawDataFp(stdout, data, MIN(32,data_len)); + + InspectionBufferSetupMulti(buffer, transforms, data, data_len); + buffer->inspect_offset = frame->rel_offset < 0 ? -1 * frame->rel_offset : 0; // TODO review/test + buffer->flags = ci_flags; + return buffer; +} + +/** + * \brief Do the content inspection & validation for a signature + * + * \param de_ctx Detection engine context + * \param det_ctx Detection engine thread context + * \param s Signature to inspect + * \param p Packet + * \param frame stream frame to inspect + * + * \retval 0 no match. + * \retval 1 match. + */ +int DetectEngineInspectFrameBufferGeneric(DetectEngineThreadCtx *det_ctx, + const DetectEngineFrameInspectionEngine *engine, const Signature *s, Packet *p, + const Frames *frames, const Frame *frame, const uint32_t idx) +{ + const int list_id = engine->sm_list; + SCLogDebug("running inspect on %d", list_id); + + SCLogDebug("list %d transforms %p", engine->sm_list, engine->v1.transforms); + + /* if prefilter didn't already run, we need to consider transformations */ + const DetectEngineTransforms *transforms = NULL; + if (!engine->mpm) { + transforms = engine->v1.transforms; + } + + const InspectionBuffer *buffer = + DetectFrame2InspectBuffer(det_ctx, transforms, p, frames, frame, list_id, idx, false); + if (unlikely(buffer == NULL)) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + + const uint32_t data_len = buffer->inspect_len; + const uint8_t *data = buffer->inspect; + const uint64_t offset = buffer->inspect_offset; + + det_ctx->discontinue_matching = 0; + det_ctx->buffer_offset = 0; + det_ctx->inspection_recursion_counter = 0; +#ifdef DEBUG + const uint8_t ci_flags = buffer->flags; + SCLogDebug("frame %p rel_offset %" PRIi64 " type %u len %" PRIi64 + " ci_flags %02x (start:%s, end:%s)", + frame, frame->rel_offset, frame->type, frame->len, ci_flags, + (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false", + (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false"); + SCLogDebug("buffer %p offset %" PRIu64 " len %u ci_flags %02x (start:%s, end:%s)", buffer, + buffer->inspect_offset, buffer->inspect_len, ci_flags, + (ci_flags & DETECT_CI_FLAGS_START) ? "true" : "false", + (ci_flags & DETECT_CI_FLAGS_END) ? "true" : "false"); + // PrintRawDataFp(stdout, data, data_len); + // PrintRawDataFp(stdout, data, MIN(64, data_len)); +#endif + BUG_ON((int64_t)data_len > frame->len); + + // TODO don't call if matching needs frame end and DETECT_CI_FLAGS_END not set + // TODO same for start + int r = DetectEngineContentInspection(det_ctx->de_ctx, det_ctx, s, engine->smd, p, p->flow, + (uint8_t *)data, data_len, offset, buffer->flags, + DETECT_ENGINE_CONTENT_INSPECTION_MODE_FRAME); + if (r == 1) { + return DETECT_ENGINE_INSPECT_SIG_MATCH; + } else { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } +} diff --git a/src/detect-engine-frame.h b/src/detect-engine-frame.h new file mode 100644 index 0000000000..82dc144ff2 --- /dev/null +++ b/src/detect-engine-frame.h @@ -0,0 +1,39 @@ +/* Copyright (C) 2021 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 + * + */ + +#include "app-layer-frames.h" + +void DetectRunPrefilterFrame(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, + const Frames *frames, const Frame *frame, const AppProto alproto, const uint32_t idx); +int DetectRunFrameInspectRule(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s, + Flow *f, Packet *p, const Frames *frames, const Frame *frame, const uint32_t idx); + +int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id); +InspectionBuffer *DetectFrame2InspectBuffer(DetectEngineThreadCtx *det_ctx, + const DetectEngineTransforms *transforms, Packet *p, const Frames *frames, + const Frame *frame, const int list_id, const uint32_t idx, const bool first); +int DetectEngineInspectFrameBufferGeneric(DetectEngineThreadCtx *det_ctx, + const DetectEngineFrameInspectionEngine *engine, const Signature *s, Packet *p, + const Frames *frames, const Frame *frame, const uint32_t idx); diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 1efe53baea..c55c13f2f7 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2014 Open Information Security Foundation +/* Copyright (C) 2007-2021 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 @@ -79,8 +79,8 @@ const char *builtin_mpms[] = { * Keywords are registered at engine start up */ -static DetectBufferMpmRegistery *g_mpm_list[DETECT_BUFFER_MPM_TYPE_SIZE] = { NULL, NULL }; -static int g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_SIZE] = { 0, 0 }; +static DetectBufferMpmRegistery *g_mpm_list[DETECT_BUFFER_MPM_TYPE_SIZE] = { NULL, NULL, NULL }; +static int g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_SIZE] = { 0, 0, 0 }; /** \brief register a MPM engine * @@ -287,6 +287,237 @@ int DetectMpmPrepareAppMpms(DetectEngineCtx *de_ctx) return r; } +/** \brief register a MPM engine + * + * \note to be used at start up / registration only. Errors are fatal. + */ +void DetectFrameMpmRegister(const char *name, int direction, int priority, + int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id), + AppProto alproto, uint8_t type) +{ + SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister, + AppProtoToString(alproto), type); + + DetectBufferTypeSupportsMpm(name); + DetectBufferTypeSupportsFrames(name); + DetectBufferTypeSupportsTransformations(name); + int sm_list = DetectBufferTypeGetByName(name); + if (sm_list == -1) { + FatalError(SC_ERR_INITIALIZATION, "MPM engine registration for %s failed", name); + } + + DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am)); + BUG_ON(am == NULL); + am->name = name; + snprintf(am->pname, sizeof(am->pname), "%s", am->name); + am->sm_list = sm_list; + am->direction = direction; + am->priority = priority; + am->type = DETECT_BUFFER_MPM_TYPE_FRAME; + + am->PrefilterRegisterWithListId = PrefilterRegister; + am->frame_v1.alproto = alproto; + am->frame_v1.type = type; + SCLogDebug("type %u", type); + SCLogDebug("am type %u", am->frame_v1.type); + + if (g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] == NULL) { + g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME] = am; + } else { + DetectBufferMpmRegistery *t = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME]; + while (t->next != NULL) { + t = t->next; + } + t->next = am; + am->id = t->id + 1; + } + g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME]++; + + SupportFastPatternForSigMatchList(sm_list, priority); + SCLogDebug("%s/%d done", name, sm_list); +} + +/** \brief copy a mpm engine from parent_id, add in transforms */ +void DetectFrameMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id, + DetectEngineTransforms *transforms) +{ + SCLogDebug("registering %d/%d", id, parent_id); + + DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list; + while (t) { + if (t->sm_list == parent_id) { + DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am)); + BUG_ON(am == NULL); + am->name = t->name; + snprintf(am->pname, sizeof(am->pname), "%s#%d", am->name, id); + am->sm_list = id; // use new id + am->sm_list_base = t->sm_list; + am->type = DETECT_BUFFER_MPM_TYPE_FRAME; + am->PrefilterRegisterWithListId = t->PrefilterRegisterWithListId; + am->frame_v1 = t->frame_v1; + SCLogDebug("am type %u", am->frame_v1.type); + am->priority = t->priority; + am->direction = t->direction; + am->sgh_mpm_context = t->sgh_mpm_context; + am->next = t->next; + if (transforms) { + memcpy(&am->transforms, transforms, sizeof(*transforms)); + } + am->id = de_ctx->frame_mpms_list_cnt++; + + DetectEngineRegisterFastPatternForId(de_ctx, am->sm_list, am->priority); + t->next = am; + SCLogDebug("copied mpm registration for %s id %u " + "with parent %u", + t->name, id, parent_id); + t = am; + } + t = t->next; + } +} + +void DetectEngineFrameMpmRegister(DetectEngineCtx *de_ctx, const char *name, int direction, + int priority, + int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id), + AppProto alproto, uint8_t type) +{ + SCLogDebug("registering %s/%d/%p/%s/%u", name, priority, PrefilterRegister, + AppProtoToString(alproto), type); + + const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name); + if (sm_list < 0) { + FatalError(SC_ERR_INITIALIZATION, "MPM engine registration for %s failed", name); + } + + DetectEngineBufferTypeSupportsMpm(de_ctx, name); + DetectEngineBufferTypeSupportsFrames(de_ctx, name); + DetectEngineBufferTypeSupportsTransformations(de_ctx, name); + + DetectBufferMpmRegistery *am = SCCalloc(1, sizeof(*am)); + BUG_ON(am == NULL); + am->name = name; + snprintf(am->pname, sizeof(am->pname), "%s", am->name); + am->sm_list = sm_list; + am->direction = direction; + am->priority = priority; + am->type = DETECT_BUFFER_MPM_TYPE_FRAME; + + am->PrefilterRegisterWithListId = PrefilterRegister; + am->frame_v1.alproto = alproto; + am->frame_v1.type = type; + + // TODO is it ok to do this here? + + /* default to whatever the global setting is */ + int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE); + /* see if we use a unique or shared mpm ctx for this type */ + int confshared = 0; + if (ConfGetBool("detect.mpm.frame.shared", &confshared) == 1) + shared = confshared; + + if (shared == 0) { + am->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT; + } else { + am->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(de_ctx, am->name, am->sm_list); + } + + if (de_ctx->frame_mpms_list == NULL) { + de_ctx->frame_mpms_list = am; + } else { + DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list; + while (t->next != NULL) { + t = t->next; + } + + t->next = am; + } + de_ctx->frame_mpms_list_cnt++; + + DetectEngineRegisterFastPatternForId(de_ctx, sm_list, priority); + SCLogDebug("%s/%d done", name, sm_list); +} + +void DetectMpmInitializeFrameMpms(DetectEngineCtx *de_ctx) +{ + const DetectBufferMpmRegistery *list = g_mpm_list[DETECT_BUFFER_MPM_TYPE_FRAME]; + while (list != NULL) { + DetectBufferMpmRegistery *n = SCCalloc(1, sizeof(*n)); + BUG_ON(n == NULL); + + *n = *list; + n->next = NULL; + + if (de_ctx->frame_mpms_list == NULL) { + de_ctx->frame_mpms_list = n; + } else { + DetectBufferMpmRegistery *t = de_ctx->frame_mpms_list; + while (t->next != NULL) { + t = t->next; + } + + t->next = n; + } + + /* default to whatever the global setting is */ + int shared = (de_ctx->sgh_mpm_ctx_cnf == ENGINE_SGH_MPM_FACTORY_CONTEXT_SINGLE); + + /* see if we use a unique or shared mpm ctx for this type */ + int confshared = 0; + char confstring[256] = "detect.mpm."; + strlcat(confstring, n->name, sizeof(confstring)); + strlcat(confstring, ".shared", sizeof(confstring)); + if (ConfGetBool(confstring, &confshared) == 1) + shared = confshared; + + if (shared == 0) { + if (!(de_ctx->flags & DE_QUIET)) { + SCLogPerf("using unique mpm ctx' for %s", n->name); + } + n->sgh_mpm_context = MPM_CTX_FACTORY_UNIQUE_CONTEXT; + } else { + if (!(de_ctx->flags & DE_QUIET)) { + SCLogPerf("using shared mpm ctx' for %s", n->name); + } + n->sgh_mpm_context = MpmFactoryRegisterMpmCtxProfile(de_ctx, n->name, n->sm_list); + } + + list = list->next; + } + de_ctx->frame_mpms_list_cnt = g_mpm_list_cnt[DETECT_BUFFER_MPM_TYPE_FRAME]; + SCLogDebug("mpm: de_ctx frame_mpms_list %p %u", de_ctx->frame_mpms_list, + de_ctx->frame_mpms_list_cnt); +} + +/** + * \brief initialize mpm contexts for applayer buffers that are in + * "single or "shared" mode. + */ +int DetectMpmPrepareFrameMpms(DetectEngineCtx *de_ctx) +{ + SCLogDebug("preparing frame mpm"); + int r = 0; + const DetectBufferMpmRegistery *am = de_ctx->frame_mpms_list; + while (am != NULL) { + SCLogDebug("am %p %s sgh_mpm_context %d", am, am->name, am->sgh_mpm_context); + SCLogDebug("%s", am->name); + if (am->sgh_mpm_context != MPM_CTX_FACTORY_UNIQUE_CONTEXT) { + int dir = (am->direction == SIG_FLAG_TOSERVER) ? 1 : 0; + MpmCtx *mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, am->sgh_mpm_context, dir); + SCLogDebug("%s: %d mpm_Ctx %p", am->name, r, mpm_ctx); + if (mpm_ctx != NULL) { + if (mpm_table[de_ctx->mpm_matcher].Prepare != NULL) { + r |= mpm_table[de_ctx->mpm_matcher].Prepare(mpm_ctx); + SCLogDebug("%s: %d", am->name, r); + } + } + } + am = am->next; + } + return r; +} + /** \brief register a MPM engine * * \note to be used at start up / registration only. Errors are fatal. @@ -1119,6 +1350,9 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx) int pkt_mpms_cnt = de_ctx->buffer_type_id; uint32_t pktstats[pkt_mpms_cnt + 1]; // +1 to silence scan-build memset(&pktstats, 0x00, sizeof(pktstats)); + int frame_mpms_cnt = de_ctx->buffer_type_id; + uint32_t framestats[frame_mpms_cnt + 1]; // +1 to silence scan-build + memset(&framestats, 0x00, sizeof(framestats)); for (htb = HashListTableGetListHead(de_ctx->mpm_hash_table); htb != NULL; @@ -1151,6 +1385,12 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx) ms->mpm_ctx); appstats[am->sm_list]++; break; + case DETECT_BUFFER_MPM_TYPE_FRAME: + SCLogDebug("%s: %u patterns. Min %u, Max %u. Ctx %p", am->name, + ms->mpm_ctx->pattern_cnt, ms->mpm_ctx->minlen, ms->mpm_ctx->maxlen, + ms->mpm_ctx); + framestats[am->sm_list]++; + break; case DETECT_BUFFER_MPM_TYPE_SIZE: break; } @@ -1180,6 +1420,14 @@ void MpmStoreReportStats(const DetectEngineCtx *de_ctx) } pm = pm->next; } + const DetectBufferMpmRegistery *um = de_ctx->frame_mpms_list; + while (um != NULL) { + if (framestats[um->sm_list] > 0) { + const char *name = um->name; + SCLogPerf("Frame MPM \"%s\": %u", name, framestats[um->sm_list]); + } + um = um->next; + } } } @@ -1235,8 +1483,9 @@ static void MpmStoreSetup(const DetectEngineCtx *de_ctx, MpmStore *ms) } ms->mpm_ctx = MpmFactoryGetMpmCtxForProfile(de_ctx, ms->sgh_mpm_context, dir); - if (ms->mpm_ctx == NULL) + if (ms->mpm_ctx == NULL) { return; + } MpmInitCtx(ms->mpm_ctx, de_ctx->mpm_matcher); @@ -1246,8 +1495,10 @@ static void MpmStoreSetup(const DetectEngineCtx *de_ctx, MpmStore *ms) s = de_ctx->sig_array[sig]; if (s == NULL) continue; - if ((s->flags & ms->direction) == 0) + if ((s->flags & ms->direction) == 0) { + SCLogDebug("s->flags %x ms->direction %x", s->flags, ms->direction); continue; + } if (s->init_data->mpm_sm == NULL) continue; int list = SigMatchListSMBelongsTo(s, s->init_data->mpm_sm); @@ -1578,6 +1829,77 @@ static MpmStore *MpmStorePrepareBufferPkt(DetectEngineCtx *de_ctx, return NULL; } +static MpmStore *MpmStorePrepareBufferFrame( + DetectEngineCtx *de_ctx, SigGroupHead *sgh, const DetectBufferMpmRegistery *am) +{ + const Signature *s = NULL; + uint32_t sig; + uint32_t cnt = 0; + uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx) / 8 + 1; + uint8_t sids_array[max_sid]; + memset(sids_array, 0x00, max_sid); + + SCLogDebug("handling %s for list %d", am->name, am->sm_list); + + for (sig = 0; sig < sgh->init->sig_cnt; sig++) { + s = sgh->init->match_array[sig]; + if (s == NULL) + continue; + + if (s->init_data->mpm_sm == NULL) + continue; + + if ((s->flags & am->direction) == 0) + continue; + + int list = SigMatchListSMBelongsTo(s, s->init_data->mpm_sm); + if (list < 0) + continue; + + if (list != am->sm_list) + continue; + + sids_array[s->num / 8] |= 1 << (s->num % 8); + cnt++; + } + + if (cnt == 0) + return NULL; + + MpmStore lookup = { sids_array, max_sid, am->direction, MPMB_MAX, am->sm_list, 0, NULL }; + SCLogDebug("am->sm_list %d", am->sm_list); + + MpmStore *result = MpmStoreLookup(de_ctx, &lookup); + if (result == NULL) { + SCLogDebug("new unique mpm for %s: %u patterns", am->name, cnt); + + MpmStore *copy = SCCalloc(1, sizeof(MpmStore)); + if (copy == NULL) + return NULL; + uint8_t *sids = SCCalloc(1, max_sid); + if (sids == NULL) { + SCFree(copy); + return NULL; + } + + memcpy(sids, sids_array, max_sid); + copy->sid_array = sids; + copy->sid_array_size = max_sid; + copy->buffer = MPMB_MAX; + copy->direction = am->direction; + copy->sm_list = am->sm_list; + copy->sgh_mpm_context = am->sgh_mpm_context; + + MpmStoreSetup(de_ctx, copy); + MpmStoreAdd(de_ctx, copy); + return copy; + } else { + SCLogDebug("using existing mpm %p", result); + return result; + } + return NULL; +} + static void SetRawReassemblyFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh) { const Signature *s = NULL; @@ -1663,6 +1985,39 @@ static void PreparePktMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh) } } +static void PrepareFrameMpms(DetectEngineCtx *de_ctx, SigGroupHead *sh) +{ + if (de_ctx->frame_mpms_list_cnt == 0) + return; + + sh->init->frame_mpms = SCCalloc(de_ctx->frame_mpms_list_cnt, sizeof(MpmCtx *)); + BUG_ON(sh->init->frame_mpms == NULL); + + DetectBufferMpmRegistery *a = de_ctx->frame_mpms_list; + while (a != NULL) { + SCLogDebug("a %s direction %d PrefilterRegisterWithListId %p", a->name, a->direction, + a->PrefilterRegisterWithListId); + MpmStore *mpm_store = MpmStorePrepareBufferFrame(de_ctx, sh, a); + if (mpm_store != NULL) { + sh->init->frame_mpms[a->id] = mpm_store->mpm_ctx; + + SCLogDebug("a %p a->name %s a->reg->PrefilterRegisterWithListId %p " + "mpm_store->mpm_ctx %p", + a, a->name, a->PrefilterRegisterWithListId, mpm_store->mpm_ctx); + + /* if we have just certain types of negated patterns, + * mpm_ctx can be NULL */ + SCLogDebug("mpm_store %p mpm_ctx %p", mpm_store, mpm_store->mpm_ctx); + if (a->PrefilterRegisterWithListId && mpm_store->mpm_ctx) { + BUG_ON(a->PrefilterRegisterWithListId( + de_ctx, sh, mpm_store->mpm_ctx, a, a->sm_list) != 0); + SCLogDebug("mpm %s %d set up", a->name, a->sm_list); + } + } + a = a->next; + } +} + /** \brief Prepare the pattern matcher ctx in a sig group head. * */ @@ -1718,6 +2073,7 @@ int PatternMatchPrepareGroup(DetectEngineCtx *de_ctx, SigGroupHead *sh) PrepareAppMpms(de_ctx, sh); PreparePktMpms(de_ctx, sh); + PrepareFrameMpms(de_ctx, sh); return 0; } diff --git a/src/detect-engine-mpm.h b/src/detect-engine-mpm.h index e9ed4078cc..566287f8c1 100644 --- a/src/detect-engine-mpm.h +++ b/src/detect-engine-mpm.h @@ -32,6 +32,8 @@ #include "stream.h" +void DetectMpmInitializeFrameMpms(DetectEngineCtx *de_ctx); +int DetectMpmPrepareFrameMpms(DetectEngineCtx *de_ctx); void DetectMpmInitializePktMpms(DetectEngineCtx *de_ctx); int DetectMpmPreparePktMpms(DetectEngineCtx *de_ctx); void DetectMpmInitializeAppMpms(DetectEngineCtx *de_ctx); @@ -112,11 +114,24 @@ void DetectPktMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id, DetectEngineTransforms *transforms); +void DetectFrameMpmRegister(const char *name, int direction, int priority, + int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id), + AppProto alproto, uint8_t type); +void DetectFrameMpmRegisterByParentId(DetectEngineCtx *de_ctx, const int id, const int parent_id, + DetectEngineTransforms *transforms); +void DetectEngineFrameMpmRegister(DetectEngineCtx *de_ctx, const char *name, int direction, + int priority, + int (*PrefilterRegister)(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id), + AppProto alproto, uint8_t type); int PrefilterGenericMpmPktRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, const DetectBufferMpmRegistery *mpm_reg, int list_id); +int PrefilterGenericMpmFrameRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh, MpmCtx *mpm_ctx, + const DetectBufferMpmRegistery *mpm_reg, int list_id); typedef struct PrefilterMpmListId { int list_id; diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index 3b23ba98f6..6cf0fbed5d 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2016 Open Information Security Foundation +/* Copyright (C) 2016-2021 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 @@ -47,8 +47,10 @@ #include "suricata-common.h" #include "suricata.h" +#include "detect-engine.h" #include "detect-engine-prefilter.h" #include "detect-engine-mpm.h" +#include "detect-engine-frame.h" #include "app-layer-parser.h" #include "app-layer-htp.h" @@ -107,10 +109,10 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, do { if (engine->alproto != alproto) goto next; - if (engine->tx_min_progress > tx->tx_progress) + if (engine->ctx.tx_min_progress > tx->tx_progress) break; - if (tx->tx_progress > engine->tx_min_progress) { - if (tx->prefilter_flags & BIT_U64(engine->tx_min_progress)) { + if (tx->tx_progress > engine->ctx.tx_min_progress) { + if (tx->prefilter_flags & BIT_U64(engine->ctx.tx_min_progress)) { goto next; } } @@ -120,8 +122,8 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, p, p->flow, tx->tx_ptr, tx->tx_id, flow_flags); PREFILTER_PROFILING_END(det_ctx, engine->gid); - if (tx->tx_progress > engine->tx_min_progress && engine->is_last_for_progress) { - tx->prefilter_flags |= BIT_U64(engine->tx_min_progress); + if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) { + tx->prefilter_flags |= BIT_U64(engine->ctx.tx_min_progress); } next: if (engine->is_last) @@ -142,7 +144,16 @@ void Prefilter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, Packet *p, const uint8_t flags) { SCEnter(); - +#if 0 + /* TODO review this check */ + SCLogDebug("sgh %p frame_engines %p", sgh, sgh->frame_engines); + if (p->proto == IPPROTO_TCP && sgh->frame_engines && p->flow && + p->flow->alproto != ALPROTO_UNKNOWN && p->flow->alparser != NULL) { + PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_RECORD); + PrefilterFrames(det_ctx, sgh, p, flags, p->flow->alproto); + PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_RECORD); + } +#endif if (sgh->pkt_engines) { PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_PKT); /* run packet engines */ @@ -295,6 +306,41 @@ int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, return 0; } +int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, + PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx, + void (*FreeFunc)(void *pectx), const char *name) +{ + if (sgh == NULL || PrefilterFrameFunc == NULL || pectx == NULL) + return -1; + + PrefilterEngineList *e = SCMallocAligned(sizeof(*e), CLS); + if (e == NULL) + return -1; + memset(e, 0x00, sizeof(*e)); + + e->frame_type = frame_type; + e->alproto = alproto; + e->PrefilterFrame = PrefilterFrameFunc; + e->pectx = pectx; + e->Free = FreeFunc; + + if (sgh->init->frame_engines == NULL) { + sgh->init->frame_engines = e; + } else { + PrefilterEngineList *t = sgh->init->frame_engines; + while (t->next != NULL) { + t = t->next; + } + + t->next = e; + e->id = t->id + 1; + } + + e->name = name; + e->gid = PrefilterStoreGetId(de_ctx, e->name, e->Free); + return 0; +} + static void PrefilterFreeEngineList(PrefilterEngineList *e) { if (e->Free && e->pectx) { @@ -345,20 +391,24 @@ void PrefilterCleanupRuleGroup(const DetectEngineCtx *de_ctx, SigGroupHead *sgh) PrefilterFreeEngines(de_ctx, sgh->tx_engines); sgh->tx_engines = NULL; } + if (sgh->frame_engines) { + PrefilterFreeEngines(de_ctx, sgh->frame_engines); + sgh->frame_engines = NULL; + } } static int PrefilterSetupRuleGroupSortHelper(const void *a, const void *b) { const PrefilterEngine *s0 = a; const PrefilterEngine *s1 = b; - if (s1->tx_min_progress == s0->tx_min_progress) { + if (s1->ctx.tx_min_progress == s0->ctx.tx_min_progress) { if (s1->alproto == s0->alproto) { return s0->local_id > s1->local_id ? 1 : -1; } else { return s0->alproto > s1->alproto ? 1 : -1; } } else { - return s0->tx_min_progress > s1->tx_min_progress ? 1 : -1; + return s0->ctx.tx_min_progress > s1->ctx.tx_min_progress ? 1 : -1; } } @@ -453,7 +503,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) for (el = sgh->init->tx_engines ; el != NULL; el = el->next) { e->local_id = local_id++; e->alproto = el->alproto; - e->tx_min_progress = el->tx_min_progress; + e->ctx.tx_min_progress = el->tx_min_progress; e->cb.PrefilterTx = el->PrefilterTx; e->pectx = el->pectx; el->pectx = NULL; // e now owns the ctx @@ -477,9 +527,9 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) PrefilterEngine *prev_engine = NULL; engine = sgh->tx_engines; do { - BUG_ON(engine->tx_min_progress < last_tx_progress); + BUG_ON(engine->ctx.tx_min_progress < last_tx_progress); if (engine->alproto == a) { - if (last_tx_progress_set && engine->tx_min_progress > last_tx_progress) { + if (last_tx_progress_set && engine->ctx.tx_min_progress > last_tx_progress) { if (prev_engine) { prev_engine->is_last_for_progress = true; } @@ -492,7 +542,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) prev_engine->is_last_for_progress = true; } } - last_tx_progress = engine->tx_min_progress; + last_tx_progress = engine->ctx.tx_min_progress; if (engine->is_last) break; engine++; @@ -504,7 +554,7 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) do { SCLogDebug("engine: gid %u alproto %s tx_min_progress %d is_last %s " "is_last_for_progress %s", - engine->gid, AppProtoToString(engine->alproto), engine->tx_min_progress, + engine->gid, AppProtoToString(engine->alproto), engine->ctx.tx_min_progress, engine->is_last ? "true" : "false", engine->is_last_for_progress ? "true" : "false"); if (engine->is_last) @@ -513,6 +563,33 @@ void PrefilterSetupRuleGroup(DetectEngineCtx *de_ctx, SigGroupHead *sgh) } while (1); #endif } + if (sgh->init->frame_engines != NULL) { + uint32_t cnt = 0; + for (el = sgh->init->frame_engines; el != NULL; el = el->next) { + cnt++; + de_ctx->prefilter_maxid = MAX(de_ctx->prefilter_maxid, el->gid); + } + sgh->frame_engines = SCMallocAligned(cnt * sizeof(PrefilterEngine), CLS); + if (sgh->frame_engines == NULL) { + return; + } + memset(sgh->frame_engines, 0x00, (cnt * sizeof(PrefilterEngine))); + + PrefilterEngine *e = sgh->frame_engines; + for (el = sgh->init->frame_engines; el != NULL; el = el->next) { + e->local_id = el->id; + e->ctx.frame_type = el->frame_type; + e->cb.PrefilterFrame = el->PrefilterFrame; + e->alproto = el->alproto; + e->pectx = el->pectx; + el->pectx = NULL; // e now owns the ctx + e->gid = el->gid; + if (el->next == NULL) { + e->is_last = TRUE; + } + e++; + } + } } /* hash table for assigning a unique id to each engine type. */ diff --git a/src/detect-engine-prefilter.h b/src/detect-engine-prefilter.h index cb0e6eb267..5ad6d1b623 100644 --- a/src/detect-engine-prefilter.h +++ b/src/detect-engine-prefilter.h @@ -44,12 +44,13 @@ int PrefilterAppendPayloadEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, void *pectx, void (*FreeFunc)(void *pectx), const char *name); int PrefilterAppendTxEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, - void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx, - Packet *p, Flow *f, void *tx, - const uint64_t idx, const uint8_t flags), - const AppProto alproto, const int tx_min_progress, - void *pectx, void (*FreeFunc)(void *pectx), - const char *name); + void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f, + void *tx, const uint64_t idx, const uint8_t flags), + const AppProto alproto, const int tx_min_progress, void *pectx, + void (*FreeFunc)(void *pectx), const char *name); +int PrefilterAppendFrameEngine(DetectEngineCtx *de_ctx, SigGroupHead *sgh, + PrefilterFrameFn PrefilterFrameFunc, AppProto alproto, uint8_t frame_type, void *pectx, + void (*FreeFunc)(void *pectx), const char *name); void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh, diff --git a/src/detect-engine-siggroup.c b/src/detect-engine-siggroup.c index b1d0057cce..b9e00f777e 100644 --- a/src/detect-engine-siggroup.c +++ b/src/detect-engine-siggroup.c @@ -71,10 +71,14 @@ void SigGroupHeadInitDataFree(SigGroupHeadInitData *sghid) if (sghid->pkt_mpms != NULL) { SCFree(sghid->pkt_mpms); } + if (sghid->frame_mpms != NULL) { + SCFree(sghid->frame_mpms); + } PrefilterFreeEnginesList(sghid->tx_engines); PrefilterFreeEnginesList(sghid->pkt_engines); PrefilterFreeEnginesList(sghid->payload_engines); + PrefilterFreeEnginesList(sghid->frame_engines); SCFree(sghid); } diff --git a/src/detect-engine.c b/src/detect-engine.c index 9a405a42f0..0db3967b9e 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -100,6 +100,7 @@ static uint32_t DetectEngineTentantGetIdFromPcap(const void *ctx, const Packet * static DetectEngineAppInspectionEngine *g_app_inspect_engines = NULL; static DetectEnginePktInspectionEngine *g_pkt_inspect_engines = NULL; +static DetectEngineFrameInspectionEngine *g_frame_inspect_engines = NULL; SCEnumCharMap det_ctx_event_table[] = { #ifdef UNITTESTS @@ -169,6 +170,54 @@ void DetectPktInspectEngineRegister(const char *name, } } +/** \brief register inspect engine at start up time + * + * \note errors are fatal */ +void DetectFrameInspectEngineRegister(const char *name, int dir, + InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type) +{ + DetectBufferTypeRegister(name); + const int sm_list = DetectBufferTypeGetByName(name); + if (sm_list == -1) { + FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s", name); + } + + if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (Callback == NULL)) { + SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments"); + BUG_ON(1); + } + + int direction; + if (dir == SIG_FLAG_TOSERVER) { + direction = 0; + } else { + direction = 1; + } + + DetectEngineFrameInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine)); + if (unlikely(new_engine == NULL)) { + FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s: %s", name, + strerror(errno)); + } + new_engine->sm_list = sm_list; + new_engine->sm_list_base = sm_list; + new_engine->dir = direction; + new_engine->v1.Callback = Callback; + new_engine->alproto = alproto; + new_engine->type = type; + + if (g_frame_inspect_engines == NULL) { + g_frame_inspect_engines = new_engine; + } else { + DetectEngineFrameInspectionEngine *t = g_frame_inspect_engines; + while (t->next != NULL) { + t = t->next; + } + + t->next = new_engine; + } +} + /** \brief register inspect engine at start up time * * \note errors are fatal */ @@ -361,6 +410,123 @@ static void DetectPktInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx) } } +/** \brief register inspect engine at start up time + * + * \note errors are fatal */ +void DetectEngineFrameInspectEngineRegister(DetectEngineCtx *de_ctx, const char *name, int dir, + InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type) +{ + const int sm_list = DetectEngineBufferTypeRegister(de_ctx, name); + if (sm_list < 0) { + FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s", name); + } + + if ((sm_list < DETECT_SM_LIST_MATCH) || (sm_list >= SHRT_MAX) || (Callback == NULL)) { + SCLogError(SC_ERR_INVALID_ARGUMENTS, "Invalid arguments"); + BUG_ON(1); + } + + int direction; + if (dir == SIG_FLAG_TOSERVER) { + direction = 0; + } else { + direction = 1; + } + + DetectEngineFrameInspectionEngine *new_engine = SCCalloc(1, sizeof(*new_engine)); + if (unlikely(new_engine == NULL)) { + FatalError(SC_ERR_INITIALIZATION, "failed to register inspect engine %s: %s", name, + strerror(errno)); + } + new_engine->sm_list = sm_list; + new_engine->sm_list_base = sm_list; + new_engine->dir = direction; + new_engine->v1.Callback = Callback; + new_engine->alproto = alproto; + new_engine->type = type; + + if (de_ctx->frame_inspect_engines == NULL) { + de_ctx->frame_inspect_engines = new_engine; + } else { + DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines; + while (list->next != NULL) { + list = list->next; + } + + list->next = new_engine; + } +} + +/* copy an inspect engine with transforms to a new list id. */ +static void DetectFrameInspectEngineCopy(DetectEngineCtx *de_ctx, int sm_list, int new_list, + const DetectEngineTransforms *transforms) +{ + /* take the list from the detect engine as the buffers can be registered + * dynamically. */ + const DetectEngineFrameInspectionEngine *t = de_ctx->frame_inspect_engines; + while (t) { + if (t->sm_list == sm_list) { + DetectEngineFrameInspectionEngine *new_engine = + SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine)); + if (unlikely(new_engine == NULL)) { + exit(EXIT_FAILURE); + } + new_engine->sm_list = new_list; /* use new list id */ + new_engine->sm_list_base = sm_list; + new_engine->dir = t->dir; + new_engine->alproto = t->alproto; + new_engine->type = t->type; + new_engine->v1 = t->v1; + new_engine->v1.transforms = transforms; /* assign transforms */ + + if (de_ctx->frame_inspect_engines == NULL) { + de_ctx->frame_inspect_engines = new_engine; + } else { + DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines; + while (list->next != NULL) { + list = list->next; + } + + list->next = new_engine; + } + } + t = t->next; + } +} + +/* copy inspect engines from global registrations to de_ctx list */ +static void DetectFrameInspectEngineCopyListToDetectCtx(DetectEngineCtx *de_ctx) +{ + const DetectEngineFrameInspectionEngine *t = g_frame_inspect_engines; + while (t) { + SCLogDebug("engine %p", t); + DetectEngineFrameInspectionEngine *new_engine = + SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine)); + if (unlikely(new_engine == NULL)) { + exit(EXIT_FAILURE); + } + new_engine->sm_list = t->sm_list; + new_engine->sm_list_base = t->sm_list; + new_engine->dir = t->dir; + new_engine->alproto = t->alproto; + new_engine->type = t->type; + new_engine->v1 = t->v1; + + if (de_ctx->frame_inspect_engines == NULL) { + de_ctx->frame_inspect_engines = new_engine; + } else { + DetectEngineFrameInspectionEngine *list = de_ctx->frame_inspect_engines; + while (list->next != NULL) { + list = list->next; + } + + list->next = new_engine; + } + + t = t->next; + } +} + /** \internal * \brief append the stream inspection * @@ -435,6 +601,63 @@ int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature SCLogDebug("ptrs[%d] is set", i); } + /* set up inspect engines */ + const DetectEngineFrameInspectionEngine *u = de_ctx->frame_inspect_engines; + while (u != NULL) { + SCLogDebug("u %p sm_list %u nlists %u ptrs[] %p", u, u->sm_list, nlists, + u->sm_list < nlists ? ptrs[u->sm_list] : NULL); + if (u->sm_list < nlists && ptrs[u->sm_list] != NULL) { + bool prepend = false; + + if (u->alproto == ALPROTO_UNKNOWN) { + /* special case, inspect engine applies to all protocols */ + } else if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, u->alproto)) + goto next_engine; + + if (s->flags & SIG_FLAG_TOSERVER && !(s->flags & SIG_FLAG_TOCLIENT)) { + if (u->dir == 1) + goto next_engine; + } else if (s->flags & SIG_FLAG_TOCLIENT && !(s->flags & SIG_FLAG_TOSERVER)) { + if (u->dir == 0) + goto next_engine; + } + DetectEngineFrameInspectionEngine *new_engine = + SCCalloc(1, sizeof(DetectEngineFrameInspectionEngine)); + if (unlikely(new_engine == NULL)) { + exit(EXIT_FAILURE); + } + if (mpm_list == u->sm_list) { + SCLogDebug("%s is mpm", DetectEngineBufferTypeGetNameById(de_ctx, u->sm_list)); + prepend = true; + new_engine->mpm = true; + } + + new_engine->type = u->type; + new_engine->sm_list = u->sm_list; + new_engine->sm_list_base = u->sm_list_base; + new_engine->smd = ptrs[new_engine->sm_list]; + new_engine->v1 = u->v1; + SCLogDebug("sm_list %d new_engine->v1 %p/%p", new_engine->sm_list, + new_engine->v1.Callback, new_engine->v1.transforms); + + if (s->frame_inspect == NULL) { + s->frame_inspect = new_engine; + } else if (prepend) { + new_engine->next = s->frame_inspect; + s->frame_inspect = new_engine; + } else { + DetectEngineFrameInspectionEngine *a = s->frame_inspect; + while (a->next != NULL) { + a = a->next; + } + new_engine->next = a->next; + a->next = new_engine; + } + } + next_engine: + u = u->next; + } + /* set up pkt inspect engines */ const DetectEnginePktInspectionEngine *e = de_ctx->pkt_inspect_engines; while (e != NULL) { @@ -628,8 +851,14 @@ void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signa nlists = MAX(e->sm_list + 1, nlists); e = e->next; } + DetectEngineFrameInspectionEngine *u = s->frame_inspect; + while (u) { + nlists = MAX(u->sm_list + 1, nlists); + u = u->next; + } if (nlists == 0) { BUG_ON(s->pkt_inspect); + BUG_ON(s->frame_inspect); return; } @@ -652,6 +881,13 @@ void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *de_ctx, Signa SCFree(e); e = next; } + u = s->frame_inspect; + while (u) { + DetectEngineFrameInspectionEngine *next = u->next; + ptrs[u->sm_list] = u->smd; + SCFree(u); + u = next; + } /* free the smds */ for (int i = 0; i < nlists; i++) @@ -804,6 +1040,16 @@ int DetectBufferTypeRegister(const char *name) } } +void DetectBufferTypeSupportsFrames(const char *name) +{ + BUG_ON(g_buffer_type_reg_closed); + DetectBufferTypeRegister(name); + DetectBufferType *exists = DetectBufferTypeLookupByName(name); + BUG_ON(!exists); + exists->frame = true; + SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id); +} + void DetectBufferTypeSupportsPacket(const char *name) { BUG_ON(g_buffer_type_reg_closed); @@ -887,6 +1133,40 @@ static int DetectEngineBufferTypeAdd(DetectEngineCtx *de_ctx, const char *string return map->id; } +int DetectEngineBufferTypeRegisterWithFrameEngines(DetectEngineCtx *de_ctx, const char *name, + const int direction, const AppProto alproto, const uint8_t frame_type) +{ + DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name); + if (exists) { + return exists->id; + } + + const int buffer_id = DetectEngineBufferTypeAdd(de_ctx, name); + if (buffer_id < 0) { + return -1; + } + + /* TODO hack we need the map to get the name. Should we return the map at reg? */ + const DetectBufferType *map = DetectEngineBufferTypeGetById(de_ctx, buffer_id); + BUG_ON(!map); + + /* register MPM/inspect engines */ + if (direction & SIG_FLAG_TOSERVER) { + DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOSERVER, 2, + PrefilterGenericMpmFrameRegister, alproto, frame_type); + DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOSERVER, + DetectEngineInspectFrameBufferGeneric, alproto, frame_type); + } + if (direction & SIG_FLAG_TOCLIENT) { + DetectEngineFrameMpmRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT, 2, + PrefilterGenericMpmFrameRegister, alproto, frame_type); + DetectEngineFrameInspectEngineRegister(de_ctx, map->name, SIG_FLAG_TOCLIENT, + DetectEngineInspectFrameBufferGeneric, alproto, frame_type); + } + + return buffer_id; +} + int DetectEngineBufferTypeRegister(DetectEngineCtx *de_ctx, const char *name) { DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name); @@ -926,6 +1206,14 @@ const char *DetectBufferTypeGetDescriptionByName(const char *name) return exists->description; } +void DetectEngineBufferTypeSupportsFrames(DetectEngineCtx *de_ctx, const char *name) +{ + DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name); + BUG_ON(!exists); + exists->frame = true; + SCLogDebug("%p %s -- %d supports frame inspection", exists, name, exists->id); +} + void DetectEngineBufferTypeSupportsPacket(DetectEngineCtx *de_ctx, const char *name) { DetectBufferType *exists = DetectEngineBufferTypeLookupByName(de_ctx, name); @@ -1310,6 +1598,8 @@ static void DetectBufferTypeSetupDetectEngine(DetectEngineCtx *de_ctx) PrefilterInit(de_ctx); DetectMpmInitializeAppMpms(de_ctx); DetectAppLayerInspectEngineCopyListToDetectCtx(de_ctx); + DetectMpmInitializeFrameMpms(de_ctx); + DetectFrameInspectEngineCopyListToDetectCtx(de_ctx); DetectMpmInitializePktMpms(de_ctx); DetectPktInspectEngineCopyListToDetectCtx(de_ctx); } @@ -1346,6 +1636,18 @@ static void DetectBufferTypeFreeDetectEngine(DetectEngineCtx *de_ctx) SCFree(pmlist); pmlist = next; } + DetectEngineFrameInspectionEngine *framelist = de_ctx->frame_inspect_engines; + while (framelist) { + DetectEngineFrameInspectionEngine *next = framelist->next; + SCFree(framelist); + framelist = next; + } + DetectBufferMpmRegistery *framemlist = de_ctx->frame_mpms_list; + while (framemlist) { + DetectBufferMpmRegistery *next = framemlist->next; + SCFree(framemlist); + framemlist = next; + } PrefilterDeinit(de_ctx); } } @@ -1400,9 +1702,12 @@ int DetectEngineBufferTypeGetByIdTransforms( map->transforms = t; map->mpm = base_map->mpm; map->packet = base_map->packet; + map->frame = base_map->frame; map->SetupCallback = base_map->SetupCallback; map->ValidateCallback = base_map->ValidateCallback; - if (map->packet) { + if (map->frame) { + DetectFrameMpmRegisterByParentId(de_ctx, map->id, map->parent_id, &map->transforms); + } else if (map->packet) { DetectPktMpmRegisterByParentId(de_ctx, map->id, map->parent_id, &map->transforms); } else { @@ -1415,7 +1720,9 @@ int DetectEngineBufferTypeGetByIdTransforms( SCLogDebug("buffer %s registered with id %d, parent %d", map->name, map->id, map->parent_id); de_ctx->buffer_type_id++; - if (map->packet) { + if (map->frame) { + DetectFrameInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms); + } else if (map->packet) { DetectPktInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms); } else { DetectAppLayerInspectEngineCopy(de_ctx, map->parent_id, map->id, &map->transforms); diff --git a/src/detect-engine.h b/src/detect-engine.h index 933222d3c9..018149f7d3 100644 --- a/src/detect-engine.h +++ b/src/detect-engine.h @@ -28,6 +28,8 @@ #include "tm-threads.h" #include "flow-private.h" +#include "detect-engine-frame.h" + void InspectionBufferInit(InspectionBuffer *buffer, uint32_t initial_size); void InspectionBufferSetup(DetectEngineThreadCtx *det_ctx, const int list_id, InspectionBuffer *buffer, const uint8_t *data, const uint32_t data_len); @@ -49,6 +51,7 @@ int DetectBufferTypeRegister(const char *name); int DetectBufferTypeGetByName(const char *name); void DetectBufferTypeSupportsMpm(const char *name); void DetectBufferTypeSupportsPacket(const char *name); +void DetectBufferTypeSupportsFrames(const char *name); void DetectBufferTypeSupportsTransformations(const char *name); int DetectBufferTypeMaxId(void); void DetectBufferTypeCloseRegistration(void); @@ -61,6 +64,8 @@ void DetectBufferTypeRegisterValidateCallback(const char *name, /* detect engine related buffer funcs */ +int DetectEngineBufferTypeRegisterWithFrameEngines(DetectEngineCtx *de_ctx, const char *name, + const int direction, const AppProto alproto, const uint8_t frame_type); int DetectEngineBufferTypeRegister(DetectEngineCtx *de_ctx, const char *name); const char *DetectEngineBufferTypeGetNameById(const DetectEngineCtx *de_ctx, const int id); const DetectBufferType *DetectEngineBufferTypeGetById(const DetectEngineCtx *de_ctx, const int id); @@ -75,6 +80,7 @@ bool DetectEngineBufferRunValidateCallback( const DetectEngineCtx *de_ctx, const int id, const Signature *s, const char **sigerror); bool DetectEngineBufferTypeValidateTransform(DetectEngineCtx *de_ctx, int sm_list, const uint8_t *content, uint16_t content_len, const char **namestr); +void DetectEngineBufferTypeSupportsFrames(DetectEngineCtx *de_ctx, const char *name); void DetectEngineBufferTypeSupportsPacket(DetectEngineCtx *de_ctx, const char *name); void DetectEngineBufferTypeSupportsMpm(DetectEngineCtx *de_ctx, const char *name); void DetectEngineBufferTypeSupportsTransformations(DetectEngineCtx *de_ctx, const char *name); @@ -161,9 +167,16 @@ void DetectPktInspectEngineRegister(const char *name, InspectionBufferGetPktDataPtr GetPktData, InspectionBufferPktInspectFunc Callback); +void DetectFrameInspectEngineRegister(const char *name, int dir, + InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type); +void DetectEngineFrameInspectEngineRegister(DetectEngineCtx *de_ctx, const char *name, int dir, + InspectionBufferFrameInspectFunc Callback, AppProto alproto, uint8_t type); + int DetectEngineAppInspectionEngine2Signature(DetectEngineCtx *de_ctx, Signature *s); void DetectEngineAppInspectionEngineSignatureFree(DetectEngineCtx *, Signature *s); +bool DetectEngineFrameInspectionRun(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, + const Signature *s, Flow *f, Packet *p, uint8_t *alert_flags); bool DetectEnginePktInspectionRun(ThreadVars *tv, DetectEngineThreadCtx *det_ctx, const Signature *s, Flow *f, Packet *p, diff --git a/src/detect.c b/src/detect.c index 410ccb1f76..51b8de23b3 100644 --- a/src/detect.c +++ b/src/detect.c @@ -84,6 +84,8 @@ static inline void DetectRulePacketRules(ThreadVars * const tv, static void DetectRunTx(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet *p, Flow *f, DetectRunScratchpad *scratch); +static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + Packet *p, Flow *f, DetectRunScratchpad *scratch); static inline void DetectRunPostRules(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, Packet * const p, Flow * const pflow, DetectRunScratchpad *scratch); @@ -136,6 +138,15 @@ static void DetectRun(ThreadVars *th_v, /* run tx/state inspection. Don't call for ICMP error msgs. */ if (pflow && pflow->alstate && likely(pflow->proto == p->proto)) { + if (p->proto == IPPROTO_TCP) { + const TcpSession *ssn = p->flow->protoctx; + if (ssn && (ssn->flags & STREAMTCP_FLAG_APP_LAYER_DISABLED) == 0) { + // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX); + DetectRunFrames(th_v, de_ctx, det_ctx, p, pflow, &scratch); + // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX); + } + } + PACKET_PROFILING_DETECT_START(p, PROF_DETECT_TX); DetectRunTx(th_v, de_ctx, det_ctx, p, pflow, &scratch); PACKET_PROFILING_DETECT_END(p, PROF_DETECT_TX); @@ -749,6 +760,9 @@ static inline void DetectRulePacketRules( if (s->app_inspect != NULL) { goto next; // handle sig in DetectRunTx } + if (s->frame_inspect != NULL) { + goto next; // handle sig in DetectRunFrame + } /* don't run mask check for stateful rules. * There we depend on prefilter */ @@ -1517,6 +1531,114 @@ next: } } +static void DetectRunFrames(ThreadVars *tv, DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, + Packet *p, Flow *f, DetectRunScratchpad *scratch) +{ + const SigGroupHead *const sgh = scratch->sgh; + const AppProto alproto = f->alproto; + + FramesContainer *frames_container = AppLayerFramesGetContainer(f); + if (frames_container == NULL) { + return; + } + Frames *frames; + if (PKT_IS_TOSERVER(p)) { + frames = &frames_container->toserver; + } else { + frames = &frames_container->toclient; + } + + for (uint32_t idx = 0; idx < frames->cnt; idx++) { + SCLogDebug("frame %u", idx); + const Frame *frame = FrameGetByIndex(frames, idx); + if (frame == NULL) { + continue; + } + + uint32_t array_idx = 0; + uint32_t total_rules = det_ctx->match_array_cnt; + + /* run prefilter engines and merge results into a candidates array */ + if (sgh->frame_engines) { + // PACKET_PROFILING_DETECT_START(p, PROF_DETECT_PF_TX); + DetectRunPrefilterFrame(det_ctx, sgh, p, frames, frame, alproto, idx); + // PACKET_PROFILING_DETECT_END(p, PROF_DETECT_PF_TX); + SCLogDebug("%p/%" PRIi64 " rules added from prefilter: %u candidates", frame, frame->id, + det_ctx->pmq.rule_id_array_cnt); + + total_rules += det_ctx->pmq.rule_id_array_cnt; + + if (!(RuleMatchCandidateTxArrayHasSpace( + det_ctx, total_rules))) { // TODO is it safe to overload? + RuleMatchCandidateTxArrayExpand(det_ctx, total_rules); + } + + for (uint32_t i = 0; i < det_ctx->pmq.rule_id_array_cnt; i++) { + const Signature *s = de_ctx->sig_array[det_ctx->pmq.rule_id_array[i]]; + const SigIntId id = s->num; + det_ctx->tx_candidates[array_idx].s = s; + det_ctx->tx_candidates[array_idx].id = id; + det_ctx->tx_candidates[array_idx].flags = NULL; + det_ctx->tx_candidates[array_idx].stream_reset = 0; + array_idx++; + } + PMQ_RESET(&det_ctx->pmq); + } + /* merge 'state' rules from the regular prefilter */ + uint32_t x = array_idx; + for (uint32_t i = 0; i < det_ctx->match_array_cnt; i++) { + const Signature *s = det_ctx->match_array[i]; + if (s->frame_inspect != NULL) { + const SigIntId id = s->num; + det_ctx->tx_candidates[array_idx].s = s; + det_ctx->tx_candidates[array_idx].id = id; + det_ctx->tx_candidates[array_idx].flags = NULL; + det_ctx->tx_candidates[array_idx].stream_reset = 0; + array_idx++; + + SCLogDebug("%p/%" PRIi64 " rule %u (%u) added from 'match' list", frame, frame->id, + s->id, id); + } + } + SCLogDebug("%p/%" PRIi64 " rules added from 'match' list: %u", frame, frame->id, + array_idx - x); + (void)x; + + /* run rules: inspect the match candidates */ + for (uint32_t i = 0; i < array_idx; i++) { + const Signature *s = det_ctx->tx_candidates[i].s; + + SCLogDebug("%p/%" PRIi64 " inspecting: sid %u (%u)", frame, frame->id, s->id, s->num); + + /* start new inspection */ + SCLogDebug("%p/%" PRIi64 " Start sid %u", frame, frame->id, s->id); + + /* call individual rule inspection */ + RULE_PROFILING_START(p); + int r = DetectRunInspectRuleHeader(p, f, s, s->flags, s->proto.flags); + if (r == 1) { + r = DetectRunFrameInspectRule(tv, det_ctx, s, f, p, frames, frame, idx); + if (r == 1) { + /* match */ + DetectRunPostMatch(tv, det_ctx, p, s); + + const uint8_t alert_flags = + (PACKET_ALERT_FLAG_STATE_MATCH | PACKET_ALERT_FLAG_FRAME); + // TODO set tx id if the frame has it + det_ctx->flags |= DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET; + det_ctx->frame_id = frame->id; + SCLogDebug( + "%p/%" PRIi64 " sig %u (%u) matched", frame, frame->id, s->id, s->num); + PacketAlertAppend(det_ctx, s, p, 0, alert_flags); // TODO tx id frame field + } + } + DetectVarProcessList(det_ctx, p->flow, p); + RULE_PROFILING_END(det_ctx, s, r, p); + } + InspectionBufferClean(det_ctx); + } +} + static DetectEngineThreadCtx *GetTenantById(HashTable *h, uint32_t id) { /* technically we need to pass a DetectEngineThreadCtx struct with the diff --git a/src/detect.h b/src/detect.h index 3a3758a35d..215118db4d 100644 --- a/src/detect.h +++ b/src/detect.h @@ -279,6 +279,7 @@ typedef struct DetectPort_ { /* for now a uint8_t is enough */ #define SignatureMask uint8_t +#define DETECT_ENGINE_THREAD_CTX_FRAME_ID_SET 0x0001 #define DETECT_ENGINE_THREAD_CTX_STREAM_CONTENT_MATCH 0x0004 #define FILE_SIG_NEED_FILE 0x01 @@ -423,6 +424,7 @@ typedef struct DetectBufferType_ { int parent_id; bool mpm; bool packet; /**< compat to packet matches */ + bool frame; /**< is about Frame inspection */ bool supports_transforms; void (*SetupCallback)(const struct DetectEngineCtx_ *, struct Signature_ *); bool (*ValidateCallback)(const struct Signature_ *, const char **sigerror); @@ -460,6 +462,33 @@ typedef struct DetectEnginePktInspectionEngine { struct DetectEnginePktInspectionEngine *next; } DetectEnginePktInspectionEngine; +struct Frame; +struct Frames; +struct DetectEngineFrameInspectionEngine; + +/** + * \param alert_flags[out] for setting PACKET_ALERT_FLAG_* + */ +typedef int (*InspectionBufferFrameInspectFunc)(struct DetectEngineThreadCtx_ *, + const struct DetectEngineFrameInspectionEngine *engine, const struct Signature_ *s, + Packet *p, const struct Frames *frames, const struct Frame *frame, const uint32_t idx); + +typedef struct DetectEngineFrameInspectionEngine { + AppProto alproto; + uint8_t dir; + uint8_t type; + bool mpm; + uint16_t sm_list; + uint16_t sm_list_base; + struct { + InspectionBufferFrameInspectFunc Callback; + /** pointer to the transforms in the 'DetectBuffer entry for this list */ + const DetectEngineTransforms *transforms; + } v1; + SigMatchData *smd; + struct DetectEngineFrameInspectionEngine *next; +} DetectEngineFrameInspectionEngine; + #ifdef UNITTESTS #define sm_lists init_data->smlists #define sm_lists_tail init_data->smlists_tail @@ -565,6 +594,7 @@ typedef struct Signature_ { DetectEngineAppInspectionEngine *app_inspect; DetectEnginePktInspectionEngine *pkt_inspect; + DetectEngineFrameInspectionEngine *frame_inspect; /* Matching structures for the built-ins. The others are in * their inspect engines. */ @@ -593,6 +623,7 @@ typedef struct Signature_ { enum DetectBufferMpmType { DETECT_BUFFER_MPM_TYPE_PKT, DETECT_BUFFER_MPM_TYPE_APP, + DETECT_BUFFER_MPM_TYPE_FRAME, /* must be last */ DETECT_BUFFER_MPM_TYPE_SIZE, }; @@ -629,6 +660,12 @@ typedef struct DetectBufferMpmRegistery_ { const struct DetectBufferMpmRegistery_ *mpm_reg, int list_id); InspectionBufferGetPktDataPtr GetData; } pkt_v1; + + /* frame matching: use if type == DETECT_BUFFER_MPM_TYPE_FRAME */ + struct { + AppProto alproto; + uint8_t type; + } frame_v1; }; struct DetectBufferMpmRegistery_ *next; @@ -933,6 +970,9 @@ typedef struct DetectEngineCtx_ { DetectEnginePktInspectionEngine *pkt_inspect_engines; DetectBufferMpmRegistery *pkt_mpms_list; uint32_t pkt_mpms_list_cnt; + DetectEngineFrameInspectionEngine *frame_inspect_engines; + DetectBufferMpmRegistery *frame_mpms_list; + uint32_t frame_mpms_list_cnt; uint32_t prefilter_id; HashListTable *prefilter_hash_table; @@ -1269,6 +1309,9 @@ typedef struct MpmStore_ { } MpmStore; +typedef void (*PrefilterFrameFn)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, + const struct Frames *frames, const struct Frame *frame, const uint32_t idx); + typedef struct PrefilterEngineList_ { uint16_t id; @@ -1278,6 +1321,8 @@ typedef struct PrefilterEngineList_ { * with Tx Engine */ uint8_t tx_min_progress; + uint8_t frame_type; + /** Context for matching. Might be MpmCtx for MPM engines, other ctx' * for other engines. */ void *pectx; @@ -1286,6 +1331,7 @@ typedef struct PrefilterEngineList_ { void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f, void *tx, const uint64_t idx, const uint8_t flags); + PrefilterFrameFn PrefilterFrame; struct PrefilterEngineList_ *next; @@ -1302,9 +1348,13 @@ typedef struct PrefilterEngine_ { /** App Proto this engine applies to: only used with Tx Engines */ AppProto alproto; - /** Minimal Tx progress we need before running the engine. Only used - * with Tx Engine */ - uint8_t tx_min_progress; + + union { + /** Minimal Tx progress we need before running the engine. Only used + * with Tx Engine */ + uint8_t tx_min_progress; + uint8_t frame_type; + } ctx; /** Context for matching. Might be MpmCtx for MPM engines, other ctx' * for other engines. */ @@ -1315,6 +1365,7 @@ typedef struct PrefilterEngine_ { void (*PrefilterTx)(DetectEngineThreadCtx *det_ctx, const void *pectx, Packet *p, Flow *f, void *tx, const uint64_t idx, const uint8_t flags); + PrefilterFrameFn PrefilterFrame; } cb; /* global id for this prefilter */ @@ -1335,10 +1386,12 @@ typedef struct SigGroupHeadInitData_ { MpmCtx **app_mpms; MpmCtx **pkt_mpms; + MpmCtx **frame_mpms; PrefilterEngineList *pkt_engines; PrefilterEngineList *payload_engines; PrefilterEngineList *tx_engines; + PrefilterEngineList *frame_engines; /** number of sigs in this group */ SigIntId sig_cnt; @@ -1371,6 +1424,7 @@ typedef struct SigGroupHead_ { PrefilterEngine *pkt_engines; PrefilterEngine *payload_engines; PrefilterEngine *tx_engines; + PrefilterEngine *frame_engines; /* ptr to our init data we only use at... init :) */ SigGroupHeadInitData *init; diff --git a/src/suricata-common.h b/src/suricata-common.h index d1bcd97c12..84b578d747 100644 --- a/src/suricata-common.h +++ b/src/suricata-common.h @@ -422,6 +422,7 @@ typedef enum PacketProfileDetectId_ { PROF_DETECT_PF_PKT, PROF_DETECT_PF_PAYLOAD, PROF_DETECT_PF_TX, + PROF_DETECT_PF_RECORD, PROF_DETECT_PF_SORT1, PROF_DETECT_PF_SORT2, PROF_DETECT_NONMPMLIST, diff --git a/src/util-profiling.c b/src/util-profiling.c index d8807f697a..4be72e58d0 100644 --- a/src/util-profiling.c +++ b/src/util-profiling.c @@ -1267,6 +1267,7 @@ const char * PacketProfileDetectIdToString(PacketProfileDetectId id) CASE_CODE (PROF_DETECT_PF_PKT); CASE_CODE (PROF_DETECT_PF_PAYLOAD); CASE_CODE (PROF_DETECT_PF_TX); + CASE_CODE(PROF_DETECT_PF_RECORD); CASE_CODE (PROF_DETECT_PF_SORT1); CASE_CODE (PROF_DETECT_PF_SORT2); CASE_CODE (PROF_DETECT_NONMPMLIST);