Version in base suite: 7.1.1-1.1 Version in overlay suite: 7.1.1-1.1+deb12u1 Base version: varnish_7.1.1-1.1+deb12u1 Target version: varnish_7.1.1-2+deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/v/varnish/varnish_7.1.1-1.1+deb12u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/v/varnish/varnish_7.1.1-2+deb12u1.dsc changelog | 6 control | 8 - patches/fix_vsv16 | 365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ patches/series | 1 4 files changed, 376 insertions(+), 4 deletions(-) diff -Nru varnish-7.1.1/debian/changelog varnish-7.1.1/debian/changelog --- varnish-7.1.1/debian/changelog 2025-03-31 13:06:56.000000000 +0000 +++ varnish-7.1.1/debian/changelog 2025-05-12 23:19:10.000000000 +0000 @@ -1,3 +1,9 @@ +varnish (7.1.1-2+deb12u1) bookworm-security; urgency=medium + + * Fix the VSV00016 request smuggling vulnerability. + + -- Marco d'Itri Tue, 13 May 2025 01:19:10 +0200 + varnish (7.1.1-1.1+deb12u1) bookworm; urgency=medium * Non-maintainer upload. diff -Nru varnish-7.1.1/debian/control varnish-7.1.1/debian/control --- varnish-7.1.1/debian/control 2023-01-09 20:59:22.000000000 +0000 +++ varnish-7.1.1/debian/control 2025-05-12 23:17:26.000000000 +0000 @@ -1,9 +1,8 @@ Source: varnish Maintainer: Varnish Package Maintainers -Uploaders: Jan Wagner , - Lars Bahner , +Uploaders: Stig Sandbeck Mathisen , - Emanuele Rocca + Marco d'Itri , Section: web Priority: optional Build-Depends: automake, @@ -16,7 +15,8 @@ libtool, pkg-config, python3-sphinx, - xsltproc + xsltproc, + debhelper Standards-Version: 4.6.1 Vcs-Browser: https://salsa.debian.org/varnish-team/varnish Vcs-Git: https://salsa.debian.org/varnish-team/varnish.git diff -Nru varnish-7.1.1/debian/patches/fix_vsv16 varnish-7.1.1/debian/patches/fix_vsv16 --- varnish-7.1.1/debian/patches/fix_vsv16 1970-01-01 00:00:00.000000000 +0000 +++ varnish-7.1.1/debian/patches/fix_vsv16 2025-05-12 23:18:23.000000000 +0000 @@ -0,0 +1,365 @@ +Dridi Boukelmoune (2): + v1f: Parse CRLF separately from other white space + vtc: Test coverage for VSV17 + +Nils Goroll (3): + Rename v1f_* pull callbacks for consistency + v1f: Read end-of-chunk as part of the chunk + v1f: pull chunk header parsing into an own function + +Poul-Henning Kamp (1): + (SP|TAB) is rightfully called OWS in RFC7230 + +--- a/bin/varnishd/http1/cache_http1_vfp.c ++++ b/bin/varnishd/http1/cache_http1_vfp.c +@@ -90,76 +90,118 @@ v1f_read(const struct vfp_ctx *vc, struc + + + /*-------------------------------------------------------------------- +- * Read a chunked HTTP object. ++ * read (CR)?LF at the end of a chunk ++ */ ++static enum vfp_status ++v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc) ++{ ++ char c; ++ ++ if (v1f_read(vc, htc, &c, 1) <= 0) ++ return (VFP_Error(vc, "chunked read err")); ++ if (c == '\r' && v1f_read(vc, htc, &c, 1) <= 0) ++ return (VFP_Error(vc, "chunked read err")); ++ if (c != '\n') ++ return (VFP_Error(vc, "chunked tail no NL")); ++ return (VFP_OK); ++} ++ ++ ++/*-------------------------------------------------------------------- ++ * Parse a chunk header and, for VFP_OK, return size in a pointer + * + * XXX: Reading one byte at a time is pretty pessimal. + */ + +-static enum vfp_status v_matchproto_(vfp_pull_f) +-v1f_pull_chunked(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, +- ssize_t *lp) ++static enum vfp_status ++v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) + { +- struct http_conn *htc; + char buf[20]; /* XXX: 20 is arbitrary */ +- char *q; + unsigned u; + uintmax_t cll; +- ssize_t cl, l, lr; ++ ssize_t cl, lr; ++ char *q; + + CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); +- CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); +- CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); +- AN(ptr); +- AN(lp); ++ CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); ++ AN(szp); ++ assert(*szp == -1); ++ ++ /* Skip leading whitespace */ ++ do { ++ lr = v1f_read(vc, htc, buf, 1); ++ if (lr <= 0) ++ return (VFP_Error(vc, "chunked read err")); ++ } while (vct_isows(buf[0])); + +- l = *lp; +- *lp = 0; +- if (vfe->priv2 == -1) { +- /* Skip leading whitespace */ ++ if (!vct_ishex(buf[0])) ++ return (VFP_Error(vc, "chunked header non-hex")); ++ ++ /* Collect hex digits, skipping leading zeros */ ++ for (u = 1; u < sizeof buf; u++) { + do { +- lr = v1f_read(vc, htc, buf, 1); ++ lr = v1f_read(vc, htc, buf + u, 1); + if (lr <= 0) + return (VFP_Error(vc, "chunked read err")); +- } while (vct_islws(buf[0])); ++ } while (u == 1 && buf[0] == '0' && buf[u] == '0'); ++ if (!vct_ishex(buf[u])) ++ break; ++ } ++ ++ if (u >= sizeof buf) ++ return (VFP_Error(vc, "chunked header too long")); ++ ++ /* Skip trailing white space */ ++ while (vct_isows(buf[u])) { ++ lr = v1f_read(vc, htc, buf + u, 1); ++ if (lr <= 0) ++ return (VFP_Error(vc, "chunked read err")); ++ } + +- if (!vct_ishex(buf[0])) +- return (VFP_Error(vc, "chunked header non-hex")); ++ if (buf[u] == '\r' && v1f_read(vc, htc, buf + u, 1) <= 0) ++ return (VFP_Error(vc, "chunked read err")); ++ if (buf[u] != '\n') ++ return (VFP_Error(vc, "chunked header no NL")); + +- /* Collect hex digits, skipping leading zeros */ +- for (u = 1; u < sizeof buf; u++) { +- do { +- lr = v1f_read(vc, htc, buf + u, 1); +- if (lr <= 0) +- return (VFP_Error(vc, +- "chunked read err")); +- } while (u == 1 && buf[0] == '0' && buf[u] == '0'); +- if (!vct_ishex(buf[u])) +- break; +- } ++ buf[u] = '\0'; + +- if (u >= sizeof buf) +- return (VFP_Error(vc, "chunked header too long")); ++ cll = strtoumax(buf, &q, 16); ++ if (q == NULL || *q != '\0') ++ return (VFP_Error(vc, "chunked header number syntax")); ++ cl = (ssize_t)cll; ++ if (cl < 0 || (uintmax_t)cl != cll) ++ return (VFP_Error(vc, "bogusly large chunk size")); + +- /* Skip trailing white space */ +- while (vct_islws(buf[u]) && buf[u] != '\n') { +- lr = v1f_read(vc, htc, buf + u, 1); +- if (lr <= 0) +- return (VFP_Error(vc, "chunked read err")); +- } ++ *szp = cl; ++ return (VFP_OK); ++} + +- if (buf[u] != '\n') +- return (VFP_Error(vc, "chunked header no NL")); + +- buf[u] = '\0'; ++/*-------------------------------------------------------------------- ++ * Read a chunked HTTP object. ++ * ++ */ ++ ++static enum vfp_status v_matchproto_(vfp_pull_f) ++v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ++ ssize_t *lp) ++{ ++ static enum vfp_status vfps; ++ struct http_conn *htc; ++ ssize_t l, lr; + +- cll = strtoumax(buf, &q, 16); +- if (q == NULL || *q != '\0') +- return (VFP_Error(vc, "chunked header number syntax")); +- cl = (ssize_t)cll; +- if (cl < 0 || (uintmax_t)cl != cll) +- return (VFP_Error(vc, "bogusly large chunk size")); ++ CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); ++ CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); ++ CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); ++ AN(ptr); ++ AN(lp); + +- vfe->priv2 = cl; ++ l = *lp; ++ *lp = 0; ++ if (vfe->priv2 == -1) { ++ vfps = v1f_chunked_hdr(vc, htc, &vfe->priv2); ++ if (vfps != VFP_OK) ++ return (vfps); + } + if (vfe->priv2 > 0) { + if (vfe->priv2 < l) +@@ -169,30 +211,27 @@ v1f_pull_chunked(struct vfp_ctx *vc, str + return (VFP_Error(vc, "chunked insufficient bytes")); + *lp = lr; + vfe->priv2 -= lr; +- if (vfe->priv2 == 0) +- vfe->priv2 = -1; +- return (VFP_OK); ++ if (vfe->priv2 != 0) ++ return (VFP_OK); ++ ++ vfe->priv2 = -1; ++ return (v1f_chunk_end(vc, htc)); + } + AZ(vfe->priv2); +- if (v1f_read(vc, htc, buf, 1) <= 0) +- return (VFP_Error(vc, "chunked read err")); +- if (buf[0] == '\r' && v1f_read(vc, htc, buf, 1) <= 0) +- return (VFP_Error(vc, "chunked read err")); +- if (buf[0] != '\n') +- return (VFP_Error(vc, "chunked tail no NL")); +- return (VFP_END); ++ vfps = v1f_chunk_end(vc, htc); ++ return (vfps == VFP_OK ? VFP_END : vfps); + } + + static const struct vfp v1f_chunked = { + .name = "V1F_CHUNKED", +- .pull = v1f_pull_chunked, ++ .pull = v1f_chunked_pull, + }; + + + /*--------------------------------------------------------------------*/ + + static enum vfp_status v_matchproto_(vfp_pull_f) +-v1f_pull_straight(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ++v1f_straight_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, + ssize_t *lp) + { + ssize_t l, lr; +@@ -222,13 +261,13 @@ v1f_pull_straight(struct vfp_ctx *vc, st + + static const struct vfp v1f_straight = { + .name = "V1F_STRAIGHT", +- .pull = v1f_pull_straight, ++ .pull = v1f_straight_pull, + }; + + /*--------------------------------------------------------------------*/ + + static enum vfp_status v_matchproto_(vfp_pull_f) +-v1f_pull_eof(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp) ++v1f_eof_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *p, ssize_t *lp) + { + ssize_t l, lr; + struct http_conn *htc; +@@ -253,7 +292,7 @@ v1f_pull_eof(struct vfp_ctx *vc, struct + + static const struct vfp v1f_eof = { + .name = "V1F_EOF", +- .pull = v1f_pull_eof, ++ .pull = v1f_eof_pull, + }; + + /*-------------------------------------------------------------------- +--- a/bin/varnishtest/tests/r01184.vtc ++++ b/bin/varnishtest/tests/r01184.vtc +@@ -62,6 +62,7 @@ server s1 { + sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" + sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" + sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" ++ send "\n" + expect_close + } -start + +@@ -93,6 +94,7 @@ server s1 { + sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" + sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" + sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" ++ send "\n" + expect_close + } -start + +--- a/bin/varnishtest/tests/r01729.vtc ++++ b/bin/varnishtest/tests/r01729.vtc +@@ -11,7 +11,7 @@ server s1 { + send "\r\n" + send "14\r\n" + send "0123456789" +- send "0123456789" ++ send "0123456789\n" + send "0\r\n" + send "\r\n" + +@@ -29,7 +29,7 @@ client c1 { + send "\r\n" + send "14\r\n" + send "0123456789" +- send "0123456789" ++ send "0123456789\n" + send "0\r\n" + send "\r\n" + +@@ -45,7 +45,7 @@ client c1 { + send "\r\n" + send "14\r\n" + send "0123456789" +- send "0123456789" ++ send "0123456789\n" + send "0\r\n" + send "\r\n" + +--- /dev/null ++++ b/bin/varnishtest/tests/f00017.vtc +@@ -0,0 +1,69 @@ ++varnishtest "Do not tolerate anything else than CRLF as chunked ending" ++ ++server s0 { ++ rxreq ++ expect_close ++} -dispatch ++ ++varnish v1 -vcl+backend {} -start ++ ++logexpect l1 -v v1 { ++ expect * 1001 FetchError "chunked tail no NL" ++ expect * 1004 FetchError "chunked tail no NL" ++ expect * 1007 FetchError "chunked header non-hex" ++ expect * 1010 FetchError "chunked header non-hex" ++} -start ++ ++client c1 { ++ non_fatal ++ txreq -req POST -hdr "Transfer-encoding: chunked" ++ send "1\r\n" ++ send "This is more than one byte of data\r\n" ++ send "0\r\n" ++ send "\r\n" ++ fatal ++ rxresp ++ expect resp.status == 503 ++ expect_close ++} -run ++ ++client c2 { ++ non_fatal ++ txreq -req POST -hdr "Transfer-encoding: chunked" ++ send "1\r\n" ++ send "Z 2\r\n" ++ send "3d\r\n" ++ send "0\r\n\r\nPOST /evil HTTP/1.1\r\nHost: whatever\r\nContent-Length: 5\r\n\r\n" ++ send "0\r\n" ++ send "\r\n" ++ fatal ++ rxresp ++ expect resp.status == 503 ++ expect_close ++} -run ++ ++client c3 { ++ non_fatal ++ txreq -req POST -hdr "Transfer-encoding: chunked" ++ send "d\r\n" ++ send "Spurious CRLF\r\n\r\n" ++ send "0\r\n" ++ send "\r\n" ++ fatal ++ rxresp ++ expect resp.status == 503 ++ expect_close ++} -run ++ ++client c4 { ++ non_fatal ++ txreq -req POST -hdr "Transfer-encoding: chunked" ++ send "\n0\r\n" ++ send "\r\n" ++ fatal ++ rxresp ++ expect resp.status == 503 ++ expect_close ++} -run ++ ++logexpect l1 -wait diff -Nru varnish-7.1.1/debian/patches/series varnish-7.1.1/debian/patches/series --- varnish-7.1.1/debian/patches/series 2025-03-31 13:06:56.000000000 +0000 +++ varnish-7.1.1/debian/patches/series 2025-05-12 23:18:39.000000000 +0000 @@ -1,3 +1,4 @@ Add-all-well-known-headers-to-the-perfect-hash-looku.patch hpack-fix-pseudo-headers-handling.patch 0001-req_fsm-Close-the-connection-on-a-malformed-request.patch +fix_vsv16