/* Copyright (C) 2007-2023 Open Information Security Foundation * * You can copy, redistribute or modify this Program under the terms of * the GNU General Public License version 2 as published by the Free * 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 Anoop Saldanha */ #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-event.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" #define MAX_ALPROTO_NAME 50 typedef struct DetectAppLayerEventData_ { AppProto alproto; uint8_t event_id; } DetectAppLayerEventData; static int DetectAppLayerEventPktMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx); static int DetectAppLayerEventSetup(DetectEngineCtx *, Signature *, const char *); static void DetectAppLayerEventFree(DetectEngineCtx *, void *); static uint8_t DetectEngineAptEventInspect(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_events_list_id = 0; /** * \brief Registers the keyword handlers for the "app-layer-event" keyword. */ void DetectAppLayerEventRegister(void) { sigmatch_table[DETECT_AL_APP_LAYER_EVENT].name = "app-layer-event"; sigmatch_table[DETECT_AL_APP_LAYER_EVENT].desc = "match on events generated by the App Layer Parsers and the protocol detection engine"; sigmatch_table[DETECT_AL_APP_LAYER_EVENT].url = "/rules/app-layer.html#app-layer-event"; sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Match = DetectAppLayerEventPktMatch; sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Setup = DetectAppLayerEventSetup; sigmatch_table[DETECT_AL_APP_LAYER_EVENT].Free = DetectAppLayerEventFree; DetectAppLayerInspectEngineRegister2("app-layer-events", ALPROTO_UNKNOWN, SIG_FLAG_TOSERVER, 0, DetectEngineAptEventInspect, NULL); DetectAppLayerInspectEngineRegister2("app-layer-events", ALPROTO_UNKNOWN, SIG_FLAG_TOCLIENT, 0, DetectEngineAptEventInspect, NULL); g_applayer_events_list_id = DetectBufferTypeGetByName("app-layer-events"); } static uint8_t DetectEngineAptEventInspect(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; AppLayerDecoderEvents *decoder_events = AppLayerParserGetEventsByTx(f->proto, alproto, tx); if (decoder_events == NULL) { goto end; } SigMatchData *smd = engine->smd; while (1) { DetectAppLayerEventData *aled = (DetectAppLayerEventData *)smd->ctx; KEYWORD_PROFILING_START; if (AppLayerDecoderEventsIsEventSet(decoder_events, aled->event_id)) { 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) { return DETECT_ENGINE_INSPECT_SIG_MATCH; } else { if (AppLayerParserGetStateProgress(f->proto, alproto, tx, flags) == AppLayerParserGetStateProgressCompletionStatus(alproto, flags)) { return DETECT_ENGINE_INSPECT_SIG_CANT_MATCH; } else { return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; } } } static int DetectAppLayerEventPktMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) { const DetectAppLayerEventData *aled = (const DetectAppLayerEventData *)ctx; return AppLayerDecoderEventsIsEventSet(p->app_layer_events, aled->event_id); } static DetectAppLayerEventData *DetectAppLayerEventParsePkt(const char *arg, AppLayerEventType *event_type) { int event_id = 0; int r = AppLayerGetPktEventInfo(arg, &event_id); if (r < 0 || r > UINT8_MAX) { SCLogError("app-layer-event keyword " "supplied with packet based event - \"%s\" that isn't " "supported yet.", arg); return NULL; } DetectAppLayerEventData *aled = SCCalloc(1, sizeof(DetectAppLayerEventData)); if (unlikely(aled == NULL)) return NULL; aled->event_id = (uint8_t)event_id; *event_type = APP_LAYER_EVENT_TYPE_PACKET; return aled; } static bool OutdatedEvent(const char *raw) { if (strcmp(raw, "tls.certificate_missing_element") == 0 || strcmp(raw, "tls.certificate_unknown_element") == 0 || strcmp(raw, "tls.certificate_invalid_string") == 0) { return true; } return false; } static AppProto AppLayerEventGetProtoByName(char *alproto_name) { AppProto alproto = AppLayerGetProtoByName(alproto_name); if (alproto == ALPROTO_HTTP) { // app-layer events http refer to http1 alproto = ALPROTO_HTTP1; } return alproto; } static int DetectAppLayerEventSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg) { if (arg == NULL) { SCLogError("app-layer-event keyword supplied " "with no arguments. This keyword needs an argument."); return -1; } while (*arg != '\0' && isspace((unsigned char)*arg)) arg++; AppLayerEventType event_type; DetectAppLayerEventData *data = NULL; if (strchr(arg, '.') == NULL) { data = DetectAppLayerEventParsePkt(arg, &event_type); if (data == NULL) return -1; } else { SCLogDebug("parsing %s", arg); char alproto_name[MAX_ALPROTO_NAME]; bool needs_detctx = false; const char *p_idx = strchr(arg, '.'); if (strlen(arg) > MAX_ALPROTO_NAME) { SCLogError("app-layer-event keyword is too long or malformed"); return -1; } const char *event_name = p_idx + 1; // skip . /* + 1 for trailing \0 */ strlcpy(alproto_name, arg, p_idx - arg + 1); const AppProto alproto = AppLayerEventGetProtoByName(alproto_name); if (alproto == ALPROTO_UNKNOWN) { if (!strcmp(alproto_name, "file")) { needs_detctx = true; } else { SCLogError("app-layer-event keyword " "supplied with unknown protocol \"%s\"", alproto_name); return -1; } } if (OutdatedEvent(arg)) { if (SigMatchStrictEnabled(DETECT_AL_APP_LAYER_EVENT)) { SCLogError("app-layer-event keyword no longer supports event \"%s\"", arg); return -1; } else { SCLogWarning("app-layer-event keyword no longer supports event \"%s\"", arg); return -3; } } uint8_t ipproto = 0; if (s->proto.proto[IPPROTO_TCP / 8] & 1 << (IPPROTO_TCP % 8)) { ipproto = IPPROTO_TCP; } else if (s->proto.proto[IPPROTO_UDP / 8] & 1 << (IPPROTO_UDP % 8)) { ipproto = IPPROTO_UDP; } else { SCLogError("protocol %s is disabled", alproto_name); return -1; } int r; int event_id = 0; if (!needs_detctx) { r = AppLayerParserGetEventInfo(ipproto, alproto, event_name, &event_id, &event_type); } else { r = DetectEngineGetEventInfo(event_name, &event_id, &event_type); } if (r < 0) { if (SigMatchStrictEnabled(DETECT_AL_APP_LAYER_EVENT)) { SCLogError("app-layer-event keyword's " "protocol \"%s\" doesn't have event \"%s\" registered", alproto_name, event_name); return -1; } else { SCLogWarning("app-layer-event keyword's " "protocol \"%s\" doesn't have event \"%s\" registered", alproto_name, event_name); return -3; } } if (event_id > UINT8_MAX) { SCLogWarning("app-layer-event keyword's id has invalid value"); return -4; } data = SCCalloc(1, sizeof(*data)); if (unlikely(data == NULL)) return -1; data->alproto = alproto; data->event_id = (uint8_t)event_id; } SCLogDebug("data->event_id %u", data->event_id); SigMatch *sm = SigMatchAlloc(); if (sm == NULL) goto error; sm->type = DETECT_AL_APP_LAYER_EVENT; sm->ctx = (SigMatchCtx *)data; if (event_type == APP_LAYER_EVENT_TYPE_PACKET) { SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); } else { if (DetectSignatureSetAppProto(s, data->alproto) != 0) goto error; SigMatchAppendSMToList(s, sm, g_applayer_events_list_id); s->flags |= SIG_FLAG_APPLAYER; } return 0; error: if (data) { DetectAppLayerEventFree(de_ctx, data); } if (sm) { sm->ctx = NULL; SigMatchFree(de_ctx, sm); } return -1; } static void DetectAppLayerEventFree(DetectEngineCtx *de_ctx, void *ptr) { SCFree(ptr); }