Skip to content

Commit c2b061a

Browse files
committed
Merge r1929308, r1929333, r1929361, r1929503 from trunk:
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 #562 Reviewed by: jorton, rpluem, covener Docs changes: misplaced tags in english version and fr doc XML file update. Update docs on SSLVhostSNIPolicy to cover the impact on non-SNI connections. Reorder the table for clarity. Submitted by: jorton, Aaron Ogburn <aogburn redhat.com>, rpluem, lgentis git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1929556 13f79535-47bb-0310-9956-ffa450edef68
1 parent 5ecfef0 commit c2b061a

File tree

6 files changed

+278
-109
lines changed

6 files changed

+278
-109
lines changed

docs/manual/mod/mod_ssl.xml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1816,6 +1816,97 @@ 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 the 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 policy also applies to TLS connections where an SNI extension
1838+
is not sent during the handshake, implicitly using the default or
1839+
first virtual host definition. If the Host header in an HTTP request
1840+
on such a connection identifies any other non-default virtual host,
1841+
the compatibility policy is tested.</p>
1842+
1843+
<p>The <code>strict</code> policy blocks all HTTP requests which are
1844+
identified with a different virtual host to that identifed by SNI.
1845+
The <code>insecure</code> policy allows all HTTP requests regardless
1846+
of virtual host identified; such a configuration may be vulnerable to
1847+
<a
1848+
href="https://httpd.apache.org/security/vulnerabilities_24.html">CVE-2025-23048</a>.
1849+
</p>
1850+
1851+
<p>The (default) <code>secure</code>, and <code>authonly</code>
1852+
policies compare specific aspects of the SSL configuration for the two
1853+
virtual hosts, which are grouped into two categories:</p>
1854+
1855+
<ul>
1856+
<li><strong>server certificate/key, or protocol/cipher
1857+
restrictions</strong>: directives which determine the server
1858+
certificate or key (<directive
1859+
module="mod_ssl">SSLCertificateKeyFile</directive> etc), cipher or
1860+
protocol restrictions (<directive
1861+
module="mod_ssl">SSLCipherSuite</directive> and <directive
1862+
module="mod_ssl">SSLProtocol</directive>)</li>
1863+
1864+
<li><strong>client vertification and authentication
1865+
settings</strong>: directives which affect TLS client certificate
1866+
verification or authentication, such as <directive
1867+
module="mod_ssl">SSLVerifyClient</directive>, <directive
1868+
module="mod_ssl">SSLVerifyMode</directive>, <directive
1869+
module="mod_ssl">SSLCACertificatePath</directive>, <directive
1870+
module="mod_ssl">SSLSRPVerifierFile</directive>; any use of <directive
1871+
module="mod_ssl">SSLOpenSSLConfCmd</directive></li>
1872+
</ul>
1873+
1874+
<p>This table illustrates whether an HTTP request will be blocked or
1875+
allowed when the virtual host configurations differ as described,
1876+
under each different policy setting:</p>
1877+
1878+
<table border="1" style="zebra">
1879+
<columnspec><column width=".3"/><column width=".2"/><column width=".5"/>
1880+
</columnspec>
1881+
<tr>
1882+
<th>Policy mode</th>
1883+
<th>Any VirtualHost mismatch</th>
1884+
<th>Server certificate/key, <br />or protocol/cipher restrictions</th>
1885+
<th>Client verification/<br />authentication settings</th>
1886+
</tr>
1887+
<tr>
1888+
<td><code>strict</code></td><td>blocked</td><td>blocked</td><td>blocked</td>
1889+
</tr>
1890+
<tr>
1891+
<td><code>secure</code></td><td>allowed</td><td>blocked</td><td>blocked</td>
1892+
</tr>
1893+
<tr>
1894+
<td><code>authonly</code></td><td>allowed</td><td>allowed</td><td>blocked</td>
1895+
</tr>
1896+
<tr>
1897+
<td><code>insecure</code></td><td>allowed</td><td>allowed</td><td>allowed</td>
1898+
</tr>
1899+
</table>
1900+
1901+
<example><title>Example</title>
1902+
<highlight language="config">
1903+
SSLVHostSNIPolicy authonly
1904+
</highlight>
1905+
1906+
</example>
1907+
</usage>
1908+
</directivesynopsis>
1909+
18191910
<directivesynopsis>
18201911
<name>SSLProxyMachineCertificatePath</name>
18211912
<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)