diff --git a/varnish-6.4.0_fix_VSV00007.patch b/varnish-6.4.0_fix_VSV00007.patch new file mode 100644 index 0000000..3a11ba9 --- /dev/null +++ b/varnish-6.4.0_fix_VSV00007.patch @@ -0,0 +1,137 @@ +commit 82b0a629f60136e76112c6f2c6372cce77b683be +Author: Martin Blix Grydeland +Date: Tue Jun 22 11:47:55 2021 +0200 + + Take content length into account on H/2 request bodies + + When receiving H/2 data frames, make sure to take the advertised content + length into account, and fail appropriately if the combined sum of the + data frames does not match the content length. + +diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h +index 270306d15..c1456a680 100644 +--- a/bin/varnishd/http2/cache_http2.h ++++ b/bin/varnishd/http2/cache_http2.h +@@ -134,6 +134,8 @@ struct h2_req { + /* Where to wake this stream up */ + struct worker *wrk; + ++ ssize_t reqbody_bytes; ++ + VTAILQ_ENTRY(h2_req) tx_list; + h2_error error; + }; +diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c +index c079656b1..59dd0336b 100644 +--- a/bin/varnishd/http2/cache_http2_proto.c ++++ b/bin/varnishd/http2/cache_http2_proto.c +@@ -554,6 +554,7 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2, + struct req *req, struct h2_req *r2) + { + h2_error h2e; ++ ssize_t cl; + + ASSERT_RXTHR(h2); + assert(r2->state == H2_S_OPEN); +@@ -574,16 +575,24 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2, + // XXX: Have I mentioned H/2 Is hodge-podge ? + http_CollectHdrSep(req->http, H_Cookie, "; "); // rfc7540,l,3114,3120 + ++ cl = http_GetContentLength(req->http); ++ assert(cl >= -2); ++ if (cl == -2) { ++ VSLb(h2->vsl, SLT_Debug, "Non-parseable Content-Length"); ++ return (H2SE_PROTOCOL_ERROR); ++ } ++ + if (req->req_body_status == NULL) { +- if (!http_GetHdr(req->http, H_Content_Length, NULL)) ++ if (cl == -1) + req->req_body_status = BS_EOF; + else + req->req_body_status = BS_LENGTH; ++ req->htc->content_length = cl; + } else { + /* A HEADER frame contained END_STREAM */ + assert (req->req_body_status == BS_NONE); + r2->state = H2_S_CLOS_REM; +- if (http_GetContentLength(req->http) > 0) ++ if (cl > 0) + return (H2CE_PROTOCOL_ERROR); //rfc7540,l,1838,1840 + } + +@@ -737,6 +746,7 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + int w1 = 0, w2 = 0; + char buf[4]; + unsigned wi; ++ ssize_t cl; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + ASSERT_RXTHR(h2); +@@ -755,6 +765,23 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + Lck_Unlock(&h2->sess->mtx); + return (h2->error ? h2->error : r2->error); + } ++ ++ r2->reqbody_bytes += h2->rxf_len; ++ if (h2->rxf_flags & H2FF_DATA_END_STREAM) ++ r2->state = H2_S_CLOS_REM; ++ cl = r2->req->htc->content_length; ++ if (cl >= 0 && (r2->reqbody_bytes > cl || ++ (r2->state >= H2_S_CLOS_REM && r2->reqbody_bytes != cl))) { ++ VSLb(h2->vsl, SLT_Debug, ++ "H2: stream %u: Received data and Content-Length" ++ " mismatch", h2->rxf_stream); ++ r2->error = H2SE_PROTOCOL_ERROR; // rfc7540,l,3150,3163 ++ if (r2->cond) ++ AZ(pthread_cond_signal(r2->cond)); ++ Lck_Unlock(&h2->sess->mtx); ++ return (H2SE_PROTOCOL_ERROR); ++ } ++ + AZ(h2->mailcall); + h2->mailcall = r2; + h2->req0->r_window -= h2->rxf_len; +@@ -773,6 +800,8 @@ h2_rx_data(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) + r2->r_window += wi; + w2 = 1; + } ++ ++ + Lck_Unlock(&h2->sess->mtx); + + if (w1 || w2) { +@@ -795,7 +824,7 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) + struct h2_req *r2; + struct h2_sess *h2; + unsigned l; +- enum vfp_status retval = VFP_OK; ++ enum vfp_status retval; + + CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); + CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); +@@ -808,7 +837,6 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) + *lp = 0; + + Lck_Lock(&h2->sess->mtx); +- assert (r2->state == H2_S_OPEN); + r2->cond = &vc->wrk->cond; + while (h2->mailcall != r2 && h2->error == 0 && r2->error == 0) + AZ(Lck_CondWait(r2->cond, &h2->sess->mtx, 0)); +@@ -831,12 +859,10 @@ h2_vfp_body(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) + Lck_Unlock(&h2->sess->mtx); + return (VFP_OK); + } +- if (h2->rxf_len == 0) { +- if (h2->rxf_flags & H2FF_DATA_END_STREAM) { +- retval = VFP_END; +- r2->state = H2_S_CLOS_REM; +- } +- } ++ if (h2->rxf_len == 0 && r2->state >= H2_S_CLOS_REM) ++ retval = VFP_END; ++ else ++ retval = VFP_OK; + h2->mailcall = NULL; + AZ(pthread_cond_signal(h2->cond)); + } diff --git a/varnish-6.4.0_test_for_VSV00007.patch b/varnish-6.4.0_test_for_VSV00007.patch new file mode 100644 index 0000000..82001ff --- /dev/null +++ b/varnish-6.4.0_test_for_VSV00007.patch @@ -0,0 +1,94 @@ +commit 102d0e7a9f5a097535c1afcdfcd0332f66bfb024 +Author: Martin Blix Grydeland +Date: Tue Jun 22 11:47:58 2021 +0200 + + VSV00007 Test case for H2 smuggling attack + +diff --git a/bin/varnishtest/tests/f00007.vtc b/bin/varnishtest/tests/f00007.vtc +new file mode 100644 +index 000000000..1cf45aad1 +--- /dev/null ++++ b/bin/varnishtest/tests/f00007.vtc +@@ -0,0 +1,82 @@ ++varnishtest "H/2 content length smuggling attack" ++ ++server s1 { ++ rxreqhdrs ++ expect_close ++} -start ++ ++server s2 { ++ rxreqhdrs ++ expect_close ++} -start ++ ++server s3 { ++ rxreq ++ expect_close ++} -start ++ ++server s4 { ++ rxreq ++ expect req.body == "A" ++ txresp ++} -start ++ ++varnish v1 -vcl+backend { ++ import vtc; ++ sub vcl_backend_fetch { ++ if (bereq.url == "/1") { ++ set bereq.backend = s1; ++ } else if (bereq.url == "/2") { ++ set bereq.backend = s2; ++ } else if (bereq.url == "/3") { ++ set bereq.backend = s3; ++ } else { ++ set bereq.backend = s4; ++ } ++ } ++} -start ++ ++varnish v1 -cliok "param.set feature +http2" ++varnish v1 -cliok "param.set debug +syncvsl" ++ ++client c1 { ++ stream 1 { ++ txreq -req POST -url /1 -hdr "content-length" "1" -nostrend ++ txdata -data "AGET /FAIL HTTP/1.1\r\n\r\n" ++ rxrst ++ expect rst.err == PROTOCOL_ERROR ++ } -run ++} -run ++ ++client c2 { ++ stream 1 { ++ txreq -req POST -url /2 -hdr "content-length" "1" -nostrend ++ txdata -data "AGET /FAIL HTTP/1.1\r\n\r\n" -nostrend ++ txdata ++ rxrst ++ expect rst.err == PROTOCOL_ERROR ++ } -run ++} -run ++ ++client c3 { ++ stream 1 { ++ txreq -req POST -url /3 -hdr "content-length" "1" -nostrend ++ txdata -data "A" -nostrend ++ txdata -data "GET /FAIL HTTP/1.1\r\n\r\n" ++ rxwinup ++ rxrst ++ expect rst.err == PROTOCOL_ERROR ++ } -run ++} -run ++ ++client c4 { ++ stream 1 { ++ txreq -req POST -url /4 -hdr "content-length" "1" -nostrend ++ txdata -data "A" -nostrend ++ txdata ++ rxwinup ++ rxwinup ++ rxresp ++ expect resp.status == 200 ++ } -run ++} -run diff --git a/varnish.spec b/varnish.spec index 6e387da..3388c1c 100644 --- a/varnish.spec +++ b/varnish.spec @@ -24,7 +24,7 @@ Summary: High-performance HTTP accelerator Name: varnish Version: 6.4.0 -Release: 4%{?dist} +Release: 5%{?dist} License: BSD URL: https://www.varnish-cache.org/ Source0: http://varnish-cache.org/_downloads/%{name}-%{version}%{?vd_rc}.tgz @@ -65,6 +65,12 @@ Patch16: varnish-6.4.0_el6_fix_warning_from_old_gcc.patch # Patch 018: gcc-10.0.1/s390x compilation fix, upstream commit b0af060 #Patch18: varnish-6.3.2_fix_s390x.patch +# Patch019: fix for CVE-2021-36740, upstream commit 82b0a629 +Patch19: varnish-6.4.0_fix_VSV00007.patch + +# Patch019: test for CVE-2021-36740, upstream commit 102d0e7a +Patch20: varnish-6.4.0_test_for_VSV00007.patch + %if 0%{?fedora} > 29 Provides: varnish%{_isa} = %{version}-%{release} Provides: varnishd(abi)%{_isa} = %{abi} @@ -190,6 +196,8 @@ sed -i '8 i\RPM_BUILD_ROOT=%{buildroot}' find-provides %patch4 -p0 %patch16 -p1 %endif +%patch19 -p1 +%patch20 -p1 %build %if 0%{?rhel} == 6 @@ -433,6 +441,9 @@ fi %changelog +* Say Jul 17 2021 Ingvar Hagelund 6.4.0-5 +- Added patches for CVE-2021-36740 aka VSV00007, bz#1982412 + * Tue Aug 04 2020 Ingvar Hagelund 6.4.0-4 - Added -Wno-error=free-nonheap-object to CFLAGS to build on s390x