Control: tags 1023750 + patch Control: tags 1023750 + pending Control: tags 1023751 + patch Control: tags 1023751 + pending
Hi Stig, I've prepared an NMU for varnish (versioned as 7.1.1-1.1) and uploaded it to DELAYED/2. Please feel free to tell me if I should delay it longer. Regards, Salvatore
diff -Nru varnish-7.1.1/debian/changelog varnish-7.1.1/debian/changelog --- varnish-7.1.1/debian/changelog 2022-08-12 11:23:00.000000000 +0200 +++ varnish-7.1.1/debian/changelog 2023-01-09 22:09:31.000000000 +0100 @@ -1,3 +1,12 @@ +varnish (7.1.1-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Add all well-known headers to the perfect hash lookup table + (CVE-2022-45059) (Closes: #1023750) + * hpack: fix pseudo-headers handling (CVE-2022-45060) (Closes: #1023751) + + -- Salvatore Bonaccorso <car...@debian.org> Mon, 09 Jan 2023 22:09:31 +0100 + varnish (7.1.1-1) unstable; urgency=medium * New upstream release (CVE-2022-38150, VSV00009) diff -Nru varnish-7.1.1/debian/patches/Add-all-well-known-headers-to-the-perfect-hash-looku.patch varnish-7.1.1/debian/patches/Add-all-well-known-headers-to-the-perfect-hash-looku.patch --- varnish-7.1.1/debian/patches/Add-all-well-known-headers-to-the-perfect-hash-looku.patch 1970-01-01 01:00:00.000000000 +0100 +++ varnish-7.1.1/debian/patches/Add-all-well-known-headers-to-the-perfect-hash-looku.patch 2023-01-09 21:57:49.000000000 +0100 @@ -0,0 +1,244 @@ +From: Martin Blix Grydeland <mar...@varnish-software.com> +Date: Thu, 29 Sep 2022 14:38:05 +0200 +Subject: Add all well-known headers to the perfect hash lookup table +Origin: https://github.com/varnishcache/varnish-cache/commit/fcf5722af75fdbf58dd425dd68d0beaa49bab4f4 +Bug-Debian: https://bugs.debian.org/1023750 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2022-45059 + +This expands the perfect hash lookup table to be able to match any entry +in the list of well-known headers from tbl/http_headers.h. + +Previously only the headers that had a non-zero filter flag section was +kept in the fast match table. + +Fixes: VSV00010 +--- + bin/varnishd/cache/cache_http.c | 148 +++++++++++++++++++------------ + bin/varnishtest/tests/f00010.vtc | 19 ++++ + 2 files changed, 112 insertions(+), 55 deletions(-) + create mode 100644 bin/varnishtest/tests/f00010.vtc + +diff --git a/bin/varnishd/cache/cache_http.c b/bin/varnishd/cache/cache_http.c +index 194055c3cae3..827197dedfa2 100644 +--- a/bin/varnishd/cache/cache_http.c ++++ b/bin/varnishd/cache/cache_http.c +@@ -65,73 +65,113 @@ const char H__Reason[] = "\010:reason:"; + * A suitable algorithm can be found with `gperf`: + * + * tr '" ,' ' ' < include/tbl/http_headers.h | +- * awk '$1 == "H(" && $4 != "0" {print$2}' | ++ * awk '$1 == "H(" {print $2}' | + * gperf --ignore-case + * + */ + ++#define GPERF_MIN_WORD_LENGTH 2 ++#define GPERF_MAX_WORD_LENGTH 19 ++#define GPERF_MAX_HASH_VALUE 79 ++ + static const unsigned char http_asso_values[256] = { +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 25, 39, 0, 20, 5, 39, 39, 39, 15, 0, 39, +- 10, 39, 0, 39, 15, 10, 39, 39, 0, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 25, 39, 0, 20, 5, 39, 39, 39, 15, 0, 39, +- 10, 39, 0, 39, 15, 10, 39, 39, 0, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, +- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39 ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 0, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 5, 80, 20, 0, 0, ++ 5, 10, 5, 5, 80, 0, 15, 0, 20, 80, ++ 40, 80, 0, 35, 10, 20, 55, 45, 0, 0, ++ 80, 80, 80, 80, 80, 80, 80, 5, 80, 20, ++ 0, 0, 5, 10, 5, 5, 80, 0, 15, 0, ++ 20, 80, 40, 80, 0, 35, 10, 20, 55, 45, ++ 0, 0, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80, 80, 80, 80, 80, ++ 80, 80, 80, 80, 80, 80 + }; + + static struct http_hdrflg { + char *hdr; + unsigned flag; +-} http_hdrflg[38 + 1] = { // MAX_HASH_VALUE +- { NULL }, +- { NULL }, +- { H_TE }, +- { H_Age }, +- { NULL }, ++} http_hdrflg[GPERF_MAX_HASH_VALUE + 1] = { ++ { NULL }, { NULL }, { NULL }, { NULL }, ++ { H_Date }, + { H_Range }, + { NULL }, +- { H_Upgrade }, ++ { H_Referer }, ++ { H_Age }, ++ { H_From }, ++ { H_Keep_Alive }, ++ { H_Retry_After }, ++ { H_TE }, + { H_If_Range }, +- { NULL }, +- { H_Connection }, +- { NULL }, ++ { H_ETag }, ++ { H_X_Forwarded_For }, ++ { H_Expect }, + { H_Trailer }, +- { H_If_None_Match }, +- { NULL }, +- { NULL }, +- { NULL }, +- { H_Transfer_Encoding }, +- { H_Proxy_Authenticate }, +- { H_Proxy_Authorization }, +- { H_Keep_Alive }, +- { NULL }, +- { NULL }, + { H_If_Match }, +- { H_HTTP2_Settings }, +- { NULL }, +- { NULL }, +- { NULL }, +- { H_Content_Range }, ++ { H_Host }, ++ { H_Accept_Language }, ++ { H_Accept }, ++ { H_If_Modified_Since }, ++ { H_If_None_Match }, + { H_If_Unmodified_Since }, + { NULL }, ++ { H_Cookie }, ++ { H_Upgrade }, ++ { H_Last_Modified }, ++ { H_Accept_Charset }, ++ { H_Accept_Encoding }, ++ { H_Content_MD5 }, ++ { H_Content_Type }, ++ { H_Content_Range }, ++ { NULL }, { NULL }, ++ { H_Content_Language }, ++ { H_Transfer_Encoding }, ++ { H_Authorization }, ++ { H_Content_Length }, ++ { H_User_Agent }, ++ { H_Server }, ++ { H_Expires }, ++ { H_Location }, + { NULL }, +- { H_If_Modified_Since }, ++ { H_Set_Cookie }, ++ { H_Content_Encoding }, ++ { H_Max_Forwards }, + { H_Cache_Control }, + { NULL }, ++ { H_Connection }, ++ { H_Pragma }, + { NULL }, ++ { H_Accept_Ranges }, ++ { H_HTTP2_Settings }, ++ { H_Allow }, ++ { H_Content_Location }, + { NULL }, ++ { H_Proxy_Authenticate }, ++ { H_Vary }, + { NULL }, +- { H_Accept_Ranges } ++ { H_WWW_Authenticate }, ++ { H_Warning }, ++ { H_Via }, ++ { NULL }, { NULL }, { NULL }, { NULL }, ++ { NULL }, { NULL }, { NULL }, { NULL }, ++ { NULL }, { NULL }, { NULL }, { NULL }, ++ { NULL }, { NULL }, { NULL }, ++ { H_Proxy_Authorization } + }; + + static struct http_hdrflg * +@@ -145,12 +185,12 @@ http_hdr_flags(const char *b, const char *e) + assert(b <= e); + u = (unsigned)(e - b); + assert(b + u == e); +- if (u < 2 || u > 19) // MIN_WORD_LENGTH & MAX_WORD_LENGTH +- return(NULL); +- if (u > 3) +- u += http_asso_values[((const uint8_t*)b)[3]]; +- if (u > 38) // MAX_HASH_VALUE +- return(NULL); ++ if (u < GPERF_MIN_WORD_LENGTH || u > GPERF_MAX_WORD_LENGTH) ++ return (NULL); ++ u += http_asso_values[((const uint8_t *)b)[u - 1]] + ++ http_asso_values[((const uint8_t *)b)[0]]; ++ if (u > GPERF_MAX_HASH_VALUE) ++ return (NULL); + retval = &http_hdrflg[u]; + if (retval->hdr == NULL) + return(NULL); +@@ -168,11 +208,9 @@ http_init_hdr(char *hdr, int flg) + + hdr[0] = strlen(hdr + 1); + f = http_hdr_flags(hdr + 1, hdr + hdr[0]); +- if (flg) { +- AN(f); +- assert(f->hdr == hdr); +- f->flag = flg; +- } ++ AN(f); ++ assert(f->hdr == hdr); ++ f->flag = flg; + } + + void +diff --git a/bin/varnishtest/tests/f00010.vtc b/bin/varnishtest/tests/f00010.vtc +new file mode 100644 +index 000000000000..b381b5cf37a6 +--- /dev/null ++++ b/bin/varnishtest/tests/f00010.vtc +@@ -0,0 +1,19 @@ ++varnishtest "Do not allow critical headers to be marked hop-by-hop" ++ ++varnish v1 -vcl { ++ backend default none; ++} -start ++ ++client c1 { ++ txreq -hdr "Connection: Content-Length" -body "asdf" ++ rxresp ++ expect resp.status == 400 ++ expect_close ++} -run ++ ++client c2 { ++ txreq -hdr "Connection: Host" ++ rxresp ++ expect resp.status == 400 ++ expect_close ++} -run +-- +2.39.0 + diff -Nru varnish-7.1.1/debian/patches/hpack-fix-pseudo-headers-handling.patch varnish-7.1.1/debian/patches/hpack-fix-pseudo-headers-handling.patch --- varnish-7.1.1/debian/patches/hpack-fix-pseudo-headers-handling.patch 1970-01-01 01:00:00.000000000 +0100 +++ varnish-7.1.1/debian/patches/hpack-fix-pseudo-headers-handling.patch 2023-01-09 22:08:14.000000000 +0100 @@ -0,0 +1,212 @@ +From: Asad Sajjad Ahmed <asa...@varnish-software.com> +Date: Fri, 30 Sep 2022 14:42:53 +0200 +Subject: hpack: fix pseudo-headers handling +Origin: https://github.com/varnishcache/varnish-cache/commit/515a93df894430767073ccd8265497b6b25b54b5 +Bug-Debian: https://bugs.debian.org/1023751 +Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2022-45060 + +We should apply the same restrictions on the list of allowed characters inside +H/2 pseudo-headers as we do for H/1. This error is translated into the +headers we send to a backend over H/1. + +Failure to do so could permit various exploits against a backend not handling +malformed H/1 requests. + +Signed-off-by: Asad Sajjad Ahmed <asa...@varnish-software.com> +--- + bin/varnishd/http2/cache_http2_hpack.c | 35 +++++++++++++++++++ + bin/varnishtest/tests/t02023.vtc | 48 ++++++++++++++++++++++++++ + bin/varnishtest/tests/t02024.vtc | 48 ++++++++++++++++++++++++++ + 3 files changed, 131 insertions(+) + create mode 100644 bin/varnishtest/tests/t02023.vtc + create mode 100644 bin/varnishtest/tests/t02024.vtc + +diff --git a/bin/varnishd/http2/cache_http2_hpack.c b/bin/varnishd/http2/cache_http2_hpack.c +index 6e67b55c505b..f58788b1266a 100644 +--- a/bin/varnishd/http2/cache_http2_hpack.c ++++ b/bin/varnishd/http2/cache_http2_hpack.c +@@ -96,13 +96,18 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len) + { + /* XXX: This might belong in cache/cache_http.c */ + const char *b0; ++ int disallow_empty; + unsigned n; ++ char *p; ++ int i; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + AN(b); + assert(namelen >= 2); /* 2 chars from the ': ' that we added */ + assert(namelen <= len); + ++ disallow_empty = 0; ++ + if (len > UINT_MAX) { /* XXX: cache_param max header size */ + VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", b); + return (H2SE_ENHANCE_YOUR_CALM); +@@ -117,10 +122,24 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len) + b += namelen; + len -= namelen; + n = HTTP_HDR_METHOD; ++ disallow_empty = 1; ++ ++ /* First field cannot contain SP or CTL */ ++ for (p = b, i = 0; i < len; p++, i++) { ++ if (vct_issp(*p) || vct_isctl(*p)) ++ return (H2SE_PROTOCOL_ERROR); ++ } + } else if (!strncmp(b, ":path: ", namelen)) { + b += namelen; + len -= namelen; + n = HTTP_HDR_URL; ++ disallow_empty = 1; ++ ++ /* Second field cannot contain LWS or CTL */ ++ for (p = b, i = 0; i < len; p++, i++) { ++ if (vct_islws(*p) || vct_isctl(*p)) ++ return (H2SE_PROTOCOL_ERROR); ++ } + } else if (!strncmp(b, ":scheme: ", namelen)) { + /* XXX: What to do about this one? (typically + "http" or "https"). For now set it as a normal +@@ -128,6 +147,15 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len) + b++; + len-=1; + n = hp->nhd; ++ ++ for (p = b + namelen, i = 0; i < len-namelen; ++ p++, i++) { ++ if (vct_issp(*p) || vct_isctl(*p)) ++ return (H2SE_PROTOCOL_ERROR); ++ } ++ ++ if (!i) ++ return (H2SE_PROTOCOL_ERROR); + } else if (!strncmp(b, ":authority: ", namelen)) { + b+=6; + len-=6; +@@ -164,6 +192,13 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len) + hp->hd[n].b = b; + hp->hd[n].e = b + len; + ++ if (disallow_empty && !Tlen(hp->hd[n])) { ++ VSLb(hp->vsl, SLT_BogoHeader, ++ "Empty pseudo-header %.*s", ++ (int)namelen, b0); ++ return (H2SE_PROTOCOL_ERROR); ++ } ++ + return (0); + } + +diff --git a/bin/varnishtest/tests/t02023.vtc b/bin/varnishtest/tests/t02023.vtc +new file mode 100644 +index 000000000000..cfd843da3ecb +--- /dev/null ++++ b/bin/varnishtest/tests/t02023.vtc +@@ -0,0 +1,48 @@ ++varnishtest "Empty pseudo-headers" ++ ++server s1 { ++ rxreq ++ txresp ++} -start ++ ++varnish v1 -arg "-p feature=+http2" -vcl+backend { ++} -start ++ ++client c1 { ++ txreq -url "" ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ txreq -req "" ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ txreq -proto "" ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -url "" ++ rxrst ++ } -run ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -scheme "" ++ rxrst ++ } -run ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -req "" ++ rxrst ++ } -run ++} -run +diff --git a/bin/varnishtest/tests/t02024.vtc b/bin/varnishtest/tests/t02024.vtc +new file mode 100644 +index 000000000000..0d0a1abc5d09 +--- /dev/null ++++ b/bin/varnishtest/tests/t02024.vtc +@@ -0,0 +1,48 @@ ++varnishtest "Garbage pseudo-headers" ++ ++server s1 { ++ rxreq ++ txresp ++} -start ++ ++varnish v1 -arg "-p feature=+http2" -vcl+backend { ++} -start ++ ++client c1 { ++ txreq -url " " ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ txreq -req " " ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ txreq -proto " " ++ rxresp ++ expect resp.status == 400 ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -url " " ++ rxrst ++ } -run ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -scheme " " ++ rxrst ++ } -run ++} -run ++ ++client c1 { ++ stream 1 { ++ txreq -req " " ++ rxrst ++ } -run ++} -run +-- +2.39.0 + diff -Nru varnish-7.1.1/debian/patches/series varnish-7.1.1/debian/patches/series --- varnish-7.1.1/debian/patches/series 1970-01-01 01:00:00.000000000 +0100 +++ varnish-7.1.1/debian/patches/series 2023-01-09 22:06:58.000000000 +0100 @@ -0,0 +1,2 @@ +Add-all-well-known-headers-to-the-perfect-hash-looku.patch +hpack-fix-pseudo-headers-handling.patch