Skip to content

Commit 21491b2

Browse files
committed
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 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1929308 13f79535-47bb-0310-9956-ffa450edef68
1 parent e54735b commit 21491b2

File tree

6 files changed

+273
-109
lines changed

6 files changed

+273
-109
lines changed

docs/manual/mod/mod_ssl.xml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,91 @@ SSLStrictSNIVHostCheck on
18531853
</usage>
18541854
</directivesynopsis>
18551855

1856+
<directivesynopsis>
1857+
<name>SSLVHostSNIPolicy</name>
1858+
<description>Set compatibility policy for SNI client access to virtual hosts.</description>
1859+
<syntax>SSLVHostSNIPolicy strict|secure|authonly|insecure</syntax>
1860+
<default>SSLVHostSNIPolicy secure</default>
1861+
<contextlist><context>server config</context></contextlist>
1862+
<compatibility>Available in httpd 2.5 and later</compatibility>
1863+
1864+
<usage><p>This directive sets policy applied when checking whether the
1865+
<directive module="core" type="section">VirtualHost</directive>
1866+
identified by the <code>Host</code> request header in an HTTP request
1867+
is compatible with the <directive module="core"
1868+
type="section">VirtualHost</directive> identified from the SNI
1869+
extension sent during the initial TLS connection handshake. If an HTTP
1870+
request is associated with a virtual host which has an incompatible
1871+
SSL/TLS configuration under the policy used, an HTTP error response
1872+
with status code 421 ("Misdirected Request") will be sent.</p>
1873+
1874+
<p>The <code>strict</code> policy blocks all HTTP requests which are
1875+
identified with a different virtual host to that identifed by SNI.
1876+
The <code>insecure</code> policy allows all HTTP requests regardless
1877+
of virtual host identified; such a configuration may be vulnerable to
1878+
<a
1879+
href="https://httpd.apache.org/security/vulnerabilities_24.html">CVE-2025-23048</a>.
1880+
</p>
1881+
1882+
<p>The (default) <code>secure</code>, and <code>authonly</code>
1883+
policies compare specific aspects of the SSL configuration for the two
1884+
virtual hosts, which are grouped into two categories:
1885+
1886+
<ul>
1887+
<li><strong>client vertification and authentication
1888+
settings</strong>: directives which affect TLS client certificate
1889+
verification or authentication, such as <directive
1890+
module="mod_ssl">SSLVerifyClient</directive>, <directive
1891+
module="mod_ssl">SSLVerifyMode</directive>, <directive
1892+
module="mod_ssl">SSLCACertificatePath</directive>, <directive
1893+
module="mod_ssl">SSLSRPVerifierFile</directive>; any use of <directive
1894+
module="mod_ssl">SSLOpenSSLConfCmd</directive></li>
1895+
1896+
<li><strong>server certificate/key, or protocol/cipher
1897+
restrictions</strong>: directives which determine the server
1898+
certificate or key (<directive
1899+
module="mod_ssl">SSLCertificateKeyFile</directive> etc), cipher or
1900+
protocol restrictions (<directive
1901+
module="mod_ssl">SSLCipherSuite</directive> and <directive
1902+
module="mod_ssl">SSLProtocol</directive>)</li>
1903+
</ul>
1904+
1905+
This table illustrates whether an HTTP request will be blocked or
1906+
allowed when the virtual host configurations differ as described,
1907+
under each different policy setting:
1908+
1909+
<table border="1" style="zebra">
1910+
<columnspec><column width=".3"/><column width=".2"/><column width=".5"/>
1911+
</columnspec>
1912+
<tr>
1913+
<th>Policy mode</th>
1914+
<th>Any VirtualHost mismatch</th>
1915+
<th>Client verification/<br />authentication settings</th>
1916+
<th>Server certificate/key, <br />or protocol/cipher restrictions</th>
1917+
</tr>
1918+
<tr>
1919+
<td><code>strict</code><td>blocked</td><td>blocked</td><td>blocked</td></td>
1920+
</tr>
1921+
<tr>
1922+
<td><code>secure</code><td>allowed</td><td>blocked</td><td>blocked</td></td>
1923+
</tr>
1924+
<tr>
1925+
<td><code>authonly</code><td>allowed</td><td>blocked</td><td>allowed</td></td>
1926+
</tr>
1927+
<tr>
1928+
<td><code>insecure</code><td>allowed</td><td>allowed</td><td>allowed</td></td>
1929+
</tr>
1930+
</table>
1931+
</p>
1932+
<example><title>Example</title>
1933+
<highlight language="config">
1934+
SSLVHostSNIPolicy authonly
1935+
</highlight>
1936+
1937+
</example>
1938+
</usage>
1939+
</directivesynopsis>
1940+
18561941
<directivesynopsis>
18571942
<name>SSLProxyMachineCertificatePath</name>
18581943
<description>Directory of PEM-encoded client certificates and keys to be used by the proxy</description>

