From 27bd2efa7497013c63c4adf082aef7618195ff83 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 7 May 2024 06:28:40 +0200 Subject: Adding debian version 2.4.38-3+deb10u9. Signed-off-by: Daniel Baumann --- debian/.gitlab-ci.yml | 8 + debian/changelog | 13 + debian/gbp.conf | 3 + debian/patches/CVE-2006-20001.patch | 37 ++ debian/patches/CVE-2021-33193.patch | 702 +++++++++++++++++++++++ debian/patches/CVE-2022-36760.patch | 27 + debian/patches/CVE-2022-37436.patch | 125 ++++ debian/patches/series | 4 + debian/perl-framework/t/security/CVE-2019-0215.t | 47 ++ debian/perl-framework/t/security/CVE-2020-1927.t | 60 ++ 10 files changed, 1026 insertions(+) create mode 100644 debian/.gitlab-ci.yml create mode 100644 debian/gbp.conf create mode 100644 debian/patches/CVE-2006-20001.patch create mode 100644 debian/patches/CVE-2021-33193.patch create mode 100644 debian/patches/CVE-2022-36760.patch create mode 100644 debian/patches/CVE-2022-37436.patch create mode 100644 debian/perl-framework/t/security/CVE-2019-0215.t create mode 100644 debian/perl-framework/t/security/CVE-2020-1927.t (limited to 'debian') diff --git a/debian/.gitlab-ci.yml b/debian/.gitlab-ci.yml new file mode 100644 index 0000000..e90a48f --- /dev/null +++ b/debian/.gitlab-ci.yml @@ -0,0 +1,8 @@ +include: +- https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/recipes/debian.yml + +variables: + RELEASE: 'buster' + SALSA_CI_COMPONENTS: 'main contrib non-free' + SALSA_CI_DISABLE_REPROTEST: 1 + SALSA_CI_DISABLE_LINTIAN: 1 diff --git a/debian/changelog b/debian/changelog index 64570c6..c5cbe51 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,16 @@ +apache2 (2.4.38-3+deb10u9) buster-security; urgency=medium + + * Non-maintainer upload by the LTS Team. + * CVE-2006-20001: Fix error path for "Not" prefix parsing. + * CVE-2022-36760: HTTP Requests vulnerability in mod_proxy_ajp + * CVE-2022-37436: Early truncation of response headers + * CVE-2021-33193: mod_proxy HTTP/2 validation bypass + * Add tests from master branch for CVE-2019-0215, CVE-2020-1927 + * Update debian/gbp.conf to use the branch "debian/buster" + * Add debian/.gitlab-ci.yml + + -- Lee Garrett Thu, 02 Mar 2023 15:26:27 +0100 + apache2 (2.4.38-3+deb10u8) buster; urgency=medium * Non-maintainer upload. diff --git a/debian/gbp.conf b/debian/gbp.conf new file mode 100644 index 0000000..d8c79b8 --- /dev/null +++ b/debian/gbp.conf @@ -0,0 +1,3 @@ +[DEFAULT] +debian-branch = debian/buster +merge-mode = replace diff --git a/debian/patches/CVE-2006-20001.patch b/debian/patches/CVE-2006-20001.patch new file mode 100644 index 0000000..0ba150b --- /dev/null +++ b/debian/patches/CVE-2006-20001.patch @@ -0,0 +1,37 @@ +From 7469547c3f617717ca545d0f7c56d01134703813 Mon Sep 17 00:00:00 2001 +From: Eric Covener +Date: Tue, 10 Jan 2023 13:21:48 +0000 +Subject: [PATCH] Merge r1906487 from trunk: + +* modules/dav/main/util.c (dav_process_if_header): Fix error + path for "Not" prefix parsing. + + +Submitted By: jorton +Reviewed By: jorton, covener, rpluem + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906543 13f79535-47bb-0310-9956-ffa450edef68 +--- + STATUS | 8 -------- + modules/dav/main/util.c | 8 +++++++- + 2 files changed, 7 insertions(+), 9 deletions(-) + +--- a/modules/dav/main/util.c ++++ b/modules/dav/main/util.c +@@ -746,8 +746,14 @@ + "for the same state."); + } + condition = DAV_IF_COND_NOT; ++ list += 2; ++ } ++ else { ++ return dav_new_error(r->pool, HTTP_BAD_REQUEST, ++ DAV_ERR_IF_UNK_CHAR, 0, ++ "Invalid \"If:\" header: " ++ "Unexpected character in List"); + } +- list += 2; + break; + + case ' ': diff --git a/debian/patches/CVE-2021-33193.patch b/debian/patches/CVE-2021-33193.patch new file mode 100644 index 0000000..d2737b8 --- /dev/null +++ b/debian/patches/CVE-2021-33193.patch @@ -0,0 +1,702 @@ +Description: Fix for CVE-2021-33193: mod_proxy HTTP/2 validation bypass + A crafted method sent through HTTP/2 will bypass validation and be forwarded by + mod_proxy, which can lead to request splitting or cache poisoning. +Origin: other, https://git.centos.org/rpms/httpd/blob/c496dea5e0b6e82a9f503e973fc5d5ea93a94180/f/SOURCES/httpd-2.4.37-CVE-2021-33193.patch +Forwarded: not-needed +Applied-Upstream: 2.4.49, https://github.com/apache/httpd/commit/ecebcc035ccd8d0e2984fe41420d9e944f456b3c +Last-Update: 2023-02-24 +--- +This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ +--- a/include/http_core.h ++++ b/include/http_core.h +@@ -741,6 +741,7 @@ + #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 @@ + 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 @@ + */ + + /** ++ * 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 @@ + 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_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/server/core.c ++++ b/server/core.c +@@ -494,6 +494,8 @@ + conf->protocols_honor_order = -1; + conf->merge_slashes = AP_CORE_CONFIG_UNSET; + ++ conf->strict_host_check= AP_CORE_CONFIG_UNSET; ++ + return (void *)conf; + } + +@@ -559,7 +561,13 @@ + base->protocols_honor_order : + 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; + } + +@@ -4518,7 +4526,10 @@ + AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO, + "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"), +@@ -5492,4 +5503,3 @@ + core_cmds, /* command apr_table_t */ + register_hooks /* register hooks */ + }; +- +--- a/server/core_filters.c ++++ b/server/core_filters.c +@@ -84,11 +84,6 @@ + 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 +@@ -611,8 +611,15 @@ + } + + 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. +@@ -649,13 +656,6 @@ + + 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); +@@ -720,6 +720,20 @@ + } + + 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; + +@@ -751,7 +765,6 @@ + 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]) +@@ -782,7 +795,6 @@ + /* 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]) +@@ -875,6 +887,14 @@ + 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) { +@@ -982,6 +1002,79 @@ + 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) + { +@@ -1265,16 +1358,10 @@ + 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"); +@@ -1313,6 +1400,7 @@ + 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, +@@ -1323,12 +1411,33 @@ + 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: +@@ -1344,49 +1453,38 @@ + "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"); +@@ -1402,13 +1500,8 @@ + 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 +@@ -1421,88 +1514,81 @@ + } + } + +- 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_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; + } + + AP_DECLARE(int) ap_post_read_request(request_rec *r) +--- 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 @@ + } + + +-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 +@@ -1050,11 +1057,18 @@ + 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; + } + + +@@ -1071,7 +1085,7 @@ + * 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. + */ + +@@ -1135,10 +1149,16 @@ + + 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) { + /* +@@ -1151,8 +1171,8 @@ + 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) { + /* +@@ -1173,10 +1193,16 @@ + /* 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 --git a/debian/patches/CVE-2022-36760.patch b/debian/patches/CVE-2022-36760.patch new file mode 100644 index 0000000..ebeefa3 --- /dev/null +++ b/debian/patches/CVE-2022-36760.patch @@ -0,0 +1,27 @@ +From d93e61e3e9622bacff746772cb9c97fdcaed8baf Mon Sep 17 00:00:00 2001 +From: Eric Covener +Date: Tue, 10 Jan 2023 13:20:55 +0000 +Subject: [PATCH] Merge r1906540 from trunk: + +cleanup on error + + +Reviewed By: rpluem, gbechis, covener + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906542 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/proxy/mod_proxy_ajp.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/modules/proxy/mod_proxy_ajp.c ++++ b/modules/proxy/mod_proxy_ajp.c +@@ -255,6 +255,8 @@ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10396) + "%s Transfer-Encoding is not supported", + tenc); ++ /* We had a failure: Close connection to backend */ ++ conn->close = 1; + return HTTP_INTERNAL_SERVER_ERROR; + } + } else { diff --git a/debian/patches/CVE-2022-37436.patch b/debian/patches/CVE-2022-37436.patch new file mode 100644 index 0000000..a123959 --- /dev/null +++ b/debian/patches/CVE-2022-37436.patch @@ -0,0 +1,125 @@ +From 8b6d55f6a047acf62675e32606b037f5eea8ccc7 Mon Sep 17 00:00:00 2001 +From: Eric Covener +Date: Tue, 10 Jan 2023 13:20:09 +0000 +Subject: [PATCH] Merge r1906539 from trunk: + +fail on bad header + +Submitted By: covener +Reviewed By: covener, rpluem, gbechis + + +git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1906541 13f79535-47bb-0310-9956-ffa450edef68 +--- + modules/proxy/mod_proxy_http.c | 46 ++++++++++++++++++++-------------- + server/protocol.c | 2 ++ + 2 files changed, 29 insertions(+), 19 deletions(-) + +--- a/modules/proxy/mod_proxy_http.c ++++ b/modules/proxy/mod_proxy_http.c +@@ -1011,7 +1011,7 @@ + * any sense at all, since we depend on buffer still containing + * what was read by ap_getline() upon return. + */ +-static void ap_proxy_read_headers(request_rec *r, request_rec *rr, ++static apr_status_t ap_proxy_read_headers(request_rec *r, request_rec *rr, + char *buffer, int size, + conn_rec *c, int *pread_len) + { +@@ -1043,19 +1043,26 @@ + rc = ap_proxygetline(tmp_bb, buffer, size, rr, + AP_GETLINE_FOLD | AP_GETLINE_NOSPC_EOL, &len); + +- if (len <= 0) +- break; + +- if (APR_STATUS_IS_ENOSPC(rc)) { +- /* The header could not fit in the provided buffer, warn. +- * XXX: falls through with the truncated header, 5xx instead? +- */ +- int trunc = (len > 128 ? 128 : len) / 2; +- ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) +- "header size is over the limit allowed by " +- "ResponseFieldSize (%d bytes). " +- "Bad response header: '%.*s[...]%s'", +- size, trunc, buffer, buffer + len - trunc); ++ if (rc != APR_SUCCESS) { ++ if (APR_STATUS_IS_ENOSPC(rc)) { ++ int trunc = (len > 128 ? 128 : len) / 2; ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10124) ++ "header size is over the limit allowed by " ++ "ResponseFieldSize (%d bytes). " ++ "Bad response header: '%.*s[...]%s'", ++ size, trunc, buffer, buffer + len - trunc); ++ } ++ else { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, rc, r, APLOGNO(10404) ++ "Error reading headers from backend"); ++ } ++ r->headers_out = NULL; ++ return rc; ++ } ++ ++ if (len <= 0) { ++ break; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_TRACE4, 0, r, "%s", buffer); +@@ -1078,7 +1085,7 @@ + if (psc->badopt == bad_error) { + /* Nope, it wasn't even an extra HTTP header. Give up. */ + r->headers_out = NULL; +- return; ++ return APR_EINVAL; + } + else if (psc->badopt == bad_body) { + /* if we've already started loading headers_out, then +@@ -1092,13 +1099,13 @@ + "in headers returned by %s (%s)", + r->uri, r->method); + *pread_len = len; +- return; ++ return APR_SUCCESS; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01099) + "No HTTP headers returned by %s (%s)", + r->uri, r->method); +- return; ++ return APR_SUCCESS; + } + } + } +@@ -1128,6 +1135,7 @@ + process_proxy_header(r, dconf, buffer, value); + saw_headers = 1; + } ++ return APR_SUCCESS; + } + + +@@ -1398,10 +1406,10 @@ + "Set-Cookie", NULL); + + /* shove the headers direct into r->headers_out */ +- ap_proxy_read_headers(r, backend->r, buffer, response_field_size, origin, +- &pread_len); ++ rc = ap_proxy_read_headers(r, backend->r, buffer, response_field_size, ++ origin, &pread_len); + +- if (r->headers_out == NULL) { ++ if (rc != APR_SUCCESS || r->headers_out == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01106) + "bad HTTP/%d.%d header returned by %s (%s)", + major, minor, r->uri, r->method); +--- a/server/protocol.c ++++ b/server/protocol.c +@@ -508,6 +508,8 @@ + /* PR#43039: We shouldn't accept NULL bytes within the line */ + bytes_handled = strlen(*s); + if (bytes_handled < *read) { ++ ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf, ++ "NULL bytes in header", *s, *read, 0); + *read = bytes_handled; + if (rv == APR_SUCCESS) { + rv = APR_EINVAL; diff --git a/debian/patches/series b/debian/patches/series index 5ac5730..c430013 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -49,3 +49,7 @@ CVE-2022-29404.patch CVE-2022-30522.patch CVE-2022-30556.patch CVE-2022-31813.patch +CVE-2006-20001.patch +CVE-2022-36760.patch +CVE-2022-37436.patch +CVE-2021-33193.patch diff --git a/debian/perl-framework/t/security/CVE-2019-0215.t b/debian/perl-framework/t/security/CVE-2019-0215.t new file mode 100644 index 0000000..978c1ef --- /dev/null +++ b/debian/perl-framework/t/security/CVE-2019-0215.t @@ -0,0 +1,47 @@ +use strict; +use warnings FATAL => 'all'; + +use Apache::Test; +use Apache::TestUtil; +use Apache::TestRequest; + +my $vars = Apache::Test::vars(); + +plan tests => 2, need $vars->{ssl_module_name}, need_lwp, + qw(LWP::Protocol::https); + +my $r; + +Apache::TestRequest::user_agent(ssl_opts => {SSL_version => 'TLSv13'}); +Apache::TestRequest::scheme('https'); +Apache::TestRequest::module('ssl_optional_cc'); + +$r = GET "/require/none/"; +my $tls13_works = $r->is_success; + +# Forget the above user agent settings, start fresh +Apache::TestRequest::user_agent(reset => 1); + +# If TLS 1.3 worked, run the tests using it and expect 403. +# Older TLS versions seem to show the TLS alert client side as a 500. +my $expected_status; +if ($tls13_works) { + Apache::TestRequest::user_agent(ssl_opts => {SSL_version => 'TLSv13'}); + $expected_status = 403; + t_debug "Using TLSv13, expecting status 403"; +} else { + t_debug "Using TLS before TLSv13, expecting status 500"; + $expected_status = 500; +} + +Apache::TestRequest::user_agent_keepalive(1); +Apache::TestRequest::scheme('https'); +Apache::TestRequest::module('ssl_optional_cc'); + +$r = GET "/require/any/"; + +ok t_cmp($r->code, $expected_status, "first access denied without client cert"); + +$r = GET "/require/any/"; + +ok t_cmp($r->code, $expected_status, "second access denied without client cert"); diff --git a/debian/perl-framework/t/security/CVE-2020-1927.t b/debian/perl-framework/t/security/CVE-2020-1927.t new file mode 100644 index 0000000..523feb6 --- /dev/null +++ b/debian/perl-framework/t/security/CVE-2020-1927.t @@ -0,0 +1,60 @@ +use strict; +use warnings FATAL => 'all'; + +use Apache::Test; +use Apache::TestRequest; +use Apache::TestUtil; +use MIME::Base64; +use Data::Dumper; +use HTTP::Response; +use Socket; + +plan tests => 1, need_min_apache_version('2.4.42'); + +my $sock = Apache::TestRequest::vhost_socket("core"); +if (!$sock) { + print "# failed to connect\n"; + ok(0); + next; +} + +my $req = sprintf "GET /CVE-2020-1927/%%0D%%0Ahttp://127.0.0.1/ HTTP/1.1\r\nHost: merge-disabled\r\nConnection: close\r\n\r\n"; +print "# SENDING to " . peer($sock) . "\n# $req\n"; +$sock->print("$req"); +$sock->flush(); +sleep(0.1); +$req = escape($req); +print "# SENDING to " . peer($sock) . "\n# $req\n"; + +my $response_data = ""; +my $buf; +while ($sock->read($buf, 10000) > 0) { + $response_data .= $buf; +} +my $response = HTTP::Response->parse($response_data); +if (! defined $response) { + die "HTTP::Response->parse failed"; +} +ok t_cmp($response->code, 404, "regex didn't match and redirect"); + +sub escape +{ + my $in = shift; + $in =~ s{\\}{\\\\}g; + $in =~ s{\r}{\\r}g; + $in =~ s{\n}{\\n}g; + $in =~ s{\t}{\\t}g; + $in =~ s{([\x00-\x1f])}{sprintf("\\x%02x", ord($1))}ge; + return $in; +} + +sub peer +{ + my $sock = shift; + my $hersockaddr = getpeername($sock); + return "" if !$hersockaddr; + my ($port, $iaddr) = sockaddr_in($hersockaddr); + my $herhostname = gethostbyaddr($iaddr, AF_INET); + my $herstraddr = inet_ntoa($iaddr); + return "$herstraddr:$port"; +} -- cgit v1.2.3