From: Philippe Antoine Date: Thu, 12 Jan 2023 16:03:44 +0000 (+0100) Subject: http2: rename http2.header to http.request_header X-Git-Tag: suricata-7.0.0-rc2~39 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=656554f293bd53b5de8e044909d84173bfce2d00;p=thirdparty%2Fsuricata.git http2: rename http2.header to http.request_header Or http.response_header based on the direction http2.header had a different behavior than http.header and this was confusing. Ticket: #5780 --- diff --git a/doc/userguide/rules/http2-keywords.rst b/doc/userguide/rules/http2-keywords.rst index f1bb59bd03..f328932fb6 100644 --- a/doc/userguide/rules/http2-keywords.rst +++ b/doc/userguide/rules/http2-keywords.rst @@ -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 ---------------------- diff --git a/doc/userguide/upgrade.rst b/doc/userguide/upgrade.rst index c4807b4173..e50f0e9a9d 100644 --- a/doc/userguide/upgrade.rst +++ b/doc/userguide/upgrade.rst @@ -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`. diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 633e747a31..7d6c457ef9 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -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, diff --git a/src/detect-http2.c b/src/detect-http2.c index e1163a2998..35d0dce3c4 100644 --- a/src/detect-http2.c +++ b/src/detect-http2.c @@ -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