From 8df8746eeda2dd34ad3649c7c6c33ae2f785900e Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 12 Dec 2023 22:47:01 +0100 Subject: [PATCH] doh: make dns and http keywords for doh2 Ticket: 5773 --- rust/src/http2/http2.rs | 16 ++++++++++ src/app-layer-detect-proto.c | 3 ++ src/app-layer-protos.h | 59 +++++++++++++++++++++++++++++++++++ src/detect-dns-answer-name.c | 12 +++++++ src/detect-dns-opcode.c | 6 ++++ src/detect-dns-query-name.c | 12 +++++++ src/detect-dns-query.c | 12 +++++++ src/detect-engine-mpm.c | 5 +++ src/detect-engine-prefilter.c | 2 +- src/detect-engine.c | 4 +++ src/detect-parse.c | 17 ++-------- src/detect.c | 4 ++- 12 files changed, 136 insertions(+), 16 deletions(-) diff --git a/rust/src/http2/http2.rs b/rust/src/http2/http2.rs index ffe0ba3a0399..0784dd9898ae 100644 --- a/rust/src/http2/http2.rs +++ b/rust/src/http2/http2.rs @@ -1185,6 +1185,22 @@ impl HTTP2State { // C exports. +#[no_mangle] +pub unsafe extern "C" fn SCDoH2GetDnsTx( + tx: &HTTP2Transaction, flags: u8, +) -> *mut std::os::raw::c_void { + if flags & Direction::ToServer as u8 != 0 { + if let Some(ref dtx) = &tx.dns_request_tx { + return dtx as *const _ as *mut _; + } + } else if flags & Direction::ToClient as u8 != 0 { + if let Some(ref dtx) = &tx.dns_response_tx { + return dtx as *const _ as *mut _; + } + } + std::ptr::null_mut() +} + export_tx_data_get!(rs_http2_get_tx_data, HTTP2Transaction); export_state_data_get!(rs_http2_get_state_data, HTTP2State); diff --git a/src/app-layer-detect-proto.c b/src/app-layer-detect-proto.c index 703412363451..c3f44cf1da64 100644 --- a/src/app-layer-detect-proto.c +++ b/src/app-layer-detect-proto.c @@ -2057,6 +2057,9 @@ void AppLayerProtoDetectSupportedIpprotos(AppProto alproto, uint8_t *ipprotos) if (alproto == ALPROTO_HTTP) { AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP1, ipprotos); AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP2, ipprotos); + } else if (alproto == ALPROTO_DOH2) { + // DOH2 is not detected, just HTTP2 + AppLayerProtoDetectSupportedIpprotos(ALPROTO_HTTP2, ipprotos); } else { AppLayerProtoDetectPMGetIpprotos(alproto, ipprotos); AppLayerProtoDetectPPGetIpprotos(alproto, ipprotos); diff --git a/src/app-layer-protos.h b/src/app-layer-protos.h index 9ac73ac216b5..059caeae24e6 100644 --- a/src/app-layer-protos.h +++ b/src/app-layer-protos.h @@ -85,6 +85,16 @@ static inline bool AppProtoIsValid(AppProto a) return ((a > ALPROTO_UNKNOWN && a < ALPROTO_FAILED)); } +// whether an engine proto works on a flow proto +static inline bool AppProtoCompatible(AppProto eng_proto, AppProto alproto) +{ + switch (alproto) { + case ALPROTO_DOH2: + return (eng_proto == ALPROTO_HTTP2) || (eng_proto == ALPROTO_DNS); + } + return (eng_proto == alproto); +} + // whether a signature AppProto matches a flow (or signature) AppProto static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto) { @@ -92,6 +102,13 @@ static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto) return true; } switch (sigproto) { + case ALPROTO_DNS: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_DNS); + case ALPROTO_HTTP2: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_HTTP2); + case ALPROTO_DOH2: + return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_HTTP2) || + (alproto == ALPROTO_DNS) || (alproto == ALPROTO_HTTP); case ALPROTO_HTTP: return (alproto == ALPROTO_HTTP1) || (alproto == ALPROTO_HTTP2); case ALPROTO_DCERPC: @@ -100,6 +117,48 @@ static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto) return false; } +// whether a signature AppProto matches a flow (or signature) AppProto +static inline AppProto AppProtoCommon(AppProto sigproto, AppProto alproto) +{ + switch (sigproto) { + case ALPROTO_SMB: + if (alproto == ALPROTO_DCERPC) { + // ok to have dcerpc keywords in smb sig + return ALPROTO_SMB; + } + break; + case ALPROTO_HTTP: + // we had a generic http sig, now version specific + if (alproto == ALPROTO_HTTP1) { + return ALPROTO_HTTP1; + } else if (alproto == ALPROTO_HTTP2) { + return ALPROTO_HTTP2; + } + break; + case ALPROTO_HTTP1: + // version-specific sig with a generic keyword + if (alproto == ALPROTO_HTTP) { + return ALPROTO_HTTP1; + } + break; + case ALPROTO_HTTP2: + if (alproto == ALPROTO_HTTP) { + return ALPROTO_HTTP2; + } + break; + case ALPROTO_DOH2: + // DOH2 accepts different protocol keywords + if (alproto == ALPROTO_HTTP || alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + return ALPROTO_DOH2; + } + break; + } + if (sigproto != alproto) { + return ALPROTO_FAILED; + } + return alproto; +} + /** * \brief Maps the ALPROTO_*, to its string equivalent. * diff --git a/src/detect-dns-answer-name.c b/src/detect-dns-answer-name.c index bc64f55fbf49..3e46b4e82420 100644 --- a/src/detect-dns-answer-name.c +++ b/src/detect-dns-answer-name.c @@ -83,6 +83,12 @@ static uint8_t DetectEngineInspectCb(DetectEngineCtx *de_ctx, DetectEngineThread transforms = engine->v2.transforms; } + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + } for (uint32_t i = 0;; i++) { InspectionBuffer *buffer = GetBuffer(det_ctx, flags, transforms, txv, i, engine->sm_list); if (buffer == NULL || buffer->inspect == NULL) { @@ -108,6 +114,12 @@ static void PrefilterTx(DetectEngineThreadCtx *det_ctx, const void *pectx, Packe const MpmCtx *mpm_ctx = ctx->mpm_ctx; const int list_id = ctx->list_id; + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return; + } + } for (uint32_t i = 0;; i++) { InspectionBuffer *buffer = GetBuffer(det_ctx, flags, ctx->transforms, txv, i, list_id); if (buffer == NULL) { diff --git a/src/detect-dns-opcode.c b/src/detect-dns-opcode.c index 4baee19b8cd3..1eda2a9ec61d 100644 --- a/src/detect-dns-opcode.c +++ b/src/detect-dns-opcode.c @@ -66,6 +66,12 @@ static int DetectDnsOpcodeMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state, void *txv, const Signature *s, const SigMatchCtx *ctx) { + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return 0; + } + } return rs_dns_opcode_match(txv, (void *)ctx, flags); } diff --git a/src/detect-dns-query-name.c b/src/detect-dns-query-name.c index a3983bf575cd..572ef3d31dc6 100644 --- a/src/detect-dns-query-name.c +++ b/src/detect-dns-query-name.c @@ -83,6 +83,12 @@ static uint8_t DetectEngineInspectCb(DetectEngineCtx *de_ctx, DetectEngineThread transforms = engine->v2.transforms; } + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + } for (uint32_t i = 0;; i++) { InspectionBuffer *buffer = GetBuffer(det_ctx, flags, transforms, txv, i, engine->sm_list); if (buffer == NULL || buffer->inspect == NULL) { @@ -108,6 +114,12 @@ static void PrefilterTx(DetectEngineThreadCtx *det_ctx, const void *pectx, Packe const MpmCtx *mpm_ctx = ctx->mpm_ctx; const int list_id = ctx->list_id; + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return; + } + } for (uint32_t i = 0;; i++) { InspectionBuffer *buffer = GetBuffer(det_ctx, flags, ctx->transforms, txv, i, list_id); if (buffer == NULL) { diff --git a/src/detect-dns-query.c b/src/detect-dns-query.c index 3225f126f2df..c29c9451ae74 100644 --- a/src/detect-dns-query.c +++ b/src/detect-dns-query.c @@ -108,6 +108,12 @@ static uint8_t DetectEngineInspectDnsQuery(DetectEngineCtx *de_ctx, DetectEngine transforms = engine->v2.transforms; } + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return DETECT_ENGINE_INSPECT_SIG_NO_MATCH; + } + } while(1) { struct DnsQueryGetDataArgs cbdata = { local_id, txv, }; InspectionBuffer *buffer = @@ -149,6 +155,12 @@ static void PrefilterTxDnsQuery(DetectEngineThreadCtx *det_ctx, const void *pect const int list_id = ctx->list_id; uint32_t local_id = 0; + if (f->alproto == ALPROTO_DOH2) { + txv = SCDoH2GetDnsTx(txv, flags); + if (txv == NULL) { + return; + } + } while(1) { // loop until we get a NULL diff --git a/src/detect-engine-mpm.c b/src/detect-engine-mpm.c index 02e63c5f896b..e0f3a1011910 100644 --- a/src/detect-engine-mpm.c +++ b/src/detect-engine-mpm.c @@ -107,6 +107,11 @@ void DetectAppLayerMpmRegister(const char *name, int direction, int priority, FatalError("MPM engine registration for %s failed", name); } + // every HTTP2 can be accessed from DOH2 + if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + DetectAppLayerMpmRegister(name, direction, priority, PrefilterRegister, GetData, + ALPROTO_DOH2, tx_min_progress); + } DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am)); BUG_ON(am == NULL); am->name = name; diff --git a/src/detect-engine-prefilter.c b/src/detect-engine-prefilter.c index e40a5175dafb..9611ba335f0d 100644 --- a/src/detect-engine-prefilter.c +++ b/src/detect-engine-prefilter.c @@ -107,7 +107,7 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx, PrefilterEngine *engine = sgh->tx_engines; do { - if (engine->alproto != alproto) + if (!AppProtoCompatible(engine->alproto, alproto)) goto next; if (engine->ctx.tx_min_progress > tx->tx_progress) break; diff --git a/src/detect-engine.c b/src/detect-engine.c index 13e09be71889..9fdd45783f53 100644 --- a/src/detect-engine.c +++ b/src/detect-engine.c @@ -242,6 +242,10 @@ void DetectAppLayerInspectEngineRegister(const char *name, AppProto alproto, uin } else { direction = 1; } + // every DNS or HTTP2 can be accessed from DOH2 + if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) { + DetectAppLayerInspectEngineRegister(name, ALPROTO_DOH2, dir, progress, Callback, GetData); + } DetectEngineAppInspectionEngine *new_engine = SCCalloc(1, sizeof(DetectEngineAppInspectionEngine)); diff --git a/src/detect-parse.c b/src/detect-parse.c index 31df3d0aaed3..65373c5fef53 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -1757,20 +1757,9 @@ int DetectSignatureSetAppProto(Signature *s, AppProto alproto) return -1; } - /* since AppProtoEquals is quite permissive wrt dcerpc and smb, make sure - * we refuse `alert dcerpc ... smb.share; content...` explicitly. */ - if (alproto == ALPROTO_SMB && s->alproto == ALPROTO_DCERPC) { - SCLogError("can't set rule app proto to %s: already set to %s", AppProtoToString(alproto), - AppProtoToString(s->alproto)); - return -1; - } - - if (s->alproto != ALPROTO_UNKNOWN && !AppProtoEquals(s->alproto, alproto)) { - if (AppProtoEquals(alproto, s->alproto)) { - // happens if alproto = HTTP_ANY and s->alproto = HTTP1 - // in this case, we must keep the most restrictive HTTP1 - alproto = s->alproto; - } else { + if (s->alproto != ALPROTO_UNKNOWN) { + alproto = AppProtoCommon(s->alproto, alproto); + if (alproto == ALPROTO_FAILED) { SCLogError("can't set rule app proto to %s: already set to %s", AppProtoToString(alproto), AppProtoToString(s->alproto)); return -1; diff --git a/src/detect.c b/src/detect.c index dca6fe9f651d..4f6fe8d0338f 100644 --- a/src/detect.c +++ b/src/detect.c @@ -1094,7 +1094,9 @@ static bool DetectRunTxInspectRule(ThreadVars *tv, if (!(inspect_flags & BIT_U32(engine->id)) && direction == engine->dir) { - const bool skip_engine = (engine->alproto != 0 && engine->alproto != f->alproto); + const bool skip_engine = + (engine->alproto != 0 && !AppProtoCompatible(engine->alproto, f->alproto)); + /* special case: file_data on 'alert tcp' will have engines * in the list that are not for us. */ if (unlikely(skip_engine)) {