]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
detect/app-layer-protocol: allow matching on 'unknown'
authorVictor Julien <vjulien@oisf.net>
Fri, 11 Apr 2025 14:59:05 +0000 (16:59 +0200)
committerVictor Julien <victor@inliniac.net>
Thu, 17 Apr 2025 06:22:10 +0000 (08:22 +0200)
doc/userguide/rules/app-layer.rst
src/detect-app-layer-protocol.c

index 330b38cf750d4087fcc243390befc829e24a3ef4..e72fc6439380042bb986f9e67f8f04a68021fc8e 100644 (file)
@@ -18,11 +18,15 @@ Examples::
     app-layer-protocol:!http,final;
     app-layer-protocol:http,to_server; app-layer-protocol:tls,to_client;
     app-layer-protocol:http2,final; app-layer-protocol:http1,original;
+    app-layer-protocol:unknown;
 
 A special value 'failed' can be used for matching on flows in which
 protocol detection failed. This can happen if Suricata doesn't know
 the protocol or when certain 'bail out' conditions happen.
 
+A special value 'unknown' can be used to match on a protocol being
+not yet known. It can not be negated.
+
 The different modes are
 * direction : protocol recognized on the direction of the current packet
 * to_server : protocol recognized in the direction to server
@@ -33,6 +37,9 @@ The different modes are
 
 By default, (if no mode is specified), the mode is ``direction``.
 
+.. note:: when negation is used, like ``!http``, it will not match on the
+   "unknown" state in the flow.
+
 Here is an example of a rule matching non-http traffic on port 80:
 
 .. container:: example-rule
