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.
315 lines
10 KiB
C
315 lines
10 KiB
C
/* 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 <anoopsaldanha@gmail.com>
|
|
*/
|
|
|
|
#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);
|
|
}
|