From a0aa2307322cd47bbf416810ac0292925e03be87 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 19:39:49 +0200 Subject: Adding upstream version 1:7.0.3. Signed-off-by: Daniel Baumann --- src/detect-app-layer-protocol.c | 590 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 590 insertions(+) create mode 100644 src/detect-app-layer-protocol.c (limited to 'src/detect-app-layer-protocol.c') diff --git a/src/detect-app-layer-protocol.c b/src/detect-app-layer-protocol.c new file mode 100644 index 0000000..26a5ce6 --- /dev/null +++ b/src/detect-app-layer-protocol.c @@ -0,0 +1,590 @@ +/* Copyright (C) 2007-2022 Open Information Security Foundation + * + * You can copy, redistribute or modify this Program under the terms of + * the GNU General Public License version 2 as published by the Free + * Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/** + * \file + * + * \author Anoop Saldanha + */ + +#include "suricata-common.h" +#include "detect-engine.h" +#include "detect-engine-build.h" +#include "detect-engine-prefilter.h" +#include "detect-engine-prefilter-common.h" +#include "detect-parse.h" +#include "detect-app-layer-protocol.h" +#include "app-layer.h" +#include "app-layer-parser.h" +#include "util-debug.h" +#include "util-unittest.h" +#include "util-unittest-helper.h" + +#ifdef UNITTESTS +static void DetectAppLayerProtocolRegisterTests(void); +#endif + +typedef struct DetectAppLayerProtocolData_ { + AppProto alproto; + uint8_t negated; +} DetectAppLayerProtocolData; + +static int DetectAppLayerProtocolPacketMatch( + DetectEngineThreadCtx *det_ctx, + Packet *p, const Signature *s, const SigMatchCtx *ctx) +{ + SCEnter(); + + bool r = false; + const DetectAppLayerProtocolData *data = (const DetectAppLayerProtocolData *)ctx; + + /* if the sig is PD-only we only match when PD packet flags are set */ + if (s->type == SIG_TYPE_PDONLY && + (p->flags & (PKT_PROTO_DETECT_TS_DONE | PKT_PROTO_DETECT_TC_DONE)) == 0) { + SCLogDebug("packet %"PRIu64": flags not set", p->pcap_cnt); + SCReturnInt(0); + } + + const Flow *f = p->flow; + if (f == NULL) { + SCLogDebug("packet %"PRIu64": no flow", p->pcap_cnt); + SCReturnInt(0); + } + + /* unknown means protocol detection isn't ready yet */ + + if ((f->alproto_ts != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOSERVER)) + { + SCLogDebug("toserver packet %"PRIu64": looking for %u/neg %u, got %u", + p->pcap_cnt, data->alproto, data->negated, f->alproto_ts); + + r = AppProtoEquals(data->alproto, f->alproto_ts); + + } else if ((f->alproto_tc != ALPROTO_UNKNOWN) && (p->flowflags & FLOW_PKT_TOCLIENT)) + { + SCLogDebug("toclient packet %"PRIu64": looking for %u/neg %u, got %u", + p->pcap_cnt, data->alproto, data->negated, f->alproto_tc); + + r = AppProtoEquals(data->alproto, f->alproto_tc); + } + else { + SCLogDebug("packet %"PRIu64": default case: direction %02x, approtos %u/%u/%u", + p->pcap_cnt, + p->flowflags & (FLOW_PKT_TOCLIENT|FLOW_PKT_TOSERVER), + f->alproto, f->alproto_ts, f->alproto_tc); + } + r = r ^ data->negated; + if (r) { + SCReturnInt(1); + } + SCReturnInt(0); +} + +static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg, bool negate) +{ + DetectAppLayerProtocolData *data; + AppProto alproto = ALPROTO_UNKNOWN; + + if (strcmp(arg, "failed") == 0) { + alproto = ALPROTO_FAILED; + } else { + alproto = AppLayerGetProtoByName((char *)arg); + if (alproto == ALPROTO_UNKNOWN) { + SCLogError("app-layer-protocol " + "keyword supplied with unknown protocol \"%s\"", + arg); + return NULL; + } + } + + data = SCMalloc(sizeof(DetectAppLayerProtocolData)); + if (unlikely(data == NULL)) + return NULL; + data->alproto = alproto; + data->negated = negate; + + return data; +} + +static bool HasConflicts(const DetectAppLayerProtocolData *us, + const DetectAppLayerProtocolData *them) +{ + /* mixing negated and non negated is illegal */ + if (them->negated ^ us->negated) + return true; + /* multiple non-negated is illegal */ + if (!us->negated) + return true; + /* duplicate option */ + if (us->alproto == them->alproto) + return true; + + /* all good */ + return false; +} + +static int DetectAppLayerProtocolSetup(DetectEngineCtx *de_ctx, + Signature *s, const char *arg) +{ + DetectAppLayerProtocolData *data = NULL; + SigMatch *sm = NULL; + + if (s->alproto != ALPROTO_UNKNOWN) { + SCLogError("Either we already " + "have the rule match on an app layer protocol set through " + "other keywords that match on this protocol, or have " + "already seen a non-negated app-layer-protocol."); + goto error; + } + + data = DetectAppLayerProtocolParse(arg, s->init_data->negated); + if (data == NULL) + goto error; + + SigMatch *tsm = s->init_data->smlists[DETECT_SM_LIST_MATCH]; + for ( ; tsm != NULL; tsm = tsm->next) { + if (tsm->type == DETECT_AL_APP_LAYER_PROTOCOL) { + const DetectAppLayerProtocolData *them = (const DetectAppLayerProtocolData *)tsm->ctx; + + if (HasConflicts(data, them)) { + SCLogError("can't mix " + "positive app-layer-protocol match with negated " + "match or match for 'failed'."); + goto error; + } + } + } + + sm = SigMatchAlloc(); + if (sm == NULL) + goto error; + + sm->type = DETECT_AL_APP_LAYER_PROTOCOL; + sm->ctx = (void *)data; + + SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); + return 0; + +error: + if (data != NULL) + SCFree(data); + return -1; +} + +static void DetectAppLayerProtocolFree(DetectEngineCtx *de_ctx, void *ptr) +{ + SCFree(ptr); + return; +} + +/** \internal + * \brief prefilter function for protocol detect matching + */ +static void +PrefilterPacketAppProtoMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const void *pectx) +{ + const PrefilterPacketHeaderCtx *ctx = pectx; + + if (!PrefilterPacketHeaderExtraMatch(ctx, p)) { + SCLogDebug("packet %"PRIu64": extra match failed", p->pcap_cnt); + SCReturn; + } + + if (p->flow == NULL) { + SCLogDebug("packet %"PRIu64": no flow, no alproto", p->pcap_cnt); + SCReturn; + } + + if ((p->flags & (PKT_PROTO_DETECT_TS_DONE|PKT_PROTO_DETECT_TC_DONE)) == 0) { + SCLogDebug("packet %"PRIu64": flags not set", p->pcap_cnt); + SCReturn; + } + + if ((p->flags & PKT_PROTO_DETECT_TS_DONE) && (p->flowflags & FLOW_PKT_TOSERVER)) + { + int r = (ctx->v1.u16[0] == p->flow->alproto_ts) ^ ctx->v1.u8[2]; + if (r) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + } + } else if ((p->flags & PKT_PROTO_DETECT_TC_DONE) && (p->flowflags & FLOW_PKT_TOCLIENT)) + { + int r = (ctx->v1.u16[0] == p->flow->alproto_tc) ^ ctx->v1.u8[2]; + if (r) { + PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt); + } + } +} + +static void +PrefilterPacketAppProtoSet(PrefilterPacketHeaderValue *v, void *smctx) +{ + const DetectAppLayerProtocolData *a = smctx; + v->u16[0] = a->alproto; + v->u8[2] = (uint8_t)a->negated; +} + +static bool +PrefilterPacketAppProtoCompare(PrefilterPacketHeaderValue v, void *smctx) +{ + const DetectAppLayerProtocolData *a = smctx; + if (v.u16[0] == a->alproto && + v.u8[2] == (uint8_t)a->negated) + return true; + return false; +} + +static int PrefilterSetupAppProto(DetectEngineCtx *de_ctx, SigGroupHead *sgh) +{ + return PrefilterSetupPacketHeader(de_ctx, sgh, DETECT_AL_APP_LAYER_PROTOCOL, + PrefilterPacketAppProtoSet, + PrefilterPacketAppProtoCompare, + PrefilterPacketAppProtoMatch); +} + +static bool PrefilterAppProtoIsPrefilterable(const Signature *s) +{ + if (s->type == SIG_TYPE_PDONLY) { + SCLogDebug("prefilter on PD %u", s->id); + return true; + } + return false; +} + +void DetectAppLayerProtocolRegister(void) +{ + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].name = "app-layer-protocol"; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].desc = "match on the detected app-layer protocol"; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].url = "/rules/app-layer.html#app-layer-protocol"; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Match = + DetectAppLayerProtocolPacketMatch; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Setup = + DetectAppLayerProtocolSetup; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].Free = + DetectAppLayerProtocolFree; +#ifdef UNITTESTS + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].RegisterTests = + DetectAppLayerProtocolRegisterTests; +#endif + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].flags = + (SIGMATCH_QUOTES_OPTIONAL|SIGMATCH_HANDLE_NEGATION); + + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].SetupPrefilter = + PrefilterSetupAppProto; + sigmatch_table[DETECT_AL_APP_LAYER_PROTOCOL].SupportsPrefilter = + PrefilterAppProtoIsPrefilterable; + return; +} + +/**********************************Unittests***********************************/ + +#ifdef UNITTESTS + +static int DetectAppLayerProtocolTest01(void) +{ + DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("http", false); + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated != 0); + DetectAppLayerProtocolFree(NULL, data); + PASS; +} + +static int DetectAppLayerProtocolTest02(void) +{ + DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("http", true); + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated == 0); + DetectAppLayerProtocolFree(NULL, data); + PASS; +} + +static int DetectAppLayerProtocolTest03(void) +{ + Signature *s = NULL; + DetectAppLayerProtocolData *data = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:http; sid:1;)"); + FAIL_IF_NULL(s); + + FAIL_IF(s->alproto != ALPROTO_UNKNOWN); + + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + + data = (DetectAppLayerProtocolData *)s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest04(void) +{ + Signature *s = NULL; + DetectAppLayerProtocolData *data = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:!http; sid:1;)"); + FAIL_IF_NULL(s); + FAIL_IF(s->alproto != ALPROTO_UNKNOWN); + FAIL_IF(s->flags & SIG_FLAG_APPLAYER); + + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + + data = (DetectAppLayerProtocolData *)s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated == 0); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest05(void) +{ + Signature *s = NULL; + DetectAppLayerProtocolData *data = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:!http; app-layer-protocol:!smtp; sid:1;)"); + FAIL_IF_NULL(s); + FAIL_IF(s->alproto != ALPROTO_UNKNOWN); + FAIL_IF(s->flags & SIG_FLAG_APPLAYER); + + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + + data = (DetectAppLayerProtocolData *)s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated == 0); + + data = (DetectAppLayerProtocolData *)s->init_data->smlists[DETECT_SM_LIST_MATCH]->next->ctx; + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_SMTP); + FAIL_IF(data->negated == 0); + + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest06(void) +{ + Signature *s = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any " + "(app-layer-protocol:smtp; sid:1;)"); + FAIL_IF_NOT_NULL(s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest07(void) +{ + Signature *s = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any " + "(app-layer-protocol:!smtp; sid:1;)"); + FAIL_IF_NOT_NULL(s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest08(void) +{ + Signature *s = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:!smtp; app-layer-protocol:http; sid:1;)"); + FAIL_IF_NOT_NULL(s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest09(void) +{ + Signature *s = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:http; app-layer-protocol:!smtp; sid:1;)"); + FAIL_IF_NOT_NULL(s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest10(void) +{ + Signature *s = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:smtp; app-layer-protocol:!http; sid:1;)"); + FAIL_IF_NOT_NULL(s); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest11(void) +{ + DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("failed", false); + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_FAILED); + FAIL_IF(data->negated != 0); + DetectAppLayerProtocolFree(NULL, data); + PASS; +} + +static int DetectAppLayerProtocolTest12(void) +{ + DetectAppLayerProtocolData *data = DetectAppLayerProtocolParse("failed", true); + FAIL_IF_NULL(data); + FAIL_IF(data->alproto != ALPROTO_FAILED); + FAIL_IF(data->negated == 0); + DetectAppLayerProtocolFree(NULL, data); + PASS; +} + +static int DetectAppLayerProtocolTest13(void) +{ + Signature *s = NULL; + DetectAppLayerProtocolData *data = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:failed; sid:1;)"); + FAIL_IF_NULL(s); + + FAIL_IF(s->alproto != ALPROTO_UNKNOWN); + + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + + data = (DetectAppLayerProtocolData *)s->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF(data->alproto != ALPROTO_FAILED); + FAIL_IF(data->negated); + DetectEngineCtxFree(de_ctx); + PASS; +} + +static int DetectAppLayerProtocolTest14(void) +{ + DetectAppLayerProtocolData *data = NULL; + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + FAIL_IF_NULL(de_ctx); + de_ctx->flags |= DE_QUIET; + + Signature *s1 = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:http; flowbits:set,blah; sid:1;)"); + FAIL_IF_NULL(s1); + FAIL_IF(s1->alproto != ALPROTO_UNKNOWN); + FAIL_IF_NULL(s1->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s1->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + data = (DetectAppLayerProtocolData *)s1->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated); + + Signature *s2 = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:http; flow:to_client; sid:2;)"); + FAIL_IF_NULL(s2); + FAIL_IF(s2->alproto != ALPROTO_UNKNOWN); + FAIL_IF_NULL(s2->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s2->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + data = (DetectAppLayerProtocolData *)s2->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated); + + /* flow:established and other options not supported for PD-only */ + Signature *s3 = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any " + "(app-layer-protocol:http; flow:to_client,established; sid:3;)"); + FAIL_IF_NULL(s3); + FAIL_IF(s3->alproto != ALPROTO_UNKNOWN); + FAIL_IF_NULL(s3->init_data->smlists[DETECT_SM_LIST_MATCH]); + FAIL_IF_NULL(s3->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx); + data = (DetectAppLayerProtocolData *)s3->init_data->smlists[DETECT_SM_LIST_MATCH]->ctx; + FAIL_IF(data->alproto != ALPROTO_HTTP); + FAIL_IF(data->negated); + + SigGroupBuild(de_ctx); + FAIL_IF_NOT(s1->type == SIG_TYPE_PDONLY); + FAIL_IF_NOT(s2->type == SIG_TYPE_PDONLY); + FAIL_IF(s3->type == SIG_TYPE_PDONLY); // failure now + + DetectEngineCtxFree(de_ctx); + PASS; +} + + +static void DetectAppLayerProtocolRegisterTests(void) +{ + UtRegisterTest("DetectAppLayerProtocolTest01", + DetectAppLayerProtocolTest01); + UtRegisterTest("DetectAppLayerProtocolTest02", + DetectAppLayerProtocolTest02); + UtRegisterTest("DetectAppLayerProtocolTest03", + DetectAppLayerProtocolTest03); + UtRegisterTest("DetectAppLayerProtocolTest04", + DetectAppLayerProtocolTest04); + UtRegisterTest("DetectAppLayerProtocolTest05", + DetectAppLayerProtocolTest05); + UtRegisterTest("DetectAppLayerProtocolTest06", + DetectAppLayerProtocolTest06); + UtRegisterTest("DetectAppLayerProtocolTest07", + DetectAppLayerProtocolTest07); + UtRegisterTest("DetectAppLayerProtocolTest08", + DetectAppLayerProtocolTest08); + UtRegisterTest("DetectAppLayerProtocolTest09", + DetectAppLayerProtocolTest09); + UtRegisterTest("DetectAppLayerProtocolTest10", + DetectAppLayerProtocolTest10); + UtRegisterTest("DetectAppLayerProtocolTest11", + DetectAppLayerProtocolTest11); + UtRegisterTest("DetectAppLayerProtocolTest12", + DetectAppLayerProtocolTest12); + UtRegisterTest("DetectAppLayerProtocolTest13", + DetectAppLayerProtocolTest13); + UtRegisterTest("DetectAppLayerProtocolTest14", + DetectAppLayerProtocolTest14); +} +#endif /* UNITTESTS */ -- cgit v1.2.3