From fa04996bf6dac739da13b802455f77bac86d8b87 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Jan 21 2020 21:08:53 +0000 Subject: import httpd-2.4.37-21.module+el8.2.0+5008+cca404a3 --- diff --git a/README.debrand b/README.debrand deleted file mode 100644 index 01c46d2..0000000 --- a/README.debrand +++ /dev/null @@ -1,2 +0,0 @@ -Warning: This package was configured for automatic debranding, but the changes -failed to apply. diff --git a/SOURCES/01-md.conf b/SOURCES/01-md.conf deleted file mode 100644 index 2739202..0000000 --- a/SOURCES/01-md.conf +++ /dev/null @@ -1 +0,0 @@ -LoadModule md_module modules/mod_md.so diff --git a/SOURCES/config.layout b/SOURCES/config.layout new file mode 100644 index 0000000..3a9f6c8 --- /dev/null +++ b/SOURCES/config.layout @@ -0,0 +1,24 @@ +# Layout used in Fedora httpd packaging. + + prefix: /etc/httpd + localstatedir: /var + exec_prefix: /usr + bindir: ${exec_prefix}/bin + sbindir: ${exec_prefix}/sbin + libdir: ${exec_prefix}/lib + libexecdir: ${exec_prefix}/libexec + mandir: ${exec_prefix}/man + sysconfdir: /etc/httpd/conf + datadir: ${exec_prefix}/share/httpd + installbuilddir: ${libdir}/httpd/build + errordir: ${datadir}/error + iconsdir: ${datadir}/icons + htdocsdir: ${localstatedir}/www/html + manualdir: ${datadir}/manual + cgidir: ${localstatedir}/www/cgi-bin + includedir: ${exec_prefix}/include/httpd + runtimedir: ${prefix}/run + logfiledir: ${localstatedir}/log/httpd + statedir: ${prefix}/state + proxycachedir: ${localstatedir}/cache/httpd/proxy + diff --git a/SOURCES/httpd-2.4.34-CVE-2019-9511-and-9516-and-9517.patch b/SOURCES/httpd-2.4.34-CVE-2019-9511-and-9516-and-9517.patch new file mode 100644 index 0000000..7cee845 --- /dev/null +++ b/SOURCES/httpd-2.4.34-CVE-2019-9511-and-9516-and-9517.patch @@ -0,0 +1,19 @@ +diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c +index 16e39be..2543693 100644 +--- a/server/mpm/event/event.c ++++ b/server/mpm/event/event.c +@@ -1111,10 +1111,11 @@ read_request: + "network write failure in core output filter"); + cs->pub.state = CONN_STATE_LINGER; + } +- else if (c->data_in_output_filters) { ++ else if (c->data_in_output_filters || ++ cs->pub.sense == CONN_SENSE_WANT_READ) { + /* Still in WRITE_COMPLETION_STATE: +- * Set a write timeout for this connection, and let the +- * event thread poll for writeability. ++ * Set a read/write timeout for this connection, and let the ++ * event thread poll for read/writeability. + */ + cs->queue_timestamp = apr_time_now(); + notify_suspend(cs); diff --git a/SOURCES/httpd-2.4.35-layout.patch b/SOURCES/httpd-2.4.35-layout.patch deleted file mode 100644 index 7633871..0000000 --- a/SOURCES/httpd-2.4.35-layout.patch +++ /dev/null @@ -1,35 +0,0 @@ - -Add layout for Fedora. - -diff --git a/config.layout b/config.layout -index 8579587..79fbce7 100644 ---- a/config.layout -+++ b/config.layout -@@ -394,3 +394,27 @@ - logfiledir: ${localstatedir}/log/httpd - proxycachedir: ${localstatedir}/cache/httpd - -+ -+# Fedora/RHEL layout -+ -+ prefix: /usr -+ exec_prefix: ${prefix} -+ bindir: ${prefix}/bin -+ sbindir: ${prefix}/sbin -+ libdir: ${prefix}/lib -+ libexecdir: ${prefix}/libexec -+ mandir: ${prefix}/man -+ sysconfdir: /etc/httpd/conf -+ datadir: ${prefix}/share/httpd -+ installbuilddir: ${libdir}/httpd/build -+ errordir: ${datadir}/error -+ iconsdir: ${datadir}/icons -+ htdocsdir: /var/www/html -+ manualdir: ${datadir}/manual -+ cgidir: /var/www/cgi-bin -+ includedir: ${prefix}/include/httpd -+ localstatedir: /var -+ runtimedir: /run/httpd -+ logfiledir: ${localstatedir}/log/httpd -+ proxycachedir: ${localstatedir}/cache/httpd/proxy -+ diff --git a/SOURCES/httpd-2.4.35-ocsp-wrong-ctx.patch b/SOURCES/httpd-2.4.35-ocsp-wrong-ctx.patch new file mode 100644 index 0000000..5523ea5 --- /dev/null +++ b/SOURCES/httpd-2.4.35-ocsp-wrong-ctx.patch @@ -0,0 +1,15 @@ +diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c +index a5e86e4..6611610 100644 +--- a/modules/ssl/ssl_engine_kernel.c ++++ b/modules/ssl/ssl_engine_kernel.c +@@ -1823,8 +1823,8 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx) + /* + * 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 --git a/SOURCES/httpd-2.4.37-CVE-2019-0220.patch b/SOURCES/httpd-2.4.37-CVE-2019-0220.patch index a88cc07..1fcb68e 100644 --- a/SOURCES/httpd-2.4.37-CVE-2019-0220.patch +++ b/SOURCES/httpd-2.4.37-CVE-2019-0220.patch @@ -185,3 +185,51 @@ index fd7a0a1..e0c558c 100644 /* * copy at most n leading directories of s into d +diff --git a/server/core.c b/server/core.c +index b5ab429..a31f1e4 100644 +--- a/server/core.c ++++ b/server/core.c +@@ -493,6 +493,7 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s) + */ + + conf->trace_enable = AP_TRACE_UNSET; ++ conf->merge_slashes = AP_CORE_CONFIG_UNSET; + + conf->protocols = apr_array_make(a, 5, sizeof(const char *)); + conf->protocols_honor_order = -1; +@@ -561,7 +562,9 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv) + conf->protocols_honor_order = ((virt->protocols_honor_order < 0)? + base->protocols_honor_order : + virt->protocols_honor_order); +- ++ ++ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt); ++ + return conf; + } + +@@ -1872,6 +1875,13 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag) + return NULL; + } + ++static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag) ++{ ++ core_server_config *conf = ++ ap_get_core_module_config(cmd->server->module_config); ++ return ap_set_flag_slot(cmd, conf, flag); ++} ++ + static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) + { + core_dir_config *d = d_; +@@ -4598,6 +4608,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON + "'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"), + AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF, + "Registers non-standard HTTP methods"), ++AP_INIT_FLAG("MergeSlashes", set_core_server_flag, ++ (void *)APR_OFFSETOF(core_server_config, merge_slashes), ++ RSRC_CONF, ++ "Controls whether consecutive slashes in the URI path are merged"), + { NULL } + }; + diff --git a/SOURCES/httpd-2.4.37-mod-md-mod-ssl-hooks.patch b/SOURCES/httpd-2.4.37-mod-md-mod-ssl-hooks.patch new file mode 100644 index 0000000..d7df65a --- /dev/null +++ b/SOURCES/httpd-2.4.37-mod-md-mod-ssl-hooks.patch @@ -0,0 +1,544 @@ +diff --git a/modules/ssl/mod_ssl.h b/modules/ssl/mod_ssl.h +index 24a65a0..a360911 100644 +--- a/modules/ssl/mod_ssl.h ++++ b/modules/ssl/mod_ssl.h +@@ -29,6 +29,7 @@ + #include "httpd.h" + #include "http_config.h" + #include "apr_optional.h" ++#include "apr_tables.h" /* for apr_array_header_t */ + + /* Create a set of SSL_DECLARE(type), SSL_DECLARE_NONSTD(type) and + * SSL_DECLARE_DATA with appropriate export and import tags for the platform +@@ -86,6 +87,34 @@ APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *)); + APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *, + ap_conf_vector_t *, + int proxy, int enable)); ++ ++/* Check for availability of new hooks */ ++#define SSL_CERT_HOOKS ++#ifdef SSL_CERT_HOOKS ++ ++/** Lets others add certificate and key files to the given server. ++ * For each cert a key must also be added. ++ * @param cert_file and array of const char* with the path to the certificate chain ++ * @param key_file and array of const char* with the path to the private key file ++ */ ++APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_cert_files, ++ (server_rec *s, apr_pool_t *p, ++ apr_array_header_t *cert_files, ++ apr_array_header_t *key_files)) ++ ++/** In case no certificates are available for a server, this ++ * lets other modules add a fallback certificate for the time ++ * being. Regular requests against this server will be answered ++ * with a 503. ++ * @param cert_file and array of const char* with the path to the certificate chain ++ * @param key_file and array of const char* with the path to the private key file ++ */ ++APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, add_fallback_cert_files, ++ (server_rec *s, apr_pool_t *p, ++ apr_array_header_t *cert_files, ++ apr_array_header_t *key_files)) ++ ++#endif /* SSL_CERT_HOOKS */ + + #endif /* __MOD_SSL_H__ */ + /** @} */ +diff --git a/modules/ssl/mod_ssl_openssl.h b/modules/ssl/mod_ssl_openssl.h +index 0fa654a..d4f684f 100644 +--- a/modules/ssl/mod_ssl_openssl.h ++++ b/modules/ssl/mod_ssl_openssl.h +@@ -69,5 +69,45 @@ APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, pre_handshake, + APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, proxy_post_handshake, + (conn_rec *c, SSL *ssl)) + ++/** On TLS connections that do not relate to a configured virtual host, ++ * allow other modules to provide a X509 certificate and EVP_PKEY to ++ * be used on the connection. This first hook which does not ++ * return DECLINED will determine the outcome. */ ++APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, answer_challenge, ++ (conn_rec *c, const char *server_name, ++ X509 **pcert, EVP_PKEY **pkey)) ++ ++/** During post_config phase, ask around if someone wants to provide ++ * OCSP stapling status information for the given cert (with the also ++ * provided issuer certificate). The first hook which does not ++ * return DECLINED promises to take responsibility (and respond ++ * in later calls via hook ssl_get_stapling_status). ++ * If no hook takes over, mod_ssl's own stapling implementation will ++ * be applied (if configured). ++ */ ++APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, init_stapling_status, ++ (server_rec *s, apr_pool_t *p, ++ X509 *cert, X509 *issuer)) ++ ++/** Anyone answering positive to ssl_init_stapling_status for a ++ * certificate, needs to register here and supply the actual OCSP stapling ++ * status data (OCSP_RESP) for a new connection. ++ * A hook supplying the response data must return APR_SUCCESS. ++ * The data is returned in DER encoded bytes via pder and pderlen. The ++ * returned pointer may be NULL, which indicates that data is (currently) ++ * unavailable. ++ * If DER data is returned, it MUST come from a response with ++ * status OCSP_RESPONSE_STATUS_SUCCESSFUL and V_OCSP_CERTSTATUS_GOOD ++ * or V_OCSP_CERTSTATUS_REVOKED, not V_OCSP_CERTSTATUS_UNKNOWN. This means ++ * errors in OCSP retrieval are to be handled/logged by the hook and ++ * are not done by mod_ssl. ++ * Any DER bytes returned MUST be allocated via malloc() and ownership ++ * passes to mod_ssl. Meaning, the hook must return a malloced copy of ++ * the data it has. mod_ssl (or OpenSSL) will free it. ++ */ ++APR_DECLARE_EXTERNAL_HOOK(ssl, SSL, int, get_stapling_status, ++ (unsigned char **pder, int *pderlen, ++ conn_rec *c, server_rec *s, X509 *cert)) ++ + #endif /* __MOD_SSL_OPENSSL_H__ */ + /** @} */ +diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c +index 21e41e2..ef631c1 100644 +--- a/modules/ssl/ssl_engine_init.c ++++ b/modules/ssl/ssl_engine_init.c +@@ -36,6 +36,25 @@ APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_server, + (server_rec *s,apr_pool_t *p,int is_proxy,SSL_CTX *ctx), + (s,p,is_proxy,ctx), OK, DECLINED) + ++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_cert_files, ++ (server_rec *s, apr_pool_t *p, ++ apr_array_header_t *cert_files, apr_array_header_t *key_files), ++ (s, p, cert_files, key_files), ++ OK, DECLINED) ++ ++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, add_fallback_cert_files, ++ (server_rec *s, apr_pool_t *p, ++ apr_array_header_t *cert_files, apr_array_header_t *key_files), ++ (s, p, cert_files, key_files), ++ OK, DECLINED) ++ ++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, answer_challenge, ++ (conn_rec *c, const char *server_name, ++ X509 **pcert, EVP_PKEY **pkey), ++ (c, server_name, pcert, pkey), ++ DECLINED, DECLINED) ++ ++ + /* _________________________________________________________________ + ** + ** Module Initialization +@@ -165,18 +184,18 @@ static void ssl_add_version_components(apr_pool_t *p, + modver, AP_SERVER_BASEVERSION, incver); + } + +-/**************************************************************************************************/ +-/* Managed Domains Interface */ +- +-static APR_OPTIONAL_FN_TYPE(md_is_managed) *md_is_managed; +-static APR_OPTIONAL_FN_TYPE(md_get_certificate) *md_get_certificate; +-static APR_OPTIONAL_FN_TYPE(md_is_challenge) *md_is_challenge; ++/* _________________________________________________________________ ++** ++** Let other answer special connection attempts. ++** Used in ACME challenge handling by mod_md. ++** _________________________________________________________________ ++*/ + + int ssl_is_challenge(conn_rec *c, const char *servername, + X509 **pcert, EVP_PKEY **pkey) + { +- if (md_is_challenge) { +- return md_is_challenge(c, servername, pcert, pkey); ++ if (APR_SUCCESS == ssl_run_answer_challenge(c, servername, pcert, pkey)) { ++ return 1; + } + *pcert = NULL; + *pkey = NULL; +@@ -231,16 +250,6 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, + ssl_config_global_create(base_server); /* just to avoid problems */ + ssl_config_global_fix(mc); + +- /* Initialize our interface to mod_md, if it is loaded +- */ +- md_is_managed = APR_RETRIEVE_OPTIONAL_FN(md_is_managed); +- md_get_certificate = APR_RETRIEVE_OPTIONAL_FN(md_get_certificate); +- md_is_challenge = APR_RETRIEVE_OPTIONAL_FN(md_is_challenge); +- if (!md_is_managed || !md_get_certificate) { +- md_is_managed = NULL; +- md_get_certificate = NULL; +- } +- + /* + * try to fix the configuration and open the dedicated SSL + * logfile as early as possible +@@ -1392,8 +1401,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s, + * loaded via SSLOpenSSLConfCmd Certificate), so for 1.0.2 and + * later, we defer to the code in ssl_init_server_ctx. + */ +- if ((mctx->stapling_enabled == TRUE) && +- !ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) { ++ if (!ssl_stapling_init_cert(s, p, ptemp, mctx, cert)) { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, APLOGNO(02567) + "Unable to configure certificate %s for stapling", + key_id); +@@ -1788,11 +1796,13 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, + apr_array_header_t *pphrases) + { + apr_status_t rv; ++ modssl_pk_server_t *pks; + #ifdef HAVE_SSL_CONF_CMD + ssl_ctx_param_t *param = (ssl_ctx_param_t *)sc->server->ssl_ctx_param->elts; + SSL_CONF_CTX *cctx = sc->server->ssl_ctx_config; + int i; + #endif ++ int n; + + /* + * Check for problematic re-initializations +@@ -1804,50 +1814,24 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, + return APR_EGENERAL; + } + +- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(10083) +- "Init: (%s) mod_md support is %s.", ssl_util_vhostid(p, s), +- md_is_managed? "available" : "unavailable"); +- if (md_is_managed && md_is_managed(s)) { +- modssl_pk_server_t *const pks = sc->server->pks; +- if (pks->cert_files->nelts > 0 || pks->key_files->nelts > 0) { +- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10084) +- "Init: (%s) You configured certificate/key files on this host, but " +- "is is covered by a Managed Domain. You need to remove these directives " +- "for the Managed Domain to take over.", ssl_util_vhostid(p, s)); +- } +- else { +- const char *key_file, *cert_file, *chain_file; +- +- key_file = cert_file = chain_file = NULL; +- +- if (md_get_certificate) { +- rv = md_get_certificate(s, p, &key_file, &cert_file); +- } +- else { +- rv = APR_ENOTIMPL; +- } +- +- if (key_file && cert_file) { +- ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, s, +- "%s: installing key=%s, cert=%s, chain=%s", +- ssl_util_vhostid(p, s), key_file, cert_file, chain_file); +- APR_ARRAY_PUSH(pks->key_files, const char *) = key_file; +- APR_ARRAY_PUSH(pks->cert_files, const char *) = cert_file; +- sc->server->cert_chain = chain_file; +- } +- +- if (APR_STATUS_IS_EAGAIN(rv)) { +- /* Managed Domain not ready yet. This is not a reason to fail the config */ +- ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085) +- "Init: %s will respond with '503 Service Unavailable' for now. This " +- "host is part of a Managed Domain, but no SSL certificate is " +- "available (yet).", ssl_util_vhostid(p, s)); +- pks->service_unavailable = 1; +- } +- else if (rv != APR_SUCCESS) { +- return rv; +- } +- } ++ /* Allow others to provide certificate files */ ++ pks = sc->server->pks; ++ n = pks->cert_files->nelts; ++ ssl_run_add_cert_files(s, p, pks->cert_files, pks->key_files); ++ ++ if (n < pks->cert_files->nelts) { ++ /* this overrides any old chain configuration */ ++ sc->server->cert_chain = NULL; ++ } ++ ++ if (apr_is_empty_array(pks->cert_files) && !sc->server->cert_chain) { ++ ssl_run_add_fallback_cert_files(s, p, pks->cert_files, pks->key_files); ++ ++ pks->service_unavailable = 1; ++ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, s, APLOGNO(10085) ++ "Init: %s will respond with '503 Service Unavailable' for now. There " ++ "are no SSL certificates configured and no other module contributed any.", ++ ssl_util_vhostid(p, s)); + } + + if ((rv = ssl_init_ctx(s, p, ptemp, sc->server)) != APR_SUCCESS) { +@@ -1900,7 +1884,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, + * (late) point makes sure that we catch both certificates loaded + * via SSLCertificateFile and SSLOpenSSLConfCmd Certificate. + */ +- if (sc->server->stapling_enabled == TRUE) { ++ do { + X509 *cert; + int i = 0; + int ret = SSL_CTX_set_current_cert(sc->server->ssl_ctx, +@@ -1917,7 +1901,7 @@ static apr_status_t ssl_init_server_ctx(server_rec *s, + SSL_CERT_SET_NEXT); + i++; + } +- } ++ } while(0); + #endif + + #ifdef HAVE_TLS_SESSION_TICKETS +diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c +index e6a9f67..a5e86e4 100644 +--- a/modules/ssl/ssl_engine_kernel.c ++++ b/modules/ssl/ssl_engine_kernel.c +@@ -2303,6 +2303,37 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc) + } + + #ifdef HAVE_TLSEXT ++ ++static apr_status_t set_challenge_creds(conn_rec *c, const char *servername, ++ SSL *ssl, X509 *cert, EVP_PKEY *key) ++{ ++ SSLConnRec *sslcon = myConnConfig(c); ++ ++ sslcon->service_unavailable = 1; ++ if ((SSL_use_certificate(ssl, cert) < 1)) { ++ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086) ++ "Failed to configure challenge certificate %s", ++ servername); ++ return APR_EGENERAL; ++ } ++ ++ if (!SSL_use_PrivateKey(ssl, key)) { ++ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087) ++ "error '%s' using Challenge key: %s", ++ ERR_error_string(ERR_peek_last_error(), NULL), ++ servername); ++ return APR_EGENERAL; ++ } ++ ++ if (SSL_check_private_key(ssl) < 1) { ++ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088) ++ "Challenge certificate and private key %s " ++ "do not match", servername); ++ return APR_EGENERAL; ++ } ++ return APR_SUCCESS; ++} ++ + /* + * This function sets the virtual host from an extended + * client hello with a server name indication extension ("SNI", cf. RFC 6066). +@@ -2332,30 +2363,12 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl) + return APR_SUCCESS; + } + else if (ssl_is_challenge(c, servername, &cert, &key)) { +- +- sslcon->service_unavailable = 1; +- if ((SSL_use_certificate(ssl, cert) < 1)) { +- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10086) +- "Failed to configure challenge certificate %s", +- servername); ++ /* With ACMEv1 we can have challenge connections to a unknown domains ++ * that need to be answered with a special certificate and will ++ * otherwise not answer any requests. */ ++ if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) { + return APR_EGENERAL; + } +- +- if (!SSL_use_PrivateKey(ssl, key)) { +- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10087) +- "error '%s' using Challenge key: %s", +- ERR_error_string(ERR_peek_last_error(), NULL), +- servername); +- return APR_EGENERAL; +- } +- +- if (SSL_check_private_key(ssl) < 1) { +- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, c, APLOGNO(10088) +- "Challenge certificate and private key %s " +- "do not match", servername); +- return APR_EGENERAL; +- } +- + } + else { + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02044) +@@ -2648,6 +2661,23 @@ int ssl_callback_alpn_select(SSL *ssl, + proposed); + return SSL_TLSEXT_ERR_ALERT_FATAL; + } ++ ++ /* protocol was switched, this could be a challenge protocol such as "acme-tls/1". ++ * For that to work, we need to allow overrides to our ssl certificate. ++ * However, exclude challenge checks on our best known traffic protocol. ++ * (http/1.1 is the default, we never switch to it anyway.) ++ */ ++ if (strcmp("h2", proposed)) { ++ const char *servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); ++ X509 *cert; ++ EVP_PKEY *key; ++ ++ if (ssl_is_challenge(c, servername, &cert, &key)) { ++ if (set_challenge_creds(c, servername, ssl, cert, key) != APR_SUCCESS) { ++ return SSL_TLSEXT_ERR_ALERT_FATAL; ++ } ++ } ++ } + } + + return SSL_TLSEXT_ERR_OK; +diff --git a/modules/ssl/ssl_util_stapling.c b/modules/ssl/ssl_util_stapling.c +index c3e2cfa..4df0a9a 100644 +--- a/modules/ssl/ssl_util_stapling.c ++++ b/modules/ssl/ssl_util_stapling.c +@@ -31,12 +31,28 @@ + #include "ssl_private.h" + #include "ap_mpm.h" + #include "apr_thread_mutex.h" ++#include "mod_ssl_openssl.h" ++ ++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, init_stapling_status, ++ (server_rec *s, apr_pool_t *p, ++ X509 *cert, X509 *issuer), ++ (s, p, cert, issuer), ++ DECLINED, DECLINED) ++ ++APR_IMPLEMENT_OPTIONAL_HOOK_RUN_ALL(ssl, SSL, int, get_stapling_status, ++ (unsigned char **pder, int *pderlen, ++ conn_rec *c, server_rec *s, X509 *cert), ++ (pder, pderlen, c, s, cert), ++ DECLINED, DECLINED) ++ + + #ifdef HAVE_OCSP_STAPLING + + static int stapling_cache_mutex_on(server_rec *s); + static int stapling_cache_mutex_off(server_rec *s); + ++static int stapling_cb(SSL *ssl, void *arg); ++ + /** + * Maxiumum OCSP stapling response size. This should be the response for a + * single certificate and will typically include the responder certificate chain +@@ -119,7 +135,38 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp, + OCSP_CERTID *cid = NULL; + STACK_OF(OPENSSL_STRING) *aia = NULL; + +- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1)) ++ if (x == NULL) ++ return 0; ++ ++ if (!(issuer = stapling_get_issuer(mctx, x))) { ++ /* In Apache pre 2.4.40, we use to come here only when mod_ssl stapling ++ * was enabled. With the new hooks, we give other modules the chance ++ * to provide stapling status. However, we do not want to log ssl errors ++ * where we did not do so in the past. */ ++ if (mctx->stapling_enabled == TRUE) { ++ ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217) ++ "ssl_stapling_init_cert: can't retrieve issuer " ++ "certificate!"); ++ return 0; ++ } ++ return 1; ++ } ++ ++ if (ssl_run_init_stapling_status(s, p, x, issuer) == APR_SUCCESS) { ++ /* Someone's taken over or mod_ssl's own implementation is not enabled */ ++ if (mctx->stapling_enabled != TRUE) { ++ SSL_CTX_set_tlsext_status_cb(mctx->ssl_ctx, stapling_cb); ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO() "OCSP stapling added via hook"); ++ } ++ return 1; ++ } ++ ++ if (mctx->stapling_enabled != TRUE) { ++ /* mod_ssl's own implementation is not enabled */ ++ return 1; ++ } ++ ++ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) + return 0; + + cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx)); +@@ -139,13 +186,6 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp, + return 1; + } + +- if (!(issuer = stapling_get_issuer(mctx, x))) { +- ssl_log_xerror(SSLLOG_MARK, APLOG_ERR, 0, ptemp, s, x, APLOGNO(02217) +- "ssl_stapling_init_cert: can't retrieve issuer " +- "certificate!"); +- return 0; +- } +- + cid = OCSP_cert_to_id(NULL, x, issuer); + X509_free(issuer); + if (!cid) { +@@ -182,18 +222,16 @@ int ssl_stapling_init_cert(server_rec *s, apr_pool_t *p, apr_pool_t *ptemp, + mctx->sc->vhost_id); + + apr_hash_set(stapling_certinfo, cinf->idx, sizeof(cinf->idx), cinf); +- ++ + return 1; + } + +-static certinfo *stapling_get_certinfo(server_rec *s, modssl_ctx_t *mctx, ++static certinfo *stapling_get_certinfo(server_rec *s, X509 *x, modssl_ctx_t *mctx, + SSL *ssl) + { + certinfo *cinf; +- X509 *x; + UCHAR idx[SHA_DIGEST_LENGTH]; +- x = SSL_get_certificate(ssl); +- if ((x == NULL) || (X509_digest(x, EVP_sha1(), idx, NULL) != 1)) ++ if (X509_digest(x, EVP_sha1(), idx, NULL) != 1) + return NULL; + cinf = apr_hash_get(stapling_certinfo, idx, sizeof(idx)); + if (cinf && cinf->cid) +@@ -750,18 +788,34 @@ static int stapling_cb(SSL *ssl, void *arg) + OCSP_RESPONSE *rsp = NULL; + int rv; + BOOL ok = TRUE; ++ X509 *x; ++ unsigned char *rspder = NULL; ++ int rspderlen; + ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951) ++ "stapling_cb: OCSP Stapling callback called"); ++ ++ x = SSL_get_certificate(ssl); ++ if (x == NULL) { ++ return SSL_TLSEXT_ERR_NOACK; ++ } ++ ++ if (ssl_run_get_stapling_status(&rspder, &rspderlen, conn, s, x) == APR_SUCCESS) { ++ /* a hook handles stapling for this certicate and determines the response */ ++ if (rspder == NULL || rspderlen <= 0) { ++ return SSL_TLSEXT_ERR_NOACK; ++ } ++ SSL_set_tlsext_status_ocsp_resp(ssl, rspder, rspderlen); ++ return SSL_TLSEXT_ERR_OK; ++ } ++ + if (sc->server->stapling_enabled != TRUE) { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01950) + "stapling_cb: OCSP Stapling disabled"); + return SSL_TLSEXT_ERR_NOACK; + } + +- ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01951) +- "stapling_cb: OCSP Stapling callback called"); +- +- cinf = stapling_get_certinfo(s, mctx, ssl); +- if (cinf == NULL) { ++ if ((cinf = stapling_get_certinfo(s, x, mctx, ssl)) == NULL) { + return SSL_TLSEXT_ERR_NOACK; + } + +@@ -864,9 +918,10 @@ apr_status_t modssl_init_stapling(server_rec *s, apr_pool_t *p, + if (mctx->stapling_responder_timeout == UNSET) { + mctx->stapling_responder_timeout = 10 * APR_USEC_PER_SEC; + } ++ + SSL_CTX_set_tlsext_status_cb(ctx, stapling_cb); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, APLOGNO(01960) "OCSP stapling initialized"); +- ++ + return APR_SUCCESS; + } + diff --git a/SOURCES/httpd-2.4.37-mod-mime-magic-strdup.patch b/SOURCES/httpd-2.4.37-mod-mime-magic-strdup.patch new file mode 100644 index 0000000..e093818 --- /dev/null +++ b/SOURCES/httpd-2.4.37-mod-mime-magic-strdup.patch @@ -0,0 +1,24 @@ +diff --git a/docs/conf/magic b/docs/conf/magic +index 7c56119..bc891d9 100644 +--- a/docs/conf/magic ++++ b/docs/conf/magic +@@ -87,7 +87,7 @@ + # Microsoft WAVE format (*.wav) + # [GRR 950115: probably all of the shorts and longs should be leshort/lelong] + # Microsoft RIFF +-0 string RIFF audio/unknown ++0 string RIFF + # - WAVE format + >8 string WAVE audio/x-wav + # MPEG audio. +--- a/modules/metadata/mod_mime_magic.c 2013/06/11 07:36:13 1491699 ++++ b/modules/metadata/mod_mime_magic.c 2013/06/11 07:41:40 1491700 +@@ -606,7 +606,7 @@ + /* high overhead for 1 char - just hope they don't do this much */ + str[0] = c; + str[1] = '\0'; +- return magic_rsl_add(r, str); ++ return magic_rsl_add(r, apr_pstrdup(r->pool, str)); + } + + /* allocate and copy a contiguous string from a result string list */ diff --git a/SOURCES/httpd-2.4.37-r1828172+.patch b/SOURCES/httpd-2.4.37-r1828172+.patch new file mode 100644 index 0000000..72b124b --- /dev/null +++ b/SOURCES/httpd-2.4.37-r1828172+.patch @@ -0,0 +1,1420 @@ +# ./pullrev.sh 1828172 1862968 1863191 1867878 1867882 1867968 1867970 1867971 +http://svn.apache.org/viewvc?view=revision&revision=1828172 +http://svn.apache.org/viewvc?view=revision&revision=1862968 +http://svn.apache.org/viewvc?view=revision&revision=1863191 +http://svn.apache.org/viewvc?view=revision&revision=1867878 +http://svn.apache.org/viewvc?view=revision&revision=1867882 +http://svn.apache.org/viewvc?view=revision&revision=1867968 +http://svn.apache.org/viewvc?view=revision&revision=1867970 +http://svn.apache.org/viewvc?view=revision&revision=1867971 + +--- httpd-2.4.41/modules/generators/mod_cgi.c ++++ httpd-2.4.41/modules/generators/mod_cgi.c +@@ -92,6 +92,10 @@ + apr_size_t bufbytes; + } cgi_server_conf; + ++typedef struct { ++ apr_interval_time_t timeout; ++} cgi_dirconf; ++ + static void *create_cgi_config(apr_pool_t *p, server_rec *s) + { + cgi_server_conf *c = +@@ -112,6 +116,12 @@ + return overrides->logname ? overrides : base; + } + ++static void *create_cgi_dirconf(apr_pool_t *p, char *dummy) ++{ ++ cgi_dirconf *c = (cgi_dirconf *) apr_pcalloc(p, sizeof(cgi_dirconf)); ++ return c; ++} ++ + static const char *set_scriptlog(cmd_parms *cmd, void *dummy, const char *arg) + { + server_rec *s = cmd->server; +@@ -150,6 +160,17 @@ + return NULL; + } + ++static const char *set_script_timeout(cmd_parms *cmd, void *dummy, const char *arg) ++{ ++ cgi_dirconf *dc = dummy; ++ ++ if (ap_timeout_parameter_parse(arg, &dc->timeout, "s") != APR_SUCCESS) { ++ return "CGIScriptTimeout has wrong format"; ++ } ++ ++ return NULL; ++} ++ + static const command_rec cgi_cmds[] = + { + AP_INIT_TAKE1("ScriptLog", set_scriptlog, NULL, RSRC_CONF, +@@ -158,6 +179,9 @@ + "the maximum length (in bytes) of the script debug log"), + AP_INIT_TAKE1("ScriptLogBuffer", set_scriptlog_buffer, NULL, RSRC_CONF, + "the maximum size (in bytes) to record of a POST request"), ++AP_INIT_TAKE1("CGIScriptTimeout", set_script_timeout, NULL, RSRC_CONF | ACCESS_CONF, ++ "The amount of time to wait between successful reads from " ++ "the CGI script, in seconds."), + {NULL} + }; + +@@ -471,23 +495,26 @@ + apr_filepath_name_get(r->filename)); + } + else { ++ cgi_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgi_module); ++ apr_interval_time_t timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; ++ + apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT); + + *script_in = procnew->out; + if (!*script_in) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_in, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_in, timeout); + + if (e_info->prog_type == RUN_AS_CGI) { + *script_out = procnew->in; + if (!*script_out) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_out, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_out, timeout); + + *script_err = procnew->err; + if (!*script_err) + return APR_EBADF; +- apr_file_pipe_timeout_set(*script_err, r->server->timeout); ++ apr_file_pipe_timeout_set(*script_err, timeout); + } + } + } +@@ -541,212 +568,10 @@ + return APR_SUCCESS; + } + +-static void discard_script_output(apr_bucket_brigade *bb) +-{ +- apr_bucket *e; +- const char *buf; +- apr_size_t len; +- apr_status_t rv; +- +- for (e = APR_BRIGADE_FIRST(bb); +- e != APR_BRIGADE_SENTINEL(bb); +- e = APR_BUCKET_NEXT(e)) +- { +- if (APR_BUCKET_IS_EOS(e)) { +- break; +- } +- rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); +- if (rv != APR_SUCCESS) { +- break; +- } +- } +-} +- + #if APR_FILES_AS_SOCKETS +- +-/* A CGI bucket type is needed to catch any output to stderr from the +- * script; see PR 22030. */ +-static const apr_bucket_type_t bucket_type_cgi; +- +-struct cgi_bucket_data { +- apr_pollset_t *pollset; +- request_rec *r; +-}; +- +-/* Create a CGI bucket using pipes from script stdout 'out' +- * and stderr 'err', for request 'r'. */ +-static apr_bucket *cgi_bucket_create(request_rec *r, +- apr_file_t *out, apr_file_t *err, +- apr_bucket_alloc_t *list) +-{ +- apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); +- apr_status_t rv; +- apr_pollfd_t fd; +- struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); +- +- APR_BUCKET_INIT(b); +- b->free = apr_bucket_free; +- b->list = list; +- b->type = &bucket_type_cgi; +- b->length = (apr_size_t)(-1); +- b->start = -1; +- +- /* Create the pollset */ +- rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) +- "apr_pollset_create(); check system or user limits"); +- return NULL; +- } +- +- fd.desc_type = APR_POLL_FILE; +- fd.reqevents = APR_POLLIN; +- fd.p = r->pool; +- fd.desc.f = out; /* script's stdout */ +- fd.client_data = (void *)1; +- rv = apr_pollset_add(data->pollset, &fd); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) +- "apr_pollset_add(); check system or user limits"); +- return NULL; +- } +- +- fd.desc.f = err; /* script's stderr */ +- fd.client_data = (void *)2; +- rv = apr_pollset_add(data->pollset, &fd); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) +- "apr_pollset_add(); check system or user limits"); +- return NULL; +- } +- +- data->r = r; +- b->data = data; +- return b; +-} +- +-/* Create a duplicate CGI bucket using given bucket data */ +-static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, +- apr_bucket_alloc_t *list) +-{ +- apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); +- APR_BUCKET_INIT(b); +- b->free = apr_bucket_free; +- b->list = list; +- b->type = &bucket_type_cgi; +- b->length = (apr_size_t)(-1); +- b->start = -1; +- b->data = data; +- return b; +-} +- +-/* Handle stdout from CGI child. Duplicate of logic from the _read +- * method of the real APR pipe bucket implementation. */ +-static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, +- const char **str, apr_size_t *len) +-{ +- char *buf; +- apr_status_t rv; +- +- *str = NULL; +- *len = APR_BUCKET_BUFF_SIZE; +- buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ +- +- rv = apr_file_read(out, buf, len); +- +- if (rv != APR_SUCCESS && rv != APR_EOF) { +- apr_bucket_free(buf); +- return rv; +- } +- +- if (*len > 0) { +- struct cgi_bucket_data *data = a->data; +- apr_bucket_heap *h; +- +- /* Change the current bucket to refer to what we read */ +- a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); +- h = a->data; +- h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ +- *str = buf; +- APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); +- } +- else { +- apr_bucket_free(buf); +- a = apr_bucket_immortal_make(a, "", 0); +- *str = a->data; +- } +- return rv; +-} +- +-/* Read method of CGI bucket: polls on stderr and stdout of the child, +- * sending any stderr output immediately away to the error log. */ +-static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, +- apr_size_t *len, apr_read_type_e block) +-{ +- struct cgi_bucket_data *data = b->data; +- apr_interval_time_t timeout; +- apr_status_t rv; +- int gotdata = 0; +- +- timeout = block == APR_NONBLOCK_READ ? 0 : data->r->server->timeout; +- +- do { +- const apr_pollfd_t *results; +- apr_int32_t num; +- +- rv = apr_pollset_poll(data->pollset, timeout, &num, &results); +- if (APR_STATUS_IS_TIMEUP(rv)) { +- if (timeout) { +- ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) +- "Timeout waiting for output from CGI script %s", +- data->r->filename); +- return rv; +- } +- else { +- return APR_EAGAIN; +- } +- } +- else if (APR_STATUS_IS_EINTR(rv)) { +- continue; +- } +- else if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) +- "poll failed waiting for CGI child"); +- return rv; +- } +- +- for (; num; num--, results++) { +- if (results[0].client_data == (void *)1) { +- /* stdout */ +- rv = cgi_read_stdout(b, results[0].desc.f, str, len); +- if (APR_STATUS_IS_EOF(rv)) { +- rv = APR_SUCCESS; +- } +- gotdata = 1; +- } else { +- /* stderr */ +- apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); +- if (APR_STATUS_IS_EOF(rv2)) { +- apr_pollset_remove(data->pollset, &results[0]); +- } +- } +- } +- +- } while (!gotdata); +- +- return rv; +-} +- +-static const apr_bucket_type_t bucket_type_cgi = { +- "CGI", 5, APR_BUCKET_DATA, +- apr_bucket_destroy_noop, +- cgi_bucket_read, +- apr_bucket_setaside_notimpl, +- apr_bucket_split_notimpl, +- apr_bucket_copy_notimpl +-}; +- ++#define WANT_CGI_BUCKET + #endif ++#include "cgi_common.h" + + static int cgi_handler(request_rec *r) + { +@@ -766,6 +591,8 @@ + apr_status_t rv; + cgi_exec_info_t e_info; + conn_rec *c; ++ cgi_dirconf *dc = ap_get_module_config(r->per_dir_config, &cgi_module); ++ apr_interval_time_t timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; +@@ -925,10 +752,7 @@ + AP_DEBUG_ASSERT(script_in != NULL); + + #if APR_FILES_AS_SOCKETS +- apr_file_pipe_timeout_set(script_in, 0); +- apr_file_pipe_timeout_set(script_err, 0); +- +- b = cgi_bucket_create(r, script_in, script_err, c->bucket_alloc); ++ b = cgi_bucket_create(r, dc->timeout, script_in, script_err, c->bucket_alloc); + if (b == NULL) + return HTTP_INTERNAL_SERVER_ERROR; + #else +@@ -938,111 +762,7 @@ + b = apr_bucket_eos_create(c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, b); + +- /* Handle script return... */ +- if (!nph) { +- const char *location; +- char sbuf[MAX_STRING_LEN]; +- int ret; +- +- if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, +- APLOG_MODULE_INDEX))) +- { +- ret = log_script(r, conf, ret, dbuf, sbuf, bb, script_err); +- +- /* +- * ret could be HTTP_NOT_MODIFIED in the case that the CGI script +- * does not set an explicit status and ap_meets_conditions, which +- * is called by ap_scan_script_header_err_brigade, detects that +- * the conditions of the requests are met and the response is +- * not modified. +- * In this case set r->status and return OK in order to prevent +- * running through the error processing stack as this would +- * break with mod_cache, if the conditions had been set by +- * mod_cache itself to validate a stale entity. +- * BTW: We circumvent the error processing stack anyway if the +- * CGI script set an explicit status code (whatever it is) and +- * the only possible values for ret here are: +- * +- * HTTP_NOT_MODIFIED (set by ap_meets_conditions) +- * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) +- * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the +- * processing of the response of the CGI script, e.g broken headers +- * or a crashed CGI process). +- */ +- if (ret == HTTP_NOT_MODIFIED) { +- r->status = ret; +- return OK; +- } +- +- return ret; +- } +- +- location = apr_table_get(r->headers_out, "Location"); +- +- if (location && r->status == 200) { +- /* For a redirect whether internal or not, discard any +- * remaining stdout from the script, and log any remaining +- * stderr output, as normal. */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- apr_file_pipe_timeout_set(script_err, r->server->timeout); +- log_script_err(r, script_err); +- } +- +- if (location && location[0] == '/' && r->status == 200) { +- /* This redirect needs to be a GET no matter what the original +- * method was. +- */ +- r->method = "GET"; +- r->method_number = M_GET; +- +- /* We already read the message body (if any), so don't allow +- * the redirected request to think it has one. We can ignore +- * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. +- */ +- apr_table_unset(r->headers_in, "Content-Length"); +- +- ap_internal_redirect_handler(location, r); +- return OK; +- } +- else if (location && r->status == 200) { +- /* XXX: Note that if a script wants to produce its own Redirect +- * body, it now has to explicitly *say* "Status: 302" +- */ +- return HTTP_MOVED_TEMPORARILY; +- } +- +- rv = ap_pass_brigade(r->output_filters, bb); +- } +- else /* nph */ { +- struct ap_filter_t *cur; +- +- /* get rid of all filters up through protocol... since we +- * haven't parsed off the headers, there is no way they can +- * work +- */ +- +- cur = r->proto_output_filters; +- while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { +- cur = cur->next; +- } +- r->output_filters = r->proto_output_filters = cur; +- +- rv = ap_pass_brigade(r->output_filters, bb); +- } +- +- /* don't soak up script output if errors occurred writing it +- * out... otherwise, we prolong the life of the script when the +- * connection drops or we stopped sending output for some other +- * reason */ +- if (rv == APR_SUCCESS && !r->connection->aborted) { +- apr_file_pipe_timeout_set(script_err, r->server->timeout); +- log_script_err(r, script_err); +- } +- +- apr_file_close(script_err); +- +- return OK; /* NOT r->status, even if it has changed. */ ++ return cgi_handle_response(r, nph, bb, timeout, conf, dbuf, script_err); + } + + /*============================================================================ +@@ -1277,7 +997,7 @@ + AP_DECLARE_MODULE(cgi) = + { + STANDARD20_MODULE_STUFF, +- NULL, /* dir config creater */ ++ create_cgi_dirconf, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_cgi_config, /* server config */ + merge_cgi_config, /* merge server config */ +--- httpd-2.4.41/modules/generators/config5.m4 ++++ httpd-2.4.41/modules/generators/config5.m4 +@@ -78,4 +78,15 @@ + + APR_ADDTO(INCLUDES, [-I\$(top_srcdir)/$modpath_current]) + ++AC_ARG_ENABLE(cgid-fdpassing, ++ [APACHE_HELP_STRING(--enable-cgid-fdpassing,Enable experimental mod_cgid support for fd passing)], ++ [if test "$enableval" = "yes"; then ++ AC_CHECK_DECL(CMSG_DATA, ++ [AC_DEFINE([HAVE_CGID_FDPASSING], 1, [Enable FD passing support in mod_cgid])], ++ [AC_MSG_ERROR([cannot support mod_cgid fd-passing on this system])], [ ++#include ++#include ]) ++ fi ++]) ++ + APACHE_MODPATH_FINISH +--- httpd-2.4.41/modules/generators/mod_cgid.c ++++ httpd-2.4.41/modules/generators/mod_cgid.c +@@ -342,15 +342,19 @@ + return close(fd); + } + +-/* deal with incomplete reads and signals +- * assume you really have to read buf_size bytes +- */ +-static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size) ++/* Read from the socket dealing with incomplete messages and signals. ++ * Returns 0 on success or errno on failure. Stderr fd passed as ++ * auxiliary data from other end is written to *errfd, or else stderr ++ * fileno if not present. */ ++static apr_status_t sock_readhdr(int fd, int *errfd, void *vbuf, size_t buf_size) + { +- char *buf = vbuf; + int rc; ++#ifndef HAVE_CGID_FDPASSING ++ char *buf = vbuf; + size_t bytes_read = 0; + ++ if (errfd) *errfd = 0; ++ + do { + do { + rc = read(fd, buf + bytes_read, buf_size - bytes_read); +@@ -365,9 +369,60 @@ + } + } while (bytes_read < buf_size); + ++ ++#else /* with FD passing */ ++ struct msghdr msg = {0}; ++ struct iovec vec = {vbuf, buf_size}; ++ struct cmsghdr *cmsg; ++ union { /* union to ensure alignment */ ++ struct cmsghdr cm; ++ char buf[CMSG_SPACE(sizeof(int))]; ++ } u; ++ ++ msg.msg_iov = &vec; ++ msg.msg_iovlen = 1; ++ ++ if (errfd) { ++ msg.msg_control = u.buf; ++ msg.msg_controllen = sizeof(u.buf); ++ *errfd = 0; ++ } ++ ++ /* use MSG_WAITALL to skip loop on truncated reads */ ++ do { ++ rc = recvmsg(fd, &msg, MSG_WAITALL); ++ } while (rc < 0 && errno == EINTR); ++ ++ if (rc == 0) { ++ return ECONNRESET; ++ } ++ else if (rc < 0) { ++ return errno; ++ } ++ else if (rc != buf_size) { ++ /* MSG_WAITALL should ensure the recvmsg blocks until the ++ * entire length is read, but let's be paranoid. */ ++ return APR_INCOMPLETE; ++ } ++ ++ if (errfd ++ && (cmsg = CMSG_FIRSTHDR(&msg)) != NULL ++ && cmsg->cmsg_len == CMSG_LEN(sizeof(*errfd)) ++ && cmsg->cmsg_level == SOL_SOCKET ++ && cmsg->cmsg_type == SCM_RIGHTS) { ++ *errfd = *((int *) CMSG_DATA(cmsg)); ++ } ++#endif ++ + return APR_SUCCESS; + } + ++/* As sock_readhdr but without auxiliary fd passing. */ ++static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size) ++{ ++ return sock_readhdr(fd, NULL, vbuf, buf_size); ++} ++ + /* deal with signals + */ + static apr_status_t sock_write(int fd, const void *buf, size_t buf_size) +@@ -384,7 +439,7 @@ + return APR_SUCCESS; + } + +-static apr_status_t sock_writev(int fd, request_rec *r, int count, ...) ++static apr_status_t sock_writev(int fd, int auxfd, request_rec *r, int count, ...) + { + va_list ap; + int rc; +@@ -399,9 +454,39 @@ + } + va_end(ap); + ++#ifndef HAVE_CGID_FDPASSING + do { + rc = writev(fd, vec, count); + } while (rc < 0 && errno == EINTR); ++#else ++ { ++ struct msghdr msg = { 0 }; ++ struct cmsghdr *cmsg; ++ union { /* union for alignment */ ++ char buf[CMSG_SPACE(sizeof(int))]; ++ struct cmsghdr align; ++ } u; ++ ++ msg.msg_iov = vec; ++ msg.msg_iovlen = count; ++ ++ if (auxfd) { ++ msg.msg_control = u.buf; ++ msg.msg_controllen = sizeof(u.buf); ++ ++ cmsg = CMSG_FIRSTHDR(&msg); ++ cmsg->cmsg_level = SOL_SOCKET; ++ cmsg->cmsg_type = SCM_RIGHTS; ++ cmsg->cmsg_len = CMSG_LEN(sizeof(int)); ++ *((int *) CMSG_DATA(cmsg)) = auxfd; ++ } ++ ++ do { ++ rc = sendmsg(fd, &msg, 0); ++ } while (rc < 0 && errno == EINTR); ++ } ++#endif ++ + if (rc < 0) { + return errno; + } +@@ -410,7 +495,7 @@ + } + + static apr_status_t get_req(int fd, request_rec *r, char **argv0, char ***env, +- cgid_req_t *req) ++ int *errfd, cgid_req_t *req) + { + int i; + char **environ; +@@ -421,7 +506,7 @@ + r->server = apr_pcalloc(r->pool, sizeof(server_rec)); + + /* read the request header */ +- stat = sock_read(fd, req, sizeof(*req)); ++ stat = sock_readhdr(fd, errfd, req, sizeof(*req)); + if (stat != APR_SUCCESS) { + return stat; + } +@@ -479,14 +564,15 @@ + return APR_SUCCESS; + } + +-static apr_status_t send_req(int fd, request_rec *r, char *argv0, char **env, +- int req_type) ++static apr_status_t send_req(int fd, apr_file_t *errpipe, request_rec *r, ++ char *argv0, char **env, int req_type) + { + int i; + cgid_req_t req = {0}; + apr_status_t stat; + ap_unix_identity_t * ugid = ap_run_get_suexec_identity(r); + core_dir_config *core_conf = ap_get_core_module_config(r->per_dir_config); ++ int errfd; + + + if (ugid == NULL) { +@@ -507,16 +593,21 @@ + req.args_len = r->args ? strlen(r->args) : 0; + req.loglevel = r->server->log.level; + ++ if (errpipe) ++ apr_os_file_get(&errfd, errpipe); ++ else ++ errfd = 0; ++ + /* Write the request header */ + if (req.args_len) { +- stat = sock_writev(fd, r, 5, ++ stat = sock_writev(fd, errfd, r, 5, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, + r->uri, req.uri_len, + r->args, req.args_len); + } else { +- stat = sock_writev(fd, r, 4, ++ stat = sock_writev(fd, errfd, r, 4, + &req, sizeof(req), + r->filename, req.filename_len, + argv0, req.argv0_len, +@@ -531,7 +622,7 @@ + for (i = 0; i < req.env_count; i++) { + apr_size_t curlen = strlen(env[i]); + +- if ((stat = sock_writev(fd, r, 2, &curlen, sizeof(curlen), ++ if ((stat = sock_writev(fd, 0, r, 2, &curlen, sizeof(curlen), + env[i], curlen)) != APR_SUCCESS) { + return stat; + } +@@ -582,20 +673,34 @@ + } + } + ++/* Callback executed in the forked child process if exec of the CGI ++ * script fails. For the fd-passing case, output to stderr goes to ++ * the client (request handling thread) and is logged via ++ * ap_log_rerror there. For the non-fd-passing case, the "fake" ++ * request_rec passed via userdata is used to log. */ + static void cgid_child_errfn(apr_pool_t *pool, apr_status_t err, + const char *description) + { +- request_rec *r; + void *vr; + + apr_pool_userdata_get(&vr, ERRFN_USERDATA_KEY, pool); +- r = vr; +- +- /* sure we got r, but don't call ap_log_rerror() because we don't +- * have r->headers_in and possibly other storage referenced by +- * ap_log_rerror() +- */ +- ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, APLOGNO(01241) "%s", description); ++ if (vr) { ++ request_rec *r = vr; ++ ++ /* sure we got r, but don't call ap_log_rerror() because we don't ++ * have r->headers_in and possibly other storage referenced by ++ * ap_log_rerror() ++ */ ++ ap_log_error(APLOG_MARK, APLOG_ERR, err, r->server, APLOGNO(01241) "%s", description); ++ } ++ else { ++ const char *logstr; ++ ++ logstr = apr_psprintf(pool, APLOGNO(01241) "error spawning CGI child: %s (%pm)\n", ++ description, &err); ++ fputs(logstr, stderr); ++ fflush(stderr); ++ } + } + + static int cgid_server(void *data) +@@ -669,7 +774,7 @@ + } + + while (!daemon_should_exit) { +- int errfileno = STDERR_FILENO; ++ int errfileno; + char *argv0 = NULL; + char **env = NULL; + const char * const *argv; +@@ -709,7 +814,7 @@ + r = apr_pcalloc(ptrans, sizeof(request_rec)); + procnew = apr_pcalloc(ptrans, sizeof(*procnew)); + r->pool = ptrans; +- stat = get_req(sd2, r, &argv0, &env, &cgid_req); ++ stat = get_req(sd2, r, &argv0, &env, &errfileno, &cgid_req); + if (stat != APR_SUCCESS) { + ap_log_error(APLOG_MARK, APLOG_ERR, stat, + main_server, APLOGNO(01248) +@@ -741,6 +846,16 @@ + continue; + } + ++ if (errfileno == 0) { ++ errfileno = STDERR_FILENO; ++ } ++ else { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, main_server, ++ "using passed fd %d as stderr", errfileno); ++ /* Limit the received fd lifetime to pool lifetime */ ++ apr_pool_cleanup_register(ptrans, (void *)((long)errfileno), ++ close_unix_socket, close_unix_socket); ++ } + apr_os_file_put(&r->server->error_log, &errfileno, 0, r->pool); + apr_os_file_put(&inout, &sd2, 0, r->pool); + +@@ -800,7 +915,10 @@ + close(sd2); + } + else { +- apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); ++ if (errfileno == STDERR_FILENO) { ++ /* Used by cgid_child_errfn without fd-passing. */ ++ apr_pool_userdata_set(r, ERRFN_USERDATA_KEY, apr_pool_cleanup_null, ptrans); ++ } + + argv = (const char * const *)create_argv(r->pool, NULL, NULL, NULL, argv0, r->args); + +@@ -1099,6 +1217,33 @@ + return ret; + } + ++/* Soak up stderr from a script and redirect it to the error log. ++ * TODO: log_scripterror() and this could move to cgi_common.h. */ ++static apr_status_t log_script_err(request_rec *r, apr_file_t *script_err) ++{ ++ char argsbuffer[HUGE_STRING_LEN]; ++ char *newline; ++ apr_status_t rv; ++ cgid_server_conf *conf = ap_get_module_config(r->server->module_config, &cgid_module); ++ ++ while ((rv = apr_file_gets(argsbuffer, HUGE_STRING_LEN, ++ script_err)) == APR_SUCCESS) { ++ ++ newline = strchr(argsbuffer, '\n'); ++ if (newline) { ++ char *prev = newline - 1; ++ if (prev >= argsbuffer && *prev == '\r') { ++ newline = prev; ++ } ++ ++ *newline = '\0'; ++ } ++ log_scripterror(r, conf, r->status, 0, argsbuffer); ++ } ++ ++ return rv; ++} ++ + static int log_script(request_rec *r, cgid_server_conf * conf, int ret, + char *dbuf, const char *sbuf, apr_bucket_brigade *bb, + apr_file_t *script_err) +@@ -1204,6 +1349,13 @@ + return ret; + } + ++/* Pull in CGI bucket implementation. */ ++#define cgi_server_conf cgid_server_conf ++#ifdef HAVE_CGID_FDPASSING ++#define WANT_CGI_BUCKET ++#endif ++#include "cgi_common.h" ++ + static int connect_to_daemon(int *sdptr, request_rec *r, + cgid_server_conf *conf) + { +@@ -1270,27 +1422,6 @@ + return OK; + } + +-static void discard_script_output(apr_bucket_brigade *bb) +-{ +- apr_bucket *e; +- const char *buf; +- apr_size_t len; +- apr_status_t rv; +- +- for (e = APR_BRIGADE_FIRST(bb); +- e != APR_BRIGADE_SENTINEL(bb); +- e = APR_BUCKET_NEXT(e)) +- { +- if (APR_BUCKET_IS_EOS(e)) { +- break; +- } +- rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ); +- if (rv != APR_SUCCESS) { +- break; +- } +- } +-} +- + /**************************************************************** + * + * Actual cgid handling... +@@ -1395,6 +1526,7 @@ + + static int cgid_handler(request_rec *r) + { ++ conn_rec *c = r->connection; + int retval, nph, dbpos; + char *argv0, *dbuf; + apr_bucket_brigade *bb; +@@ -1404,10 +1536,11 @@ + int seen_eos, child_stopped_reading; + int sd; + char **env; +- apr_file_t *tempsock; ++ apr_file_t *tempsock, *script_err, *errpipe_out; + struct cleanup_script_info *info; + apr_status_t rv; + cgid_dirconf *dc; ++ apr_interval_time_t timeout; + + if (strcmp(r->handler, CGI_MAGIC_TYPE) && strcmp(r->handler, "cgi-script")) { + return DECLINED; +@@ -1416,7 +1549,7 @@ + conf = ap_get_module_config(r->server->module_config, &cgid_module); + dc = ap_get_module_config(r->per_dir_config, &cgid_module); + +- ++ timeout = dc->timeout > 0 ? dc->timeout : r->server->timeout; + is_included = !strcmp(r->protocol, "INCLUDED"); + + if ((argv0 = strrchr(r->filename, '/')) != NULL) { +@@ -1469,6 +1602,17 @@ + } + */ + ++#ifdef HAVE_CGID_FDPASSING ++ rv = apr_file_pipe_create(&script_err, &errpipe_out, r->pool); ++ if (rv) { ++ return log_scripterror(r, conf, HTTP_SERVICE_UNAVAILABLE, rv, APLOGNO(10176) ++ "could not create pipe for stderr"); ++ } ++#else ++ script_err = NULL; ++ errpipe_out = NULL; ++#endif ++ + /* + * httpd core function used to add common environment variables like + * DOCUMENT_ROOT. +@@ -1481,12 +1625,16 @@ + return retval; + } + +- rv = send_req(sd, r, argv0, env, CGI_REQ); ++ rv = send_req(sd, errpipe_out, r, argv0, env, CGI_REQ); + if (rv != APR_SUCCESS) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01268) + "write to cgi daemon process"); + } + ++ /* The write-end of the pipe is only used by the server, so close ++ * it here. */ ++ if (errpipe_out) apr_file_close(errpipe_out); ++ + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; + info->r = r; +@@ -1508,12 +1656,7 @@ + */ + + apr_os_pipe_put_ex(&tempsock, &sd, 1, r->pool); +- if (dc->timeout > 0) { +- apr_file_pipe_timeout_set(tempsock, dc->timeout); +- } +- else { +- apr_file_pipe_timeout_set(tempsock, r->server->timeout); +- } ++ apr_file_pipe_timeout_set(tempsock, timeout); + apr_pool_cleanup_kill(r->pool, (void *)((long)sd), close_unix_socket); + + /* Transfer any put/post args, CERN style... +@@ -1605,114 +1748,19 @@ + */ + shutdown(sd, 1); + +- /* Handle script return... */ +- if (!nph) { +- conn_rec *c = r->connection; +- const char *location; +- char sbuf[MAX_STRING_LEN]; +- int ret; +- +- bb = apr_brigade_create(r->pool, c->bucket_alloc); +- b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- +- if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, +- APLOG_MODULE_INDEX))) +- { +- ret = log_script(r, conf, ret, dbuf, sbuf, bb, NULL); +- +- /* +- * ret could be HTTP_NOT_MODIFIED in the case that the CGI script +- * does not set an explicit status and ap_meets_conditions, which +- * is called by ap_scan_script_header_err_brigade, detects that +- * the conditions of the requests are met and the response is +- * not modified. +- * In this case set r->status and return OK in order to prevent +- * running through the error processing stack as this would +- * break with mod_cache, if the conditions had been set by +- * mod_cache itself to validate a stale entity. +- * BTW: We circumvent the error processing stack anyway if the +- * CGI script set an explicit status code (whatever it is) and +- * the only possible values for ret here are: +- * +- * HTTP_NOT_MODIFIED (set by ap_meets_conditions) +- * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) +- * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the +- * processing of the response of the CGI script, e.g broken headers +- * or a crashed CGI process). +- */ +- if (ret == HTTP_NOT_MODIFIED) { +- r->status = ret; +- return OK; +- } +- +- return ret; +- } +- +- location = apr_table_get(r->headers_out, "Location"); +- +- if (location && location[0] == '/' && r->status == 200) { +- +- /* Soak up all the script output */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- /* This redirect needs to be a GET no matter what the original +- * method was. +- */ +- r->method = "GET"; +- r->method_number = M_GET; +- +- /* We already read the message body (if any), so don't allow +- * the redirected request to think it has one. We can ignore +- * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. +- */ +- apr_table_unset(r->headers_in, "Content-Length"); +- +- ap_internal_redirect_handler(location, r); +- return OK; +- } +- else if (location && r->status == 200) { +- /* XXX: Note that if a script wants to produce its own Redirect +- * body, it now has to explicitly *say* "Status: 302" +- */ +- discard_script_output(bb); +- apr_brigade_destroy(bb); +- return HTTP_MOVED_TEMPORARILY; +- } +- +- rv = ap_pass_brigade(r->output_filters, bb); +- if (rv != APR_SUCCESS) { +- ap_log_rerror(APLOG_MARK, APLOG_TRACE1, rv, r, +- "Failed to flush CGI output to client"); +- } +- } +- +- if (nph) { +- conn_rec *c = r->connection; +- struct ap_filter_t *cur; +- +- /* get rid of all filters up through protocol... since we +- * haven't parsed off the headers, there is no way they can +- * work +- */ +- +- cur = r->proto_output_filters; +- while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { +- cur = cur->next; +- } +- r->output_filters = r->proto_output_filters = cur; +- +- bb = apr_brigade_create(r->pool, c->bucket_alloc); +- b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- b = apr_bucket_eos_create(c->bucket_alloc); +- APR_BRIGADE_INSERT_TAIL(bb, b); +- ap_pass_brigade(r->output_filters, bb); +- } ++ bb = apr_brigade_create(r->pool, c->bucket_alloc); ++#ifdef HAVE_CGID_FDPASSING ++ b = cgi_bucket_create(r, dc->timeout, tempsock, script_err, c->bucket_alloc); ++ if (b == NULL) ++ return HTTP_INTERNAL_SERVER_ERROR; /* should call log_scripterror() w/ _UNAVAILABLE? */ ++#else ++ b = apr_bucket_pipe_create(tempsock, c->bucket_alloc); ++#endif ++ APR_BRIGADE_INSERT_TAIL(bb, b); ++ b = apr_bucket_eos_create(c->bucket_alloc); ++ APR_BRIGADE_INSERT_TAIL(bb, b); + +- return OK; /* NOT r->status, even if it has changed. */ ++ return cgi_handle_response(r, nph, bb, timeout, conf, dbuf, script_err); + } + + +@@ -1829,7 +1877,7 @@ + return retval; + } + +- send_req(sd, r, command, env, SSI_REQ); ++ send_req(sd, NULL, r, command, env, SSI_REQ); + + info = apr_palloc(r->pool, sizeof(struct cleanup_script_info)); + info->conf = conf; +--- httpd-2.4.41/modules/generators/cgi_common.h ++++ httpd-2.4.41/modules/generators/cgi_common.h +@@ -0,0 +1,359 @@ ++/* Licensed to the Apache Software Foundation (ASF) under one or more ++ * contributor license agreements. See the NOTICE file distributed with ++ * this work for additional information regarding copyright ownership. ++ * The ASF licenses this file to You under the Apache License, Version 2.0 ++ * (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++#include "apr.h" ++#include "apr_strings.h" ++#include "apr_buckets.h" ++#include "apr_lib.h" ++#include "apr_poll.h" ++ ++#define APR_WANT_STRFUNC ++#define APR_WANT_MEMFUNC ++#include "apr_want.h" ++ ++#include "httpd.h" ++#include "util_filter.h" ++ ++static void discard_script_output(apr_bucket_brigade *bb) ++{ ++ apr_bucket *e; ++ const char *buf; ++ apr_size_t len; ++ ++ for (e = APR_BRIGADE_FIRST(bb); ++ e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e); ++ e = APR_BRIGADE_FIRST(bb)) ++ { ++ if (apr_bucket_read(e, &buf, &len, APR_BLOCK_READ)) { ++ break; ++ } ++ apr_bucket_delete(e); ++ } ++} ++ ++#ifdef WANT_CGI_BUCKET ++/* A CGI bucket type is needed to catch any output to stderr from the ++ * script; see PR 22030. */ ++static const apr_bucket_type_t bucket_type_cgi; ++ ++struct cgi_bucket_data { ++ apr_pollset_t *pollset; ++ request_rec *r; ++ apr_interval_time_t timeout; ++}; ++ ++/* Create a CGI bucket using pipes from script stdout 'out' ++ * and stderr 'err', for request 'r'. */ ++static apr_bucket *cgi_bucket_create(request_rec *r, ++ apr_interval_time_t timeout, ++ apr_file_t *out, apr_file_t *err, ++ apr_bucket_alloc_t *list) ++{ ++ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); ++ apr_status_t rv; ++ apr_pollfd_t fd; ++ struct cgi_bucket_data *data = apr_palloc(r->pool, sizeof *data); ++ ++ /* Disable APR timeout handling since we'll use poll() entirely. */ ++ apr_file_pipe_timeout_set(out, 0); ++ apr_file_pipe_timeout_set(err, 0); ++ ++ APR_BUCKET_INIT(b); ++ b->free = apr_bucket_free; ++ b->list = list; ++ b->type = &bucket_type_cgi; ++ b->length = (apr_size_t)(-1); ++ b->start = -1; ++ ++ /* Create the pollset */ ++ rv = apr_pollset_create(&data->pollset, 2, r->pool, 0); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01217) ++ "apr_pollset_create(); check system or user limits"); ++ return NULL; ++ } ++ ++ fd.desc_type = APR_POLL_FILE; ++ fd.reqevents = APR_POLLIN; ++ fd.p = r->pool; ++ fd.desc.f = out; /* script's stdout */ ++ fd.client_data = (void *)1; ++ rv = apr_pollset_add(data->pollset, &fd); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01218) ++ "apr_pollset_add(); check system or user limits"); ++ return NULL; ++ } ++ ++ fd.desc.f = err; /* script's stderr */ ++ fd.client_data = (void *)2; ++ rv = apr_pollset_add(data->pollset, &fd); ++ if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01219) ++ "apr_pollset_add(); check system or user limits"); ++ return NULL; ++ } ++ ++ data->r = r; ++ data->timeout = timeout; ++ b->data = data; ++ return b; ++} ++ ++/* Create a duplicate CGI bucket using given bucket data */ ++static apr_bucket *cgi_bucket_dup(struct cgi_bucket_data *data, ++ apr_bucket_alloc_t *list) ++{ ++ apr_bucket *b = apr_bucket_alloc(sizeof(*b), list); ++ APR_BUCKET_INIT(b); ++ b->free = apr_bucket_free; ++ b->list = list; ++ b->type = &bucket_type_cgi; ++ b->length = (apr_size_t)(-1); ++ b->start = -1; ++ b->data = data; ++ return b; ++} ++ ++/* Handle stdout from CGI child. Duplicate of logic from the _read ++ * method of the real APR pipe bucket implementation. */ ++static apr_status_t cgi_read_stdout(apr_bucket *a, apr_file_t *out, ++ const char **str, apr_size_t *len) ++{ ++ char *buf; ++ apr_status_t rv; ++ ++ *str = NULL; ++ *len = APR_BUCKET_BUFF_SIZE; ++ buf = apr_bucket_alloc(*len, a->list); /* XXX: check for failure? */ ++ ++ rv = apr_file_read(out, buf, len); ++ ++ if (rv != APR_SUCCESS && rv != APR_EOF) { ++ apr_bucket_free(buf); ++ return rv; ++ } ++ ++ if (*len > 0) { ++ struct cgi_bucket_data *data = a->data; ++ apr_bucket_heap *h; ++ ++ /* Change the current bucket to refer to what we read */ ++ a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free); ++ h = a->data; ++ h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */ ++ *str = buf; ++ APR_BUCKET_INSERT_AFTER(a, cgi_bucket_dup(data, a->list)); ++ } ++ else { ++ apr_bucket_free(buf); ++ a = apr_bucket_immortal_make(a, "", 0); ++ *str = a->data; ++ } ++ return rv; ++} ++ ++/* Read method of CGI bucket: polls on stderr and stdout of the child, ++ * sending any stderr output immediately away to the error log. */ ++static apr_status_t cgi_bucket_read(apr_bucket *b, const char **str, ++ apr_size_t *len, apr_read_type_e block) ++{ ++ struct cgi_bucket_data *data = b->data; ++ apr_interval_time_t timeout = 0; ++ apr_status_t rv; ++ int gotdata = 0; ++ ++ if (block != APR_NONBLOCK_READ) { ++ timeout = data->timeout > 0 ? data->timeout : data->r->server->timeout; ++ } ++ ++ do { ++ const apr_pollfd_t *results; ++ apr_int32_t num; ++ ++ rv = apr_pollset_poll(data->pollset, timeout, &num, &results); ++ if (APR_STATUS_IS_TIMEUP(rv)) { ++ if (timeout) { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, data->r, APLOGNO(01220) ++ "Timeout waiting for output from CGI script %s", ++ data->r->filename); ++ return rv; ++ } ++ else { ++ return APR_EAGAIN; ++ } ++ } ++ else if (APR_STATUS_IS_EINTR(rv)) { ++ continue; ++ } ++ else if (rv != APR_SUCCESS) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, data->r, APLOGNO(01221) ++ "poll failed waiting for CGI child"); ++ return rv; ++ } ++ ++ for (; num; num--, results++) { ++ if (results[0].client_data == (void *)1) { ++ /* stdout */ ++ rv = cgi_read_stdout(b, results[0].desc.f, str, len); ++ if (APR_STATUS_IS_EOF(rv)) { ++ rv = APR_SUCCESS; ++ } ++ gotdata = 1; ++ } else { ++ /* stderr */ ++ apr_status_t rv2 = log_script_err(data->r, results[0].desc.f); ++ if (APR_STATUS_IS_EOF(rv2)) { ++ apr_pollset_remove(data->pollset, &results[0]); ++ } ++ } ++ } ++ ++ } while (!gotdata); ++ ++ return rv; ++} ++ ++static const apr_bucket_type_t bucket_type_cgi = { ++ "CGI", 5, APR_BUCKET_DATA, ++ apr_bucket_destroy_noop, ++ cgi_bucket_read, ++ apr_bucket_setaside_notimpl, ++ apr_bucket_split_notimpl, ++ apr_bucket_copy_notimpl ++}; ++ ++#endif /* WANT_CGI_BUCKET */ ++ ++/* Handle the CGI response output, having set up the brigade with the ++ * CGI or PIPE bucket as appropriate. */ ++static int cgi_handle_response(request_rec *r, int nph, apr_bucket_brigade *bb, ++ apr_interval_time_t timeout, cgi_server_conf *conf, ++ char *logdata, apr_file_t *script_err) ++{ ++ apr_status_t rv; ++ ++ /* Handle script return... */ ++ if (!nph) { ++ const char *location; ++ char sbuf[MAX_STRING_LEN]; ++ int ret; ++ ++ if ((ret = ap_scan_script_header_err_brigade_ex(r, bb, sbuf, ++ APLOG_MODULE_INDEX))) ++ { ++ ret = log_script(r, conf, ret, logdata, sbuf, bb, script_err); ++ ++ /* ++ * ret could be HTTP_NOT_MODIFIED in the case that the CGI script ++ * does not set an explicit status and ap_meets_conditions, which ++ * is called by ap_scan_script_header_err_brigade, detects that ++ * the conditions of the requests are met and the response is ++ * not modified. ++ * In this case set r->status and return OK in order to prevent ++ * running through the error processing stack as this would ++ * break with mod_cache, if the conditions had been set by ++ * mod_cache itself to validate a stale entity. ++ * BTW: We circumvent the error processing stack anyway if the ++ * CGI script set an explicit status code (whatever it is) and ++ * the only possible values for ret here are: ++ * ++ * HTTP_NOT_MODIFIED (set by ap_meets_conditions) ++ * HTTP_PRECONDITION_FAILED (set by ap_meets_conditions) ++ * HTTP_INTERNAL_SERVER_ERROR (if something went wrong during the ++ * processing of the response of the CGI script, e.g broken headers ++ * or a crashed CGI process). ++ */ ++ if (ret == HTTP_NOT_MODIFIED) { ++ r->status = ret; ++ return OK; ++ } ++ ++ return ret; ++ } ++ ++ location = apr_table_get(r->headers_out, "Location"); ++ ++ if (location && r->status == 200) { ++ /* For a redirect whether internal or not, discard any ++ * remaining stdout from the script, and log any remaining ++ * stderr output, as normal. */ ++ discard_script_output(bb); ++ apr_brigade_destroy(bb); ++ ++ if (script_err) { ++ apr_file_pipe_timeout_set(script_err, timeout); ++ log_script_err(r, script_err); ++ } ++ } ++ ++ if (location && location[0] == '/' && r->status == 200) { ++ /* This redirect needs to be a GET no matter what the original ++ * method was. ++ */ ++ r->method = "GET"; ++ r->method_number = M_GET; ++ ++ /* We already read the message body (if any), so don't allow ++ * the redirected request to think it has one. We can ignore ++ * Transfer-Encoding, since we used REQUEST_CHUNKED_ERROR. ++ */ ++ apr_table_unset(r->headers_in, "Content-Length"); ++ ++ ap_internal_redirect_handler(location, r); ++ return OK; ++ } ++ else if (location && r->status == 200) { ++ /* XXX: Note that if a script wants to produce its own Redirect ++ * body, it now has to explicitly *say* "Status: 302" ++ */ ++ discard_script_output(bb); ++ apr_brigade_destroy(bb); ++ return HTTP_MOVED_TEMPORARILY; ++ } ++ ++ rv = ap_pass_brigade(r->output_filters, bb); ++ } ++ else /* nph */ { ++ struct ap_filter_t *cur; ++ ++ /* get rid of all filters up through protocol... since we ++ * haven't parsed off the headers, there is no way they can ++ * work ++ */ ++ ++ cur = r->proto_output_filters; ++ while (cur && cur->frec->ftype < AP_FTYPE_CONNECTION) { ++ cur = cur->next; ++ } ++ r->output_filters = r->proto_output_filters = cur; ++ ++ rv = ap_pass_brigade(r->output_filters, bb); ++ } ++ ++ /* don't soak up script output if errors occurred writing it ++ * out... otherwise, we prolong the life of the script when the ++ * connection drops or we stopped sending output for some other ++ * reason */ ++ if (script_err && rv == APR_SUCCESS && !r->connection->aborted) { ++ apr_file_pipe_timeout_set(script_err, timeout); ++ log_script_err(r, script_err); ++ } ++ ++ if (script_err) apr_file_close(script_err); ++ ++ return OK; /* NOT r->status, even if it has changed. */ ++} diff --git a/SOURCES/httpd-2.4.37-r1842929+.patch b/SOURCES/httpd-2.4.37-r1842929+.patch index bb388db..ab5bba6 100644 --- a/SOURCES/httpd-2.4.37-r1842929+.patch +++ b/SOURCES/httpd-2.4.37-r1842929+.patch @@ -37,130 +37,6 @@ index 0ad0c13..a8c2804 100644 ]) dnl -diff --git a/config.layout b/config.layout -index 79fbce7..becb858 100644 ---- a/config.layout -+++ b/config.layout -@@ -29,6 +29,7 @@ - includedir: ${prefix}/include - localstatedir: ${prefix} - runtimedir: ${localstatedir}/logs -+ statedir: ${localstatedir}/state - logfiledir: ${localstatedir}/logs - proxycachedir: ${localstatedir}/proxy - -@@ -54,6 +55,7 @@ - includedir: ${prefix}/include+ - localstatedir: ${prefix}/var+ - runtimedir: ${localstatedir}/run -+ statedir: ${localstatedir}/state - logfiledir: ${localstatedir}/log - proxycachedir: ${localstatedir}/proxy - -@@ -78,6 +80,7 @@ - includedir: /System/Library/Frameworks/Apache.framework/Versions/2.0/Headers - localstatedir: /var - runtimedir: ${prefix}/Logs -+ statedir: ${prefix}/State - logfiledir: ${prefix}/Logs - proxycachedir: ${prefix}/ProxyCache - -@@ -102,6 +105,7 @@ - includedir: ${prefix}/include+ - localstatedir: /var - runtimedir: ${localstatedir}/run -+ statedir: ${localstatedir}/state - logfiledir: ${localstatedir}/log+ - proxycachedir: ${runtimedir}/proxy - -@@ -126,6 +130,7 @@ - includedir: ${prefix}/include/apache - localstatedir: /var - runtimedir: ${localstatedir}/run -+ statedir: ${localstatedir}/lib/httpd - logfiledir: ${localstatedir}/log/httpd - proxycachedir: ${localstatedir}/cache/httpd - -@@ -151,6 +156,7 @@ - includedir: ${prefix}/include/httpd - runtimedir: /run/httpd - logfiledir: ${localstatedir}/log/httpd -+ statedir: ${localstatedir}/lib/httpd - proxycachedir: ${localstatedir}/cache/httpd/proxy - - -@@ -175,6 +181,7 @@ - localstatedir: /var${prefix} - runtimedir: ${localstatedir}/run - logfiledir: ${localstatedir}/logs -+ statedir: ${localstatedir}/state - proxycachedir: ${localstatedir}/proxy - - -@@ -197,6 +204,7 @@ - cgidir: ${datadir}/cgi-bin - includedir: ${prefix}/include/apache - localstatedir: /var/lib/httpd -+ statedir: ${localstatedir} - runtimedir: /var/run - logfiledir: /var/log/httpd - proxycachedir: /var/cache/httpd -@@ -223,6 +231,7 @@ - localstatedir: /var - runtimedir: ${localstatedir}/run - logfiledir: ${localstatedir}/log/httpd -+ statedir: ${prefix}/state - proxycachedir: ${localstatedir}/proxy - - -@@ -246,6 +255,7 @@ - includedir: ${exec_prefix}/include - localstatedir: ${prefix} - runtimedir: /var/run -+ statedir: ${datadir}/state - logfiledir: ${datadir}/logs - proxycachedir: ${datadir}/proxy - -@@ -271,6 +281,7 @@ - localstatedir: ${prefix} - runtimedir: ${prefix}/logs - logfiledir: ${prefix}/logs -+ statedir: ${prefix}/state - proxycachedir: ${prefix}/proxy - - -@@ -315,6 +326,7 @@ - cgidir: ${prefix}/usr/lib/cgi-bin - includedir: ${exec_prefix}/include/apache2 - localstatedir: ${prefix}/var/lock/apache2 -+ statedir: ${prefix}/var/lib/apache2 - runtimedir: ${prefix}/var/run/apache2 - logfiledir: ${prefix}/var/log/apache2 - proxycachedir: ${prefix}/var/cache/apache2/proxy -@@ -343,6 +355,7 @@ - manualdir: ${datadir}/manual - cgidir: ${datadir}/cgi-bin - runtimedir: ${localstatedir}/run -+ runtimedir: ${localstatedir}/lib/httpd - logfiledir: ${localstatedir}/log/httpd - proxycachedir: ${localstatedir}/cache/httpd/cache-root - -@@ -366,6 +379,7 @@ - manualdir: ${prefix}/manual - includedir: ${prefix}/include - localstatedir: /var/httpd -+ statedir: ${localstatedir}/state - runtimedir: ${localstatedir}/run - logfiledir: ${localstatedir}/logs - proxycachedir: ${localstatedir}/proxy -@@ -391,6 +405,7 @@ - includedir: ${prefix}/include/httpd - localstatedir: /var - runtimedir: ${localstatedir}/run/httpd -+ statedir: ${localstatedir}/lib/httpd - logfiledir: ${localstatedir}/log/httpd - proxycachedir: ${localstatedir}/cache/httpd - diff --git a/configure.in b/configure.in index a208b53..de6a8ad 100644 --- a/configure.in diff --git a/SOURCES/httpd-2.4.37-r1861793+.patch b/SOURCES/httpd-2.4.37-r1861793+.patch new file mode 100644 index 0000000..a74ece4 --- /dev/null +++ b/SOURCES/httpd-2.4.37-r1861793+.patch @@ -0,0 +1,209 @@ +diff --git a/configure.in b/configure.in +index de6a8ad..4ca489d 100644 +--- a/configure.in ++++ b/configure.in +@@ -465,6 +465,28 @@ LIBS="" + AC_SEARCH_LIBS(crypt, crypt) + CRYPT_LIBS="$LIBS" + APACHE_SUBST(CRYPT_LIBS) ++ ++if test "$ac_cv_search_crypt" != "no"; then ++ # Test crypt() with the SHA-512 test vector from https://akkadia.org/drepper/SHA-crypt.txt ++ AC_CACHE_CHECK([whether crypt() supports SHA-2], [ap_cv_crypt_sha2], [ ++ AC_RUN_IFELSE([AC_LANG_PROGRAM([[ ++#include ++#include ++#include ++ ++#define PASSWD_0 "Hello world!" ++#define SALT_0 "\$6\$saltstring" ++#define EXPECT_0 "\$6\$saltstring\$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJu" \ ++ "esI68u4OTLiBFdcbYEdFCoEOfaS35inz1" ++]], [char *result = crypt(PASSWD_0, SALT_0); ++ if (!result) return 1; ++ if (strcmp(result, EXPECT_0)) return 2; ++])], [ap_cv_crypt_sha2=yes], [ap_cv_crypt_sha2=no])]) ++ if test "$ap_cv_crypt_sha2" = yes; then ++ AC_DEFINE([HAVE_CRYPT_SHA2], 1, [Define if crypt() supports SHA-2 hashes]) ++ fi ++fi ++ + LIBS="$saved_LIBS" + + dnl See Comment #Spoon +diff --git a/support/htpasswd.c b/support/htpasswd.c +index 660a27c..136f62a 100644 +--- a/support/htpasswd.c ++++ b/support/htpasswd.c +@@ -98,28 +98,32 @@ static int mkrecord(struct passwd_ctx *ctx, char *user) + static void usage(void) + { + apr_file_printf(errfile, "Usage:" NL +- "\thtpasswd [-cimBdpsDv] [-C cost] passwordfile username" NL +- "\thtpasswd -b[cmBdpsDv] [-C cost] passwordfile username password" NL ++ "\thtpasswd [-cimB25dpsDv] [-C cost] [-r rounds] passwordfile username" NL ++ "\thtpasswd -b[cmB25dpsDv] [-C cost] [-r rounds] passwordfile username password" NL + NL +- "\thtpasswd -n[imBdps] [-C cost] username" NL +- "\thtpasswd -nb[mBdps] [-C cost] username password" NL ++ "\thtpasswd -n[imB25dps] [-C cost] [-r rounds] username" NL ++ "\thtpasswd -nb[mB25dps] [-C cost] [-r rounds] username password" NL + " -c Create a new file." NL + " -n Don't update file; display results on stdout." NL + " -b Use the password from the command line rather than prompting " + "for it." NL + " -i Read password from stdin without verification (for script usage)." NL + " -m Force MD5 encryption of the password (default)." NL +- " -B Force bcrypt encryption of the password (very secure)." NL ++ " -2 Force SHA-256 crypt() hash of the password (secure)." NL ++ " -5 Force SHA-512 crypt() hash of the password (secure)." NL ++ " -B Force bcrypt aencryption of the password (very secure)." NL + " -C Set the computing time used for the bcrypt algorithm" NL + " (higher is more secure but slower, default: %d, valid: 4 to 31)." NL ++ " -r Set the number of rounds used for the SHA-256, SHA-512 algorithms" NL ++ " (higher is more secure but slower, default: 5000)." NL + " -d Force CRYPT encryption of the password (8 chars max, insecure)." NL +- " -s Force SHA encryption of the password (insecure)." NL ++ " -s Force SHA-1 encryption of the password (insecure)." NL + " -p Do not encrypt the password (plaintext, insecure)." NL + " -D Delete the specified user." NL + " -v Verify password for the specified user." NL + "On other systems than Windows and NetWare the '-p' flag will " + "probably not work." NL +- "The SHA algorithm does not use a salt and is less secure than the " ++ "The SHA-1 algorithm does not use a salt and is less secure than the " + "MD5 algorithm." NL, + BCRYPT_DEFAULT_COST + ); +@@ -178,7 +182,7 @@ static void check_args(int argc, const char *const argv[], + if (rv != APR_SUCCESS) + exit(ERR_SYNTAX); + +- while ((rv = apr_getopt(state, "cnmspdBbDiC:v", &opt, &opt_arg)) == APR_SUCCESS) { ++ while ((rv = apr_getopt(state, "cnmspdBbDi25C:r:v", &opt, &opt_arg)) == APR_SUCCESS) { + switch (opt) { + case 'c': + *mask |= APHTP_NEWFILE; +diff --git a/support/passwd_common.c b/support/passwd_common.c +index 664e509..d45657c 100644 +--- a/support/passwd_common.c ++++ b/support/passwd_common.c +@@ -185,10 +185,15 @@ int mkhash(struct passwd_ctx *ctx) + #if CRYPT_ALGO_SUPPORTED + char *cbuf; + #endif ++#ifdef HAVE_CRYPT_SHA2 ++ const char *setting; ++ char method; ++#endif + +- if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT) { ++ if (ctx->cost != 0 && ctx->alg != ALG_BCRYPT ++ && ctx->alg != ALG_CRYPT_SHA256 && ctx->alg != ALG_CRYPT_SHA512 ) { + apr_file_printf(errfile, +- "Warning: Ignoring -C argument for this algorithm." NL); ++ "Warning: Ignoring -C/-r argument for this algorithm." NL); + } + + if (ctx->passwd == NULL) { +@@ -246,6 +251,34 @@ int mkhash(struct passwd_ctx *ctx) + break; + #endif /* CRYPT_ALGO_SUPPORTED */ + ++#ifdef HAVE_CRYPT_SHA2 ++ case ALG_CRYPT_SHA256: ++ case ALG_CRYPT_SHA512: ++ ret = generate_salt(salt, 16, &ctx->errstr, ctx->pool); ++ if (ret != 0) ++ break; ++ ++ method = ctx->alg == ALG_CRYPT_SHA256 ? '5': '6'; ++ ++ if (ctx->cost) ++ setting = apr_psprintf(ctx->pool, "$%c$rounds=%d$%s", ++ method, ctx->cost, salt); ++ else ++ setting = apr_psprintf(ctx->pool, "$%c$%s", ++ method, salt); ++ ++ cbuf = crypt(pw, setting); ++ if (cbuf == NULL) { ++ rv = APR_FROM_OS_ERROR(errno); ++ ctx->errstr = apr_psprintf(ctx->pool, "crypt() failed: %pm", &rv); ++ ret = ERR_PWMISMATCH; ++ break; ++ } ++ ++ apr_cpystrn(ctx->out, cbuf, ctx->out_len - 1); ++ break; ++#endif /* HAVE_CRYPT_SHA2 */ ++ + #if BCRYPT_ALGO_SUPPORTED + case ALG_BCRYPT: + rv = apr_generate_random_bytes((unsigned char*)salt, 16); +@@ -294,6 +327,19 @@ int parse_common_options(struct passwd_ctx *ctx, char opt, + case 's': + ctx->alg = ALG_APSHA; + break; ++#ifdef HAVE_CRYPT_SHA2 ++ case '2': ++ ctx->alg = ALG_CRYPT_SHA256; ++ break; ++ case '5': ++ ctx->alg = ALG_CRYPT_SHA512; ++ break; ++#else ++ case '2': ++ case '5': ++ ctx->errstr = "SHA-2 crypt() algorithms are not supported on this platform."; ++ return ERR_ALG_NOT_SUPP; ++#endif + case 'p': + ctx->alg = ALG_PLAIN; + #if !PLAIN_ALGO_SUPPORTED +@@ -324,11 +370,12 @@ int parse_common_options(struct passwd_ctx *ctx, char opt, + return ERR_ALG_NOT_SUPP; + #endif + break; +- case 'C': { ++ case 'C': ++ case 'r': { + char *endptr; + long num = strtol(opt_arg, &endptr, 10); + if (*endptr != '\0' || num <= 0) { +- ctx->errstr = "argument to -C must be a positive integer"; ++ ctx->errstr = "argument to -C/-r must be a positive integer"; + return ERR_SYNTAX; + } + ctx->cost = num; +diff --git a/support/passwd_common.h b/support/passwd_common.h +index 660081e..f1b3cd7 100644 +--- a/support/passwd_common.h ++++ b/support/passwd_common.h +@@ -28,6 +28,8 @@ + #include "apu_version.h" + #endif + ++#include "ap_config_auto.h" ++ + #define MAX_STRING_LEN 256 + + #define ALG_PLAIN 0 +@@ -35,6 +37,8 @@ + #define ALG_APMD5 2 + #define ALG_APSHA 3 + #define ALG_BCRYPT 4 ++#define ALG_CRYPT_SHA256 5 ++#define ALG_CRYPT_SHA512 6 + + #define BCRYPT_DEFAULT_COST 5 + +@@ -84,7 +88,7 @@ struct passwd_ctx { + apr_size_t out_len; + char *passwd; + int alg; +- int cost; ++ int cost; /* cost for bcrypt, rounds for SHA-2 */ + enum { + PW_PROMPT = 0, + PW_ARG, diff --git a/SOURCES/httpd-2.4.37-r1870095+.patch b/SOURCES/httpd-2.4.37-r1870095+.patch new file mode 100644 index 0000000..bd43c5c --- /dev/null +++ b/SOURCES/httpd-2.4.37-r1870095+.patch @@ -0,0 +1,117 @@ +# ./pullrev.sh 1870095 1870097 +http://svn.apache.org/viewvc?view=revision&revision=1870095 +http://svn.apache.org/viewvc?view=revision&revision=1870097 + +--- httpd-2.4.37/modules/ssl/ssl_engine_kernel.c ++++ httpd-2.4.37/modules/ssl/ssl_engine_kernel.c +@@ -114,6 +114,45 @@ + return result; + } + ++/* If a renegotiation is required for the location, and the request ++ * includes a message body (and the client has not requested a "100 ++ * Continue" response), then the client will be streaming the request ++ * body over the wire already. In that case, it is not possible to ++ * stop and perform a new SSL handshake immediately; once the SSL ++ * library moves to the "accept" state, it will reject the SSL packets ++ * which the client is sending for the request body. ++ * ++ * To allow authentication to complete in the hook, the solution used ++ * here is to fill a (bounded) buffer with the request body, and then ++ * to reinject that request body later. ++ * ++ * This function is called to fill the renegotiation buffer for the ++ * location as required, or fail. Returns zero on success or HTTP_ ++ * error code on failure. ++ */ ++static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc) ++{ ++ int rv; ++ apr_size_t rsize; ++ ++ /* ### this is HTTP/1.1 specific, special case for protocol? */ ++ if (r->expecting_100 || !ap_request_has_body(r)) { ++ return 0; ++ } ++ ++ rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE : dc->nRenegBufferSize; ++ if (rsize > 0) { ++ /* Fill the I/O buffer with the request body if possible. */ ++ rv = ssl_io_buffer_fill(r, rsize); ++ } ++ else { ++ /* If the reneg buffer size is set to zero, just fail. */ ++ rv = HTTP_REQUEST_ENTITY_TOO_LARGE; ++ } ++ ++ return rv; ++} ++ + #ifdef HAVE_TLSEXT + static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2) + { +@@ -814,41 +853,14 @@ + } + } + +- /* If a renegotiation is now required for this location, and the +- * request includes a message body (and the client has not +- * requested a "100 Continue" response), then the client will be +- * streaming the request body over the wire already. In that +- * case, it is not possible to stop and perform a new SSL +- * handshake immediately; once the SSL library moves to the +- * "accept" state, it will reject the SSL packets which the client +- * is sending for the request body. +- * +- * To allow authentication to complete in this auth hook, the +- * solution used here is to fill a (bounded) buffer with the +- * request body, and then to reinject that request body later. +- */ +- if (renegotiate && !renegotiate_quick +- && !r->expecting_100 +- && ap_request_has_body(r)) { +- int rv; +- apr_size_t rsize; +- +- rsize = dc->nRenegBufferSize == UNSET ? DEFAULT_RENEG_BUFFER_SIZE : +- dc->nRenegBufferSize; +- if (rsize > 0) { +- /* Fill the I/O buffer with the request body if possible. */ +- rv = ssl_io_buffer_fill(r, rsize); +- } +- else { +- /* If the reneg buffer size is set to zero, just fail. */ +- rv = HTTP_REQUEST_ENTITY_TOO_LARGE; +- } +- +- if (rv) { ++ /* Fill reneg buffer if required. */ ++ if (renegotiate && !renegotiate_quick) { ++ rc = fill_reneg_buffer(r, dc); ++ if (rc) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02257) + "could not buffer message body to allow " + "SSL renegotiation to proceed"); +- return rv; ++ return rc; + } + } + +@@ -1132,6 +1144,17 @@ + } + } + ++ /* Fill reneg buffer if required. */ ++ if (change_vmode) { ++ rc = fill_reneg_buffer(r, dc); ++ if (rc) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10228) ++ "could not buffer message body to allow " ++ "TLS Post-Handshake Authentication to proceed"); ++ return rc; ++ } ++ } ++ + if (change_vmode) { + char peekbuf[1]; + diff --git a/SOURCES/httpd-2.4.37-sslkeylogfile-support.patch b/SOURCES/httpd-2.4.37-sslkeylogfile-support.patch new file mode 100644 index 0000000..9d4cc19 --- /dev/null +++ b/SOURCES/httpd-2.4.37-sslkeylogfile-support.patch @@ -0,0 +1,123 @@ +diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c +index 1d201d9..0c4bf1f 100644 +--- a/modules/ssl/ssl_engine_config.c ++++ b/modules/ssl/ssl_engine_config.c +@@ -75,6 +75,10 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s) + mc->stapling_refresh_mutex = NULL; + #endif + ++#ifdef HAVE_OPENSSL_KEYLOG ++ mc->keylog_file = NULL; ++#endif ++ + apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, + apr_pool_cleanup_null, + pool); +diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c +index ef631c1..b286053 100644 +--- a/modules/ssl/ssl_engine_init.c ++++ b/modules/ssl/ssl_engine_init.c +@@ -437,6 +437,28 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, + init_bio_methods(); + #endif + ++#ifdef HAVE_OPENSSL_KEYLOG ++ { ++ const char *logfn = getenv("SSLKEYLOGFILE"); ++ ++ if (logfn) { ++ rv = apr_file_open(&mc->keylog_file, logfn, ++ APR_FOPEN_CREATE|APR_FOPEN_WRITE|APR_FOPEN_APPEND|APR_FOPEN_LARGEFILE, ++ APR_FPROT_UREAD|APR_FPROT_UWRITE, ++ mc->pPool); ++ if (rv) { ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, rv, s, APLOGNO(10226) ++ "Could not open log file '%s' configured via SSLKEYLOGFILE", ++ logfn); ++ return rv; ++ } ++ ++ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, APLOGNO(10227) ++ "Init: Logging SSL private key material to %s", logfn); ++ } ++ } ++#endif ++ + return OK; + } + +@@ -796,6 +818,12 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s, + * https://github.com/openssl/openssl/issues/7178 */ + SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY); + #endif ++ ++#ifdef HAVE_OPENSSL_KEYLOG ++ if (mctx->sc->mc->keylog_file) { ++ SSL_CTX_set_keylog_callback(ctx, modssl_callback_keylog); ++ } ++#endif + + return APR_SUCCESS; + } +diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c +index 6611610..7058865 100644 +--- a/modules/ssl/ssl_engine_kernel.c ++++ b/modules/ssl/ssl_engine_kernel.c +@@ -2719,3 +2719,17 @@ int ssl_callback_SRPServerParams(SSL *ssl, int *ad, void *arg) + } + + #endif /* HAVE_SRP */ ++ ++ ++#ifdef HAVE_OPENSSL_KEYLOG ++/* Callback used with SSL_CTX_set_keylog_callback. */ ++void modssl_callback_keylog(const SSL *ssl, const char *line) ++{ ++ conn_rec *conn = SSL_get_app_data(ssl); ++ SSLSrvConfigRec *sc = mySrvConfig(conn->base_server); ++ ++ if (sc && sc->mc->keylog_file) { ++ apr_file_printf(sc->mc->keylog_file, "%s\n", line); ++ } ++} ++#endif +diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h +index 0fac5d1..2514407 100644 +--- a/modules/ssl/ssl_private.h ++++ b/modules/ssl/ssl_private.h +@@ -250,6 +250,10 @@ void free_bio_methods(void); + #endif + #endif + ++#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER) ++#define HAVE_OPENSSL_KEYLOG ++#endif ++ + /* mod_ssl headers */ + #include "ssl_util_ssl.h" + +@@ -617,6 +621,12 @@ typedef struct { + apr_global_mutex_t *stapling_cache_mutex; + apr_global_mutex_t *stapling_refresh_mutex; + #endif ++ ++#ifdef HAVE_OPENSSL_KEYLOG ++ /* Used for logging if SSLKEYLOGFILE is set at startup. */ ++ apr_file_t *keylog_file; ++#endif ++ + } SSLModConfigRec; + + /** Structure representing configured filenames for certs and keys for +@@ -970,6 +980,11 @@ int ssl_stapling_init_cert(server_rec *, apr_pool_t *, apr_pool_t *, + int ssl_callback_SRPServerParams(SSL *, int *, void *); + #endif + ++#ifdef HAVE_OPENSSL_KEYLOG ++/* Callback used with SSL_CTX_set_keylog_callback. */ ++void modssl_callback_keylog(const SSL *ssl, const char *line); ++#endif ++ + /** I/O */ + void ssl_io_filter_init(conn_rec *, request_rec *r, SSL *); + void ssl_io_filter_register(apr_pool_t *); diff --git a/SPECS/httpd.spec b/SPECS/httpd.spec index 4394c7d..a855696 100644 --- a/SPECS/httpd.spec +++ b/SPECS/httpd.spec @@ -13,7 +13,7 @@ Summary: Apache HTTP Server Name: httpd Version: 2.4.37 -Release: 12%{?dist} +Release: 21%{?dist} URL: https://httpd.apache.org/ Source0: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 Source1: index.html @@ -43,7 +43,6 @@ Source25: 01-session.conf Source26: 10-listen443.conf Source27: httpd.socket Source28: 00-optional.conf -Source29: 01-md.conf # Documentation Source30: README.confd Source31: README.confmod @@ -55,6 +54,7 @@ Source41: htcacheclean.sysconf Source42: httpd-init.service Source43: httpd-ssl-gencerts Source44: httpd@.service +Source45: config.layout # build/scripts patches # http://bugzilla.redhat.com/show_bug.cgi?id=1231924 @@ -63,7 +63,6 @@ Source44: httpd@.service Patch1: httpd-2.4.35-apachectl.patch Patch2: httpd-2.4.28-apxs.patch Patch3: httpd-2.4.35-deplibs.patch -Patch4: httpd-2.4.35-layout.patch # Needed for socket activation and mod_systemd patch Patch19: httpd-2.4.35-detect-systemd.patch @@ -87,6 +86,12 @@ Patch30: httpd-2.4.35-freebind.patch Patch31: httpd-2.4.35-r1830819+.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1638738 Patch32: httpd-2.4.37-sslprotdefault.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1747898 +Patch33: httpd-2.4.37-mod-md-mod-ssl-hooks.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1725031 +Patch34: httpd-2.4.37-r1861793+.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1704317ě +Patch35: httpd-2.4.37-sslkeylogfile-support.patch # Bug fixes # https://bugzilla.redhat.com/show_bug.cgi?id=1397243 @@ -109,6 +114,14 @@ Patch68: httpd-2.4.37-fips-segfault.patch Patch70: httpd-2.4.37-r1840554.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1673022 Patch71: httpd-2.4.37-mod-md-perms.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1724549 +Patch72: httpd-2.4.37-mod-mime-magic-strdup.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1724034 +Patch73: httpd-2.4.35-ocsp-wrong-ctx.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1633224 +Patch74: httpd-2.4.37-r1828172+.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1775158 +Patch75: httpd-2.4.37-r1870095+.patch # Security fixes Patch200: httpd-2.4.37-r1851471.patch @@ -120,6 +133,10 @@ Patch202: httpd-2.4.37-CVE-2019-0215.patch Patch203: httpd-2.4.37-CVE-2019-0217.patch # https://bugzilla.redhat.com/show_bug.cgi?id=1696097 Patch204: httpd-2.4.37-CVE-2019-0220.patch +# https://bugzilla.redhat.com/show_bug.cgi?id=1741860 +# https://bugzilla.redhat.com/show_bug.cgi?id=1741864 +# https://bugzilla.redhat.com/show_bug.cgi?id=1741868 +Patch205: httpd-2.4.34-CVE-2019-9511-and-9516-and-9517.patch License: ASL 2.0 Group: System Environment/Daemons @@ -209,19 +226,6 @@ The mod_ssl module provides strong cryptography for the Apache Web server via the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols. -%package -n mod_md -Group: System Environment/Daemons -Summary: Certificate provisioning using ACME for the Apache HTTP Server -Requires: httpd = 0:%{version}-%{release}, httpd-mmn = %{mmnisa} -BuildRequires: jansson-devel, libcurl-devel - -%description -n mod_md -This module manages common properties of domains for one or more -virtual hosts. Specifically it can use the ACME protocol (RFC Draft) -to automate certificate provisioning. These will be configured for -managed domains and their virtual hosts automatically. This includes -renewal of certificates before they expire. - %package -n mod_proxy_html Group: System Environment/Daemons Summary: HTML and XML content filters for the Apache HTTP Server @@ -258,7 +262,6 @@ interface for storing and accessing per-user session data. %patch1 -p1 -b .apctl %patch2 -p1 -b .apxs %patch3 -p1 -b .deplibs -%patch4 -p1 -b .layout %patch19 -p1 -b .detectsystemd %patch20 -p1 -b .export @@ -274,6 +277,9 @@ interface for storing and accessing per-user session data. %patch30 -p1 -b .freebind %patch31 -p1 -b .r1830819+ %patch32 -p1 -b .sslprotdefault +%patch33 -p1 -b .mod-md-mod-ssl-hooks +%patch34 -p1 -b .r1861793+ +%patch35 -p1 -b .sslkeylogfile-support %patch61 -p1 -b .r1738878 %patch62 -p1 -b .r1633085 @@ -284,12 +290,17 @@ interface for storing and accessing per-user session data. %patch68 -p1 -b .fipscore %patch70 -p1 -b .r1840554 %patch71 -p1 -b .modmdperms +%patch72 -p1 -b .mimemagic +%patch73 -p1 -b .ocspwrongctx +%patch74 -p1 -b .r1828172+ +%patch75 -p1 -b .r1870095+ %patch200 -p1 -b .r1851471 %patch201 -p1 -b .CVE-2019-0211 %patch202 -p1 -b .CVE-2019-0215 %patch203 -p1 -b .CVE-2019-0217 %patch204 -p1 -b .CVE-2019-0220 +%patch205 -p1 -b .CVE-2019-9511-and-9516-and-9517 # Patch in the vendor string sed -i '/^#define PLATFORM/s/Unix/%{vstring}/' os/unix/os.h @@ -316,6 +327,9 @@ if test "x${vmmn}" != "x%{mmn}"; then exit 1 fi +# Provide default layout +cp $RPM_SOURCE_DIR/config.layout . + sed ' s,@MPM@,%{mpm},g s,@DOCROOT@,%{docroot},g @@ -339,7 +353,7 @@ autoheader && autoconf || exit 1 # Before configure; fix location of build dir in generated apxs %{__perl} -pi -e "s:\@exp_installbuilddir\@:%{_libdir}/httpd/build:g" \ - support/apxs.in + support/apxs.in export CFLAGS=$RPM_OPT_FLAGS export LDFLAGS="-Wl,-z,relro,-z,now" @@ -381,9 +395,11 @@ export LYNX_PATH=/usr/bin/links --enable-disk-cache \ --enable-ldap --enable-authnz-ldap \ --enable-cgid --enable-cgi \ + --enable-cgid-fdpassing \ --enable-authn-anon --enable-authn-alias \ --disable-imagemap --disable-file-cache \ --disable-http2 \ + --disable-md \ $* make %{?_smp_mflags} @@ -409,8 +425,7 @@ install -m 644 $RPM_SOURCE_DIR/README.confmod \ $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.modules.d/README for f in 00-base.conf 00-mpm.conf 00-lua.conf 01-cgi.conf 00-dav.conf \ 00-proxy.conf 00-ssl.conf 01-ldap.conf 00-proxyhtml.conf \ - 01-ldap.conf 00-systemd.conf 01-session.conf 00-optional.conf \ - 01-md.conf; do + 01-ldap.conf 00-systemd.conf 01-session.conf 00-optional.conf; do install -m 644 -p $RPM_SOURCE_DIR/$f \ $RPM_BUILD_ROOT%{_sysconfdir}/httpd/conf.modules.d/$f done @@ -517,6 +532,7 @@ ln -s ../../pixmaps/poweredby.png \ $RPM_BUILD_ROOT%{contentdir}/icons/poweredby.png # symlinks for /etc/httpd +rmdir $RPM_BUILD_ROOT/etc/httpd/{state,run} ln -s ../..%{_localstatedir}/log/httpd $RPM_BUILD_ROOT/etc/httpd/logs ln -s ../..%{_localstatedir}/lib/httpd $RPM_BUILD_ROOT/etc/httpd/state ln -s /run/httpd $RPM_BUILD_ROOT/etc/httpd/run @@ -525,11 +541,11 @@ ln -s ../..%{_libdir}/httpd/modules $RPM_BUILD_ROOT/etc/httpd/modules # install http-ssl-pass-dialog mkdir -p $RPM_BUILD_ROOT%{_libexecdir} install -m755 $RPM_SOURCE_DIR/httpd-ssl-pass-dialog \ - $RPM_BUILD_ROOT%{_libexecdir}/httpd-ssl-pass-dialog + $RPM_BUILD_ROOT%{_libexecdir}/httpd-ssl-pass-dialog # install http-ssl-gencerts install -m755 $RPM_SOURCE_DIR/httpd-ssl-gencerts \ - $RPM_BUILD_ROOT%{_libexecdir}/httpd-ssl-gencerts + $RPM_BUILD_ROOT%{_libexecdir}/httpd-ssl-gencerts # Install action scripts mkdir -p $RPM_BUILD_ROOT%{_libexecdir}/initscripts/legacy-actions/httpd @@ -541,7 +557,7 @@ done # Install logrotate config mkdir -p $RPM_BUILD_ROOT/etc/logrotate.d install -m 644 -p $RPM_SOURCE_DIR/httpd.logrotate \ - $RPM_BUILD_ROOT/etc/logrotate.d/httpd + $RPM_BUILD_ROOT/etc/logrotate.d/httpd # Install man pages install -d $RPM_BUILD_ROOT%{_mandir}/man8 $RPM_BUILD_ROOT%{_mandir}/man5 @@ -673,7 +689,6 @@ rm -rf $RPM_BUILD_ROOT %exclude %{_sysconfdir}/httpd/conf.modules.d/00-proxyhtml.conf %exclude %{_sysconfdir}/httpd/conf.modules.d/01-ldap.conf %exclude %{_sysconfdir}/httpd/conf.modules.d/01-session.conf -%exclude %{_sysconfdir}/httpd/conf.modules.d/01-md.conf %config(noreplace) %{_sysconfdir}/sysconfig/htcacheclean %{_prefix}/lib/tmpfiles.d/httpd.conf @@ -692,7 +707,6 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/httpd/modules/mod*.so %exclude %{_libdir}/httpd/modules/mod_auth_form.so %exclude %{_libdir}/httpd/modules/mod_ssl.so -%exclude %{_libdir}/httpd/modules/mod_md.so %exclude %{_libdir}/httpd/modules/mod_*ldap.so %exclude %{_libdir}/httpd/modules/mod_proxy_html.so %exclude %{_libdir}/httpd/modules/mod_xml2enc.so @@ -778,11 +792,6 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/httpd/modules/mod_auth_form.so %config(noreplace) %{_sysconfdir}/httpd/conf.modules.d/01-session.conf -%files -n mod_md -%defattr(-,root,root) -%{_libdir}/httpd/modules/mod_md.so -%config(noreplace) %{_sysconfdir}/httpd/conf.modules.d/01-md.conf - %files devel %defattr(-,root,root) %{_includedir}/httpd @@ -794,10 +803,41 @@ rm -rf $RPM_BUILD_ROOT %{_rpmconfigdir}/macros.d/macros.httpd %changelog -* Fri Aug 02 2019 CentOS Sources - 2.4.37-12.el8.centos -- Apply debranding changes +* Mon Dec 02 2019 Lubos Uhliarik - 2.4.37-21 +- Resolves: #1775158 - POST request with TLS 1.3 PHA client auth fails: + Re-negotiation handshake failed: Client certificate missing + +* Sun Dec 01 2019 Lubos Uhliarik - 2.4.37-20 +- Resolves: #1704317 - Add support for SSLKEYLOGFILE + +* Thu Nov 28 2019 Joe Orton - 2.4.37-19 +- mod_cgid: enable fd passing (#1633224) + +* Mon Nov 18 2019 Lubos Uhliarik - 2.4.37-18 +- Resolves: #1744121 - Unexpected OCSP in proxy SSL connection +- Resolves: #1725031 - htpasswd: support SHA-x passwords for FIPS compatibility +- Resolves: #1633224 - mod_cgid logging issues + +* Wed Oct 02 2019 Lubos Uhliarik - 2.4.37-17 +- remove bundled mod_md module +- Related: #1747898 - add mod_md package + +* Thu Aug 29 2019 Lubos Uhliarik - 2.4.37-16 +- Resolves: #1744999 - CVE-2019-9511 httpd:2.4/mod_http2: HTTP/2: large amount + of data request leads to denial of service +- Resolves: #1745086 - CVE-2019-9516 httpd:2.4/mod_http2: HTTP/2: 0-length + headers leads to denial of service +- Resolves: #1745154 - CVE-2019-9517 httpd:2.4/mod_http2: HTTP/2: request for + large response leads to denial of service + +* Tue Jul 16 2019 Lubos Uhliarik - 2.4.37-15 +- Resolves: #1730721 - absolute path used for default state and runtime dir by + default + +* Thu Jun 27 2019 Lubos Uhliarik - 2.4.37-14 +- Resolves: #1724549 - httpd response contains garbage in Content-Type header -* Wed Jun 12 2019 Lubos Uhliarik - 2.4.37-12 +* Wed Jun 12 2019 Lubos Uhliarik - 2.4.37-13 - Resolves: #1696142 - CVE-2019-0217 httpd:2.4/httpd: mod_auth_digest: access control bypass due to race condition - Resolves: #1696097 - CVE-2019-0220 httpd:2.4/httpd: URL normalization