]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
doh: make dns and http keywords for doh2
authorPhilippe Antoine <pantoine@oisf.net>
Tue, 12 Dec 2023 21:47:01 +0000 (22:47 +0100)
committerVictor Julien <victor@inliniac.net>
Sat, 20 Jul 2024 08:37:58 +0000 (10:37 +0200)
Ticket: 5773

rust/src/http2/http2.rs
src/app-layer-detect-proto.c
src/app-layer-protos.h
src/detect-engine-mpm.c
src/detect-engine-prefilter.c
src/detect-engine.c
src/detect-parse.c
src/detect.c
src/detect.h

index 063f80a15d8cb53a7fbd4e5545267f577fa3893c..6620c040de4ec4cf446b4879b32d2bfa3904db5e 100644 (file)
@@ -1277,6 +1277,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);
 
index 298392313e3183fc42fcfb379a1a04b92b818192..a65a98c88a41a1c7a1aaaee8c966cd2932f9df95 100644 (file)
@@ -1844,6 +1844,12 @@ bool AppLayerRequestProtocolTLSUpgrade(Flow *f)
     return AppLayerRequestProtocolChange(f, 443, ALPROTO_TLS);
 }
 
+/** \brief Forces a flow app-layer protocol change.
+ *         Happens for instance when a HTTP2 flow is seen as DOH2
+ *
+ *  \param f flow to act on
+ *  \param new_proto new app-layer protocol
+ */
 void AppLayerForceProtocolChange(Flow *f, AppProto new_proto)
 {
     if (new_proto != f->alproto) {
@@ -2035,6 +2041,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);
index 85c69b225e180b3da4a0c56fe34e7f027f706b91..348203ac2f5306967d350a8a317c279bb0603d57 100644 (file)
@@ -95,6 +95,16 @@ static inline bool AppProtoEquals(AppProto sigproto, AppProto alproto)
         return true;
     }
     switch (sigproto) {
+        case ALPROTO_DNS:
+            // a DNS signature matches on either DNS or DOH2 flows
+            return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_DNS);
+        case ALPROTO_HTTP2:
+            // a HTTP2 signature matches on either HTTP2 or DOH2 flows
+            return (alproto == ALPROTO_DOH2) || (alproto == ALPROTO_HTTP2);
+        case ALPROTO_DOH2:
+            // a DOH2 signature accepts dns, http2 or http generic keywords
+            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:
@@ -103,6 +113,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.
  *
index eca9807df1b85543b2bafd88a77bf54ce8159bf5..5e8687e34686336f16a70de41c7738fbff1f8397 100644 (file)
@@ -107,6 +107,11 @@ static void RegisterInternal(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) {
+        RegisterInternal(name, direction, priority, PrefilterRegister, GetData, GetMultiData,
+                ALPROTO_DOH2, tx_min_progress);
+    }
     DetectBufferMpmRegistry *am = SCCalloc(1, sizeof(*am));
     BUG_ON(am == NULL);
     am->name = name;