modules/ssl/mod_ssl.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ static const command_rec ssl_config_cmds[] = {
9595
SSL_CMD_SRV(RandomSeed, TAKE23,
9696
"SSL Pseudo Random Number Generator (PRNG) seeding source "
9797
"('startup|connect builtin|file:/path|exec:/path [bytes]')")
98+
SSL_CMD_SRV(VHostSNIPolicy, TAKE1,
99+
"SSL VirtualHost SNI compatibility policy setting")
98100

99101
/*
100102
* Per-server context configuration directives

modules/ssl/ssl_engine_config.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ static SSLModConfigRec *ssl_config_global_create(apr_pool_t *pool, server_rec *s
6060
* initialize per-module configuration
6161
*/
6262
mc->sesscache_mode = SSL_SESS_CACHE_OFF;
63+
#ifdef HAVE_TLSEXT
64+
mc->snivh_policy = MODSSL_SNIVH_SECURE;
65+
#endif
6366
#ifdef MODSSL_USE_SSLRAND
6467
mc->aRandSeed = apr_array_make(pool, 4,
6568
sizeof(ssl_randseed_t));
@@ -2038,6 +2041,44 @@ const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag
20382041
#endif
20392042
}
20402043

2044+
const char *ssl_cmd_SSLVHostSNIPolicy(cmd_parms *cmd, void *dcfg, const char *arg)
2045+
{
2046+
#ifdef HAVE_TLSEXT
2047+
SSLModConfigRec *mc = myModConfig(cmd->server);
2048+
const char *err;
2049+
2050+
if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
2051+
return err;
2052+
}
2053+
if (!mc) {
2054+
return "SSLVHostSNIPolicy cannot be used inside SSLPolicyDefine";
2055+
}
2056+
2057+
if (strcEQ(arg, "secure")) {
2058+
mc->snivh_policy = MODSSL_SNIVH_SECURE;
2059+
}
2060+
else if (strcEQ(arg, "strict")) {
2061+
mc->snivh_policy = MODSSL_SNIVH_STRICT;
2062+
}
2063+
else if (strcEQ(arg, "insecure")) {
2064+
mc->snivh_policy = MODSSL_SNIVH_INSECURE;
2065+
}
2066+
else if (strcEQ(arg, "authonly")) {
2067+
mc->snivh_policy = MODSSL_SNIVH_AUTHONLY;
2068+
}
2069+
else {
2070+
return apr_psprintf(cmd->pool, "Invalid SSLVhostSNIPolicy "
2071+
"argument '%s'", arg);
2072+
}
2073+
2074+
return NULL;
2075+
#else
2076+
return "SSLVHostSNIPolicy cannot be used, OpenSSL is not built with "
2077+
"support for TLS extensions and SNI indication. Refer to the "
2078+
"documentation, and build a compatible version of OpenSSL."
2079+
#endif
2080+
}
2081+
20412082
#ifdef HAVE_OCSP_STAPLING
20422083

20432084
const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd,

modules/ssl/ssl_engine_init.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,110 @@ static int load_echkeys(SSL_CTX *ctx, const char *echdir, server_rec *s,
297297
}
298298
#endif
299299

