Version in base suite: 8.0.2+ds-1+deb10u1 Base version: trafficserver_8.0.2+ds-1+deb10u1 Target version: trafficserver_8.0.2+ds-1+deb10u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/t/trafficserver/trafficserver_8.0.2+ds-1+deb10u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/t/trafficserver/trafficserver_8.0.2+ds-1+deb10u2.dsc changelog | 9 + patches/0016-CVE-2019-17559.patch | 213 +++++++++++++++++++++++++ patches/0016-CVE-2019-17565.patch | 262 +++++++++++++++++++++++++++++++ patches/0016-CVE-2020-1944.patch | 48 +++++ patches/0016-CVE-2020-9481.patch | 318 ++++++++++++++++++++++++++++++++++++++ patches/series | 4 6 files changed, 854 insertions(+) diff -Nru trafficserver-8.0.2+ds/debian/changelog trafficserver-8.0.2+ds/debian/changelog --- trafficserver-8.0.2+ds/debian/changelog 2019-08-26 11:55:33.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/changelog 2020-04-16 19:36:40.000000000 +0000 @@ -1,3 +1,12 @@ +trafficserver (8.0.2+ds-1+deb10u2) buster-security; urgency=medium + + * Add fix from upstream for CVE-2019-17559 + * Add fix from upstream for CVE-2019-17565 + * Add fix from upstream for CVE-2020-1944 + * Add fix from upstream for CVE-2020-9481 + + -- Jean Baptiste Favre Thu, 16 Apr 2020 21:36:40 +0200 + trafficserver (8.0.2+ds-1+deb10u1) buster-security; urgency=high * Add patch for security backport from 8.0.4 for CVE-2019-9512, diff -Nru trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17559.patch trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17559.patch --- trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17559.patch 1970-01-01 00:00:00.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17559.patch 2020-04-16 19:36:40.000000000 +0000 @@ -0,0 +1,213 @@ +Description: Fix for CVE-2019-17559 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6389 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/proxy/hdrs/HdrTest.cc 2020-04-16 19:03:21.522369124 +0200 ++++ trafficserver/proxy/hdrs/HdrTest.cc 2020-04-16 19:03:21.522369124 +0200 +@@ -343,44 +343,100 @@ + // Start with an easy one... + "http://trafficserver.apache.org/index.html", + +- // "cheese://bogosity", This fails, but it's not clear it should work... ++ "cheese://bogosity", + +- "some.place", "some.place/", "http://some.place", "http://some.place/", "http://some.place/path", +- "http://some.place/path;params", "http://some.place/path;params?query", "http://some.place/path;params?query#fragment", +- "http://some.place/path?query#fragment", "http://some.place/path#fragment", +- +- "some.place:80", "some.place:80/", "http://some.place:80", "http://some.place:80/", +- +- "foo@some.place:80", "foo@some.place:80/", "http://foo@some.place:80", "http://foo@some.place:80/", +- +- "foo:bar@some.place:80", "foo:bar@some.place:80/", "http://foo:bar@some.place:80", "http://foo:bar@some.place:80/", ++ "some.place", ++ "some.place/", ++ "http://some.place", ++ "http://some.place/", ++ "http://some.place/path", ++ "http://some.place/path;params", ++ "http://some.place/path;params?query", ++ "http://some.place/path;params?query#fragment", ++ "http://some.place/path?query#fragment", ++ "http://some.place/path#fragment", ++ ++ "some.place:80", ++ "some.place:80/", ++ "http://some.place:80", ++ "http://some.place:80/", ++ ++ "foo@some.place:80", ++ "foo@some.place:80/", ++ "http://foo@some.place:80", ++ "http://foo@some.place:80/", ++ ++ "foo:bar@some.place:80", ++ "foo:bar@some.place:80/", ++ "http://foo:bar@some.place:80", ++ "http://foo:bar@some.place:80/", + + // Some address stuff +- "http://172.16.28.101", "http://172.16.28.101:8080", "http://[::]", "http://[::1]", "http://[fc01:172:16:28::101]", +- "http://[fc01:172:16:28::101]:80", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]", +- "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080", "http://172.16.28.101/some/path", "http://172.16.28.101:8080/some/path", +- "http://[::1]/some/path", "http://[fc01:172:16:28::101]/some/path", "http://[fc01:172:16:28::101]:80/some/path", +- "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path", +- "http://172.16.28.101/", "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/", ++ "http://172.16.28.101", ++ "http://172.16.28.101:8080", ++ "http://[::]", ++ "http://[::1]", ++ "http://[fc01:172:16:28::101]", ++ "http://[fc01:172:16:28::101]:80", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080", ++ "http://172.16.28.101/some/path", ++ "http://172.16.28.101:8080/some/path", ++ "http://[::1]/some/path", ++ "http://[fc01:172:16:28::101]/some/path", ++ "http://[fc01:172:16:28::101]:80/some/path", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]/some/path", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/some/path", ++ "http://172.16.28.101/", ++ "http://[fc01:172:16:28:BAAD:BEEF:DEAD:101]:8080/", ++ ++ // "foo:@some.place", TODO - foo:@some.place is change to foo@some.place in the test ++ "foo:bar@some.place", ++ "foo:bar@some.place/", ++ "http://foo:bar@some.place", ++ "http://foo:bar@some.place/", ++ "http://foo:bar@[::1]:8080/", ++ "http://foo@[::1]", ++ ++ "mms://sm02.tsqa.example.com/0102rally.asf", ++ "pnm://foo:bar@some.place:80/path;params?query#fragment", ++ "rtsp://foo:bar@some.place:80/path;params?query#fragment", ++ "rtspu://foo:bar@some.place:80/path;params?query#fragment", ++ "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo", ++ "http://a.b.com/xx.jpg?newpath=http://bob.dave.com", + +- "foo:bar@some.place", "foo:bar@some.place/", "http://foo:bar@some.place", "http://foo:bar@some.place/", +- "http://foo:bar@[::1]:8080/", "http://foo@[::1]", ++ "ht-tp://a.b.com", ++ "ht+tp://a.b.com", ++ "ht.tp://a.b.com", + +- "mms://sm02.tsqa.example.com/0102rally.asf", "pnm://foo:bar@some.place:80/path;params?query#fragment", +- "rtsp://foo:bar@some.place:80/path;params?query#fragment", "rtspu://foo:bar@some.place:80/path;params?query#fragment", +- "/finance/external/cbsm/*http://cbs.marketwatch.com/archive/19990713/news/current/net.htx?source=blq/yhoo&dist=yhoo", +- "http://a.b.com/xx.jpg?newpath=http://bob.dave.com"}; ++ "h1ttp://a.b.com", ++ "http1://a.b.com", ++ }; + + static const char *bad[] = { + "http://[1:2:3:4:5:6:7:8:9]", + "http://1:2:3:4:5:6:7:8:A:B", + "http://bob.com[::1]", + "http://[::1].com", ++ + "http://foo:bar:baz@bob.com/", + "http://foo:bar:baz@[::1]:8080/", ++ + "http://]", + "http://:", ++ + "http:/", ++ "http:/foo.bar.com/", ++ "~http://invalid.char.in.scheme/foo", ++ "http~://invalid.char.in.scheme/foo", ++ "ht~tp://invalid.char.in.scheme/foo", ++ "1http://first.char.not.alpha", ++ "some.domain.com/http://invalid.domain/foo", ++ ":", ++ "://", ++ ++ // maybe this should be a valid URL ++ "a.b.com/xx.jpg?newpath=http://bob.dave.com", + }; + + int err, failed; +@@ -400,6 +456,7 @@ + url.create(nullptr); + err = url.parse(&start, end); + if (err < 0) { ++ printf("Failed to parse url '%s'\n", start); + failed = 1; + break; + } +@@ -448,6 +505,8 @@ + failed = 1; + printf("Successfully parsed invalid url '%s'", x); + break; ++ } else { ++ printf(" bad URL - PARSE FAILED: '%s'\n", bad[i]); + } + } + +--- trafficserver.orig/proxy/hdrs/URL.cc 2020-04-16 19:03:21.522369124 +0200 ++++ trafficserver/proxy/hdrs/URL.cc 2020-04-16 19:03:21.522369124 +0200 +@@ -1120,34 +1120,41 @@ + const char *scheme_end = nullptr; + int scheme_wks_idx; + ++ // Skip over spaces + while (' ' == *cur && ++cur < end) { +- ; + } ++ + if (cur < end) { + scheme_start = scheme_end = cur; +- // special case 'http:' for performance +- if ((end - cur >= 5) && (((cur[0] ^ 'h') | (cur[1] ^ 't') | (cur[2] ^ 't') | (cur[3] ^ 'p') | (cur[4] ^ ':')) == 0)) { +- scheme_end = cur + 4; // point to colon +- url_scheme_set(heap, url, scheme_start, URL_WKSIDX_HTTP, 4, copy_strings_p); +- } else if ('/' != *cur) { +- // For forward transparent mode, the URL for the method can just be a path, +- // so don't scan that for a scheme, as we could find a false positive if there +- // is a URL in the parameters (which is legal). ++ ++ // If the URL is more complex then a path, parse to see if there is a scheme ++ if ('/' != *cur) { ++ // Search for a : it could be part of a scheme or a username:password + while (':' != *cur && ++cur < end) { +- ; + } +- if (cur < end) { // found a colon +- scheme_wks_idx = hdrtoken_tokenize(scheme_start, cur - scheme_start, &scheme_wks); + +- /* Distinguish between a scheme only and a username by looking past the colon. If it is missing +- or it's a slash, presume scheme. Otherwise it's a username with a password. +- */ +- if ((scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME) || // known scheme +- (cur >= end - 1 || cur[1] == '/')) // no more data or slash past colon +- { +- scheme_end = cur; +- url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p); ++ // If there is a :// then there is a scheme ++ if (cur + 2 < end && cur[1] == '/' && cur[2] == '/') { // found "://" ++ scheme_end = cur; ++ scheme_wks_idx = hdrtoken_tokenize(scheme_start, scheme_end - scheme_start, &scheme_wks); ++ ++ if (!(scheme_wks_idx > 0 && hdrtoken_wks_to_token_type(scheme_wks) == HDRTOKEN_TYPE_SCHEME)) { ++ // Unknown scheme, validate the scheme ++ ++ // RFC 3986 Section 3.1 ++ // These are the valid characters in a scheme: ++ // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) ++ // return an error if there is another character in the scheme ++ if (!ParseRules::is_alpha(*scheme_start)) { ++ return PARSE_RESULT_ERROR; ++ } ++ for (cur = scheme_start + 1; cur < scheme_end; ++cur) { ++ if (!(ParseRules::is_alnum(*cur) != 0 || *cur == '+' || *cur == '-' || *cur == '.')) { ++ return PARSE_RESULT_ERROR; ++ } ++ } + } ++ url_scheme_set(heap, url, scheme_start, scheme_wks_idx, scheme_end - scheme_start, copy_strings_p); + } + } + *start = scheme_end; diff -Nru trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17565.patch trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17565.patch --- trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17565.patch 1970-01-01 00:00:00.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/patches/0016-CVE-2019-17565.patch 2020-04-16 19:36:40.000000000 +0000 @@ -0,0 +1,262 @@ +Description: Fix for CVE-2019-17565 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6398 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/iocore/net/UnixNetVConnection.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/iocore/net/UnixNetVConnection.cc 2020-04-16 19:12:51.964334223 +0200 +@@ -933,7 +933,7 @@ + try_to_write = 0; + + while (niov < NET_MAX_IOV) { +- int64_t wavail = towrite - total_written; ++ int64_t wavail = towrite - total_written - try_to_write; + int64_t len = tmp_reader->block_read_avail(); + + // Check if we have done this block. +--- trafficserver.orig/proxy/http/HttpTransact.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTransact.cc 2020-04-16 19:12:51.964334223 +0200 +@@ -6785,9 +6785,8 @@ + + // check that the client is HTTP 1.1 and the conf allows chunking or the client + // protocol unchunks before returning to the user agent (i.e. is http/2) +- if (s->client_info.http_version == HTTPVersion(1, 1) && +- (s->txn_conf->chunking_enabled == 1 || +- (s->state_machine->plugin_tag && (!strncmp(s->state_machine->plugin_tag, "http/2", 6)))) && ++ if (s->client_info.http_version == HTTPVersion(1, 1) && s->txn_conf->chunking_enabled == 1 && ++ s->state_machine->ua_txn->is_chunked_encoding_supported() && + // if we're not sending a body, don't set a chunked header regardless of server response + !is_response_body_precluded(s->hdr_info.client_response.status_get(), s->method) && + // we do not need chunked encoding for internal error messages +--- trafficserver.orig/proxy/http/HttpTunnel.cc 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTunnel.cc 2020-04-16 19:12:51.968334236 +0200 +@@ -871,10 +871,9 @@ + consumer_n = (producer_n = INT64_MAX); + } + +- // Do the IO on the consumers first so +- // data doesn't disappear out from +- // under the tunnel +- for (c = p->consumer_list.head; c;) { ++ // At least set up the consumer readers first so the data ++ // doesn't disappear out from under the tunnel ++ for (c = p->consumer_list.head; c; c = c->link.next) { + // Create a reader for each consumer. The reader allows + // us to implement skip bytes + if (c->vc_type == HT_CACHE_WRITE) { +@@ -905,45 +904,6 @@ + ink_assert(c->skip_bytes <= c->buffer_reader->read_avail()); + c->buffer_reader->consume(c->skip_bytes); + } +- int64_t c_write = consumer_n; +- +- // INKqa05109 - if we don't know the length leave it at +- // INT64_MAX or else the cache may bounce the write +- // because it thinks the document is too big. INT64_MAX +- // is a special case for the max document size code +- // in the cache +- if (c_write != INT64_MAX) { +- c_write -= c->skip_bytes; +- } +- // Fix for problems with not chunked content being chunked and +- // not sending the entire data. The content length grows when +- // it is being chunked. +- if (p->do_chunking == true) { +- c_write = INT64_MAX; +- } +- +- if (c_write == 0) { +- // Nothing to do, call back the cleanup handlers +- c->write_vio = nullptr; +- consumer_handler(VC_EVENT_WRITE_COMPLETE, c); +- } else { +- // In the client half close case, all the data that will be sent +- // from the client is already in the buffer. Go ahead and set +- // the amount to read since we know it. We will forward the FIN +- // to the server on VC_EVENT_WRITE_COMPLETE. +- if (p->vc_type == HT_HTTP_CLIENT) { +- ProxyClientTransaction *ua_vc = static_cast(p->vc); +- if (ua_vc->get_half_close_flag()) { +- c_write = c->buffer_reader->read_avail(); +- p->alive = false; +- p->handler_state = HTTP_SM_POST_SUCCESS; +- } +- } +- c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader); +- ink_assert(c_write > 0); +- } +- +- c = c->link.next; + } + + // YTS Team, yamsat Plugin +@@ -1003,7 +963,56 @@ + producer_n = 0; + } + } ++ for (c = p->consumer_list.head; c; c = c->link.next) { ++ int64_t c_write = consumer_n; + ++ if (!p->alive) { ++ // Adjust the amount of chunked data to write if the only data was in the initial read ++ // The amount to write in some cases is dependent on the type of the consumer, so this ++ // value must be computed for each consumer ++ c_write = final_consumer_bytes_to_write(p, c); ++ } else { ++ // INKqa05109 - if we don't know the length leave it at ++ // INT64_MAX or else the cache may bounce the write ++ // because it thinks the document is too big. INT64_MAX ++ // is a special case for the max document size code ++ // in the cache ++ if (c_write != INT64_MAX) { ++ c_write -= c->skip_bytes; ++ } ++ // Fix for problems with not chunked content being chunked and ++ // not sending the entire data. The content length grows when ++ // it is being chunked. ++ if (p->do_chunking == true) { ++ c_write = INT64_MAX; ++ } ++ } ++ ++ if (c_write == 0) { ++ // Nothing to do, call back the cleanup handlers ++ c->write_vio = nullptr; ++ consumer_handler(VC_EVENT_WRITE_COMPLETE, c); ++ } else { ++ // In the client half close case, all the data that will be sent ++ // from the client is already in the buffer. Go ahead and set ++ // the amount to read since we know it. We will forward the FIN ++ // to the server on VC_EVENT_WRITE_COMPLETE. ++ if (p->vc_type == HT_HTTP_CLIENT) { ++ ProxyClientTransaction *ua_vc = static_cast(p->vc); ++ if (ua_vc->get_half_close_flag()) { ++ int tmp = c->buffer_reader->read_avail(); ++ if (tmp < c_write) { ++ c_write = tmp; ++ } ++ p->alive = false; ++ p->handler_state = HTTP_SM_POST_SUCCESS; ++ } ++ } ++ // Start the writes now that we know we will consume all the initial data ++ c->write_vio = c->vc->do_io_write(this, c_write, c->buffer_reader); ++ ink_assert(c_write > 0); ++ } ++ } + if (p->alive) { + ink_assert(producer_n >= 0); + +@@ -1184,8 +1193,8 @@ + } + } // end of added logic for partial copy of POST + +- Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d", p->alive == true, +- sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event); ++ Debug("http_redirect", "[HttpTunnel::producer_handler] enable_redirection: [%d %d %d] event: %d, state: %d", p->alive == true, ++ sm->enable_redirection, (p->self_consumer && p->self_consumer->alive == true), event, p->chunked_handler.state); + + switch (event) { + case VC_EVENT_READ_READY: +@@ -1470,21 +1479,18 @@ + } + } + +-// void HttpTunnel::chain_finish_internal(HttpTunnelProducer* p) + // +-// Internal function for finishing all consumers. Takes +-// chain argument about where to finish just immediate +-// consumer or all those downstream ++// Determine the number of bytes a consumer should read from a producer + // +-void +-HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain) ++int64_t ++HttpTunnel::final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c) + { +- ink_assert(p->alive == false); +- HttpTunnelConsumer *c = p->consumer_list.head; +- int64_t total_bytes = 0; +- TunnelChunkingAction_t action = p->chunking_action; +- +- while (c) { ++ int64_t total_bytes = 0; ++ int64_t consumer_n = 0; ++ if (p->alive) { ++ consumer_n = INT64_MAX; ++ } else { ++ TunnelChunkingAction_t action = p->chunking_action; + if (c->alive) { + if (c->vc_type == HT_CACHE_WRITE) { + switch (action) { +@@ -1503,12 +1509,48 @@ + total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.chunked_size; + } else if (action == TCA_DECHUNK_CONTENT) { + total_bytes = p->chunked_handler.skip_bytes + p->chunked_handler.dechunked_size; ++ } else if (action == TCA_PASSTHRU_CHUNKED_CONTENT) { ++ total_bytes = p->bytes_read + p->init_bytes_done; + } else { + total_bytes = p->bytes_read + p->init_bytes_done; + } ++ consumer_n = total_bytes - c->skip_bytes; ++ } ++ } ++ return consumer_n; ++} ++ ++// ++// void HttpTunnel::finish_all_internal(HttpTunnelProducer* p) ++// ++// Internal function for finishing all consumers. Takes ++// chain argument about where to finish just immediate ++// consumer or all those downstream ++// ++void ++HttpTunnel::finish_all_internal(HttpTunnelProducer *p, bool chain) ++{ ++ ink_assert(p->alive == false); ++ HttpTunnelConsumer *c = p->consumer_list.head; ++ int64_t total_bytes = 0; ++ TunnelChunkingAction_t action = p->chunking_action; ++ ++ if (action == TCA_PASSTHRU_CHUNKED_CONTENT) { ++ // if the only chunked data was in the initial read, make sure we don't consume too much ++ if (p->bytes_read == 0) { ++ int num_read = p->buffer_start->read_avail() - p->chunked_handler.chunked_reader->read_avail(); ++ if (num_read < p->init_bytes_done) { ++ p->init_bytes_done = num_read; ++ } ++ } ++ } + ++ while (c) { ++ if (c->alive) { + if (c->write_vio) { +- c->write_vio->nbytes = total_bytes - c->skip_bytes; ++ // Adjust the number of bytes to write in the case of ++ // a completed unlimited producer ++ c->write_vio->nbytes = final_consumer_bytes_to_write(p, c); + ink_assert(c->write_vio->nbytes >= 0); + + if (c->write_vio->nbytes < 0) { +@@ -1525,7 +1567,7 @@ + // is nothing to do. Check to see if there is + // nothing to do and take the appripriate + // action +- if (c->write_vio && c->write_vio->nbytes == c->write_vio->ndone) { ++ if (c->write_vio && c->alive && c->write_vio->nbytes == c->write_vio->ndone) { + consumer_handler(VC_EVENT_WRITE_COMPLETE, c); + } + } +--- trafficserver.orig/proxy/http/HttpTunnel.h 2020-04-16 19:12:51.968334236 +0200 ++++ trafficserver/proxy/http/HttpTunnel.h 2020-04-16 19:12:51.968334236 +0200 +@@ -312,6 +312,7 @@ + void chain_abort_all(HttpTunnelProducer *p); + void abort_cache_write_finish_others(HttpTunnelProducer *p); + void append_message_to_producer_buffer(HttpTunnelProducer *p, const char *msg, int64_t msg_len); ++ int64_t final_consumer_bytes_to_write(HttpTunnelProducer *p, HttpTunnelConsumer *c); + + /** Mark a producer and consumer as the same underlying object. + diff -Nru trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-1944.patch trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-1944.patch --- trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-1944.patch 1970-01-01 00:00:00.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-1944.patch 2020-04-16 19:36:40.000000000 +0000 @@ -0,0 +1,48 @@ +Description: Fix for CVE-2020-1944 +Author: Bryan Call +Origin: backport +Applied-Upstream: https://github.com/apache/trafficserver/pull/6390 +Last-Update: 2020-04-16 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- trafficserver.orig/proxy/hdrs/HTTP.cc 2020-04-16 19:20:19.621738649 +0200 ++++ trafficserver/proxy/hdrs/HTTP.cc 2020-04-16 19:23:55.166369632 +0200 +@@ -1125,19 +1125,18 @@ + + end = real_end; + parser->m_parsing_http = false; +- +- ParseResult ret = mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); +- // If we're done with the main parse do some validation +- if (ret == PARSE_RESULT_DONE) { +- ret = validate_hdr_host(hh); // check HOST header +- } +- if (ret == PARSE_RESULT_DONE) { +- ret = validate_hdr_content_length(heap, hh); +- } +- return ret; + } + +- return mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); ++ ParseResult ret = ++ mime_parser_parse(&parser->m_mime_parser, heap, hh->m_fields_impl, start, end, must_copy_strings, eof); ++ // If we're done with the main parse do some validation ++ if (ret == PARSE_RESULT_DONE) { ++ ret = validate_hdr_host(hh); // check HOST header ++ } ++ if (ret == PARSE_RESULT_DONE) { ++ ret = validate_hdr_content_length(heap, hh); ++ } ++ return ret; + } + + ParseResult +@@ -1189,7 +1188,7 @@ + if (mime_hdr_field_find(hh->m_fields_impl, MIME_FIELD_TRANSFER_ENCODING, MIME_LEN_TRANSFER_ENCODING) != nullptr) { + // Delete all Content-Length headers + Debug("http", "Transfer-Encoding header and Content-Length headers the request, removing all Content-Length headers"); +- mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field); ++ mime_hdr_field_delete(heap, hh->m_fields_impl, content_length_field, true); + return PARSE_RESULT_DONE; + } + diff -Nru trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-9481.patch trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-9481.patch --- trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-9481.patch 1970-01-01 00:00:00.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/patches/0016-CVE-2020-9481.patch 2020-04-16 19:36:40.000000000 +0000 @@ -0,0 +1,318 @@ +Index: trafficserver/doc/admin-guide/files/records.config.en.rst +=================================================================== +--- trafficserver.orig/doc/admin-guide/files/records.config.en.rst 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/doc/admin-guide/files/records.config.en.rst 2020-04-16 21:35:49.123162682 +0200 +@@ -3493,7 +3493,7 @@ + :ts:cv:`proxy.config.http2.min_concurrent_streams_in`. + To disable, set to zero (``0``). + +-.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 1048576 ++.. ts:cv:: CONFIG proxy.config.http2.initial_window_size_in INT 65535 + :reloadable: + + The initial window size for inbound connections. +Index: trafficserver/mgmt/RecordsConfig.cc +=================================================================== +--- trafficserver.orig/mgmt/RecordsConfig.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/mgmt/RecordsConfig.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -1304,7 +1304,7 @@ + , + {RECT_CONFIG, "proxy.config.http2.max_active_streams_in", RECD_INT, "0", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , +- {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "1048576", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} ++ {RECT_CONFIG, "proxy.config.http2.initial_window_size_in", RECD_INT, "65535", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , + {RECT_CONFIG, "proxy.config.http2.max_frame_size", RECD_INT, "16384", RECU_DYNAMIC, RR_NULL, RECC_STR, "^[0-9]+$", RECA_NULL} + , +Index: trafficserver/proxy/http2/HTTP2.cc +=================================================================== +--- trafficserver.orig/proxy/http2/HTTP2.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/HTTP2.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -722,7 +722,7 @@ + uint32_t Http2::max_active_streams_in = 0; + bool Http2::throttling = false; + uint32_t Http2::stream_priority_enabled = 0; +-uint32_t Http2::initial_window_size = 1048576; ++uint32_t Http2::initial_window_size = 65535; + uint32_t Http2::max_frame_size = 16384; + uint32_t Http2::header_table_size = 4096; + uint32_t Http2::max_header_list_size = 4294967295; +Index: trafficserver/proxy/http2/Http2ClientSession.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ClientSession.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ClientSession.cc 2020-04-16 21:35:49.123162682 +0200 +@@ -348,11 +348,9 @@ + break; + + case VC_EVENT_WRITE_READY: +- retval = 0; +- break; +- + case VC_EVENT_WRITE_COMPLETE: +- // Seems as this is being closed already ++ this->connection_state.restart_streams(); ++ + retval = 0; + break; + +@@ -586,3 +584,9 @@ + // Do something else every 128 incoming frames + return (this->_n_frame_read & 0x7F) == 0; + } ++ ++int64_t ++Http2ClientSession::write_avail() ++{ ++ return this->write_buffer->write_avail(); ++} +Index: trafficserver/proxy/http2/Http2ClientSession.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ClientSession.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ClientSession.h 2020-04-16 21:35:49.123162682 +0200 +@@ -319,6 +319,8 @@ + return write_buffer->max_read_avail(); + } + ++ int64_t write_avail(); ++ + // noncopyable + Http2ClientSession(Http2ClientSession &) = delete; + Http2ClientSession &operator=(const Http2ClientSession &) = delete; +Index: trafficserver/proxy/http2/Http2ConnectionState.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ConnectionState.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ConnectionState.cc 2020-04-16 21:35:49.127162696 +0200 +@@ -151,6 +151,12 @@ + cstate.decrement_server_rwnd(payload_length); + stream->decrement_server_rwnd(payload_length); + ++ if (is_debug_tag_set("http2_con")) { ++ uint32_t rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); ++ Http2StreamDebug(cstate.ua_session, stream->get_id(), "Received DATA frame: rwnd con=%zd/%" PRId32 " stream=%zd/%" PRId32, ++ cstate.server_rwnd(), rwnd, stream->server_rwnd(), rwnd); ++ } ++ + const uint32_t unpadded_length = payload_length - pad_length; + // If we call write() multiple times, we must keep the same reader, so we can + // update its offset via consume. Otherwise, we will read the same data on the +@@ -172,21 +178,6 @@ + } + myreader->writer()->dealloc_reader(myreader); + +- uint32_t initial_rwnd = cstate.server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); +- uint32_t min_rwnd = std::min(initial_rwnd, cstate.server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); +- // Connection level WINDOW UPDATE +- if (cstate.server_rwnd() <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - cstate.server_rwnd(); +- cstate.increment_server_rwnd(diff_size); +- cstate.send_window_update_frame(0, diff_size); +- } +- // Stream level WINDOW UPDATE +- if (stream->server_rwnd() <= min_rwnd) { +- Http2WindowSize diff_size = initial_rwnd - stream->server_rwnd(); +- stream->increment_server_rwnd(diff_size); +- cstate.send_window_update_frame(stream->get_id(), diff_size); +- } +- + return Http2Error(Http2ErrorClass::HTTP2_ERROR_CLASS_NONE); + } + +@@ -1196,6 +1187,35 @@ + } + + void ++Http2ConnectionState::restart_receiving(Http2Stream *stream) ++{ ++ uint32_t initial_rwnd = this->server_settings.get(HTTP2_SETTINGS_INITIAL_WINDOW_SIZE); ++ uint32_t min_rwnd = std::min(initial_rwnd, this->server_settings.get(HTTP2_SETTINGS_MAX_FRAME_SIZE)); ++ ++ // Connection level WINDOW UPDATE ++ if (this->server_rwnd() < min_rwnd) { ++ Http2WindowSize diff_size = initial_rwnd - this->server_rwnd(); ++ this->increment_server_rwnd(diff_size); ++ this->send_window_update_frame(0, diff_size); ++ } ++ ++ // Stream level WINDOW UPDATE ++ if (stream == nullptr || stream->server_rwnd() >= min_rwnd) { ++ return; ++ } ++ ++ // If read_vio is buffering data, do not fully update window ++ int64_t data_size = stream->read_vio_read_avail(); ++ if (data_size >= initial_rwnd) { ++ return; ++ } ++ ++ Http2WindowSize diff_size = initial_rwnd - std::max(static_cast(stream->server_rwnd()), data_size); ++ stream->increment_server_rwnd(diff_size); ++ this->send_window_update_frame(stream->get_id(), diff_size); ++} ++ ++void + Http2ConnectionState::cleanup_streams() + { + Http2Stream *s = stream_list.head; +@@ -1415,6 +1435,12 @@ + return Http2SendDataFrameResult::ERROR; + } + ++ SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); ++ if (this->ua_session->write_avail() == 0) { ++ Http2StreamDebug(this->ua_session, stream->get_id(), "Not write avail"); ++ return Http2SendDataFrameResult::NOT_WRITE_AVAIL; ++ } ++ + // Select appropriate payload length + if (read_available_size > 0) { + // We only need to check for window size when there is a payload +@@ -1458,7 +1484,6 @@ + current_reader->consume(payload_length); + + // xmit event +- SCOPED_MUTEX_LOCK(lock, this->ua_session->mutex, this_ethread()); + this->ua_session->handleEvent(HTTP2_SESSION_EVENT_XMIT, &data); + + if (flags & HTTP2_FLAGS_DATA_END_STREAM) { +@@ -1845,7 +1870,7 @@ + void + Http2ConnectionState::send_window_update_frame(Http2StreamId id, uint32_t size) + { +- Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame"); ++ Http2StreamDebug(ua_session, id, "Send WINDOW_UPDATE frame: size=%" PRIu32, size); + + // Create WINDOW_UPDATE frame + Http2Frame window_update(HTTP2_FRAME_TYPE_WINDOW_UPDATE, id, 0x0); +Index: trafficserver/proxy/http2/Http2ConnectionState.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2ConnectionState.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2ConnectionState.h 2020-04-16 21:35:49.127162696 +0200 +@@ -34,6 +34,7 @@ + NO_ERROR = 0, + NO_WINDOW, + NO_PAYLOAD, ++ NOT_WRITE_AVAIL, + ERROR, + DONE, + }; +@@ -128,6 +129,8 @@ + void + init() + { ++ this->_server_rwnd = Http2::initial_window_size; ++ + local_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); + remote_hpack_handle = new HpackHandle(HTTP2_HEADER_TABLE_SIZE); + dependency_tree = new DependencyTree(Http2::max_concurrent_streams_in); +@@ -171,6 +174,7 @@ + void release_stream(Http2Stream *stream); + void cleanup_streams(); + ++ void restart_receiving(Http2Stream *stream); + void update_initial_rwnd(Http2WindowSize new_size); + + Http2StreamId +@@ -357,7 +361,7 @@ + + // Connection level window size + ssize_t _client_rwnd = HTTP2_INITIAL_WINDOW_SIZE; +- ssize_t _server_rwnd = Http2::initial_window_size; ++ ssize_t _server_rwnd = 0; + + std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; + int _recent_rwnd_increment_index = 0; +Index: trafficserver/proxy/http2/Http2Stream.cc +=================================================================== +--- trafficserver.orig/proxy/http2/Http2Stream.cc 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2Stream.cc 2020-04-16 21:35:49.127162696 +0200 +@@ -524,6 +524,19 @@ + void + Http2Stream::restart_sending() + { ++ if (!this->response_header_done) { ++ return; ++ } ++ ++ IOBufferReader *reader = this->response_get_data_reader(); ++ if (reader && !reader->is_read_avail_more_than(0)) { ++ return; ++ } ++ ++ if (this->write_vio.mutex && this->write_vio.ntodo() == 0) { ++ return; ++ } ++ + this->send_response_body(true); + } + +@@ -705,6 +718,12 @@ + SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); + update_write_request(vio->get_reader(), INT64_MAX, true); + } else if (vio->op == VIO::READ) { ++ Http2ClientSession *h2_proxy_ssn = static_cast(this->get_parent()); ++ { ++ SCOPED_MUTEX_LOCK(lock, h2_proxy_ssn->connection_state.mutex, this_ethread()); ++ h2_proxy_ssn->connection_state.restart_receiving(this); ++ } ++ + SCOPED_MUTEX_LOCK(lock, this->mutex, this_ethread()); + update_read_request(INT64_MAX, true); + } +@@ -964,3 +983,14 @@ + current_reader = nullptr; // State machine is on its own way down. + this->do_io_close(); + } ++ ++int64_t ++Http2Stream::read_vio_read_avail() ++{ ++ MIOBuffer *writer = this->read_vio.get_writer(); ++ if (writer) { ++ return writer->max_read_avail(); ++ } ++ ++ return 0; ++} +Index: trafficserver/proxy/http2/Http2Stream.h +=================================================================== +--- trafficserver.orig/proxy/http2/Http2Stream.h 2020-04-16 21:35:49.127162696 +0200 ++++ trafficserver/proxy/http2/Http2Stream.h 2020-04-16 21:35:49.127162696 +0200 +@@ -38,10 +38,7 @@ + { + public: + typedef ProxyClientTransaction super; ///< Parent type. +- Http2Stream(Http2StreamId sid = 0, ssize_t initial_rwnd = Http2::initial_window_size) : _id(sid), _client_rwnd(initial_rwnd) +- { +- SET_HANDLER(&Http2Stream::main_event_handler); +- } ++ Http2Stream() { SET_HANDLER(&Http2Stream::main_event_handler); } + + void + init(Http2StreamId sid, ssize_t initial_rwnd) +@@ -50,6 +47,7 @@ + _start_time = Thread::get_hrtime(); + _thread = this_ethread(); + this->_client_rwnd = initial_rwnd; ++ this->_server_rwnd = Http2::initial_window_size; + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_CURRENT_CLIENT_STREAM_COUNT, _thread); + HTTP2_INCREMENT_THREAD_DYN_STAT(HTTP2_STAT_TOTAL_CLIENT_STREAM_COUNT, _thread); + sm_reader = request_reader = request_buffer.alloc_reader(); +@@ -177,6 +175,7 @@ + ssize_t server_rwnd() const; + Http2ErrorCode increment_server_rwnd(size_t amount); + Http2ErrorCode decrement_server_rwnd(size_t amount); ++ int64_t read_vio_read_avail(); + + uint8_t *header_blocks = nullptr; + uint32_t header_blocks_length = 0; // total length of header blocks (not include +@@ -292,8 +291,8 @@ + uint64_t data_length = 0; + uint64_t bytes_sent = 0; + +- ssize_t _client_rwnd; +- ssize_t _server_rwnd = Http2::initial_window_size; ++ ssize_t _client_rwnd = 0; ++ ssize_t _server_rwnd = 0; + + std::vector _recent_rwnd_increment = {SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX, SIZE_MAX}; + int _recent_rwnd_increment_index = 0; diff -Nru trafficserver-8.0.2+ds/debian/patches/series trafficserver-8.0.2+ds/debian/patches/series --- trafficserver-8.0.2+ds/debian/patches/series 2019-08-26 11:55:33.000000000 +0000 +++ trafficserver-8.0.2+ds/debian/patches/series 2020-04-16 19:36:40.000000000 +0000 @@ -9,3 +9,7 @@ 0014-use_system_yaml-cpp.patch 0015-8.0.4-CVE-backport.patch 0015-8.0.5-CVE-backport.patch +0016-CVE-2019-17559.patch +0016-CVE-2019-17565.patch +0016-CVE-2020-1944.patch +0016-CVE-2020-9481.patch