|
|
fb3f8d |
diff --git a/docs/manual/mod/mod_ssl.html.en b/docs/manual/mod/mod_ssl.html.en
|
|
|
fb3f8d |
index b543150..ab72d4f 100644
|
|
|
fb3f8d |
--- a/docs/manual/mod/mod_ssl.html.en
|
|
|
fb3f8d |
+++ b/docs/manual/mod/mod_ssl.html.en
|
|
|
fb3f8d |
@@ -1524,6 +1524,32 @@ The available (case-insensitive) protocols are:
|
|
|
fb3f8d |
|
|
|
fb3f8d |
|
|
|
fb3f8d |
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+SSLProtocol for name-based virtual hosts
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+Before OpenSSL 1.1.1, even though the Server Name Indication (SNI) allowed to
|
|
|
fb3f8d |
+determine the targeted virtual host early in the TLS handshake, it was not
|
|
|
fb3f8d |
+possible to switch the TLS protocol version of the connection at this point,
|
|
|
fb3f8d |
+and thus the SSLProtocol negotiated was always based off
|
|
|
fb3f8d |
+the one of the base virtual host (first virtual host declared on the
|
|
|
fb3f8d |
+listening IP:port of the connection).
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+Beginning with Apache HTTP server version 2.4.42, when built/linked against
|
|
|
fb3f8d |
+OpenSSL 1.1.1 or later, and when the SNI is provided by the client in the TLS
|
|
|
fb3f8d |
+handshake, the SSLProtocol of each (name-based) virtual
|
|
|
fb3f8d |
+host can and will be honored.
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+For compatibility with previous versions, if no
|
|
|
fb3f8d |
+SSLProtocol is configured in a name-based virtual host,
|
|
|
fb3f8d |
+the one from the base virtual host still applies, unless
|
|
|
fb3f8d |
+SSLProtocol is configured globally in which case the
|
|
|
fb3f8d |
+global value applies (this latter exception is more sensible than compatible,
|
|
|
fb3f8d |
+though).
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
|
|
|
fb3f8d |
|
|
|
fb3f8d |
|
|
|
fb3f8d |
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
|
|
|
fb3f8d |
index 0c4bf1f..ca5f702 100644
|
|
|
fb3f8d |
--- a/modules/ssl/ssl_engine_config.c
|
|
|
fb3f8d |
+++ b/modules/ssl/ssl_engine_config.c
|
|
|
fb3f8d |
@@ -269,6 +269,7 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
|
|
|
fb3f8d |
mrg->protocol_set = 1;
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
else {
|
|
|
fb3f8d |
+ mrg->protocol_set = base->protocol_set;
|
|
|
fb3f8d |
mrg->protocol = base->protocol;
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
|
|
|
fb3f8d |
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
|
|
|
fb3f8d |
index 31062bc..70d151e 100644
|
|
|
fb3f8d |
--- a/modules/ssl/ssl_engine_init.c
|
|
|
fb3f8d |
+++ b/modules/ssl/ssl_engine_init.c
|
|
|
fb3f8d |
@@ -520,7 +520,9 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
|
|
|
fb3f8d |
"Configuring TLS extension handling");
|
|
|
fb3f8d |
|
|
|
fb3f8d |
/*
|
|
|
fb3f8d |
- * Server name indication (SNI)
|
|
|
fb3f8d |
+ * The Server Name Indication (SNI) provided by the ClientHello can be
|
|
|
fb3f8d |
+ * used to select the right (name-based-)vhost and its SSL configuration
|
|
|
fb3f8d |
+ * before the handshake takes place.
|
|
|
fb3f8d |
*/
|
|
|
fb3f8d |
if (!SSL_CTX_set_tlsext_servername_callback(mctx->ssl_ctx,
|
|
|
fb3f8d |
ssl_callback_ServerNameIndication) ||
|
|
|
fb3f8d |
@@ -532,6 +534,16 @@ static apr_status_t ssl_init_ctx_tls_extensions(server_rec *s,
|
|
|
fb3f8d |
return ssl_die(s);
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
|
|
|
fb3f8d |
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
fb3f8d |
+ /*
|
|
|
fb3f8d |
+ * The ClientHello callback also allows to retrieve the SNI, but since it
|
|
|
fb3f8d |
+ * runs at the earliest possible connection stage we can even set the TLS
|
|
|
fb3f8d |
+ * protocol version(s) according to the selected (name-based-)vhost, which
|
|
|
fb3f8d |
+ * is not possible at the SNI callback stage (due to OpenSSL internals).
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+ SSL_CTX_set_client_hello_cb(mctx->ssl_ctx, ssl_callback_ClientHello, NULL);
|
|
|
fb3f8d |
+#endif
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
#ifdef HAVE_OCSP_STAPLING
|
|
|
fb3f8d |
/*
|
|
|
fb3f8d |
* OCSP Stapling support, status_request extension
|
|
|
fb3f8d |
@@ -708,7 +720,7 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
|
|
|
fb3f8d |
#else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
|
|
|
fb3f8d |
/* We first determine the maximum protocol version we should provide */
|
|
|
fb3f8d |
#if SSL_HAVE_PROTOCOL_TLSV1_3
|
|
|
fb3f8d |
- if (SSL_HAVE_PROTOCOL_TLSV1_3 && (protocol & SSL_PROTOCOL_TLSV1_3)) {
|
|
|
fb3f8d |
+ if (protocol & SSL_PROTOCOL_TLSV1_3) {
|
|
|
fb3f8d |
prot = TLS1_3_VERSION;
|
|
|
fb3f8d |
} else
|
|
|
fb3f8d |
#endif
|
|
|
fb3f8d |
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
|
|
|
fb3f8d |
index 8b44674..7313a55 100644
|
|
|
fb3f8d |
--- a/modules/ssl/ssl_engine_kernel.c
|
|
|
fb3f8d |
+++ b/modules/ssl/ssl_engine_kernel.c
|
|
|
fb3f8d |
@@ -2357,28 +2357,31 @@ static apr_status_t set_challenge_creds(conn_rec *c, const char *servername,
|
|
|
fb3f8d |
* This function sets the virtual host from an extended
|
|
|
fb3f8d |
* client hello with a server name indication extension ("SNI", cf. RFC 6066).
|
|
|
fb3f8d |
*/
|
|
|
fb3f8d |
-static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
|
|
|
fb3f8d |
+static apr_status_t init_vhost(conn_rec *c, SSL *ssl, const char *servername)
|
|
|
fb3f8d |
{
|
|
|
fb3f8d |
- const char *servername;
|
|
|
fb3f8d |
X509 *cert;
|
|
|
fb3f8d |
EVP_PKEY *key;
|
|
|
fb3f8d |
|
|
|
fb3f8d |
if (c) {
|
|
|
fb3f8d |
SSLConnRec *sslcon = myConnConfig(c);
|
|
|
fb3f8d |
-
|
|
|
fb3f8d |
- if (sslcon->server != c->base_server) {
|
|
|
fb3f8d |
- /* already found the vhost */
|
|
|
fb3f8d |
- return APR_SUCCESS;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ if (sslcon->vhost_found) {
|
|
|
fb3f8d |
+ /* already found the vhost? */
|
|
|
fb3f8d |
+ return sslcon->vhost_found > 0 ? APR_SUCCESS : APR_NOTFOUND;
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
+ sslcon->vhost_found = -1;
|
|
|
fb3f8d |
|
|
|
fb3f8d |
- servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
|
|
fb3f8d |
+ if (!servername) {
|
|
|
fb3f8d |
+ servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
|
|
|
fb3f8d |
+ }
|
|
|
fb3f8d |
if (servername) {
|
|
|
fb3f8d |
if (ap_vhost_iterate_given_conn(c, ssl_find_vhost,
|
|
|
fb3f8d |
(void *)servername)) {
|
|
|
fb3f8d |
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(02043)
|
|
|
fb3f8d |
"SSL virtual host for servername %s found",
|
|
|
fb3f8d |
servername);
|
|
|
fb3f8d |
-
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ sslcon->vhost_found = +1;
|
|
|
fb3f8d |
return APR_SUCCESS;
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
else if (ssl_is_challenge(c, servername, &cert, &key)) {
|
|
|
fb3f8d |
@@ -2428,11 +2431,72 @@ static apr_status_t init_vhost(conn_rec *c, SSL *ssl)
|
|
|
fb3f8d |
int ssl_callback_ServerNameIndication(SSL *ssl, int *al, modssl_ctx_t *mctx)
|
|
|
fb3f8d |
{
|
|
|
fb3f8d |
conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
|
|
|
fb3f8d |
- apr_status_t status = init_vhost(c, ssl);
|
|
|
fb3f8d |
+ apr_status_t status = init_vhost(c, ssl, NULL);
|
|
|
fb3f8d |
|
|
|
fb3f8d |
return (status == APR_SUCCESS)? SSL_TLSEXT_ERR_OK : SSL_TLSEXT_ERR_NOACK;
|
|
|
fb3f8d |
}
|
|
|
fb3f8d |
|
|
|
fb3f8d |
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
fb3f8d |
+/*
|
|
|
fb3f8d |
+ * This callback function is called when the ClientHello is received.
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+int ssl_callback_ClientHello(SSL *ssl, int *al, void *arg)
|
|
|
fb3f8d |
+{
|
|
|
fb3f8d |
+ char *servername = NULL;
|
|
|
fb3f8d |
+ conn_rec *c = (conn_rec *)SSL_get_app_data(ssl);
|
|
|
fb3f8d |
+ const unsigned char *pos;
|
|
|
fb3f8d |
+ size_t len, remaining;
|
|
|
fb3f8d |
+ (void)arg;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /* We can't use SSL_get_servername() at this earliest OpenSSL connection
|
|
|
fb3f8d |
+ * stage, and there is no SSL_client_hello_get0_servername() provided as
|
|
|
fb3f8d |
+ * of OpenSSL 1.1.1. So the code below, that extracts the SNI from the
|
|
|
fb3f8d |
+ * ClientHello's TLS extensions, is taken from some test code in OpenSSL,
|
|
|
fb3f8d |
+ * i.e. client_hello_select_server_ctx() in "test/handshake_helper.c".
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /*
|
|
|
fb3f8d |
+ * The server_name extension was given too much extensibility when it
|
|
|
fb3f8d |
+ * was written, so parsing the normal case is a bit complex.
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+ if (!SSL_client_hello_get0_ext(ssl, TLSEXT_TYPE_server_name, &pos,
|
|
|
fb3f8d |
+ &remaining)
|
|
|
fb3f8d |
+ || remaining <= 2)
|
|
|
fb3f8d |
+ goto give_up;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /* Extract the length of the supplied list of names. */
|
|
|
fb3f8d |
+ len = (*(pos++) << 8);
|
|
|
fb3f8d |
+ len += *(pos++);
|
|
|
fb3f8d |
+ if (len + 2 != remaining)
|
|
|
fb3f8d |
+ goto give_up;
|
|
|
fb3f8d |
+ remaining = len;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /*
|
|
|
fb3f8d |
+ * The list in practice only has a single element, so we only consider
|
|
|
fb3f8d |
+ * the first one.
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+ if (remaining <= 3 || *pos++ != TLSEXT_NAMETYPE_host_name)
|
|
|
fb3f8d |
+ goto give_up;
|
|
|
fb3f8d |
+ remaining--;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /* Now we can finally pull out the byte array with the actual hostname. */
|
|
|
fb3f8d |
+ len = (*(pos++) << 8);
|
|
|
fb3f8d |
+ len += *(pos++);
|
|
|
fb3f8d |
+ if (len + 2 != remaining)
|
|
|
fb3f8d |
+ goto give_up;
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+ /* Use the SNI to switch to the relevant vhost, should it differ from
|
|
|
fb3f8d |
+ * c->base_server.
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+ servername = apr_pstrmemdup(c->pool, (const char *)pos, len);
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+give_up:
|
|
|
fb3f8d |
+ init_vhost(c, ssl, servername);
|
|
|
fb3f8d |
+ return SSL_CLIENT_HELLO_SUCCESS;
|
|
|
fb3f8d |
+}
|
|
|
fb3f8d |
+#endif /* OPENSSL_VERSION_NUMBER < 0x10101000L */
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
/*
|
|
|
fb3f8d |
* Find a (name-based) SSL virtual host where either the ServerName
|
|
|
fb3f8d |
* or one of the ServerAliases matches the supplied name (to be used
|
|
|
fb3f8d |
@@ -2452,12 +2516,25 @@ static int ssl_find_vhost(void *servername, conn_rec *c, server_rec *s)
|
|
|
fb3f8d |
if (found && (ssl = sslcon->ssl) &&
|
|
|
fb3f8d |
(sc = mySrvConfig(s))) {
|
|
|
fb3f8d |
SSL_CTX *ctx = SSL_set_SSL_CTX(ssl, sc->server->ssl_ctx);
|
|
|
fb3f8d |
+
|
|
|
fb3f8d |
/*
|
|
|
fb3f8d |
* SSL_set_SSL_CTX() only deals with the server cert,
|
|
|
fb3f8d |
* so we need to duplicate a few additional settings
|
|
|
fb3f8d |
* from the ctx by hand
|
|
|
fb3f8d |
*/
|
|
|
fb3f8d |
SSL_set_options(ssl, SSL_CTX_get_options(ctx));
|
|
|
fb3f8d |
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L \
|
|
|
fb3f8d |
+ && (!defined(LIBRESSL_VERSION_NUMBER) \
|
|
|
fb3f8d |
+ || LIBRESSL_VERSION_NUMBER >= 0x20800000L)
|
|
|
fb3f8d |
+ /*
|
|
|
fb3f8d |
+ * Don't switch the protocol if none is configured for this vhost,
|
|
|
fb3f8d |
+ * the default in this case is still the base server's SSLProtocol.
|
|
|
fb3f8d |
+ */
|
|
|
fb3f8d |
+ if (myCtxConfig(sslcon, sc)->protocol_set) {
|
|
|
fb3f8d |
+ SSL_set_min_proto_version(ssl, SSL_CTX_get_min_proto_version(ctx));
|
|
|
fb3f8d |
+ SSL_set_max_proto_version(ssl, SSL_CTX_get_max_proto_version(ctx));
|
|
|
fb3f8d |
+ }
|
|
|
fb3f8d |
+#endif
|
|
|
fb3f8d |
if ((SSL_get_verify_mode(ssl) == SSL_VERIFY_NONE) ||
|
|
|
fb3f8d |
(SSL_num_renegotiations(ssl) == 0)) {
|
|
|
fb3f8d |
/*
|
|
|
fb3f8d |
@@ -2654,7 +2731,7 @@ int ssl_callback_alpn_select(SSL *ssl,
|
|
|
fb3f8d |
* they callback the SNI. We need to make sure that we know which vhost
|
|
|
fb3f8d |
* we are dealing with so we respect the correct protocols.
|
|
|
fb3f8d |
*/
|
|
|
fb3f8d |
- init_vhost(c, ssl);
|
|
|
fb3f8d |
+ init_vhost(c, ssl, NULL);
|
|
|
fb3f8d |
|
|
|
fb3f8d |
proposed = ap_select_protocol(c, NULL, sslconn->server, client_protos);
|
|
|
fb3f8d |
if (!proposed) {
|
|
|
fb3f8d |
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
|
|
|
fb3f8d |
index 8055200..f8a1db7 100644
|
|
|
fb3f8d |
--- a/modules/ssl/ssl_private.h
|
|
|
fb3f8d |
+++ b/modules/ssl/ssl_private.h
|
|
|
fb3f8d |
@@ -563,6 +563,7 @@ typedef struct {
|
|
|
fb3f8d |
|
|
|
fb3f8d |
const char *cipher_suite; /* cipher suite used in last reneg */
|
|
|
fb3f8d |
int service_unavailable; /* thouugh we negotiate SSL, no requests will be served */
|
|
|
fb3f8d |
+ int vhost_found; /* whether we found vhost from SNI already */
|
|
|
fb3f8d |
} SSLConnRec;
|
|
|
fb3f8d |
|
|
|
fb3f8d |
/* BIG FAT WARNING: SSLModConfigRec has unusual memory lifetime: it is
|
|
|
fb3f8d |
@@ -946,6 +947,9 @@ void ssl_callback_Info(const SSL *, int, int);
|
|
|
fb3f8d |
#ifdef HAVE_TLSEXT
|
|
|
fb3f8d |
int ssl_callback_ServerNameIndication(SSL *, int *, modssl_ctx_t *);
|
|
|
fb3f8d |
#endif
|
|
|
fb3f8d |
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(LIBRESSL_VERSION_NUMBER)
|
|
|
fb3f8d |
+int ssl_callback_ClientHello(SSL *, int *, void *);
|
|
|
fb3f8d |
+#endif
|
|
|
fb3f8d |
#ifdef HAVE_TLS_SESSION_TICKETS
|
|
|
fb3f8d |
int ssl_callback_SessionTicket(SSL *, unsigned char *, unsigned char *,
|
|
|
fb3f8d |
EVP_CIPHER_CTX *, HMAC_CTX *, int);
|