]> git.ipfire.org Git - thirdparty/suricata.git/commitdiff
http2: rename http2.header to http.request_header
authorPhilippe Antoine <pantoine@oisf.net>
Thu, 12 Jan 2023 16:03:44 +0000 (17:03 +0100)
committerVictor Julien <vjulien@oisf.net>
Fri, 9 Jun 2023 09:44:31 +0000 (11:44 +0200)
Or http.response_header based on the direction

http2.header had a different behavior than http.header and this was
confusing.

Ticket: #5780

doc/userguide/rules/http2-keywords.rst
doc/userguide/upgrade.rst
src/detect-engine-register.h
src/detect-http2.c

index f1bb59bd03541ddc7a77df71e3be45b89832e11e..f328932fb6d061e34a3a7fb10faee45e5774dc9f 100644 (file)
@@ -110,22 +110,38 @@ Examples::
 ``http2.header_name`` can be used as ``fast_pattern``.
 
 
-http2.header
------------------
+http.request_header
+-------------------
 
-Match on the name and value of a HTTP2 header from a HEADER frame (or PUSH_PROMISE or CONTINUATION).
+Match on the name and value of a HTTP2 request header from a HEADER frame (or PUSH_PROMISE or CONTINUATION).
 Name and value get concatenated by ": ", colon and space.
 Each colon in the name or the value should be escaped as a double colon "::" for detection
 
 Examples::
 
-  http2.header; content:"agent: nghttp2";
-  http2.header; content:"custom-header: I love::colons";
+  http.request_header; content:"agent: nghttp2";
+  http.request_header; content:"custom-header: I love::colons";
+
+``http.request_header`` is a 'sticky buffer'.
+
+``http.request_header`` can be used as ``fast_pattern``.
+
+
+http.response_header
+--------------------
+
+Match on the name and value of a HTTP2 response header from a HEADER frame (or PUSH_PROMISE or CONTINUATION).
+Name and value get concatenated by ": ", colon and space.
+Each colon in the name or the value should be escaped as a double colon "::" for detection
+
+Examples::
 
-``http2.header`` is a 'sticky buffer'.
+  http.response_header; content:"server: nghttp2";
+  http.response_header; content:"custom-header: I love::colons";
 
-``http2.header`` can be used as ``fast_pattern``.
+``http.response_header`` is a 'sticky buffer'.
 
+``http.response_header`` can be used as ``fast_pattern``.
 
 Additional information
 ----------------------
index c4807b41731d44133c53709035f078f4a7fbcab8..e50f0e9a9d3c9c3e3eda70fee44622749e5f599e 100644 (file)
@@ -72,6 +72,7 @@ Deprecations
 
 Other changes
 ~~~~~~~~~~~~~
+- Experimental keyword `http2.header` is removed. `http.header`, `http.request_header`, and `http.response_header` are to be used.
 - NSS is no longer required. File hashing and JA3 can now be used without the NSS compile time dependency.
 - If installing Suricata without the bundled Suricata-Update, the ``default-rule-path`` has been changed from ``/etc/suricata/rules`` to ``/var/lib/suricata/rules`` to be consistent with Suricata when installed with Suricata-Update.
 - FTP has been updated with a maximum command request and response line length of 4096 bytes. To change the default see :ref:`suricata-yaml-configure-ftp`.
