Skip to content

Commit 1d0441b

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
1 parent 6146c77 commit 1d0441b

File tree

7 files changed

+275
-109
lines changed

7 files changed

+275
-109
lines changed

changes-entries/pr69743.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*) mod_ssl: Add SSLVHostSNIPolicy directive to control the virtual
2+
host compatibility policy. PR 69743. [Joe Orton]
3+

docs/manual/mod/mod_ssl.xml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,91 @@ SSLStrictSNIVHostCheck on
18161816
</usage>
18171817
</directivesynopsis>
18181818

1819+
<directivesynopsis>
1820+
<name>SSLVHostSNIPolicy</name>
1821+
<description>Set compatibility policy for SNI client access to virtual hosts.</description>
1822+
<syntax>SSLVHostSNIPolicy strict|secure|authonly|insecure</syntax>
1823+
<default>SSLVHostSNIPolicy secure</default>
1824+
<contextlist><context>server config</context></contextlist>
1825+
<compatibility>Available in httpd 2.5 and later</compatibility>
1826+
1827+
<usage><p>This directive sets policy applied when checking whether the
1828+
<directive module="core" type="section">VirtualHost</directive>
1829+
identified by the <code>Host</code> request header in an HTTP request
1830+
is compatible with the <directive module="core"
1831+
type="section">VirtualHost</directive> identified from the SNI
1832+
extension sent during the initial TLS connection handshake. If an HTTP
1833+
request is associated with a virtual host which has an incompatible
1834+
SSL/TLS configuration under the policy used, an HTTP error response
1835+
with status code 421 ("Misdirected Request") will be sent.</p>
1836+
1837+
<p>The <code>strict</code> policy blocks all HTTP requests which are
1838+
identified with a different virtual host to that identifed by SNI.
1839+
The <code>insecure</code> policy allows all HTTP requests regardless
1840+
of virtual host identified; such a configuration may be vulnerable to
1841+
<a
1842+
href="https://httpd.apache.org/security/vulnerabilities_24.html">CVE-2025-23048</a>.
1843+
</p>
1844+
1845+
<p>The (default) <code>secure</code>, and <code>authonly</code>
1846+
policies compare specific aspects of the SSL configuration for the two
1847+
virtual hosts, which are grouped into two categories:
1848+
1849+
<ul>
1850+
<li><strong>client vertification and authentication
1851+
settings</strong>: directives which affect TLS client certificate
1852+
verification or authentication, such as <directive
1853+
module="mod_ssl">SSLVerifyClient</directive>, <directive
1854+
module="mod_ssl">SSLVerifyMode</directive>, <directive
1855+
module="mod_ssl">SSLCACertificatePath</directive>, <directive
1856+
module="mod_ssl">SSLSRPVerifierFile</directive>; any use of <directive
1857+
module="mod_ssl">SSLOpenSSLConfCmd</directive></li>
1858+
1859+
<li><strong>server certificate/key, or protocol/cipher
1860+
restrictions</strong>: directives which determine the server
1861+
certificate or key (<directive
1862+
module="mod_ssl">SSLCertificateKeyFile</directive> etc), cipher or
1863+
protocol restrictions (<directive
1864+
module="mod_ssl">SSLCipherSuite</directive> and <directive
1865+
module="mod_ssl">SSLProtocol</directive>)</li>
1866+
</ul>
1867+
1868+
This table illustrates whether an HTTP request will be blocked or
1869+
allowed when the virtual host configurations differ as described,
1870+
under each different policy setting:
1871+
1872+
<table border="1" style="zebra">
1873+
<columnspec><column width=".3"/><column width=".2"/><column width=".5"/>
1874+
</columnspec>
1875+
<tr>
1876+
<th>Policy mode</th>
1877+
<th>Any VirtualHost mismatch</th>
1878+
<th>Client verification/<br />authentication settings</th>
1879+
<th>Server certificate/key, <br />or protocol/cipher restrictions</th>
1880+
</tr>
1881+
<tr>
1882+
<td><code>strict</code><td>blocked</td><td>blocked</td><td>blocked</td></td>
1883+
</tr>
1884+
<tr>
1885+
<td><code>secure</code><td>allowed</td><td>blocked</td><td>blocked</td></td>
1886+
</tr>
1887+
<tr>
1888+
<td><code>authonly</code><td>allowed</td><td>blocked</td><td>allowed</td></td>
1889+
</tr>
1890+
<tr>
1891+
<td><code>insecure</code><td>allowed</td><td>allowed</td><td>allowed</td></td>
1892+
</tr>
1893+
</table>
1894+
</p>
1895+
<example><title>Example</title>
1896+
<highlight language="config">
1897+
SSLVHostSNIPolicy authonly
1898+
</highlight>
1899+
1900+
</example>
1901+
</usage>
1902+
</directivesynopsis>
1903+
18191904
<directivesynopsis>
18201905
<name>SSLProxyMachineCertificatePath</name>
18211906
<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
@@ -80,6 +80,8 @@ static const command_rec ssl_config_cmds[] = {
8080
SSL_CMD_SRV(RandomSeed, TAKE23,
8181
"SSL Pseudo Random Number Generator (PRNG) seeding source "
8282
"('startup|connect builtin|file:/path|exec:/path [bytes]')")
83+
SSL_CMD_SRV(VHostSNIPolicy, TAKE1,
84+
"SSL VirtualHost SNI compatibility policy setting")
8385

8486
/*
8587
* Per-server context configuration directives

modules/ssl/ssl_engine_config.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ SSLModConfigRec *ssl_config_global_create(server_rec *s)
6363
mc->sesscache_mode = SSL_SESS_CACHE_OFF;
6464
mc->sesscache = NULL;
6565
mc->pMutex = NULL;
66+
#ifdef HAVE_TLSEXT
67+
mc->snivh_policy = MODSSL_SNIVH_SECURE;
68+
#endif
6669
mc->aRandSeed = apr_array_make(pool, 4,
6770
sizeof(ssl_randseed_t));
6871
mc->tVHostKeys = apr_hash_make(pool);
@@ -1909,6 +1912,41 @@ const char *ssl_cmd_SSLStrictSNIVHostCheck(cmd_parms *cmd, void *dcfg, int flag
19091912
#endif
19101913
}
19111914

1915+
const char *ssl_cmd_SSLVHostSNIPolicy(cmd_parms *cmd, void *dcfg, const char *arg)
1916+
{
1917+
#ifdef HAVE_TLSEXT
1918+
SSLModConfigRec *mc = myModConfig(cmd->server);
1919+
const char *err;
1920+
1921+
if ((err = ap_check_cmd_context(cmd, GLOBAL_ONLY))) {
1922+
return err;
1923+
}
1924+
1925+
if (strcEQ(arg, "secure")) {
1926+
mc->snivh_policy = MODSSL_SNIVH_SECURE;
1927+
}
1928+
else if (strcEQ(arg, "strict")) {
1929+
mc->snivh_policy = MODSSL_SNIVH_STRICT;
1930+
}
1931+
else if (strcEQ(arg, "insecure")) {
1932+
mc->snivh_policy = MODSSL_SNIVH_INSECURE;
1933+
}
1934+
else if (strcEQ(arg, "authonly")) {
1935+
mc->snivh_policy = MODSSL_SNIVH_AUTHONLY;
1936+
}
1937+
else {
1938+
return apr_psprintf(cmd->pool, "Invalid SSLVhostSNIPolicy "
1939+
"argument '%s'", arg);
1940+
}
1941+
1942+
return NULL;
1943+
#else
1944+
return "SSLVHostSNIPolicy cannot be used, OpenSSL is not built with "
1945+
"support for TLS extensions and SNI indication. Refer to the "
1946+
"documentation, and build a compatible version of OpenSSL."
1947+
#endif
1948+
}
1949+
19121950
#ifdef HAVE_OCSP_STAPLING
19131951

19141952
const char *ssl_cmd_SSLStaplingCache(cmd_parms *cmd,

modules/ssl/ssl_engine_init.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
-- Unknown */
2929
#include "ssl_private.h"
3030

31+
#include <apr_md5.h>
32+
3133
#include "mpm_common.h"
3234
#include "mod_md.h"
3335

@@ -186,6 +188,110 @@ static void ssl_add_version_components(apr_pool_t *ptemp, apr_pool_t *pconf,
186188
modver, AP_SERVER_BASEVERSION, incver);
187189
}
188190

