From 42daacc7d1e60ebb6717dd4a592f77dc0053cf51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= Date: Sat, 18 Oct 2025 03:57:50 +0200 Subject: [PATCH 1/4] new version 2.4.65 --- .gitignore | 1 + httpd-2.4.64-r1927270.patch | 25 ------------------------- httpd.spec | 9 +++++---- sources | 4 ++-- 4 files changed, 8 insertions(+), 31 deletions(-) delete mode 100644 httpd-2.4.64-r1927270.patch diff --git a/.gitignore b/.gitignore index 0453dd4..f2fccfd 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ x86_64 /httpd-2.4.62.tar.bz2.asc /httpd-2.4.63.tar.bz2.asc /httpd-2.4.64.tar.bz2.asc +/httpd-2.4.65.tar.bz2.asc diff --git a/httpd-2.4.64-r1927270.patch b/httpd-2.4.64-r1927270.patch deleted file mode 100644 index 21b2b9d..0000000 --- a/httpd-2.4.64-r1927270.patch +++ /dev/null @@ -1,25 +0,0 @@ -From 6e59b124221014bd1c23053e9bb63fb3362387d9 Mon Sep 17 00:00:00 2001 -From: Eric Covener -Date: Wed, 16 Jul 2025 17:33:21 +0000 -Subject: [PATCH] rc already is set by result of expression eval - -git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1927270 13f79535-47bb-0310-9956-ffa450edef68 ---- - modules/mappers/mod_rewrite.c | 3 --- - 1 file changed, 3 deletions(-) - -diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c -index a6c35a0be1f..a71110bc76e 100644 ---- a/modules/mappers/mod_rewrite.c -+++ b/modules/mappers/mod_rewrite.c -@@ -4283,9 +4283,6 @@ static cond_return_type apply_rewrite_cond(rewritecond_entry *p, rewrite_ctx *ct - p->pattern - p->pskip, err); - rc = COND_RC_NOMATCH; - } -- else { -- rc = COND_RC_MATCH; -- } - /* update briRC backref info */ - if (rc && !(p->flags & CONDFLAG_NOTMATCH)) { - ctx->briRC.source = source; - diff --git a/httpd.spec b/httpd.spec index 2952a56..c2b352d 100644 --- a/httpd.spec +++ b/httpd.spec @@ -30,8 +30,8 @@ Summary: Apache HTTP Server Name: httpd -Version: 2.4.64 -Release: 3%{?dist} +Version: 2.4.65 +Release: 1%{?dist} URL: https://httpd.apache.org/ Source0: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 Source1: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2.asc @@ -103,8 +103,6 @@ Patch30: httpd-2.4.64-separate-systemd-fns.patch # Bug fixes # https://bugzilla.redhat.com/show_bug.cgi?id=1397243 Patch60: httpd-2.4.43-enable-sslv3.patch -# https://bz.apache.org/bugzilla/show_bug.cgi?id=69745 -Patch61: httpd-2.4.64-r1927270.patch # Security fixes # Patch200: ... @@ -847,6 +845,9 @@ exit $rv %{_rpmconfigdir}/macros.d/macros.httpd %changelog +* Sat Oct 18 2025 Luboš Uhliarik - 2.4.65-1 +- new version 2.4.65 + * Thu Jul 24 2025 Fedora Release Engineering - 2.4.64-3 - Rebuilt for https://fedoraproject.org/wiki/Fedora_43_Mass_Rebuild diff --git a/sources b/sources index c5568c0..8d4165c 100644 --- a/sources +++ b/sources @@ -1,3 +1,3 @@ -SHA512 (httpd-2.4.64.tar.bz2) = 299cb0d87a7e0e0a99d22bba7349b6b07c69222897410f9670af29896288d1f4e1da81d22ac9e1d8d6ea096e88044ab1dd34555b40a4b1b1cb3fd4b1d1897a7a -SHA512 (httpd-2.4.64.tar.bz2.asc) = 1a28214c8468288477f706422ab3419e186ef37dc1eb55760a7e951605e08e295230b6d828948693a9d5ba247e352b205254c999763fa6845dab2f703152e1d8 +SHA512 (httpd-2.4.65.tar.bz2) = 202f8bfe2aafcfbcd7315191d466e9c10b9a8c0abafb7864510b6e1abe4cb660aaacc2456aa77d43e48ef7a49e591d0b54170d3daf67abc3e06c3da1c63fdffc +SHA512 (httpd-2.4.65.tar.bz2.asc) = edcf02be97567fb28fef78f757e3355802575782ab452eaea79da70812b20ef4ba49a609e409a9664ec2546720b303deebbd563f3079ce4e77d67b91816210d1 SHA512 (KEYS) = 88c848b7ab9e4915d6625dcad3e8328673b0448f2ce76f2c44eecc612cf6afbce3287a4ee7219a44c6fcc61d5ecb2a1a8545456a4a16b90400263d7249cbf192 From 423fcafc4dfcf403964dc9c9df2ce808e744d6f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= Date: Tue, 4 Nov 2025 12:08:54 +0100 Subject: [PATCH 2/4] mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level required for VirtualHost matching. --- httpd-2.4.65-sslvhostsnipolicy.patch | 545 +++++++++++++++++++++++++++ httpd.spec | 7 +- 2 files changed, 551 insertions(+), 1 deletion(-) create mode 100644 httpd-2.4.65-sslvhostsnipolicy.patch diff --git a/httpd-2.4.65-sslvhostsnipolicy.patch b/httpd-2.4.65-sslvhostsnipolicy.patch new file mode 100644 index 0000000..401882a --- /dev/null +++ b/httpd-2.4.65-sslvhostsnipolicy.patch @@ -0,0 +1,545 @@ +From 0e0995bef84a66c9dabe75d6e81376896cc819bd Mon Sep 17 00:00:00 2001 +From: Joe Orton +Date: Tue, 4 Nov 2025 12:05:05 +0100 +Subject: [PATCH] [PATCH] mod_ssl: Add SSLVHostSNIPolicy directive to set the + compatibility level required for VirtualHost matching. + +For "secure" and "authonly" modes, a hash of the policy-relevant vhost +configuration is created and stored in the post_config hooks, reducing +the runtime code complexity (and overhead). + +* modules/ssl/ssl_engine_kernel.c (ssl_check_vhost_sni_policy): New + function, replacing ssl_server_compatible et al. + +* modules/ssl/ssl_engine_config.c (ssl_cmd_SSLVHostSNIPolicy): New + function. + +* modules/ssl/ssl_engine_init.c (md5_strarray_cmp, md5_strarray_hash, + hash_sni_policy_pk, hash_sni_policy_auth, create_sni_policy_hash): + New functions. + (ssl_init_Module): Invoke create_sni_policy_hash to store the hash + for every SSLSrvConfigRec. + +* modules/ssl/ssl_private.h (SSLModConfigRec): Add snivh_policy field. + (SSLSrvConfigRec): Add sni_policy_hash field. + +PR: 69743 +GitHub: closes #561 +--- + docs/manual/mod/mod_ssl.html.en | 77 +++++++++++++++++++ + modules/ssl/mod_ssl.c | 2 + + modules/ssl/ssl_engine_config.c | 41 ++++++++++ + modules/ssl/ssl_engine_init.c | 106 ++++++++++++++++++++++++++ + modules/ssl/ssl_engine_kernel.c | 131 ++++++-------------------------- + modules/ssl/ssl_private.h | 17 +++++ + 6 files changed, 265 insertions(+), 109 deletions(-) + +diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en +index e2a4a99..95f0667 100644 +--- a/docs/manual/mod/mod_ssl.html.en ++++ b/docs/manual/mod/mod_ssl.html.en +@@ -125,6 +125,7 @@ to provide the cryptography engine.

+
  • SSLUseStapling
  • +
  • SSLVerifyClient
  • +
  • SSLVerifyDepth
  • ++
  • SSLVHostSNIPolicy
  • + +

    Bugfix checklist

    See also

    +
      +@@ -2863,6 +2864,82 @@ known to the server (i.e. the CA's certificate is under +

      Example

      SSLVerifyDepth 10
      +
      + ++ ++
      top
      ++

      SSLVHostSNIPolicy Directive

      ++ ++ ++ ++ ++ ++ ++ ++ ++
      Description:Set compatibility policy for SNI client access to virtual hosts.
      Syntax:SSLVHostSNIPolicy strict|secure|authonly|insecure
      Default:SSLVHostSNIPolicy secure
      Context:server config
      Status:Extension
      Module:mod_ssl
      Compatibility:Available in httpd 2.4.65 and later

      This directive sets the policy applied when checking whether the ++<VirtualHost> ++identified by the Host request header in an HTTP request ++is compatible with the <VirtualHost> identified from the SNI ++extension sent during the initial TLS connection handshake. If an HTTP ++request is associated with a virtual host which has an incompatible ++SSL/TLS configuration under the policy used, an HTTP error response ++with status code 421 ("Misdirected Request") will be sent.

      ++ ++

      The policy also applies to TLS connections where an SNI extension ++is not sent during the handshake, implicitly using the default or ++first virtual host definition. If the Host header in an HTTP request ++on such a connection identifies any other non-default virtual host, ++the compatibility policy is tested.

      ++ ++

      The strict policy blocks all HTTP requests which are ++identified with a different virtual host to that identifed by SNI. ++The insecure policy allows all HTTP requests regardless ++of virtual host identified; such a configuration may be vulnerable to ++CVE-2025-23048. ++

      ++ ++

      The (default) secure, and authonly ++policies compare specific aspects of the SSL configuration for the two ++virtual hosts, which are grouped into two categories: ++ ++

      ++ ++This table illustrates whether an HTTP request will be blocked or ++allowed when the virtual host configurations differ as described, ++under each different policy setting: ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
      Policy modeAny VirtualHost mismatchServer certificate/key,
      or protocol/cipher restrictions
      Client verification/
      authentication settings
      strictblockedblockedblocked
      secureallowedblockedblocked
      authonlyallowedblockedallowed
      insecureallowedallowedallowed
      ++

      ++

      Example

      SSLVHostSNIPolicy authonly
      ++
      ++ ++ +
      + +
      +diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c +index fb66d18..c0fdafd 100644 +--- a/modules/ssl/mod_ssl.c ++++ b/modules/ssl/mod_ssl.c +@@ -80,6 +80,8 @@ static const command_rec ssl_config_cmds[] = { + SSL_CMD_SRV(RandomSeed, TAKE23, + "SSL Pseudo Random Number Generator (PRNG) seeding source " + "('startup|connect builtin|file:/path|exec:/path [bytes]')") ++ SSL_CMD_SRV(VHostSNIPolicy, TAKE1, ++ "SSL VirtualHost SNI compatibility policy setting") + + /* + * Per-server context configuration directives +diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c +index c5dce7f..31a1942 100644 +--- a/modules/ssl/ssl_engine_config.c ++++ b/modules/ssl/ssl_engine_config.c +@@ -82,6 +82,9 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s) + #ifdef HAVE_FIPS + mc->fips = UNSET; + #endif ++#ifdef HAVE_TLSEXT ++ mc->snivh_policy = MODSSL_SNIVH_SECURE; ++#endi + + apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, + apr_pool_cleanup_null, +@@ -1918,6 +1921,44 @@ const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag + #endif + } + ++const char *ssl_cmd_SSLVHostSNIPolicy(cmd_parms *cmd, void *dcfg, const char *arg) ++{ ++#ifdef HAVE_TLSEXT ++ SSLModConfigRec *mc = myModConfig(cmd->server); ++ const char *err; ++ ++ if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) { ++ return err; ++ } ++ if (!mc) { ++ return "SSLVHostSNIPolicy cannot be used inside SSLPolicyDefine"; ++ } ++ ++ if (strcEQ(arg, "secure")) { ++ mc->snivh_policy = MODSSL_SNIVH_SECURE; ++ } ++ else if (strcEQ(arg, "strict")) { ++ mc->snivh_policy = MODSSL_SNIVH_STRICT; ++ } ++ else if (strcEQ(arg, "insecure")) { ++ mc->snivh_policy = MODSSL_SNIVH_INSECURE; ++ } ++ else if (strcEQ(arg, "authonly")) { ++ mc->snivh_policy = MODSSL_SNIVH_AUTHONLY; ++ } ++ else { ++ return apr_psprintf(cmd->pool, "Invalid SSLVhostSNIPolicy " ++ "argument '%s'", arg); ++ } ++ ++ return NULL; ++#else ++ return "SSLVHostSNIPolicy cannot be used, OpenSSL is not built with " ++ "support for TLS extensions and SNI indication. Refer to the " ++ "documentation, and build a compatible version of OpenSSL." ++#endif ++} ++ + #ifdef HAVE_OCSP_STAPLING + + const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd, +diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c +index 2fbd076..64a9f32 100644 +--- a/modules/ssl/ssl_engine_init.c ++++ b/modules/ssl/ssl_engine_init.c +@@ -186,6 +186,110 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf, + modver, AP_SERVER_BASEVERSION, incver); + } + ++#ifdef HAVE_TLSEXT ++/* Helper functions to create the SNI vhost policy hash. The policy ++ * hash captures the configuration elements relevant to the mode ++ * selected at runtime by SSLVHostSNIPolicy. */ ++ ++#define md5_str_update(ctx_, pfx_, str_) do { apr_md5_update(ctx_, pfx_, strlen(pfx_)); apr_md5_update(ctx_, str_, strlen(str_)); } while (0) ++#define md5_ifstr_update(ctx_, pfx_, str_) do { apr_md5_update(ctx_, pfx_, strlen(pfx_)); if (str_) apr_md5_update(ctx_, str_, strlen(str_)); } while (0) ++#define md5_fmt_update(ctx_, fmt_, i_) do { char s_[128]; apr_snprintf(s_, sizeof s_, fmt_, i_); \ ++ apr_md5_update(ctx_, s_, strlen(s_)); } while (0) ++ ++static int md5_strarray_cmp(const void *p1, const void *p2) ++{ ++ return strcmp(*(char **)p1, *(char **)p2); ++} ++ ++/* Hashes an array of strings in sorted order. */ ++static void md5_strarray_hash(apr_pool_t *ptemp, apr_md5_ctx_t *hash, ++ const char *pfx, apr_array_header_t *s) ++{ ++ char **elts = apr_pmemdup(ptemp, s->elts, s->nelts * sizeof *elts); ++ int i; ++ ++ qsort(elts, s->nelts, sizeof(char *), md5_strarray_cmp); ++ ++ apr_md5_update(hash, pfx, strlen(pfx)); ++ for (i = 0; i < s->nelts; i++) { ++ md5_str_update(hash, "elm:", elts[i]); ++ } ++} ++ ++static void hash_sni_policy_pk(apr_pool_t *ptemp, apr_md5_ctx_t *hash, modssl_ctx_t *ctx) ++{ ++ md5_fmt_update(hash, "protocol:%d", ctx->protocol); ++ ++ md5_ifstr_update(hash, "ciphers:", ctx->auth.cipher_suite); ++ md5_ifstr_update(hash, "tls13_ciphers:", ctx->auth.tls13_ciphers); ++ ++ md5_strarray_hash(ptemp, hash, "cert_files:", ctx->pks->cert_files); ++ md5_strarray_hash(ptemp, hash, "key_files:", ctx->pks->key_files); ++} ++ ++static void hash_sni_policy_auth(apr_md5_ctx_t *hash, modssl_ctx_t *ctx) ++{ ++ modssl_pk_server_t *pks = ctx->pks; ++ modssl_auth_ctx_t *a = &ctx->auth; ++ ++ md5_fmt_update(hash, "verify_depth:%d", a->verify_depth); ++ md5_fmt_update(hash, "verify_mode:%d", a->verify_mode); ++ ++ md5_ifstr_update(hash, "ca_name_path:", pks->ca_name_path); ++ md5_ifstr_update(hash, "ca_name_file:", pks->ca_name_file); ++ md5_ifstr_update(hash, "ca_cert_path:", a->ca_cert_path); ++ md5_ifstr_update(hash, "ca_cert_file:", a->ca_cert_file); ++ md5_ifstr_update(hash, "crl_path:", ctx->crl_path); ++ md5_ifstr_update(hash, "crl_file:", ctx->crl_file); ++ md5_fmt_update(hash, "crl_check_mask:%d", ctx->crl_check_mask); ++ md5_fmt_update(hash, "ocsp_mask:%d", ctx->ocsp_mask); ++ md5_fmt_update(hash, "ocsp_force_default:%d", ctx->ocsp_force_default); ++ md5_ifstr_update(hash, "ocsp_responder:", ctx->ocsp_responder); ++ ++#ifdef HAVE_SRP ++ md5_ifstr_update(hash, "srp_vfile:", ctx->srp_vfile); ++#endif ++ ++#ifdef HAVE_SSL_CONF_CMD ++ { ++ apr_array_header_t *parms = ctx->ssl_ctx_param; ++ int n; ++ ++ for (n = 0; n < parms->nelts; n++) { ++ ssl_ctx_param_t *p = &APR_ARRAY_IDX(parms, n, ssl_ctx_param_t); ++ ++ md5_str_update(hash, "param:", p->name); ++ md5_str_update(hash, "value:", p->value); ++ } ++ } ++#endif ++} ++#endif ++ ++static char *create_sni_policy_hash(apr_pool_t *p, apr_pool_t *ptemp, ++ modssl_snivhpolicy_t policy, ++ SSLSrvConfigRec *sc) ++{ ++ char *rv = NULL; ++#ifdef HAVE_TLSEXT ++ if (policy != MODSSL_SNIVH_STRICT && policy != MODSSL_SNIVH_INSECURE) { ++ apr_md5_ctx_t hash; ++ unsigned char digest[APR_MD5_DIGESTSIZE]; ++ ++ /* Create the vhost policy hash for comparison later. */ ++ apr_md5_init(&hash); ++ hash_sni_policy_auth(&hash, sc->server); ++ if (policy == MODSSL_SNIVH_SECURE) ++ hash_sni_policy_pk(ptemp, &hash, sc->server); ++ apr_md5_final(digest, &hash); ++ ++ rv = apr_palloc(p, 2 * APR_MD5_DIGESTSIZE + 1); ++ ap_bin2hex(digest, APR_MD5_DIGESTSIZE, rv); /* sets final '\0' */ ++ } ++#endif ++ return rv; ++} ++ + /* _________________________________________________________________ + ** + ** Let other answer special connection attempts. +@@ -439,6 +543,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, + return rv; + } + } ++ ++ sc->sni_policy_hash = create_sni_policy_hash(p, ptemp, mc->snivh_policy, sc); + } + + /* +diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c +index 33aa1f7..a6af633 100644 +--- a/modules/ssl/ssl_engine_kernel.c ++++ b/modules/ssl/ssl_engine_kernel.c +@@ -101,112 +101,28 @@ static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc) + } + + #ifdef HAVE_TLSEXT +-static int ap_array_same_str_set(apr_array_header_t *s1, apr_array_header_t *s2) ++/* Check whether a transition from vhost sc1 to sc2 from SNI to Host: ++ * vhost selection is permitted according to the SSLVHostSNIPolicy ++ * setting. Returns 1 if the policy treats the vhosts as compatible, ++ * else 0. */ ++static int ssl_check_vhost_sni_policy(SSLSrvConfigRec *sc1, ++ SSLSrvConfigRec *sc2) + { +- int i; +- const char *c; +- +- if (s1 == s2) { ++ modssl_snivhpolicy_t policy = sc1->mc->snivh_policy; ++ ++ /* Policy: insecure => allow everything. */ ++ if (policy == MODSSL_SNIVH_INSECURE) + return 1; +- } +- else if (!s1 || !s2 || (s1->nelts != s2->nelts)) { +- return 0; +- } + +- for (i = 0; i < s1->nelts; i++) { +- c = APR_ARRAY_IDX(s1, i, const char *); +- if (!c || !ap_array_str_contains(s2, c)) { +- return 0; +- } +- } +- return 1; +-} +- +-static int ssl_pk_server_compatible(modssl_pk_server_t *pks1, +- modssl_pk_server_t *pks2) +-{ +- if (!pks1 || !pks2) { +- return 0; +- } +- /* both have the same certificates? */ +- if ((pks1->ca_name_path != pks2->ca_name_path) +- && (!pks1->ca_name_path || !pks2->ca_name_path +- || strcmp(pks1->ca_name_path, pks2->ca_name_path))) { +- return 0; +- } +- if ((pks1->ca_name_file != pks2->ca_name_file) +- && (!pks1->ca_name_file || !pks2->ca_name_file +- || strcmp(pks1->ca_name_file, pks2->ca_name_file))) { +- return 0; +- } +- if (!ap_array_same_str_set(pks1->cert_files, pks2->cert_files) +- || !ap_array_same_str_set(pks1->key_files, pks2->key_files)) { +- return 0; +- } +- return 1; +-} +- +-static int ssl_auth_compatible(modssl_auth_ctx_t *a1, +- modssl_auth_ctx_t *a2) +-{ +- if (!a1 || !a2) { +- return 0; +- } +- /* both have the same verification */ +- if ((a1->verify_depth != a2->verify_depth) +- || (a1->verify_mode != a2->verify_mode)) { +- return 0; +- } +- /* both have the same ca path/file */ +- if ((a1->ca_cert_path != a2->ca_cert_path) +- && (!a1->ca_cert_path || !a2->ca_cert_path +- || strcmp(a1->ca_cert_path, a2->ca_cert_path))) { +- return 0; +- } +- if ((a1->ca_cert_file != a2->ca_cert_file) +- && (!a1->ca_cert_file || !a2->ca_cert_file +- || strcmp(a1->ca_cert_file, a2->ca_cert_file))) { +- return 0; +- } +- /* both have the same ca cipher suite string */ +- if ((a1->cipher_suite != a2->cipher_suite) +- && (!a1->cipher_suite || !a2->cipher_suite +- || strcmp(a1->cipher_suite, a2->cipher_suite))) { +- return 0; +- } +- /* both have the same ca cipher suite string */ +- if ((a1->tls13_ciphers != a2->tls13_ciphers) +- && (!a1->tls13_ciphers || !a2->tls13_ciphers +- || strcmp(a1->tls13_ciphers, a2->tls13_ciphers))) { +- return 0; +- } +- return 1; +-} +- +-static int ssl_ctx_compatible(modssl_ctx_t *ctx1, +- modssl_ctx_t *ctx2) +-{ +- if (!ctx1 || !ctx2 +- || (ctx1->protocol != ctx2->protocol) +- || !ssl_auth_compatible(&ctx1->auth, &ctx2->auth) +- || !ssl_pk_server_compatible(ctx1->pks, ctx2->pks)) { ++ /* Policy: strict => fail for any vhost transition. */ ++ if (policy == MODSSL_SNIVH_STRICT && sc1 != sc2) + return 0; +- } +- return 1; +-} + +-static int ssl_server_compatible(server_rec *s1, server_rec *s2) +-{ +- SSLSrvConfigRec *sc1 = s1? mySrvConfig(s1) : NULL; +- SSLSrvConfigRec *sc2 = s2? mySrvConfig(s2) : NULL; ++ /* For authonly/secure policy, compare the hash. */ ++ AP_DEBUG_ASSERT(sc1->sni_policy_hash); ++ AP_DEBUG_ASSERT(sc2->sni_policy_hash); + +- /* both use the same TLS protocol? */ +- if (!sc1 || !sc2 +- || !ssl_ctx_compatible(sc1->server, sc2->server)) { +- return 0; +- } +- +- return 1; ++ return strcmp(sc1->sni_policy_hash, sc2->sni_policy_hash) == 0; + } + #endif + +@@ -275,6 +191,8 @@ int ssl_hook_ReadReq(request_rec *r) + server_rec *handshakeserver = sslconn->server; + SSLSrvConfigRec *hssc = mySrvConfig(handshakeserver); + ++ AP_DEBUG_ASSERT(hssc); ++ + if ((servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))) { + /* + * The SNI extension supplied a hostname. So don't accept requests +@@ -315,19 +233,14 @@ int ssl_hook_ReadReq(request_rec *r) + "which is required to access this server.
      \n"); + return HTTP_FORBIDDEN; + } +- if (r->server != handshakeserver +- && !ssl_server_compatible(sslconn->server, r->server)) { +- /* +- * The request does not select the virtual host that was +- * selected for handshaking and its SSL parameters are different +- */ +- ++ /* Enforce SSL SNI vhost compatibility policy. */ ++ if (!ssl_check_vhost_sni_policy(sc, hssc)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02032) + "Hostname %s %s and hostname %s provided" +- " via HTTP have no compatible SSL setup", ++ " via HTTP have no compatible SSL setup for policy '%s'", + servername ? servername : handshakeserver->server_hostname, + servername ? "provided via SNI" : "(default host as no SNI was provided)", +- r->hostname); ++ r->hostname, MODSSL_SNIVH_NAME(sc->mc->snivh_policy)); + return HTTP_MISDIRECTED_REQUEST; + } + } +diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h +index 2b18189..dbef182 100644 +--- a/modules/ssl/ssl_private.h ++++ b/modules/ssl/ssl_private.h +@@ -556,6 +556,19 @@ typedef struct { + int nBytes; + } ssl_randseed_t; + ++/* SNI vhost compatibility policy. */ ++typedef enum { ++ MODSSL_SNIVH_STRICT = 0, ++ MODSSL_SNIVH_SECURE = 1, ++ MODSSL_SNIVH_AUTHONLY = 2, ++ MODSSL_SNIVH_INSECURE = 3 ++} modssl_snivhpolicy_t; ++ ++/* Maps modssl_snivhpolicy_t back into a config option string. */ ++#define MODSSL_SNIVH_NAME(p_) ((p_) == MODSSL_SNIVH_STRICT ? "strict" : \ ++ ((p_) == MODSSL_SNIVH_SECURE ? "secure" : \ ++ ((p_) == MODSSL_SNIVH_AUTHONLY ? "authonly" : "insecure" ))) ++ + /** + * Define the structure of an ASN.1 anything + */ +@@ -689,6 +702,8 @@ typedef struct { + #ifdef HAVE_FIPS + BOOL fips; + #endif ++ ++ modssl_snivhpolicy_t snivh_policy; + } SSLModConfigRec; + + /** Structure representing configured filenames for certs and keys for +@@ -843,6 +858,7 @@ struct SSLSrvConfigRec { + modssl_ctx_t *server; + #ifdef HAVE_TLSEXT + ssl_enabled_t strict_sni_vhost_check; ++ const char *sni_policy_hash; + #endif + #ifndef OPENSSL_NO_COMP + BOOL compression; +@@ -918,6 +934,7 @@ const char *ssl_cmd_SSLRequire(cmd_parms *, void *, const char *); + const char *ssl_cmd_SSLUserName(cmd_parms *, void *, const char *); + const char *ssl_cmd_SSLRenegBufferSize(cmd_parms *cmd, void *dcfg, const char *arg); + const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag); ++const char *ssl_cmd_SSLVHostSNIPolicy(cmd_parms *cmd, void *dcfg, const char *arg); + const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int flag); + + const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag); +-- +2.44.0 + diff --git a/httpd.spec b/httpd.spec index c2b352d..c1d98a2 100644 --- a/httpd.spec +++ b/httpd.spec @@ -31,7 +31,7 @@ Summary: Apache HTTP Server Name: httpd Version: 2.4.65 -Release: 1%{?dist} +Release: 2%{?dist} URL: https://httpd.apache.org/ Source0: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 Source1: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2.asc @@ -99,6 +99,7 @@ Patch27: httpd-2.4.64-sslprotdefault.patch Patch28: httpd-2.4.43-logjournal.patch Patch29: httpd-2.4.63-r1912477+.patch Patch30: httpd-2.4.64-separate-systemd-fns.patch +Patch31: httpd-2.4.65-sslvhostsnipolicy.patch # Bug fixes # https://bugzilla.redhat.com/show_bug.cgi?id=1397243 @@ -845,6 +846,10 @@ exit $rv %{_rpmconfigdir}/macros.d/macros.httpd %changelog +* Tue Nov 04 2025 Luboš Uhliarik - 2.4.65-2 +- mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level + required for VirtualHost matching. + * Sat Oct 18 2025 Luboš Uhliarik - 2.4.65-1 - new version 2.4.65 From e67b3958da702ed43edf8010ad05918032f5dc2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= Date: Tue, 4 Nov 2025 13:14:49 +0100 Subject: [PATCH 3/4] Fix typo in patch + include util_md5.h mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level required for VirtualHost matching. --- httpd-2.4.65-sslvhostsnipolicy.patch | 30 ++++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/httpd-2.4.65-sslvhostsnipolicy.patch b/httpd-2.4.65-sslvhostsnipolicy.patch index 401882a..6767cbc 100644 --- a/httpd-2.4.65-sslvhostsnipolicy.patch +++ b/httpd-2.4.65-sslvhostsnipolicy.patch @@ -1,7 +1,7 @@ -From 0e0995bef84a66c9dabe75d6e81376896cc819bd Mon Sep 17 00:00:00 2001 -From: Joe Orton -Date: Tue, 4 Nov 2025 12:05:05 +0100 -Subject: [PATCH] [PATCH] mod_ssl: Add SSLVHostSNIPolicy directive to set the +From ba168444686368377c6e69373e20e6dae8ebb702 Mon Sep 17 00:00:00 2001 +From: Joe Orton +Date: Tue, 4 Nov 2025 12:49:36 +0100 +Subject: [PATCH] [PATCH] mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level required for VirtualHost matching. For "secure" and "authonly" modes, a hash of the policy-relevant vhost @@ -29,10 +29,10 @@ GitHub: closes #561 docs/manual/mod/mod_ssl.html.en | 77 +++++++++++++++++++ modules/ssl/mod_ssl.c | 2 + modules/ssl/ssl_engine_config.c | 41 ++++++++++ - modules/ssl/ssl_engine_init.c | 106 ++++++++++++++++++++++++++ + modules/ssl/ssl_engine_init.c | 107 ++++++++++++++++++++++++++ modules/ssl/ssl_engine_kernel.c | 131 ++++++-------------------------- modules/ssl/ssl_private.h | 17 +++++ - 6 files changed, 265 insertions(+), 109 deletions(-) + 6 files changed, 266 insertions(+), 109 deletions(-) diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en index e2a4a99..95f0667 100644 @@ -143,7 +143,7 @@ index fb66d18..c0fdafd 100644 /* * Per-server context configuration directives diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c -index c5dce7f..31a1942 100644 +index c5dce7f..f856b18 100644 --- a/modules/ssl/ssl_engine_config.c +++ b/modules/ssl/ssl_engine_config.c @@ -82,6 +82,9 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s) @@ -152,7 +152,7 @@ index c5dce7f..31a1942 100644 #endif +#ifdef HAVE_TLSEXT + mc->snivh_policy = MODSSL_SNIVH_SECURE; -+#endi ++#endif apr_pool_userdata_set(mc, SSL_MOD_CONFIG_KEY, apr_pool_cleanup_null, @@ -202,10 +202,18 @@ index c5dce7f..31a1942 100644 const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd, diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c -index 2fbd076..64a9f32 100644 +index 2fbd076..1bf6ac4 100644 --- a/modules/ssl/ssl_engine_init.c +++ b/modules/ssl/ssl_engine_init.c -@@ -186,6 +186,110 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf, +@@ -30,6 +30,7 @@ + + #include "mpm_common.h" + #include "mod_md.h" ++#include "util_md5.h" + + static apr_status_t ssl_init_ca_cert_path(server_rec *, apr_pool_t *, const char *, + STACK_OF(X509_NAME) *, STACK_OF(X509_INFO) *); +@@ -186,6 +187,110 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf, modver, AP_SERVER_BASEVERSION, incver); } @@ -316,7 +324,7 @@ index 2fbd076..64a9f32 100644 /* _________________________________________________________________ ** ** Let other answer special connection attempts. -@@ -439,6 +543,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, +@@ -439,6 +544,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog, return rv; } } From 722da26b4e1ba8ca72127619ca62f7b835bc6d4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lubo=C5=A1=20Uhliarik?= Date: Wed, 12 Nov 2025 11:53:43 +0100 Subject: [PATCH 4/4] mod_ssl: SSLVHostSNIPolicy - Fix handling of STRICT mode --- httpd-2.4.65-sslvhostsnipolicy.patch | 41 ++++++++++++++-------------- httpd.spec | 5 +++- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/httpd-2.4.65-sslvhostsnipolicy.patch b/httpd-2.4.65-sslvhostsnipolicy.patch index 6767cbc..0831efc 100644 --- a/httpd-2.4.65-sslvhostsnipolicy.patch +++ b/httpd-2.4.65-sslvhostsnipolicy.patch @@ -1,7 +1,7 @@ -From ba168444686368377c6e69373e20e6dae8ebb702 Mon Sep 17 00:00:00 2001 +From 0263d1e290bc029c5c059fdaa4f21ca9885f9c84 Mon Sep 17 00:00:00 2001 From: Joe Orton -Date: Tue, 4 Nov 2025 12:49:36 +0100 -Subject: [PATCH] [PATCH] mod_ssl: Add SSLVHostSNIPolicy directive to set the +Date: Wed, 12 Nov 2025 11:11:41 +0100 +Subject: [PATCH] mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level required for VirtualHost matching. For "secure" and "authonly" modes, a hash of the policy-relevant vhost @@ -26,13 +26,13 @@ the runtime code complexity (and overhead). PR: 69743 GitHub: closes #561 --- - docs/manual/mod/mod_ssl.html.en | 77 +++++++++++++++++++ + docs/manual/mod/mod_ssl.html.en | 77 ++++++++++++++++++ modules/ssl/mod_ssl.c | 2 + modules/ssl/ssl_engine_config.c | 41 ++++++++++ - modules/ssl/ssl_engine_init.c | 107 ++++++++++++++++++++++++++ - modules/ssl/ssl_engine_kernel.c | 131 ++++++-------------------------- - modules/ssl/ssl_private.h | 17 +++++ - 6 files changed, 266 insertions(+), 109 deletions(-) + modules/ssl/ssl_engine_init.c | 107 +++++++++++++++++++++++++ + modules/ssl/ssl_engine_kernel.c | 133 ++++++-------------------------- + modules/ssl/ssl_private.h | 17 ++++ + 6 files changed, 267 insertions(+), 110 deletions(-) diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en index e2a4a99..95f0667 100644 @@ -60,7 +60,7 @@ index e2a4a99..95f0667 100644 +Context:server config +Status:Extension +Module:mod_ssl -+Compatibility:Available in httpd 2.4.65 and later ++Compatibility:Available in httpd 2.4.65 in Fedora Linux +

      This directive sets the policy applied when checking whether the +<VirtualHost> +identified by the Host request header in an HTTP request @@ -334,7 +334,7 @@ index 2fbd076..1bf6ac4 100644 /* diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c -index 33aa1f7..a6af633 100644 +index 33aa1f7..83ae90e 100644 --- a/modules/ssl/ssl_engine_kernel.c +++ b/modules/ssl/ssl_engine_kernel.c @@ -101,112 +101,28 @@ static int fill_reneg_buffer(request_rec *r, SSLDirConfigRec *dc) @@ -371,7 +371,10 @@ index 33aa1f7..a6af633 100644 - } - return 1; -} -- ++ /* Policy: strict => fail for any vhost transition. */ ++ if (policy == MODSSL_SNIVH_STRICT) ++ return sc1 == sc2; + -static int ssl_pk_server_compatible(modssl_pk_server_t *pks1, - modssl_pk_server_t *pks2) -{ @@ -395,7 +398,10 @@ index 33aa1f7..a6af633 100644 - } - return 1; -} -- ++ /* For authonly/secure policy, compare the hash. */ ++ AP_DEBUG_ASSERT(sc1->sni_policy_hash); ++ AP_DEBUG_ASSERT(sc2->sni_policy_hash); + -static int ssl_auth_compatible(modssl_auth_ctx_t *a1, - modssl_auth_ctx_t *a2) -{ @@ -440,21 +446,16 @@ index 33aa1f7..a6af633 100644 - || (ctx1->protocol != ctx2->protocol) - || !ssl_auth_compatible(&ctx1->auth, &ctx2->auth) - || !ssl_pk_server_compatible(ctx1->pks, ctx2->pks)) { -+ /* Policy: strict => fail for any vhost transition. */ -+ if (policy == MODSSL_SNIVH_STRICT && sc1 != sc2) - return 0; +- return 0; - } - return 1; -} - +- -static int ssl_server_compatible(server_rec *s1, server_rec *s2) -{ - SSLSrvConfigRec *sc1 = s1? mySrvConfig(s1) : NULL; - SSLSrvConfigRec *sc2 = s2? mySrvConfig(s2) : NULL; -+ /* For authonly/secure policy, compare the hash. */ -+ AP_DEBUG_ASSERT(sc1->sni_policy_hash); -+ AP_DEBUG_ASSERT(sc2->sni_policy_hash); - +- - /* both use the same TLS protocol? */ - if (!sc1 || !sc2 - || !ssl_ctx_compatible(sc1->server, sc2->server)) { diff --git a/httpd.spec b/httpd.spec index c1d98a2..025203e 100644 --- a/httpd.spec +++ b/httpd.spec @@ -31,7 +31,7 @@ Summary: Apache HTTP Server Name: httpd Version: 2.4.65 -Release: 2%{?dist} +Release: 3%{?dist} URL: https://httpd.apache.org/ Source0: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 Source1: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2.asc @@ -846,6 +846,9 @@ exit $rv %{_rpmconfigdir}/macros.d/macros.httpd %changelog +* Wed Nov 12 2025 Luboš Uhliarik - 2.4.65-3 +- mod_ssl: SSLVHostSNIPolicy - Fix handling of STRICT mode + * Tue Nov 04 2025 Luboš Uhliarik - 2.4.65-2 - mod_ssl: Add SSLVHostSNIPolicy directive to set the compatibility level required for VirtualHost matching.