index b95d099db5e1f6c564cb68dda63a23a62e0de455..071a3ee9397958eb0b019c15900bd4c401686243 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2007-2022 Open Information Security Foundation
+/* Copyright (C) 2007-2025 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
@@ -77,41 +77,70 @@ static int DetectAppLayerProtocolPacketMatch(
 
     switch (data->mode) {
         case DETECT_ALPROTO_DIRECTION:
-            if (p->flowflags & FLOW_PKT_TOSERVER) {
-                if (f->alproto_ts == ALPROTO_UNKNOWN)
-                    SCReturnInt(0);
-                r = AppProtoEquals(data->alproto, f->alproto_ts);
+            if (data->negated) {
+                if (p->flowflags & FLOW_PKT_TOSERVER) {
+                    if (f->alproto_ts == ALPROTO_UNKNOWN)
+                        SCReturnInt(0);
+                    r = AppProtoEquals(data->alproto, f->alproto_ts);
+                } else {
+                    if (f->alproto_tc == ALPROTO_UNKNOWN)
+                        SCReturnInt(0);
+                    r = AppProtoEquals(data->alproto, f->alproto_tc);
+                }
             } else {
-                if (f->alproto_tc == ALPROTO_UNKNOWN)
-                    SCReturnInt(0);
-                r = AppProtoEquals(data->alproto, f->alproto_tc);
+                if (p->flowflags & FLOW_PKT_TOSERVER) {
+                    r = AppProtoEquals(data->alproto, f->alproto_ts);
+                } else {
+                    r = AppProtoEquals(data->alproto, f->alproto_tc);
+                }
             }
             break;
         case DETECT_ALPROTO_ORIG:
-            if (f->alproto_orig == ALPROTO_UNKNOWN)
-                SCReturnInt(0);
-            r = AppProtoEquals(data->alproto, f->alproto_orig);
+            if (data->negated) {
+                if (f->alproto_orig == ALPROTO_UNKNOWN)
+                    SCReturnInt(0);
+                r = AppProtoEquals(data->alproto, f->alproto_orig);
+            } else {
+                r = AppProtoEquals(data->alproto, f->alproto_orig);
+            }
             break;
         case DETECT_ALPROTO_FINAL:
-            if (f->alproto == ALPROTO_UNKNOWN)
-                SCReturnInt(0);
-            r = AppProtoEquals(data->alproto, f->alproto);
+            if (data->negated) {
+                if (f->alproto == ALPROTO_UNKNOWN)
+                    SCReturnInt(0);
+                r = AppProtoEquals(data->alproto, f->alproto);
+            } else {
+                r = AppProtoEquals(data->alproto, f->alproto);
+            }
             break;
         case DETECT_ALPROTO_TOSERVER:
-            if (f->alproto_ts == ALPROTO_UNKNOWN)
-                SCReturnInt(0);
-            r = AppProtoEquals(data->alproto, f->alproto_ts);
+            if (data->negated) {
+                if (f->alproto_ts == ALPROTO_UNKNOWN)
+                    SCReturnInt(0);
+                r = AppProtoEquals(data->alproto, f->alproto_ts);
+            } else {
+                r = AppProtoEquals(data->alproto, f->alproto_ts);
+            }
             break;
         case DETECT_ALPROTO_TOCLIENT:
-            if (f->alproto_tc == ALPROTO_UNKNOWN)
-                SCReturnInt(0);
-            r = AppProtoEquals(data->alproto, f->alproto_tc);
+            if (data->negated) {
+                if (f->alproto_tc == ALPROTO_UNKNOWN)
+                    SCReturnInt(0);
+                r = AppProtoEquals(data->alproto, f->alproto_tc);
+            } else {
+                r = AppProtoEquals(data->alproto, f->alproto_tc);
+            }
             break;
         case DETECT_ALPROTO_EITHER:
-            if (f->alproto_ts == ALPROTO_UNKNOWN && f->alproto_tc == ALPROTO_UNKNOWN)
-                SCReturnInt(0);
-            r = AppProtoEquals(data->alproto, f->alproto_tc) ||
-                AppProtoEquals(data->alproto, f->alproto_ts);
+            if (data->negated) {
+                if (f->alproto_ts == ALPROTO_UNKNOWN && f->alproto_tc == ALPROTO_UNKNOWN)
+                    SCReturnInt(0);
+                r = AppProtoEquals(data->alproto, f->alproto_tc) ||
+                    AppProtoEquals(data->alproto, f->alproto_ts);
+            } else {
+                r = AppProtoEquals(data->alproto, f->alproto_tc) ||
+                    AppProtoEquals(data->alproto, f->alproto_ts);
+            }
             break;
     }
     r = r ^ data->negated;
@@ -138,6 +167,13 @@ static DetectAppLayerProtocolData *DetectAppLayerProtocolParse(const char *arg,
     }
     if (strcmp(alproto_name, "failed") == 0) {
         alproto = ALPROTO_FAILED;
+    } else if (strcmp(alproto_name, "unknown") == 0) {
+        if (negate) {
+            SCLogError("app-layer-protocol "
+                       "keyword can't use negation with protocol 'unknown'");
+            return NULL;
+        }
+        alproto = ALPROTO_UNKNOWN;
     } else {
         alproto = AppLayerGetProtoByName(alproto_name);
         if (alproto == ALPROTO_UNKNOWN) {
@@ -293,19 +329,32 @@ PrefilterPacketAppProtoMatch(DetectEngineThreadCtx *det_ctx, Packet *p, const vo
         case DETECT_ALPROTO_EITHER:
             // check if either protocol toclient or toserver matches
             // the one in the signature ctx
-            if (f->alproto_tc != ALPROTO_UNKNOWN &&
-                    AppProtoEquals(ctx->v1.u16[0], f->alproto_tc) ^ negated) {
-                PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
-            } else if (f->alproto_ts != ALPROTO_UNKNOWN &&
-                       AppProtoEquals(ctx->v1.u16[0], f->alproto_ts) ^ negated) {
-                PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+            if (negated) {
+                if (f->alproto_tc != ALPROTO_UNKNOWN &&
+                        !AppProtoEquals(ctx->v1.u16[0], f->alproto_tc)) {
+                    PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+                } else if (f->alproto_ts != ALPROTO_UNKNOWN &&
+                           !AppProtoEquals(ctx->v1.u16[0], f->alproto_ts)) {
+                    PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+                }
+            } else {
+                if (AppProtoEquals(ctx->v1.u16[0], f->alproto_tc) ||
+                        AppProtoEquals(ctx->v1.u16[0], f->alproto_ts)) {
+                    PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+                }
             }
             // We return right away to avoid calling PrefilterAddSids again
             return;
     }
 
-    if (alproto != ALPROTO_UNKNOWN) {
-        if (AppProtoEquals(ctx->v1.u16[0], alproto) ^ negated) {
+    if (negated) {
+        if (alproto != ALPROTO_UNKNOWN) {
+            if (!AppProtoEquals(ctx->v1.u16[0], alproto)) {
+                PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
+            }
+        }
+    } else {
+        if (AppProtoEquals(ctx->v1.u16[0], alproto)) {
             PrefilterAddSids(&det_ctx->pmq, ctx->sigs_array, ctx->sigs_cnt);
         }
     }