diff -Nru apache2-2.4.41/debian/changelog apache2-2.4.41/debian/changelog --- apache2-2.4.41/debian/changelog 2021-06-17 18:27:53.000000000 +0000 +++ apache2-2.4.41/debian/changelog 2021-09-23 16:58:57.000000000 +0000 @@ -1,3 +1,52 @@ +apache2 (2.4.41-4ubuntu3.5) focal-security; urgency=medium + + * SECURITY UPDATE: request splitting over HTTP/2 + - debian/patches/CVE-2021-33193-pre1.patch: process early errors via a + dummy HTTP/1.1 request as well in modules/http2/h2.h, + modules/http2/h2_request.c, modules/http2/h2_session.c, + modules/http2/h2_stream.c. + - debian/patches/CVE-2021-33193-pre2.patch: sync with github standalone + version 1.15.17 in modules/http2/h2_bucket_beam.c, + modules/http2/h2_config.c, modules/http2/h2_config.h, + modules/http2/h2_h2.c, modules/http2/h2_headers.c, + modules/http2/h2_headers.h, modules/http2/h2_mplx.c, + modules/http2/h2_request.c, modules/http2/h2_stream.h, + modules/http2/h2_task.c, modules/http2/h2_task.h, + modules/http2/h2_version.h. + - debian/patches/CVE-2021-33193.patch: refactor request parsing in + include/ap_mmn.h, include/http_core.h, include/http_protocol.h, + include/http_vhost.h, modules/http2/h2_request.c, server/core.c, + server/core_filters.c, server/protocol.c, server/vhost.c. + - CVE-2021-33193 + * SECURITY UPDATE: NULL deref via malformed requests + - debian/patches/CVE-2021-34798.patch: add NULL check in + server/scoreboard.c. + - CVE-2021-34798 + * SECURITY UPDATE: DoS in mod_proxy_uwsgi + - debian/patches/CVE-2021-36160.patch: fix PATH_INFO setting for + generic worker in modules/proxy/mod_proxy_uwsgi.c. + - CVE-2021-36160 + * SECURITY UPDATE: buffer overflow in ap_escape_quotes + - debian/patches/CVE-2021-39275.patch: fix ap_escape_quotes + substitution logic in server/util.c. + - CVE-2021-39275 + * SECURITY UPDATE: arbitrary origin server via crafted request uri-path + - debian/patches/CVE-2021-40438-pre1.patch: faster unix socket path + parsing in the "proxy:" URL in modules/proxy/mod_proxy.c, + modules/proxy/proxy_util.c. + - debian/patches/CVE-2021-40438.patch: add sanity checks on the + configured UDS path in modules/proxy/proxy_util.c. + - CVE-2021-40438 + + -- Marc Deslauriers Thu, 23 Sep 2021 12:58:57 -0400 + +apache2 (2.4.41-4ubuntu3.4) focal; urgency=medium + + * d/p/lp-1930430-Backport-r1865740.patch: fix OCSP in proxy mode + (LP: #1930430) + + -- Christian Ehrhardt Mon, 05 Jul 2021 09:16:56 +0200 + apache2 (2.4.41-4ubuntu3.3) focal-security; urgency=medium * SECURITY UPDATE: mod_proxy_http denial of service. diff -Nru apache2-2.4.41/debian/patches/CVE-2021-33193.patch apache2-2.4.41/debian/patches/CVE-2021-33193.patch --- apache2-2.4.41/debian/patches/CVE-2021-33193.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-33193.patch 2021-09-23 16:58:57.000000000 +0000 @@ -0,0 +1,918 @@ +Backport of: + +From ecebcc035ccd8d0e2984fe41420d9e944f456b3c Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Thu, 27 May 2021 13:08:21 +0000 +Subject: [PATCH] Merged + r1734009,r1734231,r1734281,r1838055,r1838079,r1840229,r1876664,r1876674,r1876784,r1879078,r1881620,r1887311,r1888871 + from trunk: + + *) core: Split ap_create_request() from ap_read_request(). [Graham Leggett] + + *) core, h2: common ap_parse_request_line() and ap_check_request_header() + code. [Yann Ylavic] + + *) core: Add StrictHostCheck to allow unconfigured hostnames to be + rejected. [Eric Covener] + + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1890245 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 8 + + docs/manual/mod/core.xml | 36 ++++ + include/ap_mmn.h | 5 +- + include/http_core.h | 6 + + include/http_protocol.h | 21 +++ + include/http_vhost.h | 13 ++ + modules/http2/h2_request.c | 108 +----------- + server/core.c | 14 +- + server/core_filters.c | 5 - + server/protocol.c | 328 +++++++++++++++++++++++-------------- + server/vhost.c | 38 ++++- + 11 files changed, 337 insertions(+), 245 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index 7256c1db243..484877551ed 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,14 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.4.49 +# +#+ *) core: Split ap_create_request() from ap_read_request(). [Graham Leggett] +#+ +#+ *) core, h2: common ap_parse_request_line() and ap_check_request_header() +#+ code. [Yann Ylavic] +#+ +#+ *) core: Add StrictHostCheck to allow unconfigured hostnames to be +#+ rejected. [Eric Covener] +#+ +# Changes with Apache 2.4.48 +# +# *) mod_proxy_wstunnel: Add ProxyWebsocketFallbackToProxyHttp to opt-out the +#diff --git a/docs/manual/mod/core.xml b/docs/manual/mod/core.xml +#index 6b9f1f03859..b576c532fce 100644 +#--- a/docs/manual/mod/core.xml +#+++ b/docs/manual/mod/core.xml +#@@ -5206,6 +5206,42 @@ recognized methods to modules.

+# AllowMethods +# +# +#+ +#+StrictHostCheck +#+Controls whether the server requires the requested hostname be +#+ listed enumerated in the virtual host handling the request +#+ +#+StrictHostCheck ON|OFF +#+StrictHostCheck OFF +#+server configvirtual host +#+ +#+Added in 2.5.1 +#+ +#+ +#+

By default, the server will respond to requests for any hostname, +#+ including requests addressed to unexpected or unconfigured hostnames. +#+ While this is convenient, it is sometimes desirable to limit what hostnames +#+ a backend application handles since it will often generate self-referential +#+ responses.

+#+ +#+

By setting StrictHostCheck to ON, +#+ the server will return an HTTP 400 error if the requested hostname +#+ hasn't been explicitly listed by either ServerName or ServerAlias in the virtual host that best matches the +#+ details of the incoming connection.

+#+ +#+

This directive also allows matching of the requested hostname to hostnames +#+ specified within the opening VirtualHost +#+ tag, which is a relatively obscure configuration mechanism that acts like +#+ additional ServerAlias entries.

+#+ +#+

This directive has no affect in non-default virtual hosts. The value +#+ inherited from the global server configuration, or the default virtualhost +#+ for the ip:port the underlying connection, determine the effective value.

+#+
+#+
+#+ +# +# MergeSlashes +# Controls whether the server merges consecutive slashes in URLs. +--- a/include/http_core.h ++++ b/include/http_core.h +@@ -741,6 +741,7 @@ typedef struct { + #define AP_HTTP_METHODS_REGISTERED 2 + char http_methods; + unsigned int merge_slashes; ++ unsigned int strict_host_check; + } core_server_config; + + /* for AddOutputFiltersByType in core.c */ +@@ -769,6 +770,11 @@ AP_DECLARE(void) ap_set_server_protocol( + typedef struct core_output_filter_ctx core_output_filter_ctx_t; + typedef struct core_filter_ctx core_ctx_t; + ++struct core_filter_ctx { ++ apr_bucket_brigade *b; ++ apr_bucket_brigade *tmpbb; ++}; ++ + typedef struct core_net_rec { + /** Connection to the client */ + apr_socket_t *client_socket; +--- a/include/http_protocol.h ++++ b/include/http_protocol.h +@@ -54,6 +54,13 @@ AP_DECLARE_DATA extern ap_filter_rec_t * + */ + + /** ++ * Read an empty request and set reasonable defaults. ++ * @param c The current connection ++ * @return The new request_rec ++ */ ++AP_DECLARE(request_rec *) ap_create_request(conn_rec *c); ++ ++/** + * Read a request and fill in the fields. + * @param c The current connection + * @return The new request_rec +@@ -61,6 +68,20 @@ AP_DECLARE_DATA extern ap_filter_rec_t * + request_rec *ap_read_request(conn_rec *c); + + /** ++ * Parse and validate the request line. ++ * @param r The current request ++ * @return 1 on success, 0 on failure ++ */ ++AP_DECLARE(int) ap_parse_request_line(request_rec *r); ++ ++/** ++ * Validate the request header and select vhost. ++ * @param r The current request ++ * @return 1 on success, 0 on failure ++ */ ++AP_DECLARE(int) ap_check_request_header(request_rec *r); ++ ++/** + * Read the mime-encoded headers. + * @param r The current request + */ +--- a/include/http_vhost.h ++++ b/include/http_vhost.h +@@ -100,6 +100,19 @@ AP_DECLARE(void) ap_update_vhost_given_i + AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r); + + /** ++ * Updates r->server with the best name-based virtual host match, within ++ * the chain of matching virtual hosts selected by ap_update_vhost_given_ip. ++ * @param r The current request ++ * @param require_match 1 to return an HTTP error if the requested hostname is ++ * not explicitly matched to a VirtualHost. ++ * @return return HTTP_OK unless require_match was specified and the requested ++ * hostname did not match any ServerName, ServerAlias, or VirtualHost ++ * address-spec. ++ */ ++AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match); ++ ++ ++/** + * Match the host in the header with the hostname of the server for this + * request. + * @param r The current request +--- a/modules/http2/h2_request.c ++++ b/modules/http2/h2_request.c +@@ -207,75 +207,12 @@ h2_request *h2_request_clone(apr_pool_t + return dst; + } + +-#if !AP_MODULE_MAGIC_AT_LEAST(20150222, 13) +-static request_rec *my_ap_create_request(conn_rec *c) +-{ +- apr_pool_t *p; +- request_rec *r; +- +- apr_pool_create(&p, c->pool); +- apr_pool_tag(p, "request"); +- r = apr_pcalloc(p, sizeof(request_rec)); +- AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)c); +- r->pool = p; +- r->connection = c; +- r->server = c->base_server; +- +- r->user = NULL; +- r->ap_auth_type = NULL; +- +- r->allowed_methods = ap_make_method_list(p, 2); +- +- r->headers_in = apr_table_make(r->pool, 5); +- r->trailers_in = apr_table_make(r->pool, 5); +- r->subprocess_env = apr_table_make(r->pool, 25); +- r->headers_out = apr_table_make(r->pool, 12); +- r->err_headers_out = apr_table_make(r->pool, 5); +- r->trailers_out = apr_table_make(r->pool, 5); +- r->notes = apr_table_make(r->pool, 5); +- +- r->request_config = ap_create_request_config(r->pool); +- /* Must be set before we run create request hook */ +- +- r->proto_output_filters = c->output_filters; +- r->output_filters = r->proto_output_filters; +- r->proto_input_filters = c->input_filters; +- r->input_filters = r->proto_input_filters; +- ap_run_create_request(r); +- r->per_dir_config = r->server->lookup_defaults; +- +- r->sent_bodyct = 0; /* bytect isn't for body */ +- +- r->read_length = 0; +- r->read_body = REQUEST_NO_BODY; +- +- r->status = HTTP_OK; /* Until further notice */ +- r->header_only = 0; +- r->the_request = NULL; +- +- /* Begin by presuming any module can make its own path_info assumptions, +- * until some module interjects and changes the value. +- */ +- r->used_path_info = AP_REQ_DEFAULT_PATH_INFO; +- +- r->useragent_addr = c->client_addr; +- r->useragent_ip = c->client_ip; +- +- return r; +-} +-#endif +- + request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) + { +- int access_status; ++ int access_status = HTTP_OK; + +-#if AP_MODULE_MAGIC_AT_LEAST(20150222, 13) + request_rec *r = ap_create_request(c); +-#else +- request_rec *r = my_ap_create_request(c); +-#endif + +-#if AP_MODULE_MAGIC_AT_LEAST(20200331, 3) + ap_run_pre_read_request(r, c); + + /* Time to populate r with the data we have. */ +@@ -304,49 +241,6 @@ request_rec *h2_request_create_rec(const + r->status = HTTP_OK; + goto die; + } +-#else +- { +- const char *s; +- +- r->headers_in = apr_table_clone(r->pool, req->headers); +- ap_run_pre_read_request(r, c); +- +- /* Time to populate r with the data we have. */ +- r->request_time = req->request_time; +- r->method = apr_pstrdup(r->pool, req->method); +- /* Provide quick information about the request method as soon as known */ +- r->method_number = ap_method_number_of(r->method); +- if (r->method_number == M_GET && r->method[0] == 'H') { +- r->header_only = 1; +- } +- ap_parse_uri(r, req->path ? req->path : ""); +- r->protocol = (char*)"HTTP/2.0"; +- r->proto_num = HTTP_VERSION(2, 0); +- r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", +- r->method, req->path ? req->path : ""); +- +- /* Start with r->hostname = NULL, ap_check_request_header() will get it +- * form Host: header, otherwise we get complains about port numbers. +- */ +- r->hostname = NULL; +- ap_update_vhost_from_headers(r); +- +- /* we may have switched to another server */ +- r->per_dir_config = r->server->lookup_defaults; +- +- s = apr_table_get(r->headers_in, "Expect"); +- if (s && s[0]) { +- if (ap_cstr_casecmp(s, "100-continue") == 0) { +- r->expecting_100 = 1; +- } +- else { +- r->status = HTTP_EXPECTATION_FAILED; +- access_status = r->status; +- goto die; +- } +- } +- } +-#endif + + /* we may have switched to another server */ + r->per_dir_config = r->server->lookup_defaults; +--- a/server/core.c ++++ b/server/core.c +@@ -492,6 +492,8 @@ static void *create_core_server_config(a + conf->protocols_honor_order = -1; + conf->merge_slashes = AP_CORE_CONFIG_UNSET; + ++ conf->strict_host_check= AP_CORE_CONFIG_UNSET; ++ + return (void *)conf; + } + +@@ -558,6 +560,12 @@ static void *merge_core_server_configs(a + virt->protocols_honor_order); + AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt); + ++ conf->strict_host_check = (virt->strict_host_check != AP_CORE_CONFIG_UNSET) ++ ? virt->strict_host_check ++ : base->strict_host_check; ++ ++ AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt); ++ + return conf; + } + +@@ -4510,6 +4518,10 @@ AP_INIT_FLAG("QualifyRedirectURL", set_q + "Controls whether HTTP authorization headers, normally hidden, will " + "be passed to scripts"), + ++AP_INIT_FLAG("StrictHostCheck", set_core_server_flag, ++ (void *)APR_OFFSETOF(core_server_config, strict_host_check), ++ RSRC_CONF, ++ "Controls whether a hostname match is required"), + AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower, + (void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO, + "a mime type that overrides other configured type"), +@@ -5483,4 +5495,3 @@ AP_DECLARE_MODULE(core) = { + core_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ + }; +- +--- a/server/core_filters.c ++++ b/server/core_filters.c +@@ -84,11 +84,6 @@ struct core_output_filter_ctx { + apr_size_t bytes_written; + }; + +-struct core_filter_ctx { +- apr_bucket_brigade *b; +- apr_bucket_brigade *tmpbb; +-}; +- + + apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b, + ap_input_mode_t mode, apr_read_type_e block, +--- a/server/protocol.c ++++ b/server/protocol.c +@@ -609,8 +609,15 @@ AP_CORE_DECLARE(void) ap_parse_uri(reque + } + + r->args = r->parsed_uri.query; +- r->uri = r->parsed_uri.path ? r->parsed_uri.path +- : apr_pstrdup(r->pool, "/"); ++ if (r->parsed_uri.path) { ++ r->uri = r->parsed_uri.path; ++ } ++ else if (r->method_number == M_OPTIONS) { ++ r->uri = apr_pstrdup(r->pool, "*"); ++ } ++ else { ++ r->uri = apr_pstrdup(r->pool, "/"); ++ } + + #if defined(OS2) || defined(WIN32) + /* Handle path translations for OS/2 and plug security hole. +@@ -645,13 +652,6 @@ static int field_name_len(const char *fi + + static int read_request_line(request_rec *r, apr_bucket_brigade *bb) + { +- enum { +- rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, +- rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext, +- rrl_badmethod09, rrl_reject09 +- } deferred_error = rrl_none; +- char *ll; +- char *uri; + apr_size_t len; + int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES; + core_server_config *conf = ap_get_core_module_config(r->server->module_config); +@@ -711,6 +711,20 @@ static int read_request_line(request_rec + } + + r->request_time = apr_time_now(); ++ return 1; ++} ++ ++AP_DECLARE(int) ap_parse_request_line(request_rec *r) ++{ ++ core_server_config *conf = ap_get_core_module_config(r->server->module_config); ++ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE); ++ enum { ++ rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace, ++ rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext, ++ rrl_badmethod09, rrl_reject09 ++ } deferred_error = rrl_none; ++ apr_size_t len = 0; ++ char *uri, *ll; + + r->method = r->the_request; + +@@ -742,7 +756,6 @@ static int read_request_line(request_rec + if (deferred_error == rrl_none) + deferred_error = rrl_missinguri; + r->protocol = uri = ""; +- len = 0; + goto rrl_done; + } + else if (strict && ll[0] && apr_isspace(ll[1]) +@@ -773,7 +786,6 @@ static int read_request_line(request_rec + /* Verify URI terminated with a single SP, or mark as specific error */ + if (!ll) { + r->protocol = ""; +- len = 0; + goto rrl_done; + } + else if (strict && ll[0] && apr_isspace(ll[1]) +@@ -866,6 +878,14 @@ rrl_done: + r->header_only = 1; + + ap_parse_uri(r, uri); ++ if (r->status == HTTP_OK ++ && (r->parsed_uri.path != NULL) ++ && (r->parsed_uri.path[0] != '/') ++ && (r->method_number != M_OPTIONS ++ || strcmp(r->parsed_uri.path, "*") != 0)) { ++ /* Invalid request-target per RFC 7230 section 5.3 */ ++ r->status = HTTP_BAD_REQUEST; ++ } + + /* With the request understood, we can consider HTTP/0.9 specific errors */ + if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) { +@@ -973,6 +993,79 @@ rrl_failed: + return 0; + } + ++AP_DECLARE(int) ap_check_request_header(request_rec *r) ++{ ++ core_server_config *conf; ++ int strict_host_check; ++ const char *expect; ++ int access_status; ++ ++ conf = ap_get_core_module_config(r->server->module_config); ++ ++ /* update what we think the virtual host is based on the headers we've ++ * now read. may update status. ++ */ ++ strict_host_check = (conf->strict_host_check == AP_CORE_CONFIG_ON); ++ access_status = ap_update_vhost_from_headers_ex(r, strict_host_check); ++ if (strict_host_check && access_status != HTTP_OK) { ++ if (r->server == ap_server_conf) { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10156) ++ "Requested hostname '%s' did not match any ServerName/ServerAlias " ++ "in the global server configuration ", r->hostname); ++ } ++ else { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10157) ++ "Requested hostname '%s' did not match any ServerName/ServerAlias " ++ "in the matching virtual host (default vhost for " ++ "current connection is %s:%u)", ++ r->hostname, r->server->defn_name, r->server->defn_line_number); ++ } ++ r->status = access_status; ++ } ++ if (r->status != HTTP_OK) { ++ return 0; ++ } ++ ++ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1))) ++ || ((r->proto_num == HTTP_VERSION(1, 1)) ++ && !apr_table_get(r->headers_in, "Host"))) { ++ /* ++ * Client sent us an HTTP/1.1 or later request without telling us the ++ * hostname, either with a full URL or a Host: header. We therefore ++ * need to (as per the 1.1 spec) send an error. As a special case, ++ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain ++ * a Host: header, and the server MUST respond with 400 if it doesn't. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569) ++ "client sent HTTP/1.1 request without hostname " ++ "(see RFC2616 section 14.23): %s", r->uri); ++ r->status = HTTP_BAD_REQUEST; ++ return 0; ++ } ++ ++ if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) ++ && (expect[0] != '\0')) { ++ /* ++ * The Expect header field was added to HTTP/1.1 after RFC 2068 ++ * as a means to signal when a 100 response is desired and, ++ * unfortunately, to signal a poor man's mandatory extension that ++ * the server must understand or return 417 Expectation Failed. ++ */ ++ if (ap_cstr_casecmp(expect, "100-continue") == 0) { ++ r->expecting_100 = 1; ++ } ++ else { ++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570) ++ "client sent an unrecognized expectation value " ++ "of Expect: %s", expect); ++ r->status = HTTP_EXPECTATION_FAILED; ++ return 0; ++ } ++ } ++ ++ return 1; ++} ++ + static int table_do_fn_check_lengths(void *r_, const char *key, + const char *value) + { +@@ -1256,16 +1349,10 @@ AP_DECLARE(void) ap_get_mime_headers(req + apr_brigade_destroy(tmp_bb); + } + +-request_rec *ap_read_request(conn_rec *conn) ++AP_DECLARE(request_rec *) ap_create_request(conn_rec *conn) + { + request_rec *r; + apr_pool_t *p; +- const char *expect; +- int access_status; +- apr_bucket_brigade *tmp_bb; +- apr_socket_t *csd; +- apr_interval_time_t cur_timeout; +- + + apr_pool_create(&p, conn->pool); + apr_pool_tag(p, "request"); +@@ -1304,6 +1391,7 @@ request_rec *ap_read_request(conn_rec *c + r->read_body = REQUEST_NO_BODY; + + r->status = HTTP_OK; /* Until further notice */ ++ r->header_only = 0; + r->the_request = NULL; + + /* Begin by presuming any module can make its own path_info assumptions, +@@ -1314,12 +1402,34 @@ request_rec *ap_read_request(conn_rec *c + r->useragent_addr = conn->client_addr; + r->useragent_ip = conn->client_ip; + ++ return r; ++} ++ ++/* Apply the server's timeout/config to the connection/request. */ ++static void apply_server_config(request_rec *r) ++{ ++ apr_socket_t *csd; ++ ++ csd = ap_get_conn_socket(r->connection); ++ apr_socket_timeout_set(csd, r->server->timeout); ++ ++ r->per_dir_config = r->server->lookup_defaults; ++} ++ ++request_rec *ap_read_request(conn_rec *conn) ++{ ++ int access_status; ++ apr_bucket_brigade *tmp_bb; ++ ++ request_rec *r = ap_create_request(conn); ++ + tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + + ap_run_pre_read_request(r, conn); + + /* Get the request... */ +- if (!read_request_line(r, tmp_bb)) { ++ if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) { ++ apr_brigade_cleanup(tmp_bb); + switch (r->status) { + case HTTP_REQUEST_URI_TOO_LARGE: + case HTTP_BAD_REQUEST: +@@ -1335,49 +1445,38 @@ request_rec *ap_read_request(conn_rec *c + "request failed: malformed request line"); + } + access_status = r->status; +- r->status = HTTP_OK; +- ap_die(access_status, r); +- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- r = NULL; +- apr_brigade_destroy(tmp_bb); +- goto traceout; ++ goto die_unusable_input; ++ + case HTTP_REQUEST_TIME_OUT: ++ /* Just log, no further action on this connection. */ + ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL); + if (!r->connection->keepalives) + ap_run_log_transaction(r); +- apr_brigade_destroy(tmp_bb); +- goto traceout; +- default: +- apr_brigade_destroy(tmp_bb); +- r = NULL; +- goto traceout; ++ break; + } ++ /* Not worth dying with. */ ++ conn->keepalive = AP_CONN_CLOSE; ++ apr_pool_destroy(r->pool); ++ goto ignore; + } ++ apr_brigade_cleanup(tmp_bb); + + /* We may have been in keep_alive_timeout mode, so toggle back + * to the normal timeout mode as we fetch the header lines, + * as necessary. + */ +- csd = ap_get_conn_socket(conn); +- apr_socket_timeout_get(csd, &cur_timeout); +- if (cur_timeout != conn->base_server->timeout) { +- apr_socket_timeout_set(csd, conn->base_server->timeout); +- cur_timeout = conn->base_server->timeout; +- } ++ apply_server_config(r); + + if (!r->assbackwards) { + const char *tenc; + + ap_get_mime_headers_core(r, tmp_bb); ++ apr_brigade_cleanup(tmp_bb); + if (r->status != HTTP_OK) { + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567) + "request failed: error reading the headers"); +- ap_send_error_response(r, 0); +- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- apr_brigade_destroy(tmp_bb); +- goto traceout; ++ access_status = r->status; ++ goto die_unusable_input; + } + + tenc = apr_table_get(r->headers_in, "Transfer-Encoding"); +@@ -1393,13 +1492,8 @@ request_rec *ap_read_request(conn_rec *c + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539) + "client sent unknown Transfer-Encoding " + "(%s): %s", tenc, r->uri); +- r->status = HTTP_BAD_REQUEST; +- conn->keepalive = AP_CONN_CLOSE; +- ap_send_error_response(r, 0); +- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- apr_brigade_destroy(tmp_bb); +- goto traceout; ++ access_status = HTTP_BAD_REQUEST; ++ goto die_unusable_input; + } + + /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23 +@@ -1412,88 +1506,79 @@ request_rec *ap_read_request(conn_rec *c + } + } + +- apr_brigade_destroy(tmp_bb); +- +- /* update what we think the virtual host is based on the headers we've +- * now read. may update status. +- */ +- ap_update_vhost_from_headers(r); +- access_status = r->status; +- +- /* Toggle to the Host:-based vhost's timeout mode to fetch the +- * request body and send the response body, if needed. +- */ +- if (cur_timeout != r->server->timeout) { +- apr_socket_timeout_set(csd, r->server->timeout); +- cur_timeout = r->server->timeout; +- } +- +- /* we may have switched to another server */ +- r->per_dir_config = r->server->lookup_defaults; +- +- if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1))) +- || ((r->proto_num == HTTP_VERSION(1, 1)) +- && !apr_table_get(r->headers_in, "Host"))) { +- /* +- * Client sent us an HTTP/1.1 or later request without telling us the +- * hostname, either with a full URL or a Host: header. We therefore +- * need to (as per the 1.1 spec) send an error. As a special case, +- * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain +- * a Host: header, and the server MUST respond with 400 if it doesn't. +- */ +- access_status = HTTP_BAD_REQUEST; +- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569) +- "client sent HTTP/1.1 request without hostname " +- "(see RFC2616 section 14.23): %s", r->uri); +- } +- + /* + * Add the HTTP_IN filter here to ensure that ap_discard_request_body + * called by ap_die and by ap_send_error_response works correctly on + * status codes that do not cause the connection to be dropped and + * in situations where the connection should be kept alive. + */ +- + ap_add_input_filter_handle(ap_http_input_filter_handle, + NULL, r, r->connection); + +- if (access_status != HTTP_OK +- || (access_status = ap_run_post_read_request(r))) { +- ap_die(access_status, r); +- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- r = NULL; +- goto traceout; ++ /* Validate Host/Expect headers and select vhost. */ ++ if (!ap_check_request_header(r)) { ++ /* we may have switched to another server still */ ++ apply_server_config(r); ++ access_status = r->status; ++ goto die_before_hooks; + } + +- if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL) +- && (expect[0] != '\0')) { +- /* +- * The Expect header field was added to HTTP/1.1 after RFC 2068 +- * as a means to signal when a 100 response is desired and, +- * unfortunately, to signal a poor man's mandatory extension that +- * the server must understand or return 417 Expectation Failed. +- */ +- if (strcasecmp(expect, "100-continue") == 0) { +- r->expecting_100 = 1; +- } +- else { +- r->status = HTTP_EXPECTATION_FAILED; +- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570) +- "client sent an unrecognized expectation value of " +- "Expect: %s", expect); +- ap_send_error_response(r, 0); +- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- goto traceout; +- } ++ /* we may have switched to another server */ ++ apply_server_config(r); ++ ++ if ((access_status = ap_run_post_read_request(r))) { ++ goto die; + } + +- AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status); ++ AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, ++ (char *)r->uri, (char *)r->server->defn_name, ++ r->status); + return r; +- traceout: ++ ++ /* Everything falls through on failure */ ++ ++die_unusable_input: ++ /* Input filters are in an undeterminate state, cleanup (including ++ * CORE_IN's socket) such that any further attempt to read is EOF. ++ */ ++ { ++ ap_filter_t *f = conn->input_filters; ++ while (f) { ++ if (f->frec == ap_core_input_filter_handle) { ++ core_net_rec *net = f->ctx; ++ apr_brigade_cleanup(net->in_ctx->b); ++ break; ++ } ++ ap_remove_input_filter(f); ++ f = f->next; ++ } ++ conn->input_filters = r->input_filters = f; ++ conn->keepalive = AP_CONN_CLOSE; ++ } ++ ++die_before_hooks: ++ /* First call to ap_die() (non recursive) */ ++ r->status = HTTP_OK; ++ ++die: ++ ap_die(access_status, r); ++ ++ /* ap_die() sent the response through the output filters, we must now ++ * end the request with an EOR bucket for stream/pipeline accounting. ++ */ ++ { ++ apr_bucket_brigade *eor_bb; ++ eor_bb = apr_brigade_create(conn->pool, conn->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(eor_bb, ++ ap_bucket_eor_create(conn->bucket_alloc, r)); ++ ap_pass_brigade(conn->output_filters, eor_bb); ++ apr_brigade_cleanup(eor_bb); ++ } ++ ++ignore: ++ r = NULL; + AP_READ_REQUEST_FAILURE((uintptr_t)r); +- return r; ++ return NULL; + } + + /* if a request with a body creates a subrequest, remove original request's +--- a/server/vhost.c ++++ b/server/vhost.c +@@ -34,6 +34,7 @@ + #include "http_vhost.h" + #include "http_protocol.h" + #include "http_core.h" ++#include "http_main.h" + + #if APR_HAVE_ARPA_INET_H + #include +@@ -973,7 +974,13 @@ AP_DECLARE(int) ap_matches_request_vhost + } + + +-static void check_hostalias(request_rec *r) ++/* ++ * Updates r->server from ServerName/ServerAlias. Per the interaction ++ * of ip and name-based vhosts, it only looks in the best match from the ++ * connection-level ip-based matching. ++ * Returns HTTP_BAD_REQUEST if there was no match. ++ */ ++static int update_server_from_aliases(request_rec *r) + { + /* + * Even if the request has a Host: header containing a port we ignore +@@ -1051,11 +1058,18 @@ static void check_hostalias(request_rec + goto found; + } + +- return; ++ if (!r->connection->vhost_lookup_data) { ++ if (matches_aliases(r->server, host)) { ++ s = r->server; ++ goto found; ++ } ++ } ++ return HTTP_BAD_REQUEST; + + found: + /* s is the first matching server, we're done */ + r->server = s; ++ return HTTP_OK; + } + + +@@ -1072,7 +1086,7 @@ static void check_serverpath(request_rec + * This is in conjunction with the ServerPath code in http_core, so we + * get the right host attached to a non- Host-sending request. + * +- * See the comment in check_hostalias about how each vhost can be ++ * See the comment in update_server_from_aliases about how each vhost can be + * listed multiple times. + */ + +@@ -1136,10 +1150,16 @@ static APR_INLINE const char *construct_ + + AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r) + { ++ ap_update_vhost_from_headers_ex(r, 0); ++} ++ ++AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match) ++{ + core_server_config *conf = ap_get_core_module_config(r->server->module_config); + const char *host_header = apr_table_get(r->headers_in, "Host"); + int is_v6literal = 0; + int have_hostname_from_url = 0; ++ int rc = HTTP_OK; + + if (r->hostname) { + /* +@@ -1152,8 +1172,8 @@ AP_DECLARE(void) ap_update_vhost_from_he + else if (host_header != NULL) { + is_v6literal = fix_hostname(r, host_header, conf->http_conformance); + } +- if (r->status != HTTP_OK) +- return; ++ if (!require_match && r->status != HTTP_OK) ++ return HTTP_OK; + + if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) { + /* +@@ -1174,10 +1194,16 @@ AP_DECLARE(void) ap_update_vhost_from_he + /* check if we tucked away a name_chain */ + if (r->connection->vhost_lookup_data) { + if (r->hostname) +- check_hostalias(r); ++ rc = update_server_from_aliases(r); + else + check_serverpath(r); + } ++ else if (require_match && r->hostname) { ++ /* check the base server config */ ++ rc = update_server_from_aliases(r); ++ } ++ ++ return rc; + } + + /** diff -Nru apache2-2.4.41/debian/patches/CVE-2021-33193-pre1.patch apache2-2.4.41/debian/patches/CVE-2021-33193-pre1.patch --- apache2-2.4.41/debian/patches/CVE-2021-33193-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-33193-pre1.patch 2021-09-23 16:58:57.000000000 +0000 @@ -0,0 +1,175 @@ +From f7173e91099ce59dc1654e5a94b3cde7a892f7fa Mon Sep 17 00:00:00 2001 +From: Ruediger Pluem +Date: Mon, 16 Nov 2020 12:24:12 +0000 +Subject: [PATCH] Merge r1881620, r1881635 from trunk: + +Process early errors via a dummy HTTP/1.1 request as well + +Process early errors via a dummy HTTP/1.1 request as well to ensure +that the request gets logged correctly and possible custom error +pages are considered. The previous way of directly sending a HTTP/2 +answer with the HTTP status code appropriate for the error is more +efficient, but does not log the request nor sents a possible custom +error page. + +* modules/http2/h2.h: Add http_status to h2_request struct and define + H2_HTTP_STATUS_UNSET. + +* modules/http2/h2_request.c(h2_request_create_rec): Check if + http_status is set for the request and die with the + status code it contains if set. + +* modules/http2/h2_session.c(on_header_cb): Adjust the error condition + now that we mark early errors via http_status: Only return an error + if the status is not success and http_status is not H2_HTTP_STATUS_UNSET. + +* modules/http2/h2_stream.c(set_error_response): Set http_status + on the request instead of creating headers for a response and a + respective brigade. + +Github: closes #137 + + +* Changelog for r1881620 + +Submitted by: rpluem +Reviewed by: rpluem, giovanni, ylavic + +Github: closes #142 + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1883475 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 4 ++++ + STATUS | 10 ---------- + modules/http2/h2.h | 11 +++++++++++ + modules/http2/h2_request.c | 18 +++++++++++++----- + modules/http2/h2_session.c | 4 +++- + modules/http2/h2_stream.c | 11 +---------- + 6 files changed, 32 insertions(+), 26 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index 2603312f31c..92c4d1d85eb 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,10 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.4.47 +# +#+ *) mod_http2: Log requests and sent the configured error response in case of +#+ early detected errors like too many or too long headers. +#+ [Ruediger Pluem, Stefan Eissing] +#+ +# *) mod_md: lowered the required minimal libcurl version from 7.50 to 7.29 +# as proposed by . [Stefan Eissing] +# +#diff --git a/STATUS b/STATUS +#index 3591db74edc..e5416fc7bca 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -154,16 +154,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# 2.4.x patch: http://people.apache.org/~covener/patches/wstunnel-decline.diff +# +1 covener, jim, rpluem, jfclere +# +#- *) mod_http2: Log requests and sent the configured error response in case of +#- early detected errors like too many or too long headers. +#- Trunk version of patch: +#- https://svn.apache.org/r1881620 +#- https://svn.apache.org/r1881635 +#- Backport version for 2.4.x of patch: +#- https://github.com/apache/httpd/pull/142 +#- https://github.com/apache/httpd/pull/142.diff +#- +1: rpluem, giovanni, ylavic +#- +# *) core: Avoid a core dump if 'AllowOverride nonfatal' is used in a configuration +# file. +# Trunk version of patch: +--- a/modules/http2/h2.h ++++ b/modules/http2/h2.h +@@ -141,8 +141,19 @@ struct h2_request { + unsigned int chunked : 1; /* iff requst body needs to be forwarded as chunked */ + unsigned int serialize : 1; /* iff this request is written in HTTP/1.1 serialization */ + apr_off_t raw_bytes; /* RAW network bytes that generated this request - if known. */ ++ int http_status; /* Store a possible HTTP status code that gets ++ * defined before creating the dummy HTTP/1.1 ++ * request e.g. due to an error already ++ * detected. ++ */ + }; + ++/* ++ * A possible HTTP status code is not defined yet. See the http_status field ++ * in struct h2_request above for further explanation. ++ */ ++#define H2_HTTP_STATUS_UNSET (0) ++ + typedef struct h2_headers h2_headers; + + struct h2_headers { +--- a/modules/http2/h2_request.c ++++ b/modules/http2/h2_request.c +@@ -79,11 +79,12 @@ apr_status_t h2_request_rcreate(h2_reque + } + + req = apr_pcalloc(pool, sizeof(*req)); +- req->method = apr_pstrdup(pool, r->method); +- req->scheme = scheme; +- req->authority = authority; +- req->path = path; +- req->headers = apr_table_make(pool, 10); ++ req->method = apr_pstrdup(pool, r->method); ++ req->scheme = scheme; ++ req->authority = authority; ++ req->path = path; ++ req->headers = apr_table_make(pool, 10); ++ req->http_status = H2_HTTP_STATUS_UNSET; + if (r->server) { + req->serialize = h2_config_rgeti(r, H2_CONF_SER_HEADERS); + } +@@ -324,6 +325,13 @@ request_rec *h2_request_create_rec(const + } + } + ++ if (req->http_status != H2_HTTP_STATUS_UNSET) { ++ access_status = req->http_status; ++ r->status = HTTP_OK; ++ /* Be safe and close the connection */ ++ c->keepalive = AP_CONN_CLOSE; ++ } ++ + /* + * Add the HTTP_IN filter here to ensure that ap_discard_request_body + * called by ap_die and by ap_send_error_response works correctly on +--- a/modules/http2/h2_session.c ++++ b/modules/http2/h2_session.c +@@ -311,7 +311,9 @@ static int on_header_cb(nghttp2_session + + status = h2_stream_add_header(stream, (const char *)name, namelen, + (const char *)value, valuelen); +- if (status != APR_SUCCESS && !h2_stream_is_ready(stream)) { ++ if (status != APR_SUCCESS ++ && (!stream->rtmp ++ || stream->rtmp->http_status == H2_HTTP_STATUS_UNSET)) { + return NGHTTP2_ERR_TEMPORAL_CALLBACK_FAILURE; + } + return 0; +--- a/modules/http2/h2_stream.c ++++ b/modules/http2/h2_stream.c +@@ -639,16 +639,7 @@ void h2_stream_set_request(h2_stream *st + static void set_error_response(h2_stream *stream, int http_status) + { + if (!h2_stream_is_ready(stream)) { +- conn_rec *c = stream->session->c; +- apr_bucket *b; +- h2_headers *response; +- +- response = h2_headers_die(http_status, stream->request, stream->pool); +- prep_output(stream); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b); +- b = h2_bucket_headers_create(c->bucket_alloc, response); +- APR_BRIGADE_INSERT_HEAD(stream->out_buffer, b); ++ stream->rtmp->http_status = http_status; + } + } + diff -Nru apache2-2.4.41/debian/patches/CVE-2021-33193-pre2.patch apache2-2.4.41/debian/patches/CVE-2021-33193-pre2.patch --- apache2-2.4.41/debian/patches/CVE-2021-33193-pre2.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-33193-pre2.patch 2021-09-23 16:58:57.000000000 +0000 @@ -0,0 +1,521 @@ +Backport of: + +From c0c8c96a8c6bb923589942d8b7754b81a0377cee Mon Sep 17 00:00:00 2001 +From: Stefan Eissing +Date: Wed, 24 Mar 2021 14:28:49 +0000 +Subject: [PATCH] *) mod_http2: sync with github standalone version 1.15.17 + - Log requests and sent the configured error response in case of early + detected errors like too many or too long headers. [Ruediger Pluem] + - new option 'H2OutputBuffering on/off' which controls the buffering of + stream output. The default is on, which is the behaviour of older + mod-h2 versions. When off, all bytes are made available immediately to + the main connection for sending them out to the client. This fixes + interop issues with certain flavours of gRPC, see also + . + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1888011 13f79535-47bb-0310-9956-ffa450edef68 +--- + docs/manual/mod/mod_http2.xml | 22 ++++++ + modules/http2/h2_bucket_beam.c | 1 + + modules/http2/h2_config.c | 24 ++++++ + modules/http2/h2_config.h | 1 + + modules/http2/h2_h2.c | 1 + + modules/http2/h2_headers.c | 15 ++++ + modules/http2/h2_headers.h | 5 ++ + modules/http2/h2_mplx.c | 17 ---- + modules/http2/h2_request.c | 138 ++++++++++++++++++++++----------- + modules/http2/h2_stream.h | 3 +- + modules/http2/h2_task.c | 18 +++-- + modules/http2/h2_task.h | 1 + + modules/http2/h2_version.h | 5 +- + 13 files changed, 180 insertions(+), 71 deletions(-) + +#diff --git a/docs/manual/mod/mod_http2.xml b/docs/manual/mod/mod_http2.xml +#index 5cf8d0acccc..287e774d33c 100644 +#--- a/docs/manual/mod/mod_http2.xml +#+++ b/docs/manual/mod/mod_http2.xml +#@@ -981,4 +981,26 @@ H2TLSCoolDownSecs 0 +#

+# +#
+#+ +#+ +#+ H2OutputBuffering +#+ Determine buffering behaviour of output +#+ H2OutputBuffering on/off +#+ H2OutputBuffering on +#+ +#+ server config +#+ virtual host +#+ +#+ Available in version 2.4.48 and later. +#+ +#+ +#+

+#+ The option 'H2OutputBuffering on/off' controls the buffering of stream output. +#+ The default is on, which is the behaviour of previous versions. When off, all +#+ bytes are made available immediately to the main connection for sending them +#+ out to the client. This fixes interop issues with certain flavours of gRPC. +#+

+#+
+#+
+#+ +# +--- a/modules/http2/h2_bucket_beam.c ++++ b/modules/http2/h2_bucket_beam.c +@@ -1039,6 +1039,7 @@ transfer: + H2_BLIST_INSERT_TAIL(&beam->hold_list, bsender); + + remain -= bsender->length; ++ beam->received_bytes += bsender->length; + ++transferred; + ++transferred_buckets; + continue; +--- a/modules/http2/h2_config.c ++++ b/modules/http2/h2_config.c +@@ -78,6 +78,7 @@ typedef struct h2_config { + int early_hints; /* support status code 103 */ + int padding_bits; + int padding_always; ++ int output_buffered; + } h2_config; + + typedef struct h2_dir_config { +@@ -115,6 +116,7 @@ static h2_config defconf = { + 0, /* early hints, http status 103 */ + 0, /* padding bits */ + 1, /* padding always */ ++ 1, /* strean output buffered */ + }; + + static h2_dir_config defdconf = { +@@ -159,6 +161,7 @@ void *h2_config_create_svr(apr_pool_t *p + conf->early_hints = DEF_VAL; + conf->padding_bits = DEF_VAL; + conf->padding_always = DEF_VAL; ++ conf->output_buffered = DEF_VAL; + return conf; + } + +@@ -193,6 +196,7 @@ static void *h2_config_merge(apr_pool_t + } + n->push_diary_size = H2_CONFIG_GET(add, base, push_diary_size); + n->copy_files = H2_CONFIG_GET(add, base, copy_files); ++ n->output_buffered = H2_CONFIG_GET(add, base, output_buffered); + if (add->push_list && base->push_list) { + n->push_list = apr_array_append(pool, base->push_list, add->push_list); + } +@@ -287,6 +291,8 @@ static apr_int64_t h2_srv_config_geti64( + return H2_CONFIG_GET(conf, &defconf, padding_bits); + case H2_CONF_PADDING_ALWAYS: + return H2_CONFIG_GET(conf, &defconf, padding_always); ++ case H2_CONF_OUTPUT_BUFFER: ++ return H2_CONFIG_GET(conf, &defconf, output_buffered); + default: + return DEF_VAL; + } +@@ -352,6 +358,9 @@ static void h2_srv_config_seti(h2_config + case H2_CONF_PADDING_ALWAYS: + H2_CONFIG_SET(conf, padding_always, val); + break; ++ case H2_CONF_OUTPUT_BUFFER: ++ H2_CONFIG_SET(conf, output_buffered, val); ++ break; + default: + break; + } +@@ -905,6 +914,19 @@ static const char *h2_conf_set_padding(c + return NULL; + } + ++static const char *h2_conf_set_output_buffer(cmd_parms *cmd, ++ void *dirconf, const char *value) ++{ ++ if (!strcasecmp(value, "On")) { ++ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 1); ++ return NULL; ++ } ++ else if (!strcasecmp(value, "Off")) { ++ CONFIG_CMD_SET(cmd, dirconf, H2_CONF_OUTPUT_BUFFER, 0); ++ return NULL; ++ } ++ return "value must be On or Off"; ++} + + void h2_get_num_workers(server_rec *s, int *minw, int *maxw) + { +@@ -976,6 +998,8 @@ const command_rec h2_cmds[] = { + RSRC_CONF, "on to enable interim status 103 responses"), + AP_INIT_TAKE1("H2Padding", h2_conf_set_padding, NULL, + RSRC_CONF, "set payload padding"), ++ AP_INIT_TAKE1("H2OutputBuffering", h2_conf_set_output_buffer, NULL, ++ RSRC_CONF, "set stream output buffer on/off"), + AP_END_CMD + }; + +--- a/modules/http2/h2_config.h ++++ b/modules/http2/h2_config.h +@@ -44,6 +44,7 @@ typedef enum { + H2_CONF_EARLY_HINTS, + H2_CONF_PADDING_BITS, + H2_CONF_PADDING_ALWAYS, ++ H2_CONF_OUTPUT_BUFFER, + } h2_config_var_t; + + struct apr_hash_t; +--- a/modules/http2/h2_h2.c ++++ b/modules/http2/h2_h2.c +@@ -749,6 +749,7 @@ static int h2_h2_late_fixups(request_rec + if (task) { + /* check if we copy vs. setaside files in this location */ + task->output.copy_files = h2_config_rgeti(r, H2_CONF_COPY_FILES); ++ task->output.buffered = h2_config_rgeti(r, H2_CONF_OUTPUT_BUFFER); + if (task->output.copy_files) { + ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c, + "h2_secondary_out(%s): copy_files on", task->id); +--- a/modules/http2/h2_headers.c ++++ b/modules/http2/h2_headers.c +@@ -64,6 +64,7 @@ apr_bucket * h2_bucket_headers_make(apr_ + + b = apr_bucket_shared_make(b, br, 0, 0); + b->type = &h2_bucket_type_headers; ++ b->length = h2_headers_length(r); + + return b; + } +@@ -125,6 +126,20 @@ h2_headers *h2_headers_create(int status + return headers; + } + ++static int add_header_lengths(void *ctx, const char *name, const char *value) ++{ ++ apr_size_t *plen = ctx; ++ *plen += strlen(name) + strlen(value); ++ return 1; ++} ++ ++apr_size_t h2_headers_length(h2_headers *headers) ++{ ++ apr_size_t len = 0; ++ apr_table_do(add_header_lengths, &len, headers->headers, NULL); ++ return len; ++} ++ + h2_headers *h2_headers_rcreate(request_rec *r, int status, + apr_table_t *header, apr_pool_t *pool) + { +--- a/modules/http2/h2_headers.h ++++ b/modules/http2/h2_headers.h +@@ -82,4 +82,9 @@ h2_headers *h2_headers_die(apr_status_t + + int h2_headers_are_response(h2_headers *headers); + ++/** ++ * Give the number of bytes of all contained header strings. ++ */ ++apr_size_t h2_headers_length(h2_headers *headers); ++ + #endif /* defined(__mod_h2__h2_headers__) */ +--- a/modules/http2/h2_mplx.c ++++ b/modules/http2/h2_mplx.c +@@ -91,10 +91,6 @@ apr_status_t h2_mplx_m_child_init(apr_po + + static void mst_check_data_for(h2_mplx *m, h2_stream *stream, int mplx_is_locked); + +-static void mst_stream_output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) +-{ +-} +- + static void mst_stream_input_ev(void *ctx, h2_bucket_beam *beam) + { + h2_stream *stream = ctx; +@@ -299,18 +295,6 @@ static int m_stream_destroy_iter(void *c + stream->task = NULL; + secondary = task->c; + if (secondary) { +- /* On non-serialized requests, the IO logging has not accounted for any +- * meta data send over the network: response headers and h2 frame headers. we +- * counted this on the stream and need to add this now. +- * This is supposed to happen before the EOR bucket triggers the +- * logging of the transaction. *fingers crossed* */ +- if (task->request && !task->request->serialize && h2_task_logio_add_bytes_out) { +- apr_off_t unaccounted = stream->out_frame_octets - stream->out_data_octets; +- if (unaccounted > 0) { +- h2_task_logio_add_bytes_out(secondary, unaccounted); +- } +- } +- + if (m->s->keep_alive_max == 0 || secondary->keepalives < m->s->keep_alive_max) { + reuse_secondary = ((m->spare_secondary->nelts < (m->limit_active * 3 / 2)) + && !task->rst_error); +@@ -540,7 +524,6 @@ static apr_status_t t_out_open(h2_mplx * + "h2_mplx(%s): out open", stream->task->id); + } + +- h2_beam_on_consumed(stream->output, NULL, mst_stream_output_consumed, stream); + h2_beam_on_produced(stream->output, mst_output_produced, stream); + if (stream->task->output.copy_files) { + h2_beam_on_file_beam(stream->output, h2_beam_no_files, NULL); +--- a/modules/http2/h2_request.c ++++ b/modules/http2/h2_request.c +@@ -267,9 +267,7 @@ static request_rec *my_ap_create_request + + request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c) + { +- int access_status = HTTP_OK; +- const char *rpath; +- const char *s; ++ int access_status; + + #if AP_MODULE_MAGIC_AT_LEAST(20150222, 13) + request_rec *r = ap_create_request(c); +@@ -277,59 +275,88 @@ request_rec *h2_request_create_rec(const + request_rec *r = my_ap_create_request(c); + #endif + +- r->headers_in = apr_table_clone(r->pool, req->headers); +- ++#if AP_MODULE_MAGIC_AT_LEAST(20200331, 3) + ap_run_pre_read_request(r, c); +- ++ + /* Time to populate r with the data we have. */ + r->request_time = req->request_time; +- r->method = apr_pstrdup(r->pool, req->method); +- /* Provide quick information about the request method as soon as known */ +- r->method_number = ap_method_number_of(r->method); +- if (r->method_number == M_GET && r->method[0] == 'H') { +- r->header_only = 1; +- } +- r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", ++ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", + req->method, req->path ? req->path : ""); + r->headers_in = apr_table_clone(r->pool, req->headers); + +- rpath = (req->path ? req->path : ""); +- ap_parse_uri(r, rpath); +- r->protocol = (char*)"HTTP/2.0"; +- r->proto_num = HTTP_VERSION(2, 0); +- +- r->the_request = apr_psprintf(r->pool, "%s %s %s", +- r->method, rpath, r->protocol); +- +- /* update what we think the virtual host is based on the headers we've +- * now read. may update status. +- * Leave r->hostname empty, vhost will parse if form our Host: header, +- * otherwise we get complains about port numbers. ++ /* Start with r->hostname = NULL, ap_check_request_header() will get it ++ * form Host: header, otherwise we get complains about port numbers. + */ + r->hostname = NULL; +- ap_update_vhost_from_headers(r); +- r->protocol = "HTTP/2.0"; +- r->proto_num = HTTP_VERSION(2, 0); + +- /* we may have switched to another server */ +- r->per_dir_config = r->server->lookup_defaults; +- +- s = apr_table_get(r->headers_in, "Expect"); +- if (s && s[0]) { +- if (ap_cstr_casecmp(s, "100-continue") == 0) { +- r->expecting_100 = 1; ++ /* Validate HTTP/1 request and select vhost. */ ++ if (!ap_parse_request_line(r) || !ap_check_request_header(r)) { ++ /* we may have switched to another server still */ ++ r->per_dir_config = r->server->lookup_defaults; ++ if (req->http_status != H2_HTTP_STATUS_UNSET) { ++ access_status = req->http_status; ++ /* Be safe and close the connection */ ++ c->keepalive = AP_CONN_CLOSE; + } + else { +- r->status = HTTP_EXPECTATION_FAILED; +- ap_send_error_response(r, 0); ++ access_status = r->status; ++ } ++ r->status = HTTP_OK; ++ goto die; ++ } ++#else ++ { ++ const char *s; ++ ++ r->headers_in = apr_table_clone(r->pool, req->headers); ++ ap_run_pre_read_request(r, c); ++ ++ /* Time to populate r with the data we have. */ ++ r->request_time = req->request_time; ++ r->method = apr_pstrdup(r->pool, req->method); ++ /* Provide quick information about the request method as soon as known */ ++ r->method_number = ap_method_number_of(r->method); ++ if (r->method_number == M_GET && r->method[0] == 'H') { ++ r->header_only = 1; + } ++ ap_parse_uri(r, req->path ? req->path : ""); ++ r->protocol = (char*)"HTTP/2.0"; ++ r->proto_num = HTTP_VERSION(2, 0); ++ r->the_request = apr_psprintf(r->pool, "%s %s HTTP/2.0", ++ r->method, req->path ? req->path : ""); ++ ++ /* Start with r->hostname = NULL, ap_check_request_header() will get it ++ * form Host: header, otherwise we get complains about port numbers. ++ */ ++ r->hostname = NULL; ++ ap_update_vhost_from_headers(r); ++ ++ /* we may have switched to another server */ ++ r->per_dir_config = r->server->lookup_defaults; ++ ++ s = apr_table_get(r->headers_in, "Expect"); ++ if (s && s[0]) { ++ if (ap_cstr_casecmp(s, "100-continue") == 0) { ++ r->expecting_100 = 1; ++ } ++ else { ++ r->status = HTTP_EXPECTATION_FAILED; ++ access_status = r->status; ++ goto die; ++ } ++ } + } ++#endif ++ ++ /* we may have switched to another server */ ++ r->per_dir_config = r->server->lookup_defaults; + + if (req->http_status != H2_HTTP_STATUS_UNSET) { + access_status = req->http_status; + r->status = HTTP_OK; + /* Be safe and close the connection */ + c->keepalive = AP_CONN_CLOSE; ++ goto die; + } + + /* +@@ -341,28 +368,47 @@ request_rec *h2_request_create_rec(const + ap_add_input_filter_handle(ap_http_input_filter_handle, + NULL, r, r->connection); + +- if (access_status != HTTP_OK +- || (access_status = ap_run_post_read_request(r))) { ++ if ((access_status = ap_run_post_read_request(r))) { + /* Request check post hooks failed. An example of this would be a + * request for a vhost where h2 is disabled --> 421. + */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03367) + "h2_request: access_status=%d, request_create failed", + access_status); +- ap_die(access_status, r); +- ap_update_child_status(c->sbh, SERVER_BUSY_LOG, r); +- ap_run_log_transaction(r); +- r = NULL; +- goto traceout; ++ goto die; + } + + AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, + (char *)r->uri, (char *)r->server->defn_name, + r->status); + return r; +-traceout: ++ ++die: ++ ap_die(access_status, r); ++ ++ /* ap_die() sent the response through the output filters, we must now ++ * end the request with an EOR bucket for stream/pipeline accounting. ++ */ ++ { ++ apr_bucket_brigade *eor_bb; ++#if AP_MODULE_MAGIC_AT_LEAST(20180905, 1) ++ eor_bb = ap_acquire_brigade(c); ++ APR_BRIGADE_INSERT_TAIL(eor_bb, ++ ap_bucket_eor_create(c->bucket_alloc, r)); ++ ap_pass_brigade(c->output_filters, eor_bb); ++ ap_release_brigade(c, eor_bb); ++#else ++ eor_bb = apr_brigade_create(c->pool, c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(eor_bb, ++ ap_bucket_eor_create(c->bucket_alloc, r)); ++ ap_pass_brigade(c->output_filters, eor_bb); ++ apr_brigade_destroy(eor_bb); ++#endif ++ } ++ ++ r = NULL; + AP_READ_REQUEST_FAILURE((uintptr_t)r); +- return r; ++ return NULL; + } + + +--- a/modules/http2/h2_stream.h ++++ b/modules/http2/h2_stream.h +@@ -92,7 +92,8 @@ struct h2_stream { + unsigned int input_eof : 1; /* no more request data coming */ + unsigned int out_checked : 1; /* output eof was double checked */ + unsigned int push_policy; /* which push policy to use for this request */ +- ++ unsigned int input_buffering : 1; /* buffer request bodies for efficiency */ ++ + struct h2_task *task; /* assigned task to fullfill request */ + + const h2_priority *pref_priority; /* preferred priority for this stream */ +--- a/modules/http2/h2_task.c ++++ b/modules/http2/h2_task.c +@@ -89,6 +89,14 @@ static apr_status_t open_output(h2_task + return h2_mplx_t_out_open(task->mplx, task->stream_id, task->output.beam); + } + ++static void output_consumed(void *ctx, h2_bucket_beam *beam, apr_off_t length) ++{ ++ h2_task *task = ctx; ++ if (task && h2_task_logio_add_bytes_out) { ++ h2_task_logio_add_bytes_out(task->c, length); ++ } ++} ++ + static apr_status_t send_out(h2_task *task, apr_bucket_brigade* bb, int block) + { + apr_off_t written, left; +@@ -108,9 +116,6 @@ static apr_status_t send_out(h2_task *ta + status = APR_SUCCESS; + } + if (status == APR_SUCCESS) { +- if (h2_task_logio_add_bytes_out) { +- h2_task_logio_add_bytes_out(task->c, written); +- } + ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, + "h2_task(%s): send_out done", task->id); + } +@@ -183,7 +188,9 @@ send: + } + } + +- if (APR_SUCCESS == rv && !task->output.opened && flush) { ++ ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, task->c, ++ "h2_secondary_out(%s): buffered=%d", task->id, task->output.buffered); ++ if (APR_SUCCESS == rv && !task->output.opened && (flush || !task->output.buffered)) { + /* got a flush or could not write all, time to tell someone to read */ + rv = open_output(task); + } +@@ -596,7 +603,8 @@ apr_status_t h2_task_do(h2_task *task, a + + h2_beam_buffer_size_set(task->output.beam, task->output.max_buffer); + h2_beam_send_from(task->output.beam, task->pool); +- ++ h2_beam_on_consumed(task->output.beam, NULL, output_consumed, task); ++ + h2_ctx_create_for(c, task); + apr_table_setn(c->notes, H2_TASK_ID_NOTE, task->id); + +--- a/modules/http2/h2_task.h ++++ b/modules/http2/h2_task.h +@@ -71,6 +71,7 @@ struct h2_task { + unsigned int opened : 1; + unsigned int sent_response : 1; + unsigned int copy_files : 1; ++ unsigned int buffered : 1; + struct h2_response_parser *rparser; + apr_bucket_brigade *bb; + apr_size_t max_buffer; diff -Nru apache2-2.4.41/debian/patches/CVE-2021-34798.patch apache2-2.4.41/debian/patches/CVE-2021-34798.patch --- apache2-2.4.41/debian/patches/CVE-2021-34798.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-34798.patch 2021-09-23 16:58:27.000000000 +0000 @@ -0,0 +1,33 @@ +From fa7b2a5250e54363b3a6c8ac3aaa7de4e8da9b2e Mon Sep 17 00:00:00 2001 +From: Yann Ylavic +Date: Tue, 7 Sep 2021 16:05:31 +0000 +Subject: [PATCH] Merge r1878092 from trunk: + +Fix a NULL pointer dereference + +* server/scoreboard.c (ap_increment_counts): In certain cases like certain + invalid requests r->method might be NULL here. r->method_number defaults + to M_GET and hence is M_GET in these cases. + +Submitted by: rpluem +Reviewed by: covener, ylavic, jfclere + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1893051 13f79535-47bb-0310-9956-ffa450edef68 +--- + server/scoreboard.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/server/scoreboard.c b/server/scoreboard.c +index b40b45df590..12dd56abead 100644 +--- a/server/scoreboard.c ++++ b/server/scoreboard.c +@@ -388,7 +388,7 @@ AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r) + if (pfn_ap_logio_get_last_bytes != NULL) { + bytes = pfn_ap_logio_get_last_bytes(r->connection); + } +- else if (r->method_number == M_GET && r->method[0] == 'H') { ++ else if (r->method_number == M_GET && r->method && r->method[0] == 'H') { + bytes = 0; + } + else { diff -Nru apache2-2.4.41/debian/patches/CVE-2021-36160.patch apache2-2.4.41/debian/patches/CVE-2021-36160.patch --- apache2-2.4.41/debian/patches/CVE-2021-36160.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-36160.patch 2021-09-23 16:58:32.000000000 +0000 @@ -0,0 +1,73 @@ +From 6d9672bf096592fe16c1840f73fa947fd458ee68 Mon Sep 17 00:00:00 2001 +From: Yann Ylavic +Date: Fri, 3 Sep 2021 17:00:07 +0000 +Subject: [PATCH] Merge r1892805 from trunk: + +mod_proxy_uwsgi: Fix PATH_INFO setting for generic worker. + +When the generic "proxy:reverse" worker is selected for an uwsgi scheme, the +worker name is irrelevant so uwscgi_handler() should point to the PATH_INFO +directly from the given URL. + + +Submitted by: ylavic +Reviewed by: ylavic, covener, rpluem + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1892875 13f79535-47bb-0310-9956-ffa450edef68 +--- + changes-entries/uwsgi_path_info.txt | 1 + + modules/proxy/mod_proxy_uwsgi.c | 22 +++++----------------- + 2 files changed, 6 insertions(+), 17 deletions(-) + create mode 100644 changes-entries/uwsgi_path_info.txt + +#diff --git a/changes-entries/uwsgi_path_info.txt b/changes-entries/uwsgi_path_info.txt +#new file mode 100644 +#index 00000000000..4591366cbe0 +#--- /dev/null +#+++ b/changes-entries/uwsgi_path_info.txt +#@@ -0,0 +1 @@ +#+ *) mod_proxy_uwsgi: Fix PATH_INFO setting for generic worker. [Yann Ylavic] +--- a/modules/proxy/mod_proxy_uwsgi.c ++++ b/modules/proxy/mod_proxy_uwsgi.c +@@ -453,11 +453,8 @@ static int uwsgi_handler(request_rec *r, + const char *proxyname, apr_port_t proxyport) + { + int status; +- int delta = 0; +- int decode_status; + proxy_conn_rec *backend = NULL; + apr_pool_t *p = r->pool; +- size_t w_len; + char server_portstr[32]; + char *u_path_info; + apr_uri_t *uri; +@@ -469,23 +466,14 @@ static int uwsgi_handler(request_rec *r, + + uri = apr_palloc(r->pool, sizeof(*uri)); + +- /* ADD PATH_INFO */ +-#if AP_MODULE_MAGIC_AT_LEAST(20111130,0) +- w_len = strlen(worker->s->name); +-#else +- w_len = strlen(worker->name); +-#endif +- u_path_info = r->filename + 6 + w_len; +- if (u_path_info[0] != '/') { +- delta = 1; +- } +- decode_status = ap_unescape_url(url + w_len - delta); +- if (decode_status) { ++ /* ADD PATH_INFO (unescaped) */ ++ u_path_info = ap_strchr(url + sizeof(UWSGI_SCHEME) + 2, '/'); ++ if (!u_path_info || ap_unescape_url(u_path_info) != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10100) +- "unable to decode uri: %s", url + w_len - delta); ++ "unable to decode uwsgi uri: %s", url); + return HTTP_INTERNAL_SERVER_ERROR; + } +- apr_table_add(r->subprocess_env, "PATH_INFO", url + w_len - delta); ++ apr_table_add(r->subprocess_env, "PATH_INFO", u_path_info); + + + /* Create space for state information */ diff -Nru apache2-2.4.41/debian/patches/CVE-2021-39275.patch apache2-2.4.41/debian/patches/CVE-2021-39275.patch --- apache2-2.4.41/debian/patches/CVE-2021-39275.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-39275.patch 2021-09-23 16:58:40.000000000 +0000 @@ -0,0 +1,83 @@ +Backport of: + +From ac62c7e7436560cf4f7725ee586364ce95c07804 Mon Sep 17 00:00:00 2001 +From: Graham Leggett +Date: Sat, 21 Aug 2021 21:35:04 +0000 +Subject: [PATCH] Backport: + + *) core: fix ap_escape_quotes substitution logic + trunk patch: https://svn.apache.org/r1892418 + 2.4.x patch: svn merge -c 1892418 ^/httpd/httpd/trunk . + +1: covener, rpluem, ylavic + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1892511 13f79535-47bb-0310-9956-ffa450edef68 +--- + CHANGES | 10 ++++++---- + STATUS | 5 ----- + server/util.c | 7 +++---- + 3 files changed, 9 insertions(+), 13 deletions(-) + +#diff --git a/CHANGES b/CHANGES +#index f410ed66c7b..f33b68e78d5 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,8 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.4.49 +# +#+ *) core: fix ap_escape_quotes substitution logic. [Eric Covener] +#+ +# *) Easy patches: synch 2.4.x and trunk +# - mod_auth_basic: Use ap_cstr_casecmp instead of strcasecmp. +# - mod_ldap: log and abort locking errors. +#@@ -14,10 +16,10 @@ Changes with Apache 2.4.49 +# - core: remove extra whitespace in HTTP_NOT_IMPLEMENTED +# [Christophe Jaillet] +# +#- * core/mpm: add hook 'child_stopping` that gets called when the MPM is +#- stopping a child process. The additional `graceful` parameter allows +#- registered hooks to free resources early during a graceful shutdown. +#- [Yann Ylavic, Stefan Eissing] +#+ *) core/mpm: add hook 'child_stopping` that gets called when the MPM is +#+ stopping a child process. The additional `graceful` parameter allows +#+ registered hooks to free resources early during a graceful shutdown. +#+ [Yann Ylavic, Stefan Eissing] +# +# *) mod_proxy: Fix icomplete initialization of BalancerMember(s) from the +# balancer-manager, which can lead to a crash. [Yann Ylavic] +#diff --git a/STATUS b/STATUS +#index a7b8bae4198..98d9d93a883 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -142,11 +142,6 @@ RELEASE SHOWSTOPPERS: +# PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# [ start all new proposals below, under PATCHES PROPOSED. ] +# +#- *) core: fix ap_escape_quotes substitution logic +#- trunk patch: https://svn.apache.org/r1892418 +#- 2.4.x patch: svn merge -c 1892418 ^/httpd/httpd/trunk . +#- +1: covener, rpluem, ylavic +#- +# *) Add dav_get_provider(), dav_open_lockdb(), dav_close_lockdb() and +# dav_get_resource() to mod_dav.h. +# trunk patch: http://svn.apache.org/r1879305 +--- a/server/util.c ++++ b/server/util.c +@@ -2466,13 +2466,12 @@ AP_DECLARE(char *) ap_escape_quotes(apr_ + * in front of every " that doesn't already have one. + */ + while (*inchr != '\0') { +- if ((*inchr == '\\') && (inchr[1] != '\0')) { +- *outchr++ = *inchr++; +- *outchr++ = *inchr++; +- } + if (*inchr == '"') { + *outchr++ = '\\'; + } ++ if ((*inchr == '\\') && (inchr[1] != '\0')) { ++ *outchr++ = *inchr++; ++ } + if (*inchr != '\0') { + *outchr++ = *inchr++; + } diff -Nru apache2-2.4.41/debian/patches/CVE-2021-40438.patch apache2-2.4.41/debian/patches/CVE-2021-40438.patch --- apache2-2.4.41/debian/patches/CVE-2021-40438.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-40438.patch 2021-09-23 16:58:54.000000000 +0000 @@ -0,0 +1,136 @@ +From d4901cb32133bc0e59ad193a29d1665597080d67 Mon Sep 17 00:00:00 2001 +From: Ruediger Pluem +Date: Wed, 8 Sep 2021 07:00:09 +0000 +Subject: [PATCH] Merge r1892986, r1892987 from trunk: + +mod_proxy: Follow up to r1892814. + +* modules/proxy/proxy_util.c(fix_uds_filename): + Sanity checks on the configured UDS path, fail with 500 if invalid since + continuing through proxy processing wouldn't work as expected. + + + +mod_proxy: Follow up to r1892986: APLOGNO() + +Stefan get out of this body! :) + + +Submitted by: ylavic +Reviewed by: rpluem, ylavic, covener + +Github: closes #265 + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1893101 13f79535-47bb-0310-9956-ffa450edef68 +--- + STATUS | 9 ------- + modules/proxy/proxy_util.c | 55 +++++++++++++++++++++++--------------- + 2 files changed, 34 insertions(+), 30 deletions(-) + +#diff --git a/STATUS b/STATUS +#index 34ffe2d785a..a0897138054 100644 +#--- a/STATUS +#+++ b/STATUS +#@@ -146,15 +146,6 @@ PATCHES ACCEPTED TO BACKPORT FROM TRUNK: +# 2.4.x patch: svn merge -c 1878092 ^/httpd/httpd/trunk . +# +1: covener, ylavic, jfclere +# +#- *) mod_proxy: Sanity checks on the configured UDS path, fail with 500 if +#- invalid since continuing through proxy processing wouldn't work as expected. +#- Trunk version of patch: +#- https://svn.apache.org/r1892986 +#- https://svn.apache.org/r1892987 +#- Backport version for 2.4.x of patch: +#- https://patch-diff.githubusercontent.com/raw/apache/httpd/pull/265.diff +#- +1: rpluem, ylavic, covener +#- +# *) mod_proxy: Axe unused ap_filter_input_pending in 2.4.x (only) after r1892971. +# 2.4.x patch: http://people.apache.org/~ylavic/patches/ap_filter_input_pending-unused.patch +# +1: ylavic, icing, covener +--- a/modules/proxy/proxy_util.c ++++ b/modules/proxy/proxy_util.c +@@ -2088,33 +2088,42 @@ static int ap_proxy_retry_worker(const c + * were passed a UDS url (eg: from mod_proxy) and adjust uds_path + * as required. + */ +-static void fix_uds_filename(request_rec *r, char **url) ++static int fix_uds_filename(request_rec *r, char **url) + { +- char *ptr, *ptr2; +- if (!r || !r->filename) return; ++ char *uds_url = r->filename + 6, *origin_url; + + if (!strncmp(r->filename, "proxy:", 6) && +- !ap_cstr_casecmpn(r->filename + 6, "unix:", 5) && +- (ptr2 = r->filename + 6 + 5, ptr = ap_strchr(ptr2, '|'))) { ++ !ap_cstr_casecmpn(uds_url, "unix:", 5) && ++ (origin_url = ap_strchr(uds_url + 5, '|'))) { ++ char *uds_path = NULL; ++ apr_size_t url_len; + apr_uri_t urisock; + apr_status_t rv; +- *ptr = '\0'; +- rv = apr_uri_parse(r->pool, ptr2, &urisock); +- if (rv == APR_SUCCESS) { +- char *rurl = ptr+1; +- char *sockpath = ap_runtime_dir_relative(r->pool, urisock.path); +- apr_table_setn(r->notes, "uds_path", sockpath); +- *url = apr_pstrdup(r->pool, rurl); /* so we get the scheme for the uds */ +- /* r->filename starts w/ "proxy:", so add after that */ +- memmove(r->filename+6, rurl, strlen(rurl)+1); +- ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, +- "*: rewrite of url due to UDS(%s): %s (%s)", +- sockpath, *url, r->filename); ++ ++ *origin_url = '\0'; ++ rv = apr_uri_parse(r->pool, uds_url, &urisock); ++ *origin_url++ = '|'; ++ ++ if (rv == APR_SUCCESS && urisock.path && !urisock.hostname) { ++ uds_path = ap_runtime_dir_relative(r->pool, urisock.path); + } +- else { +- *ptr = '|'; ++ if (!uds_path) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10292) ++ "Invalid proxy UDS filename (%s)", r->filename); ++ return 0; + } ++ apr_table_setn(r->notes, "uds_path", uds_path); ++ ++ /* Remove the UDS path from *url and r->filename */ ++ url_len = strlen(origin_url); ++ *url = apr_pstrmemdup(r->pool, origin_url, url_len); ++ memcpy(uds_url, *url, url_len + 1); ++ ++ ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, r, ++ "*: rewrite of url due to UDS(%s): %s (%s)", ++ uds_path, *url, r->filename); + } ++ return 1; + } + + PROXY_DECLARE(int) ap_proxy_pre_request(proxy_worker **worker, +@@ -2132,7 +2141,9 @@ PROXY_DECLARE(int) ap_proxy_pre_request( + "%s: found worker %s for %s", + (*worker)->s->scheme, (*worker)->s->name, *url); + *balancer = NULL; +- fix_uds_filename(r, url); ++ if (!fix_uds_filename(r, url)) { ++ return HTTP_INTERNAL_SERVER_ERROR; ++ } + access_status = OK; + } + else if (r->proxyreq == PROXYREQ_PROXY) { +@@ -2163,7 +2174,9 @@ PROXY_DECLARE(int) ap_proxy_pre_request( + * regarding the Connection header in the request. + */ + apr_table_setn(r->subprocess_env, "proxy-nokeepalive", "1"); +- fix_uds_filename(r, url); ++ if (!fix_uds_filename(r, url)) { ++ return HTTP_INTERNAL_SERVER_ERROR; ++ } + } + } + } diff -Nru apache2-2.4.41/debian/patches/CVE-2021-40438-pre1.patch apache2-2.4.41/debian/patches/CVE-2021-40438-pre1.patch --- apache2-2.4.41/debian/patches/CVE-2021-40438-pre1.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/CVE-2021-40438-pre1.patch 2021-09-23 16:58:47.000000000 +0000 @@ -0,0 +1,59 @@ +Backport of: + +From 496c863776c68bd08cdbeb7d8fa5935ba63b76c2 Mon Sep 17 00:00:00 2001 +From: Yann Ylavic +Date: Fri, 3 Sep 2021 16:52:38 +0000 +Subject: [PATCH] Merge r1892814, r1892853 from trunk: + +mod_proxy: Faster unix socket path parsing in the "proxy:" URL. + +The actual r->filename format is "[proxy:]unix:path|url" for UDS, no need to +strstr(,"unix:") since it's at the start of the string. + + +mod_proxy: Follow up to r1892814. + +Save some few cycles in ap_proxy_de_socketfy() too. + + +Submitted by: ylavic +Reviewed by: ylavic, covener, rpluem + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1892874 13f79535-47bb-0310-9956-ffa450edef68 +--- + changes-entries/fix_uds_filename.txt | 2 ++ + modules/proxy/mod_proxy.c | 2 +- + modules/proxy/proxy_util.c | 4 ++-- + 3 files changed, 5 insertions(+), 3 deletions(-) + create mode 100644 changes-entries/fix_uds_filename.txt + +#--- /dev/null +#+++ b/changes-entries/fix_uds_filename.txt +#@@ -0,0 +1,2 @@ +#+ *) mod_proxy: Faster unix socket path parsing in the "proxy:" URL. +#+ [Yann Ylavic] +--- a/modules/proxy/mod_proxy.c ++++ b/modules/proxy/mod_proxy.c +@@ -1703,7 +1703,7 @@ PROXY_DECLARE(const char *) ap_proxy_de_ + * the UDS path... ignore it + */ + if (!strncasecmp(url, "unix:", 5) && +- ((ptr = ap_strchr_c(url, '|')) != NULL)) { ++ ((ptr = ap_strchr_c(url + 5, '|')) != NULL)) { + /* move past the 'unix:...|' UDS path info */ + const char *ret, *c; + +--- a/modules/proxy/proxy_util.c ++++ b/modules/proxy/proxy_util.c +@@ -2094,8 +2094,8 @@ static void fix_uds_filename(request_rec + if (!r || !r->filename) return; + + if (!strncmp(r->filename, "proxy:", 6) && +- (ptr2 = ap_strcasestr(r->filename, "unix:")) && +- (ptr = ap_strchr(ptr2, '|'))) { ++ !ap_cstr_casecmpn(r->filename + 6, "unix:", 5) && ++ (ptr2 = r->filename + 6 + 5, ptr = ap_strchr(ptr2, '|'))) { + apr_uri_t urisock; + apr_status_t rv; + *ptr = '\0'; diff -Nru apache2-2.4.41/debian/patches/lp-1930430-Backport-r1865740.patch apache2-2.4.41/debian/patches/lp-1930430-Backport-r1865740.patch --- apache2-2.4.41/debian/patches/lp-1930430-Backport-r1865740.patch 1970-01-01 00:00:00.000000000 +0000 +++ apache2-2.4.41/debian/patches/lp-1930430-Backport-r1865740.patch 2021-07-05 07:15:29.000000000 +0000 @@ -0,0 +1,32 @@ +From c11b1cd3b11f073ab1b5d1d670cec9db21144683 Mon Sep 17 00:00:00 2001 +From: Graham Leggett +Date: Wed, 1 Jan 2020 23:05:42 +0000 +Subject: [PATCH] Backport r1865740. mod_ssl: OCSP does not apply to proxy + mode. + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1872226 13f79535-47bb-0310-9956-ffa450edef68 + +Origin: backport, https://github.com/apache/httpd/commit/c11b1cd3b11f +Bug-Ubuntu: https://bugs.launchpad.net/bugs/1930430 +Last-Update: 2021-07-05 +X-Backport-Note: skipped non functional changes to status (doesn't exist) and changes (does't match) + +--- + CHANGES | 2 ++ + STATUS | 5 ----- + modules/ssl/ssl_engine_kernel.c | 4 ++-- + 3 files changed, 4 insertions(+), 7 deletions(-) + +--- a/modules/ssl/ssl_engine_kernel.c ++++ b/modules/ssl/ssl_engine_kernel.c +@@ -1836,8 +1836,8 @@ int ssl_callback_SSLVerify(int ok, X509_ + /* + * Perform OCSP-based revocation checks + */ +- if (ok && ((sc->server->ocsp_mask & SSL_OCSPCHECK_CHAIN) || +- (errdepth == 0 && (sc->server->ocsp_mask & SSL_OCSPCHECK_LEAF)))) { ++ if (ok && ((mctx->ocsp_mask & SSL_OCSPCHECK_CHAIN) || ++ (errdepth == 0 && (mctx->ocsp_mask & SSL_OCSPCHECK_LEAF)))) { + /* If there was an optional verification error, it's not + * possible to perform OCSP validation since the issuer may be + * missing/untrusted. Fail in that case. */ diff -Nru apache2-2.4.41/debian/patches/series apache2-2.4.41/debian/patches/series --- apache2-2.4.41/debian/patches/series 2021-06-17 18:27:53.000000000 +0000 +++ apache2-2.4.41/debian/patches/series 2021-09-23 16:58:57.000000000 +0000 @@ -27,3 +27,12 @@ CVE-2021-26690.patch CVE-2021-26691.patch CVE-2021-30641.patch +lp-1930430-Backport-r1865740.patch +CVE-2021-34798.patch +CVE-2021-36160.patch +CVE-2021-39275.patch +CVE-2021-40438-pre1.patch +CVE-2021-40438.patch +CVE-2021-33193-pre1.patch +CVE-2021-33193-pre2.patch +CVE-2021-33193.patch