300+
#ifdef HAVE_TLSEXT
301+
/* Helper functions to create the SNI vhost policy hash. The policy
302+
* hash captures the configuration elements relevant to the mode
303+
* selected at runtime by SSLVHostSNIPolicy. */
304+
305+
#define md5_str_update(ctx_, pfx_, str_) do { apr_md5_update(ctx_, pfx_, strlen(pfx_)); apr_md5_update(ctx_, str_, strlen(str_)); } while (0)
306+
#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)
307+
#define md5_fmt_update(ctx_, fmt_, i_) do { char s_[128]; apr_snprintf(s_, sizeof s_, fmt_, i_); \
308+
apr_md5_update(ctx_, s_, strlen(s_)); } while (0)
309+
310+
static int md5_strarray_cmp(const void *p1, const void *p2)
311+
{
312+
return strcmp(*(char **)p1, *(char **)p2);
313+
}
314+
315+
/* Hashes an array of strings in sorted order. */
316+
static void md5_strarray_hash(apr_pool_t *ptemp, apr_md5_ctx_t *hash,
317+
const char *pfx, apr_array_header_t *s)
318+
{
319+
char **elts = apr_pmemdup(ptemp, s->elts, s->nelts * sizeof *elts);
320+
int i;
321+
322+
qsort(elts, s->nelts, sizeof(char *), md5_strarray_cmp);
323+
324+
apr_md5_update(hash, pfx, strlen(pfx));
325+
for (i = 0; i < s->nelts; i++) {
326+
md5_str_update(hash, "elm:", elts[i]);
327+
}
328+
}
329+
330+
static void hash_sni_policy_pk(apr_pool_t *ptemp, apr_md5_ctx_t *hash, modssl_ctx_t *ctx)
331+
{
332+
md5_fmt_update(hash, "protocol:%d", ctx->protocol);
333+
334+
md5_ifstr_update(hash, "ciphers:", ctx->auth.cipher_suite);
335+
md5_ifstr_update(hash, "tls13_ciphers:", ctx->auth.tls13_ciphers);
336+
337+
md5_strarray_hash(ptemp, hash, "cert_files:", ctx->pks->cert_files);
338+
md5_strarray_hash(ptemp, hash, "key_files:", ctx->pks->key_files);
339+
}
340+
341+
static void hash_sni_policy_auth(apr_md5_ctx_t *hash, modssl_ctx_t *ctx)
342+
{
343+
modssl_pk_server_t *pks = ctx->pks;
344+
modssl_auth_ctx_t *a = &ctx->auth;
345+
346+
md5_fmt_update(hash, "verify_depth:%d", a->verify_depth);
347+
md5_fmt_update(hash, "verify_mode:%d", a->verify_mode);
348+
349+
md5_ifstr_update(hash, "ca_name_path:", pks->ca_name_path);
350+
md5_ifstr_update(hash, "ca_name_file:", pks->ca_name_file);
351+
md5_ifstr_update(hash, "ca_cert_path:", a->ca_cert_path);
352+
md5_ifstr_update(hash, "ca_cert_file:", a->ca_cert_file);
353+
md5_ifstr_update(hash, "crl_path:", ctx->crl_path);
354+
md5_ifstr_update(hash, "crl_file:", ctx->crl_file);
355+
md5_fmt_update(hash, "crl_check_mask:%d", ctx->crl_check_mask);
356+
md5_fmt_update(hash, "ocsp_mask:%d", ctx->ocsp_mask);
357+
md5_fmt_update(hash, "ocsp_force_default:%d", ctx->ocsp_force_default);
358+
md5_ifstr_update(hash, "ocsp_responder:", ctx->ocsp_responder);
359+
360+
#ifdef HAVE_SRP
361+
md5_ifstr_update(hash, "srp_vfile:", ctx->srp_vfile);
362+
#endif
363+
364+
#ifdef HAVE_SSL_CONF_CMD
365+
{
366+
apr_array_header_t *parms = ctx->ssl_ctx_param;
367+
int n;
368+
369+
for (n = 0; n < parms->nelts; n++) {
370+
ssl_ctx_param_t *p = &APR_ARRAY_IDX(parms, n, ssl_ctx_param_t);
371+
372+
md5_str_update(hash, "param:", p->name);
373+
md5_str_update(hash, "value:", p->value);
374+
}
375+
}
376+
#endif
377+
}
378+
#endif
379+
380+
static char *create_sni_policy_hash(apr_pool_t *p, apr_pool_t *ptemp,
381+
modssl_snivhpolicy_t policy,
382+
SSLSrvConfigRec *sc)
383+
{
384+
char *rv = NULL;
385+
#ifdef HAVE_TLSEXT
386+
if (policy != MODSSL_SNIVH_STRICT && policy != MODSSL_SNIVH_INSECURE) {
387+
apr_md5_ctx_t hash;
388+
unsigned char digest[APR_MD5_DIGESTSIZE];
389+
390+
/* Create the vhost policy hash for comparison later. */
391+
apr_md5_init(&hash);
392+
hash_sni_policy_auth(&hash, sc->server);
393+
if (policy == MODSSL_SNIVH_SECURE)
394+
hash_sni_policy_pk(ptemp, &hash, sc->server);
395+
apr_md5_final(digest, &hash);
396+
397+
rv = apr_palloc(p, 2 * APR_MD5_DIGESTSIZE + 1);
398+
ap_bin2hex(digest, APR_MD5_DIGESTSIZE, rv); /* sets final '\0' */
399+
}
400+
#endif
401+
return rv;
402+
}
403+
300404
/* _________________________________________________________________
301405
**
302406
** Let other answer special connection attempts.
@@ -563,6 +667,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
563667
return rv;
564668
}
565669
}
670+
671+
sc->sni_policy_hash = create_sni_policy_hash(p, ptemp, mc->snivh_policy, sc);
566672
}
567673

568674
/*

0 commit comments

Comments
 (0)