Compare commits

...
Sign in to create a new pull request.

2 commits

Author SHA1 Message Date
Ingvar Hagelund
250f8efa4d Security: Added patch for VSV00016 AKA CVE-2025-47905 2025-07-31 21:42:51 +02:00
Ingvar Hagelund
8a0b0058aa Security: Added patch for VSV00015 aka CVE-2025-30346 2025-03-25 12:33:59 +01:00
4 changed files with 447 additions and 1 deletions

46
VSV00015.patch Normal file
View file

@ -0,0 +1,46 @@
commit 8ef69a03b36aeac5f364c01eb20f821860e47f14
Author: Dag Haavi Finstad <daghf@varnish-software.com>
Date: Fri Jan 10 13:07:54 2025 +0100
req_fsm: Close the connection on a malformed request
diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c
index 1004cbc5f..803810210 100644
--- a/bin/varnishd/cache/cache_req_fsm.c
+++ b/bin/varnishd/cache/cache_req_fsm.c
@@ -962,6 +962,7 @@ cnt_recv(struct worker *wrk, struct req *req)
if (http_CountHdr(req->http0, H_Host) > 1) {
VSLb(req->vsl, SLT_BogoHeader, "Multiple Host: headers");
wrk->stats->client_req_400++;
+ req->doclose = SC_RX_BAD;
(void)req->transport->minimal_response(req, 400);
return (REQ_FSM_DONE);
}
@@ -969,6 +970,7 @@ cnt_recv(struct worker *wrk, struct req *req)
if (http_CountHdr(req->http0, H_Content_Length) > 1) {
VSLb(req->vsl, SLT_BogoHeader, "Multiple Content-Length: headers");
wrk->stats->client_req_400++;
+ req->doclose = SC_RX_BAD;
(void)req->transport->minimal_response(req, 400);
return (REQ_FSM_DONE);
}
diff --git a/bin/varnishtest/tests/b00037.vtc b/bin/varnishtest/tests/b00037.vtc
index ce0e84112..e6185bd07 100644
--- a/bin/varnishtest/tests/b00037.vtc
+++ b/bin/varnishtest/tests/b00037.vtc
@@ -11,6 +11,7 @@ client c1 {
varnish v1 -vsl_catchup
varnish v1 -expect client_req_400 == 1
+varnish v1 -expect sc_rx_bad == 1
client c1 {
txreq -method POST -hdr "Content-Length: 12" -hdr "Content-Length: 12" -bodylen 12
@@ -20,6 +21,7 @@ client c1 {
varnish v1 -vsl_catchup
varnish v1 -expect client_req_400 == 2
+varnish v1 -expect sc_rx_bad == 2
varnish v1 -cliok "param.set feature +http2"

215
VSV00016.patch Normal file
View file

@ -0,0 +1,215 @@
based on commit 6dd6607c93af5a9cc663a174aefc3a0aeff8f26c Mon Sep 17 00:00:00 2001
From: Nils Goroll <nils.goroll@uplex.de>
Date: Mon, 30 May 2022 13:09:11 +0200
Subject: [PATCH] v1f: Read end-of-chunk as part of the chunk
Until now, we read the (CR)?LF at the end of a chunk as part of the
next chunk header (see: /* Skip leading whitespace */).
For a follow up commit, we are going to want to know if the next chunk
header is available for read, so we now consume the chunk end as part
of the chunk itself.
This also fixes a corner case: We previously accepted chunks with a
missing end-of-chunk (see fix of r01729.vtc).
Ref: https://datatracker.ietf.org/doc/html/rfc7230#section-4.1
---
bin/varnishtest/tests/r01184.vtc | 2 ++
bin/varnishtest/tests/r01506.vtc | 16 ++++++++--------
bin/varnishtest/tests/r01729.vtc | 6 +++---
4 files changed, 39 insertions(+), 21 deletions(-)
--- bin/varnishd/http1/cache_http1_vfp.c.orig 2024-11-08 11:47:35.000000000 +0100
+++ bin/varnishd/http1/cache_http1_vfp.c 2025-07-30 00:09:27.239254185 +0200
@@ -88,77 +88,116 @@
return (i + l);
}
+/*--------------------------------------------------------------------
+ * 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);
+}
+
/*--------------------------------------------------------------------
- * Read a chunked HTTP object.
+ * 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_chunked_pull(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"));
- if (!vct_ishex(buf[0]))
- return (VFP_Error(vc, "chunked header non-hex"));
+ /* 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 (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';
+ 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"));
- if (u >= sizeof buf)
- return (VFP_Error(vc, "chunked header too long"));
+ *szp = cl;
+ return (VFP_OK);
+}
- /* 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"));
- }
- if (buf[u] != '\n')
- return (VFP_Error(vc, "chunked header no NL"));
+/*--------------------------------------------------------------------
+ * Read a chunked HTTP object.
+ *
+ */
- buf[u] = '\0';
+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)
@@ -168,18 +207,15 @@
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 = {

168
VSV00016.test.patch Normal file
View file

@ -0,0 +1,168 @@
From c1e6774ae5b7e5fdd7aef7352170b1d6e7613070 Mon Sep 17 00:00:00 2001
From: Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
Date: Thu, 3 Apr 2025 15:52:56 +0200
Subject: [PATCH] vtc: Test coverage for VSV16
---
bin/varnishtest/tests/f00016.vtc | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
create mode 100644 bin/varnishtest/tests/f00016.vtc
diff --git a/bin/varnishtest/tests/f00016.vtc b/bin/varnishtest/tests/f00016.vtc
new file mode 100644
index 0000000..a38b8b1
--- /dev/null
+++ b/bin/varnishtest/tests/f00016.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 --git a/bin/varnishtest/tests/r01184.vtc b/bin/varnishtest/tests/r01184.vtc
index 0988e65..94ecd3c 100644
--- 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
diff --git a/bin/varnishtest/tests/r01506.vtc b/bin/varnishtest/tests/r01506.vtc
index 96b7b54..f7f89a7 100644
--- a/bin/varnishtest/tests/r01506.vtc
+++ b/bin/varnishtest/tests/r01506.vtc
@@ -7,15 +7,15 @@ server s0 {
txresp -nolen \
-hdr "Transfer-Encoding: chunked" \
-hdr "Connection: close"
- send "11\r\n0_23456789abcdef\n"
- send "11\r\n1_23456789abcdef\n"
- send "11\r\n2_23456789abcdef\n"
- send "11\r\n3_23456789abcdef\n"
+ send "11\r\n0_23456789abcdef\n\n"
+ send "11\r\n1_23456789abcdef\n\n"
+ send "11\r\n2_23456789abcdef\n\n"
+ send "11\r\n3_23456789abcdef\n\n"
barrier b1 sync
- send "11\r\n4_23456789abcdef\n"
- send "11\r\n5_23456789abcdef\n"
- send "11\r\n6_23456789abcdef\n"
- send "11\r\n7_23456789abcdef\n"
+ send "11\r\n4_23456789abcdef\n\n"
+ send "11\r\n5_23456789abcdef\n\n"
+ send "11\r\n6_23456789abcdef\n\n"
+ send "11\r\n7_23456789abcdef\n\n"
chunkedlen 0
} -dispatch
diff --git a/bin/varnishtest/tests/r01729.vtc b/bin/varnishtest/tests/r01729.vtc
index 883a60c..f6a01e9 100644
--- 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"
--
libgit2 1.9.0

View file

@ -37,12 +37,19 @@
Summary: High-performance HTTP accelerator
Name: varnish
Version: 7.5.0
Release: 2%{?dist}
Release: 4%{?dist}
License: BSD-2-Clause AND (BSD-2-Clause-FreeBSD AND BSD-3-Clause AND LicenseRef-Fedora-Public-Domain AND Zlib)
URL: https://www.varnish-cache.org/
Source0: http://varnish-cache.org/_downloads/%{name}-%{version}.tgz
Source1: https://github.com/varnishcache/pkg-varnish-cache/archive/%{commit1}.tar.gz#/pkg-varnish-cache-%{shortcommit1}.tar.gz
# CVE-2025-30346, patch from upstream
Patch1: VSV00015.patch
# CVE-2025-47905, patches from upstream
Patch2: VSV00016.patch
Patch3: VSV00016.test.patch
%if 0%{?fedora} > 29 || 0%{?rhel} > 7
Provides: varnish%{_isa} = %{version}-%{release}
Provides: varnishd(abi)%{_isa} = %{abi}
@ -140,6 +147,10 @@ Documentation files for %name
%prep
%setup -q
%patch 1 -p1
%patch 2 -p0
%patch 3 -p1
tar xzf %SOURCE1
ln -s pkg-varnish-cache-%{commit1}/redhat redhat
ln -s pkg-varnish-cache-%{commit1}/debian debian
@ -313,6 +324,12 @@ test -f /etc/varnish/secret || (uuidgen > /etc/varnish/secret && chmod 0600 /etc
%changelog
* Thu Jul 31 2025 Ingvar Hagelund <ingvar@redpill-linpro.com> - 7.5.0-4
- Security: Added patch for VSV00016 AKA CVE-2025-47905
* Tue Mar 25 2025 Ingvar Hagelund <ingvar@redpill-linpro.com> - 7.5.0-3
- Security: Added patch for VSV00015 aka CVE-2025-30346
* Sat Jul 20 2024 Fedora Release Engineering <releng@fedoraproject.org> - 7.5.0-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_41_Mass_Rebuild