/* Copyright (C) 2007-2025 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 "threads.h" #include "decode.h" #include "app-layer.h" #include "app-layer-protos.h" #include "app-layer-parser.h" #include "app-layer-smtp.h" #include "detect.h" #include "detect-parse.h" #include "detect-engine.h" #include "detect-engine-state.h" #include "detect-engine-build.h" #include "detect-app-layer-state.h" #include "flow.h" #include "flow-var.h" #include "flow-util.h" #include "decode-events.h" #include "util-byte.h" #include "util-debug.h" #include "util-enum.h" #include "util-profiling.h" #include "util-unittest.h" #include "util-unittest-helper.h" #include "stream-tcp-util.h" typedef struct DetectAppLayerStateData_ { uint8_t progress; int8_t mode; } DetectAppLayerStateData; static int DetectAppLayerStateSetup(DetectEngineCtx *, Signature *, const char *); static void DetectAppLayerStateFree(DetectEngineCtx *, void *); static uint8_t DetectEngineAptStateInspect(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id); static int g_applayer_state_list_id = 0; void DetectAppLayerStateRegister(void) { sigmatch_table[DETECT_APP_LAYER_STATE].name = "app-layer-state"; sigmatch_table[DETECT_APP_LAYER_STATE].desc = "match on events generated by the App Layer Parsers and the protocol detection engine"; sigmatch_table[DETECT_APP_LAYER_STATE].url = "/rules/app-layer.html#app-layer-event"; sigmatch_table[DETECT_APP_LAYER_STATE].Setup = DetectAppLayerStateSetup; sigmatch_table[DETECT_APP_LAYER_STATE].Free = DetectAppLayerStateFree; DetectAppLayerInspectEngineRegister("app-layer-state", ALPROTO_UNKNOWN, SIG_FLAG_TOSERVER, 0, DetectEngineAptStateInspect, NULL); DetectAppLayerInspectEngineRegister("app-layer-state", ALPROTO_UNKNOWN, SIG_FLAG_TOCLIENT, 0, DetectEngineAptStateInspect, NULL); g_applayer_state_list_id = DetectBufferTypeGetByName("app-layer-state"); } static uint8_t DetectEngineAptStateInspect(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx, const struct DetectEngineAppInspectionEngine_ *engine, const Signature *s, Flow *f, uint8_t flags, void *alstate, void *tx, uint64_t tx_id) { int r = 0; const AppProto alproto = f->alproto; const uint8_t tx_progress = (uint8_t)AppLayerParserGetStateProgress(f->proto, alproto, tx, flags); const SigMatchData *smd = engine->smd; while (1) { const DetectAppLayerStateData *data = (const DetectAppLayerStateData *)smd->ctx; KEYWORD_PROFILING_START; bool match = false; if (data->mode == -1) { SCLogDebug("sid:%u tx_progress %u < keyword progress %u ?", s->id, tx_progress, data->progress); if (tx_progress < data->progress) { match = true; } } else if (data->mode == 1) { SCLogDebug("sid:%u tx_progress %u > keyword progress %u ?", s->id, tx_progress, data->progress); if (tx_progress > data->progress) { match = true; } } else { BUG_ON(1); } if (match) { KEYWORD_PROFILING_END(det_ctx, smd->type, 1); if (smd->is_last) break; smd++; continue; } KEYWORD_PROFILING_END(det_ctx, smd->type, 0); goto end; } r = 1; end: if (r == 1) { SCLogDebug("DETECT_ENGINE_INSPECT_SIG_MATCH"); return DETECT_ENGINE_INSPECT_SIG_MATCH; } else { if (AppLayerParserGetStateProgress(f->proto, alproto, tx, flags) == AppLayerParserGetStateProgressCompletionStatus(alproto, flags)) { SCLogDebug("DETECT_ENGINE_INSPECT_SIG_CANT_MATCH"); return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; } else { SCLogDebug("DETECT_ENGINE_INSPECT_SIG_NO_MATCH"); return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; } } } // TODO dedup with detect-parse.c static SignatureHook SetAppHook(const AppProto alproto, int progress) { SignatureHook h = { .type = SIGNATURE_HOOK_TYPE_APP, .t.app.alproto = alproto, .t.app.app_progress = progress, }; return h; } static int DetectAppLayerStateSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { if (s->alproto == ALPROTO_UNKNOWN) { return -1; } int mode = 0; if (strlen(arg) > 0) { if (arg[0] == '<') { mode = -1; arg++; } else if (arg[0] == '>') { mode = 1; arg++; } } if (mode == 0) { const char *h = arg; const int progress_ts = AppLayerParserGetStateIdByName( IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOSERVER); if (progress_ts >= 0) { s->flags |= SIG_FLAG_TOSERVER; s->init_data->hook = SetAppHook(s->alproto, progress_ts); } else { const int progress_tc = AppLayerParserGetStateIdByName( IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOCLIENT); if (progress_tc < 0) { return -1; } s->flags |= SIG_FLAG_TOCLIENT; s->init_data->hook = SetAppHook(s->alproto, progress_tc); } SCLogDebug("hook %u", s->init_data->hook.t.app.app_progress); return 0; } int progress = 0; const char *h = arg; const int progress_ts = AppLayerParserGetStateIdByName(IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOSERVER); if (progress_ts >= 0) { s->flags |= SIG_FLAG_TOSERVER; progress = progress_ts; } else { const int progress_tc = AppLayerParserGetStateIdByName( IPPROTO_TCP /* TODO */, s->alproto, h, STREAM_TOCLIENT); if (progress_tc < 0) { return -1; } s->flags |= SIG_FLAG_TOCLIENT; progress = progress_tc; } DetectAppLayerStateData *data = SCCalloc(1, sizeof(*data)); if (data == NULL) return -1; data->progress = (uint8_t)progress; data->mode = (int8_t)mode; if (SigMatchAppendSMToList(de_ctx, s, DETECT_APP_LAYER_STATE, (SigMatchCtx *)data, g_applayer_state_list_id) == NULL) { SCFree(data); return -1; } s->flags |= SIG_FLAG_APPLAYER; return 0; } static void DetectAppLayerStateFree(DetectEngineCtx *de_ctx, void *ptr) { SCFree(ptr); }