index 83ccb2afb2912f79b2e2b5f5cec91576f59cadab..feff1251e4e2bec632d339a7f3cd7e1e0e20cfe8 100644 (file)
@@ -107,8 +107,12 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
 
     PrefilterEngine *engine = sgh->tx_engines;
     do {
-        if (engine->alproto != alproto)
+        // based on flow alproto, and engine, we get right tx_ptr
+        void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, alproto, engine->alproto, flow_flags);
+        if (tx_ptr == NULL) {
+            // incompatible engine->alproto with flow alproto
             goto next;
+        }
         if (engine->ctx.tx_min_progress > tx->tx_progress)
             break;
         if (tx->tx_progress > engine->ctx.tx_min_progress) {
@@ -118,8 +122,8 @@ void DetectRunPrefilterTx(DetectEngineThreadCtx *det_ctx,
         }
 
         PREFILTER_PROFILING_START(det_ctx);
-        engine->cb.PrefilterTx(det_ctx, engine->pectx, p, p->flow, tx->tx_ptr, tx->tx_id,
-                tx->tx_data_ptr, flow_flags);
+        engine->cb.PrefilterTx(
+                det_ctx, engine->pectx, p, p->flow, tx_ptr, tx->tx_id, tx->tx_data_ptr, flow_flags);
         PREFILTER_PROFILING_END(det_ctx, engine->gid);
 
         if (tx->tx_progress > engine->ctx.tx_min_progress && engine->is_last_for_progress) {
index 181b0023c86765cf6c2a1c396b77bf17d11bb284..c4e79682f1afdb264f5ab3b17c3f3e4996f0a33f 100644 (file)
@@ -200,6 +200,11 @@ static void AppLayerInspectEngineRegisterInternal(const char *name, AppProto alp
     } else {
         direction = 1;
     }
+    // every DNS or HTTP2 can be accessed from DOH2
+    if (alproto == ALPROTO_HTTP2 || alproto == ALPROTO_DNS) {
+        AppLayerInspectEngineRegisterInternal(
+                name, ALPROTO_DOH2, dir, progress, Callback, GetData, GetMultiData);
+    }
 
     DetectEngineAppInspectionEngine *new_engine =
             SCCalloc(1, sizeof(DetectEngineAppInspectionEngine));
index 61318521f45d24dd0652d0473bd2bd2720d876a1..984501c1dd8a138a4e2d09f0545c19a44ebb9e7a 100644 (file)
@@ -1742,20 +1742,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;
index 5d6eb4df3e8a84bacfc0179fbc5e3c4cafa2e0aa..0302a374b550d386f32c9d82b5db121df865676c 100644 (file)
@@ -1050,6 +1050,24 @@ DetectRunTxSortHelper(const void *a, const void *b)
 #define TRACE_SID_TXS(sid,txs,...)
 #endif
 
+// Get inner transaction for engine
+void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags)
+{
+    if (unlikely(alproto == ALPROTO_DOH2)) {
+        if (engine_alproto == ALPROTO_DNS) {
+            // need to get the dns tx pointer
+            tx_ptr = SCDoH2GetDnsTx(tx_ptr, flow_flags);
+        } else if (engine_alproto != ALPROTO_HTTP2) {
+            // incompatible engine->alproto with flow alproto
+            tx_ptr = NULL;
+        }
+    } else if (engine_alproto != alproto) {
+        // incompatible engine->alproto with flow alproto
+        tx_ptr = NULL;
+    }
+    return tx_ptr;
+}
+
 /** \internal
  *  \brief inspect a rule against a transaction
  *
@@ -1110,12 +1128,16 @@ 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);
-            /* special case: file_data on 'alert tcp' will have engines
-             * in the list that are not for us. */
-            if (unlikely(skip_engine)) {
-                engine = engine->next;
-                continue;
+            void *tx_ptr = DetectGetInnerTx(tx->tx_ptr, f->alproto, engine->alproto, flow_flags);
+            if (tx_ptr == NULL) {
+                if (engine->alproto != ALPROTO_UNKNOWN) {
+                    /* special case: file_data on 'alert tcp' will have engines
+                     * in the list that are not for us. */
+                    engine = engine->next;
+                    continue;
+                } else {
+                    tx_ptr = tx->tx_ptr;
+                }
             }
 
             /* engines are sorted per progress, except that the one with
@@ -1150,7 +1172,7 @@ static bool DetectRunTxInspectRule(ThreadVars *tv,
                 KEYWORD_PROFILING_SET_LIST(det_ctx, engine->sm_list);
                 DEBUG_VALIDATE_BUG_ON(engine->v2.Callback == NULL);
                 match = engine->v2.Callback(
-                        de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx->tx_ptr, tx->tx_id);
+                        de_ctx, det_ctx, engine, s, f, flow_flags, alstate, tx_ptr, tx->tx_id);
                 TRACE_SID_TXS(s->id, tx, "engine %p match %d", engine, match);
                 if (engine->stream) {
                     can->stream_stored = true;
index 3715018c1a7acf23e61f4210cb4908153f43db52..4df2dfa3d623b96f0b06f2080d5324f772208c98 100644 (file)
@@ -1586,6 +1586,7 @@ const SigGroupHead *SigMatchSignaturesGetSgh(const DetectEngineCtx *de_ctx, cons
 int DetectUnregisterThreadCtxFuncs(DetectEngineCtx *, void *data, const char *name);
 int DetectRegisterThreadCtxFuncs(DetectEngineCtx *, const char *name, void *(*InitFunc)(void *), void *data, void (*FreeFunc)(void *), int);
 void *DetectThreadCtxGetKeywordThreadCtx(DetectEngineThreadCtx *, int);
+void *DetectGetInnerTx(void *tx_ptr, AppProto alproto, AppProto engine_alproto, uint8_t flow_flags);
 
 void RuleMatchCandidateTxArrayInit(DetectEngineThreadCtx *det_ctx, uint32_t size);
 void RuleMatchCandidateTxArrayFree(DetectEngineThreadCtx *det_ctx);