191+
#ifdef HAVE_TLSEXT
192+
/* Helper functions to create the SNI vhost policy hash. The policy
193+
* hash captures the configuration elements relevant to the mode
194+
* selected at runtime by SSLVHostSNIPolicy. */
195+
196+
#define md5_str_update(ctx_, pfx_, str_) do { apr_md5_update(ctx_, pfx_, strlen(pfx_)); apr_md5_update(ctx_, str_, strlen(str_)); } while (0)
197+
#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)
198+
#define md5_fmt_update(ctx_, fmt_, i_) do { char s_[128]; apr_snprintf(s_, sizeof s_, fmt_, i_); \
199+
apr_md5_update(ctx_, s_, strlen(s_)); } while (0)
200+
201+
static int md5_strarray_cmp(const void *p1, const void *p2)
202+
{
203+
return strcmp(*(char **)p1, *(char **)p2);
204+
}
205+
206+
/* Hashes an array of strings in sorted order. */
207+
static void md5_strarray_hash(apr_pool_t *ptemp, apr_md5_ctx_t *hash,
208+
const char *pfx, apr_array_header_t *s)
209+
{
210+
char **elts = apr_pmemdup(ptemp, s->elts, s->nelts * sizeof *elts);
211+
int i;
212+
213+
qsort(elts, s->nelts, sizeof(char *), md5_strarray_cmp);
214+
215+
apr_md5_update(hash, pfx, strlen(pfx));
216+
for (i = 0; i < s->nelts; i++) {
217+
md5_str_update(hash, "elm:", elts[i]);
218+
}
219+
}
220+
221+
static void hash_sni_policy_pk(apr_pool_t *ptemp, apr_md5_ctx_t *hash, modssl_ctx_t *ctx)
222+
{
223+
md5_fmt_update(hash, "protocol:%d", ctx->protocol);
224+
225+
md5_ifstr_update(hash, "ciphers:", ctx->auth.cipher_suite);
226+
md5_ifstr_update(hash, "tls13_ciphers:", ctx->auth.tls13_ciphers);
227+
228+
md5_strarray_hash(ptemp, hash, "cert_files:", ctx->pks->cert_files);
229+
md5_strarray_hash(ptemp, hash, "key_files:", ctx->pks->key_files);
230+
}
231+
232+
static void hash_sni_policy_auth(apr_md5_ctx_t *hash, modssl_ctx_t *ctx)
233+
{
234+
modssl_pk_server_t *pks = ctx->pks;
235+
modssl_auth_ctx_t *a = &ctx->auth;
236+
237+
md5_fmt_update(hash, "verify_depth:%d", a->verify_depth);
238+
md5_fmt_update(hash, "verify_mode:%d", a->verify_mode);
239+
240+
md5_ifstr_update(hash, "ca_name_path:", pks->ca_name_path);
241+
md5_ifstr_update(hash, "ca_name_file:", pks->ca_name_file);
242+
md5_ifstr_update(hash, "ca_cert_path:", a->ca_cert_path);
243+
md5_ifstr_update(hash, "ca_cert_file:", a->ca_cert_file);
244+
md5_ifstr_update(hash, "crl_path:", ctx->crl_path);
245+
md5_ifstr_update(hash, "crl_file:", ctx->crl_file);
246+
md5_fmt_update(hash, "crl_check_mask:%d", ctx->crl_check_mask);
247+
md5_fmt_update(hash, "ocsp_mask:%d", ctx->ocsp_mask);
248+
md5_fmt_update(hash, "ocsp_force_default:%d", ctx->ocsp_force_default);
249+
md5_ifstr_update(hash, "ocsp_responder:", ctx->ocsp_responder);
250+
251+
#ifdef HAVE_SRP
252+
md5_ifstr_update(hash, "srp_vfile:", ctx->srp_vfile);
253+
#endif
254+
255+
#ifdef HAVE_SSL_CONF_CMD
256+
{
257+
apr_array_header_t *parms = ctx->ssl_ctx_param;
258+
int n;
259+
260+
for (n = 0; n < parms->nelts; n++) {
261+
ssl_ctx_param_t *p = &APR_ARRAY_IDX(parms, n, ssl_ctx_param_t);
262+
263+
md5_str_update(hash, "param:", p->name);
264+
md5_str_update(hash, "value:", p->value);
265+
}
266+
}
267+
#endif
268+
}
269+
#endif
270+
271+
static char *create_sni_policy_hash(apr_pool_t *p, apr_pool_t *ptemp,
272+
modssl_snivhpolicy_t policy,
273+
SSLSrvConfigRec *sc)
274+
{
275+
char *rv = NULL;
276+
#ifdef HAVE_TLSEXT
277+
if (policy != MODSSL_SNIVH_STRICT && policy != MODSSL_SNIVH_INSECURE) {
278+
apr_md5_ctx_t hash;
279+
unsigned char digest[APR_MD5_DIGESTSIZE];
280+
281+
/* Create the vhost policy hash for comparison later. */
282+
apr_md5_init(&hash);
283+
hash_sni_policy_auth(&hash, sc->server);
284+
if (policy == MODSSL_SNIVH_SECURE)
285+
hash_sni_policy_pk(ptemp, &hash, sc->server);
286+
apr_md5_final(digest, &hash);
287+
288+
rv = apr_palloc(p, 2 * APR_MD5_DIGESTSIZE + 1);
289+
ap_bin2hex(digest, APR_MD5_DIGESTSIZE, rv); /* sets final '\0' */
290+
}
291+
#endif
292+
return rv;
293+
}
294+
189295
/* _________________________________________________________________
190296
**
191297
** Let other answer special connection attempts.
@@ -439,6 +545,8 @@ apr_status_t ssl_init_Module(apr_pool_t *p, apr_pool_t *plog,
439545
return rv;
440546
}
441547
}
548+
549+
sc->sni_policy_hash = create_sni_policy_hash(p, ptemp, mc->snivh_policy, sc);
442550
}
443551

444552
/*

0 commit comments

Comments
 (0)