index 633e747a318a927f4eba58b0b7f25b74b5f00b7a..7d6c457ef9b08cb3b9e36da3e3d389577a375d08 100644 (file)
@@ -187,7 +187,8 @@ enum DetectKeywordId {
     DETECT_HTTP2_SIZEUPDATE,
     DETECT_HTTP2_SETTINGS,
     DETECT_HTTP2_HEADERNAME,
-    DETECT_HTTP2_HEADER,
+    DETECT_HTTP_REQUEST_HEADER,
+    DETECT_HTTP_RESPONSE_HEADER,
 
     DETECT_DCE_IFACE,
     DETECT_DCE_OPNUM,
index e1163a299810ff6758dc4287e3c9fb8f8aca2c84..35d0dce3c425fa8a2bd7b5d9829b000861024c3a 100644 (file)
@@ -92,13 +92,15 @@ static uint8_t DetectEngineInspectHttp2HeaderName(DetectEngineCtx *de_ctx,
         DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine,
         const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
 
-static int DetectHTTP2headerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
+static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
+static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg);
 static int PrefilterMpmHttp2HeaderRegister(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
         MpmCtx *mpm_ctx, const DetectBufferMpmRegistry *mpm_reg, int list_id);
 static uint8_t DetectEngineInspectHttp2Header(DetectEngineCtx *de_ctx,
         DetectEngineThreadCtx *det_ctx, const DetectEngineAppInspectionEngine *engine,
         const Signature *s, Flow *f, uint8_t flags, void *alstate, void *txv, uint64_t tx_id);
-static bool DetectHttp2HeaderValidateCallback(const Signature *s, const char **sigerror);
+static bool DetectHttp2RequestHeaderValidateCallback(const Signature *s, const char **sigerror);
+static bool DetectHttp2ResponseHeaderValidateCallback(const Signature *s, const char **sigerror);
 
 #ifdef UNITTESTS
 void DetectHTTP2RegisterTests (void);
@@ -106,8 +108,8 @@ void DetectHTTP2RegisterTests (void);
 
 static int g_http2_match_buffer_id = 0;
 static int g_http2_header_name_buffer_id = 0;
-static int g_http2_header_buffer_id = 0;
-
+static int g_http_request_header_buffer_id = 0;
+static int g_http_response_header_buffer_id = 0;
 
 /**
  * \brief Registration function for HTTP2 keywords
@@ -204,30 +206,39 @@ void DetectHttp2Register(void)
                                          "HTTP2 header name");
     g_http2_header_name_buffer_id = DetectBufferTypeGetByName("http2_header_name");
 
-    sigmatch_table[DETECT_HTTP2_HEADER].name = "http2.header";
-    sigmatch_table[DETECT_HTTP2_HEADER].desc = "sticky buffer to match on one HTTP2 header name and value";
-    sigmatch_table[DETECT_HTTP2_HEADER].url = "/rules/http2-keywords.html#header";
-    sigmatch_table[DETECT_HTTP2_HEADER].Setup = DetectHTTP2headerSetup;
-    sigmatch_table[DETECT_HTTP2_HEADER].flags |= SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
-    DetectBufferTypeSupportsMultiInstance("http2_header");
-
-    DetectAppLayerMpmRegister2("http2_header", SIG_FLAG_TOCLIENT, 2,
-                               PrefilterMpmHttp2HeaderRegister, NULL,
-                               ALPROTO_HTTP2, HTTP2StateOpen);
-    DetectAppLayerInspectEngineRegister2("http2_header",
-                                         ALPROTO_HTTP2, SIG_FLAG_TOCLIENT, HTTP2StateOpen,
-                                         DetectEngineInspectHttp2Header, NULL);
-    DetectAppLayerMpmRegister2("http2_header", SIG_FLAG_TOSERVER, 2,
-                               PrefilterMpmHttp2HeaderRegister, NULL,
-                               ALPROTO_HTTP2, HTTP2StateOpen);
-    DetectAppLayerInspectEngineRegister2("http2_header",
-                                         ALPROTO_HTTP2, SIG_FLAG_TOSERVER, HTTP2StateOpen,
-                                         DetectEngineInspectHttp2Header, NULL);
-
-    DetectBufferTypeSetDescriptionByName("http2_header",
-                                         "HTTP2 header name and value");
-    DetectBufferTypeRegisterValidateCallback("http2_header", DetectHttp2HeaderValidateCallback);
-    g_http2_header_buffer_id = DetectBufferTypeGetByName("http2_header");
+    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].name = "http.request_header";
+    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].desc =
+            "sticky buffer to match on only one HTTP header name and value";
+    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].url = "/rules/http2-keywords.html#request_header";
+    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].Setup = DetectHTTPRequestHeaderSetup;
+    sigmatch_table[DETECT_HTTP_REQUEST_HEADER].flags |=
+            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+    DetectAppLayerMpmRegister2("http_request_header", SIG_FLAG_TOSERVER, 2,
+            PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen);
+    DetectAppLayerInspectEngineRegister2("http_request_header", ALPROTO_HTTP2, SIG_FLAG_TOSERVER,
+            HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL);
+    DetectBufferTypeRegisterValidateCallback(
+            "http_request_header", DetectHttp2RequestHeaderValidateCallback);
+    DetectBufferTypeSetDescriptionByName("http_request_header", "HTTP header name and value");
+    g_http_request_header_buffer_id = DetectBufferTypeGetByName("http_request_header");
+
+    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].name = "http.response_header";
+    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].desc =
+            "sticky buffer to match on only one HTTP header name and value";
+    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].url = "/rules/http2-keywords.html#response_header";
+    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].Setup = DetectHTTPResponseHeaderSetup;
+    sigmatch_table[DETECT_HTTP_RESPONSE_HEADER].flags |=
+            SIGMATCH_NOOPT | SIGMATCH_INFO_STICKY_BUFFER;
+
+    DetectAppLayerMpmRegister2("http_response_header", SIG_FLAG_TOCLIENT, 2,
+            PrefilterMpmHttp2HeaderRegister, NULL, ALPROTO_HTTP2, HTTP2StateOpen);
+    DetectAppLayerInspectEngineRegister2("http_response_header", ALPROTO_HTTP2, SIG_FLAG_TOCLIENT,
+            HTTP2StateOpen, DetectEngineInspectHttp2Header, NULL);
+    DetectBufferTypeRegisterValidateCallback(
+            "http_response_header", DetectHttp2ResponseHeaderValidateCallback);
+    DetectBufferTypeSetDescriptionByName("http_response_header", "HTTP header name and value");
+    g_http_response_header_buffer_id = DetectBufferTypeGetByName("http_response_header");
 
     DetectAppLayerInspectEngineRegister2(
             "http2", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, 0, DetectEngineInspectGenericList, NULL);
@@ -787,9 +798,20 @@ static uint8_t DetectEngineInspectHttp2HeaderName(DetectEngineCtx *de_ctx,
     return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
 }
 
-static int DetectHTTP2headerSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
+static int DetectHTTPRequestHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
+{
+    if (DetectBufferSetActiveList(de_ctx, s, g_http_request_header_buffer_id) < 0)
+        return -1;
+
+    if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
+        return -1;
+
+    return 0;
+}
+
+static int DetectHTTPResponseHeaderSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
 {
-    if (DetectBufferSetActiveList(de_ctx, s, g_http2_header_buffer_id) < 0)
+    if (DetectBufferSetActiveList(de_ctx, s, g_http_response_header_buffer_id) < 0)
         return -1;
 
     if (DetectSignatureSetAppProto(s, ALPROTO_HTTP2) != 0)
@@ -917,54 +939,72 @@ static uint8_t DetectEngineInspectHttp2Header(DetectEngineCtx *de_ctx,
     return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
 }
 
-static bool DetectHttp2HeaderValidateCallback(const Signature *s, const char **sigerror)
+static bool DetectHttp2HeaderValidateCallback(
+        const Signature *s, const char **sigerror, int buffer_id)
 {
     for (uint32_t x = 0; x < s->init_data->buffer_index; x++) {
-        if (s->init_data->buffers[x].id != (uint32_t)g_http2_header_buffer_id)
+        if (s->init_data->buffers[x].id != (uint32_t)g_http_request_header_buffer_id &&
+                s->init_data->buffers[x].id != (uint32_t)g_http_response_header_buffer_id)
             continue;
         const SigMatch *sm = s->init_data->buffers[x].head;
         for (; sm != NULL; sm = sm->next) {
             if (sm->type != DETECT_CONTENT)
                 continue;
-            const DetectContentData *cd = (DetectContentData *)sm->ctx;
-            bool escaped = false;
-            bool namevaluesep = false;
-            for (size_t i = 0; i < cd->content_len; ++i) {
-                if (escaped) {
-                    if (cd->content[i] == ' ') {
-                        if (namevaluesep) {
-                            *sigerror = "Invalid http2.header string : "
+            const SigMatch *sm = s->init_data->buffers[x].head;
+            for (; sm != NULL; sm = sm->next) {
+                if (sm->type != DETECT_CONTENT)
+                    continue;
+                const DetectContentData *cd = (DetectContentData *)sm->ctx;
+                bool escaped = false;
+                bool namevaluesep = false;
+                for (size_t i = 0; i < cd->content_len; ++i) {
+                    if (escaped) {
+                        if (cd->content[i] == ' ') {
+                            if (namevaluesep) {
+                                *sigerror =
+                                        "Invalid http2.header string : "
                                         "': ' is a special sequence for separation between name "
                                         "and value "
                                         " and thus can only be present once";
+                                SCLogWarning("rule %u: %s", s->id, *sigerror);
+                                return false;
+                            }
+                            namevaluesep = true;
+                        } else if (cd->content[i] != ':') {
+                            *sigerror = "Invalid http2.header string : "
+                                        "':' is an escaping character for itself, "
+                                        "or space for the separation between name and value";
                             SCLogWarning("rule %u: %s", s->id, *sigerror);
                             return false;
                         }
-                        namevaluesep = true;
-                    } else if (cd->content[i] != ':') {
-                        *sigerror = "Invalid http2.header string : "
-                                    "':' is an escaping character for itself, "
-                                    "or space for the separation between name and value";
-                        SCLogWarning("rule %u: %s", s->id, *sigerror);
-                        return false;
+                        escaped = false;
+                    } else if (cd->content[i] == ':') {
+                        escaped = true;
                     }
-                    escaped = false;
-                } else if (cd->content[i] == ':') {
-                    escaped = true;
                 }
-            }
-            if (escaped) {
-                *sigerror = "Invalid http2.header string : "
-                            "':' is an escaping character for itself, "
-                            "or space for the separation between name and value";
-                SCLogWarning("rule %u: %s", s->id, *sigerror);
-                return false;
+                if (escaped) {
+                    *sigerror = "Invalid http2.header string : "
+                                "':' is an escaping character for itself, "
+                                "or space for the separation between name and value";
+                    SCLogWarning("rule %u: %s", s->id, *sigerror);
+                    return false;
+                }
             }
         }
     }
     return true;
 }
 
+static bool DetectHttp2RequestHeaderValidateCallback(const Signature *s, const char **sigerror)
+{
+    return DetectHttp2HeaderValidateCallback(s, sigerror, g_http_request_header_buffer_id);
+}
+
+static bool DetectHttp2ResponseHeaderValidateCallback(const Signature *s, const char **sigerror)
+{
+    return DetectHttp2HeaderValidateCallback(s, sigerror, g_http_response_header_buffer_id);
+}
+
 #ifdef UNITTESTS
 #include "tests/detect-http2.c"
 #endif