summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Eissing <icing@apache.org>2018-09-05 11:28:15 +0000
committerStefan Eissing <icing@apache.org>2018-09-05 11:28:15 +0000
commit223f607038e242e8f80045d39a1f0d608de05d11 (patch)
tree906ddaef20bc006a51e68323bcb64f664c46692b
parentc1f9c42593bb5bf5be50bdd63f7fdfe13842c22f (diff)
downloadhttpd-223f607038e242e8f80045d39a1f0d608de05d11.tar.gz
On the tlsv1.3-for-2.4.x branch:
Merged 1827912,1827924,1827992,1828222,1828720,1828723,1833588,1833589,1839920,1839946 from trunk *) mod_ssl: add experimental support for TLSv1.3 (tested with OpenSSL v1.1.1-pre9. SSL(Proxy)CipherSuite now has an optional first parameter for the protocol the ciphers are for. Directive "SSLVerifyClient" now triggers certificate retrieval from the client. Verifying the client fails exactly the same for HTTP/2 connections for all SSL protocols, as this would need to trigger the master connection thread - which we do not support right now. Renegotiation of ciphers is intentionally ignored for TLSv1.3 connections. "SSLCipherSuite" does not allow to specify TLSv1.3 ciphers in a directory context (because it cannot work) and TLSv1.2 or lower ciphers are not relevant for 1.3, as cipher suites are completely separate. Sites which make use of such TLSv1.2 feature need to evaluate carefully if or how they can match their needs onto the TLSv1.3 protocol. [Yann Ylavic, Stefan Eissing] git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/tlsv1.3-for-2.4.x@1840120 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--CHANGES13
-rw-r--r--docs/manual/mod/mod_ssl.xml30
-rw-r--r--modules/ssl/mod_ssl.c12
-rw-r--r--modules/ssl/ssl_engine_config.c68
-rw-r--r--modules/ssl/ssl_engine_init.c37
-rw-r--r--modules/ssl/ssl_engine_kernel.c378
-rw-r--r--modules/ssl/ssl_private.h32
7 files changed, 422 insertions, 148 deletions
diff --git a/CHANGES b/CHANGES
index 040296d511..45814786e4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,19 @@
-*- coding: utf-8 -*-
Changes with Apache 2.4.35
+ *) mod_ssl: add experimental support for TLSv1.3 (tested with OpenSSL v1.1.1-pre9.
+ SSL(Proxy)CipherSuite now has an optional first parameter for the protocol the ciphers are for.
+ Directive "SSLVerifyClient" now triggers certificate retrieval from the client.
+ Verifying the client fails exactly the same for HTTP/2 connections for all SSL protocols,
+ as this would need to trigger the master connection thread - which we do not support
+ right now.
+ Renegotiation of ciphers is intentionally ignored for TLSv1.3 connections. "SSLCipherSuite"
+ does not allow to specify TLSv1.3 ciphers in a directory context (because it cannot work) and
+ TLSv1.2 or lower ciphers are not relevant for 1.3, as cipher suites are completely separate.
+ Sites which make use of such TLSv1.2 feature need to evaluate carefully if or how they
+ can match their needs onto the TLSv1.3 protocol.
+ [Yann Ylavic, Stefan Eissing]
+
*) mod_status: Cumulate CPU time of exited child processes in the
"cu" and "cs" values. Add CPU time of the parent process to the
"c" and "s" values.
diff --git a/docs/manual/mod/mod_ssl.xml b/docs/manual/mod/mod_ssl.xml
index 5f7282760e..588798cc3e 100644
--- a/docs/manual/mod/mod_ssl.xml
+++ b/docs/manual/mod/mod_ssl.xml
@@ -654,6 +654,11 @@ The available (case-insensitive) <em>protocol</em>s are:</p>
A revision of the TLS 1.1 protocol, as defined in
<a href="http://www.ietf.org/rfc/rfc5246.txt">RFC 5246</a>.</p></li>
+<li><code>TLSv1.3</code> (when using OpenSSL 1.1.1 and later)
+ <p>
+ A new version of the TLS protocol, as defined in
+ <a href="https://github.com/tlswg/tls13-spec">RFC TBD</a>.</p></li>
+
<li><code>all</code>
<p>
This is a shortcut for ``<code>+SSLv3 +TLSv1</code>'' or
@@ -674,7 +679,7 @@ SSLProtocol TLSv1
<name>SSLCipherSuite</name>
<description>Cipher Suite available for negotiation in SSL
handshake</description>
-<syntax>SSLCipherSuite <em>cipher-spec</em></syntax>
+<syntax>SSLCipherSuite [<em>protocol</em>] <em>cipher-spec</em></syntax>
<default>SSLCipherSuite DEFAULT (depends on OpenSSL version)</default>
<contextlist><context>server config</context>
<context>virtual host</context>
@@ -686,12 +691,25 @@ handshake</description>
<p>
This complex directive uses a colon-separated <em>cipher-spec</em> string
consisting of OpenSSL cipher specifications to configure the Cipher Suite the
-client is permitted to negotiate in the SSL handshake phase. Notice that this
-directive can be used both in per-server and per-directory context. In
-per-server context it applies to the standard SSL handshake when a connection
+client is permitted to negotiate in the SSL handshake phase. The optional
+protocol specifier can configure the Cipher Suite for a specific SSL version.
+Possible values include "SSL" for all SSL Protocols up to and including TLSv1.2.
+<p>
+Notice that this
+directive can be used both in per-server and per-directory context.
+In per-server context it applies to the standard SSL handshake when a connection
is established. In per-directory context it forces a SSL renegotiation with the
reconfigured Cipher Suite after the HTTP request was read but before the HTTP
-response is sent.</p>
+response is sent. (Since renegotiation is not</p>
+<p>
+If the SSL library supports TLSv1.3 (OpenSSL 1.1.1 and later), the protocol
+specifier "TLSv1.3" can be used to configure the cipher suites for that protocol.
+Since TLSv1.3 does not offer renegotiations, specifying ciphers for it in
+a directory context is not allowed.</p>
+<p>
+For a list of TLSv1.3 cipher names, see
+<a href="https://www.openssl.org/docs/manmaster/man3/SSL_CTX_set_ciphersuites.html">the OpenSSL
+documentation</a>.</p>
<p>
An SSL cipher specification in <em>cipher-spec</em> is composed of 4 major
attributes plus a few extra minor ones:</p>
@@ -2063,7 +2081,7 @@ for additional information.
<name>SSLProxyCipherSuite</name>
<description>Cipher Suite available for negotiation in SSL
proxy handshake</description>
-<syntax>SSLProxyCipherSuite <em>cipher-spec</em></syntax>
+<syntax>SSLProxyCipherSuite [<em>protocol</em>] <em>cipher-spec</em></syntax>
<default>SSLProxyCipherSuite ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM:+LOW:+EXP</default>
<contextlist><context>server config</context> <context>virtual host</context>
<context>proxy section</context></contextlist>
diff --git a/modules/ssl/mod_ssl.c b/modules/ssl/mod_ssl.c
index b716a7b371..37947e78f7 100644
--- a/modules/ssl/mod_ssl.c
+++ b/modules/ssl/mod_ssl.c
@@ -93,9 +93,9 @@ static const command_rec ssl_config_cmds[] = {
SSL_CMD_SRV(FIPS, FLAG,
"Enable FIPS-140 mode "
"(`on', `off')")
- SSL_CMD_ALL(CipherSuite, TAKE1,
- "Colon-delimited list of permitted SSL Ciphers "
- "('XXX:...:XXX' - see manual)")
+ SSL_CMD_ALL(CipherSuite, TAKE12,
+ "Colon-delimited list of permitted SSL Ciphers, optional preceeded "
+ "by protocol identifier ('XXX:...:XXX' - see manual)")
SSL_CMD_SRV(CertificateFile, TAKE1,
"SSL Server Certificate file "
"('/path/to/file' - PEM or DER encoded)")
@@ -185,9 +185,9 @@ static const command_rec ssl_config_cmds[] = {
SSL_CMD_PXY(ProxyProtocol, RAW_ARGS,
"SSL Proxy: enable or disable SSL protocol flavors "
"('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
- SSL_CMD_PXY(ProxyCipherSuite, TAKE1,
+ SSL_CMD_PXY(ProxyCipherSuite, TAKE12,
"SSL Proxy: colon-delimited list of permitted SSL ciphers "
- "('XXX:...:XXX' - see manual)")
+ ", optionally preceeded by protocol specifier ('XXX:...:XXX' - see manual)")
SSL_CMD_PXY(ProxyVerify, TAKE1,
"SSL Proxy: whether to verify the remote certificate "
"('on' or 'off')")
@@ -398,7 +398,7 @@ static int ssl_hook_pre_config(apr_pool_t *pconf,
/* We must register the library in full, to ensure our configuration
* code can successfully test the SSL environment.
*/
-#if MODSSL_USE_OPENSSL_PRE_1_1_API
+#if MODSSL_USE_OPENSSL_PRE_1_1_API || defined(LIBRESSL_VERSION_NUMBER)
(void)CRYPTO_malloc_init();
#else
OPENSSL_malloc_init();
diff --git a/modules/ssl/ssl_engine_config.c b/modules/ssl/ssl_engine_config.c
index 195380e2f3..29db3d6d53 100644
--- a/modules/ssl/ssl_engine_config.c
+++ b/modules/ssl/ssl_engine_config.c
@@ -136,6 +136,7 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
mctx->auth.cipher_suite = NULL;
mctx->auth.verify_depth = UNSET;
mctx->auth.verify_mode = SSL_CVERIFY_UNSET;
+ mctx->auth.tls13_ciphers = NULL;
mctx->ocsp_mask = UNSET;
mctx->ocsp_force_default = UNSET;
@@ -280,6 +281,7 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
cfgMergeString(auth.cipher_suite);
cfgMergeInt(auth.verify_depth);
cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
+ cfgMergeString(auth.tls13_ciphers);
cfgMergeInt(ocsp_mask);
cfgMergeBool(ocsp_force_default);
@@ -761,22 +763,37 @@ const char *ssl_cmd_SSLFIPS(cmd_parms *cmd, void *dcfg, int flag)
const char *ssl_cmd_SSLCipherSuite(cmd_parms *cmd,
void *dcfg,
- const char *arg)
+ const char *arg1, const char *arg2)
{
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
- /* always disable null and export ciphers */
- arg = apr_pstrcat(cmd->pool, arg, ":!aNULL:!eNULL:!EXP", NULL);
-
- if (cmd->path) {
- dc->szCipherSuite = arg;
+ if (arg2 == NULL) {
+ arg2 = arg1;
+ arg1 = "SSL";
}
- else {
- sc->server->auth.cipher_suite = arg;
+
+ if (!strcmp("SSL", arg1)) {
+ /* always disable null and export ciphers */
+ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ if (cmd->path) {
+ dc->szCipherSuite = arg2;
+ }
+ else {
+ sc->server->auth.cipher_suite = arg2;
+ }
+ return NULL;
}
-
- return NULL;
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (!strcmp("TLSv1.3", arg1)) {
+ if (cmd->path) {
+ return "TLSv1.3 ciphers cannot be set inside a directory context";
+ }
+ sc->server->auth.tls13_ciphers = arg2;
+ return NULL;
+ }
+#endif
+ return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
}
#define SSL_FLAGS_CHECK_FILE \
@@ -1445,6 +1462,9 @@ static const char *ssl_cmd_protocol_parse(cmd_parms *parms,
else if (strcEQ(w, "TLSv1.2")) {
thisopt = SSL_PROTOCOL_TLSV1_2;
}
+ else if (SSL_HAVE_PROTOCOL_TLSV1_3 && strcEQ(w, "TLSv1.3")) {
+ thisopt = SSL_PROTOCOL_TLSV1_3;
+ }
#endif
else if (strcEQ(w, "all")) {
thisopt = SSL_PROTOCOL_ALL;
@@ -1506,16 +1526,28 @@ const char *ssl_cmd_SSLProxyProtocol(cmd_parms *cmd,
const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
void *dcfg,
- const char *arg)
+ const char *arg1, const char *arg2)
{
SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
-
- /* always disable null and export ciphers */
- arg = apr_pstrcat(cmd->pool, arg, ":!aNULL:!eNULL:!EXP", NULL);
-
- dc->proxy->auth.cipher_suite = arg;
-
- return NULL;
+
+ if (arg2 == NULL) {
+ arg2 = arg1;
+ arg1 = "SSL";
+ }
+
+ if (!strcmp("SSL", arg1)) {
+ /* always disable null and export ciphers */
+ arg2 = apr_pstrcat(cmd->pool, arg2, ":!aNULL:!eNULL:!EXP", NULL);
+ dc->proxy->auth.cipher_suite = arg2;
+ return NULL;
+ }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (!strcmp("TLSv1.3", arg1)) {
+ dc->proxy->auth.tls13_ciphers = arg2;
+ return NULL;
+ }
+#endif
+ return apr_pstrcat(cmd->pool, "procotol '", arg1, "' not supported", NULL);
}
const char *ssl_cmd_SSLProxyVerify(cmd_parms *cmd,
diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index 397712b601..85d43376b2 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -568,6 +568,9 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
#ifdef HAVE_TLSV1_X
(protocol & SSL_PROTOCOL_TLSV1_1 ? "TLSv1.1, " : ""),
(protocol & SSL_PROTOCOL_TLSV1_2 ? "TLSv1.2, " : ""),
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ (protocol & SSL_PROTOCOL_TLSV1_3 ? "TLSv1.3, " : ""),
+#endif
#endif
NULL);
cp[strlen(cp)-2] = NUL;
@@ -600,6 +603,13 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
TLSv1_2_client_method() : /* proxy */
TLSv1_2_server_method(); /* server */
}
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ else if (protocol == SSL_PROTOCOL_TLSV1_3) {
+ method = mctx->pkp ?
+ TLSv1_3_client_method() : /* proxy */
+ TLSv1_3_server_method(); /* server */
+ }
+#endif
#endif
else { /* For multiple protocols, we need a flexible method */
method = mctx->pkp ?
@@ -617,7 +627,8 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
SSL_CTX_set_options(ctx, SSL_OP_ALL);
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20800000L)
/* always disable SSLv2, as per RFC 6176 */
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
@@ -639,10 +650,19 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
if (!(protocol & SSL_PROTOCOL_TLSV1_2)) {
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
}
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ ssl_set_ctx_protocol_option(s, ctx, SSL_OP_NO_TLSv1_3,
+ protocol & SSL_PROTOCOL_TLSV1_3, "TLSv1.3");
+#endif
#endif
#else /* #if OPENSSL_VERSION_NUMBER < 0x10100000L */
/* We first determine the maximum protocol version we should provide */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (SSL_HAVE_PROTOCOL_TLSV1_3 && (protocol & SSL_PROTOCOL_TLSV1_3)) {
+ prot = TLS1_3_VERSION;
+ } else
+#endif
if (protocol & SSL_PROTOCOL_TLSV1_2) {
prot = TLS1_2_VERSION;
} else if (protocol & SSL_PROTOCOL_TLSV1_1) {
@@ -664,6 +684,11 @@ static apr_status_t ssl_init_ctx_protocol(server_rec *s,
/* Next we scan for the minimal protocol version we should provide,
* but we do not allow holes between max and min */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (prot == TLS1_3_VERSION && protocol & SSL_PROTOCOL_TLSV1_2) {
+ prot = TLS1_2_VERSION;
+ }
+#endif
if (prot == TLS1_2_VERSION && protocol & SSL_PROTOCOL_TLSV1_1) {
prot = TLS1_1_VERSION;
}
@@ -888,7 +913,15 @@ static apr_status_t ssl_init_ctx_cipher_suite(server_rec *s,
ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
return ssl_die(s);
}
-
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (mctx->auth.tls13_ciphers
+ && !SSL_CTX_set_ciphersuites(ctx, mctx->auth.tls13_ciphers)) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, 0, s, APLOGNO()
+ "Unable to configure permitted TLSv1.3 ciphers");
+ ssl_log_ssl_error(SSLLOG_MARK, APLOG_EMERG, s);
+ return ssl_die(s);
+ }
+#endif
return APR_SUCCESS;
}
diff --git a/modules/ssl/ssl_engine_kernel.c b/modules/ssl/ssl_engine_kernel.c
index 9a89ff9da8..76000995a3 100644
--- a/modules/ssl/ssl_engine_kernel.c
+++ b/modules/ssl/ssl_engine_kernel.c
@@ -188,6 +188,12 @@ static int ssl_auth_compatible(modssl_auth_ctx_t *a1,
|| 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;
}
@@ -424,21 +430,55 @@ static void ssl_configure_env(request_rec *r, SSLConnRec *sslconn)
}
}
+static int ssl_check_post_client_verify(request_rec *r, SSLSrvConfigRec *sc,
+ SSLDirConfigRec *dc, SSLConnRec *sslconn, SSL *ssl)
+{
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
+ (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
+ BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
+
+ if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
+ "Re-negotiation handshake failed: "
+ "Client verification failed");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ if (do_verify) {
+ X509 *peercert;
+
+ if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
+ "Re-negotiation handshake failed: "
+ "Client certificate missing");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ X509_free(peercert);
+ }
+ }
+ return OK;
+}
+
/*
- * Access Handler
+ * Access Handler, classic flavour, for SSL/TLS up to v1.2
+ * where everything can be renegotiated and no one is happy.
*/
-int ssl_hook_Access(request_rec *r)
+static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
+ SSLConnRec *sslconn, SSL *ssl)
{
- SSLDirConfigRec *dc = myDirConfig(r);
- SSLSrvConfigRec *sc = mySrvConfig(r->server);
- SSLConnRec *sslconn = myConnConfig(r->connection);
- SSL *ssl = sslconn ? sslconn->ssl : NULL;
server_rec *handshakeserver = sslconn ? sslconn->server : NULL;
SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
SSL_CTX *ctx = NULL;
apr_array_header_t *requires;
ssl_require_t *ssl_requires;
- int ok, i;
+ int ok, i, rc;
BOOL renegotiate = FALSE, renegotiate_quick = FALSE;
X509 *cert;
X509 *peercert;
@@ -446,66 +486,9 @@ int ssl_hook_Access(request_rec *r)
X509_STORE_CTX *cert_store_ctx;
STACK_OF(SSL_CIPHER) *cipher_list_old = NULL, *cipher_list = NULL;
const SSL_CIPHER *cipher = NULL;
- int depth, verify_old, verify, n, is_slave = 0;
+ int depth, verify_old, verify, n;
const char *ncipher_suite;
- /* On a slave connection, we do not expect to have an SSLConnRec, but
- * our master connection might have one. */
- if (!(sslconn && ssl) && r->connection->master) {
- sslconn = myConnConfig(r->connection->master);
- ssl = sslconn ? sslconn->ssl : NULL;
- handshakeserver = sslconn ? sslconn->server : NULL;
- hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL;
- is_slave = 1;
- }
-
- if (ssl) {
- /*
- * We should have handshaken here (on handshakeserver),
- * otherwise we are being redirected (ErrorDocument) from
- * a renegotiation failure below. The access is still
- * forbidden in the latter case, let ap_die() handle
- * this recursive (same) error.
- */
- if (!SSL_is_init_finished(ssl)) {
- return HTTP_FORBIDDEN;
- }
- ctx = SSL_get_SSL_CTX(ssl);
- }
-
- /*
- * Support for SSLRequireSSL directive
- */
- if (dc->bSSLRequired && !ssl) {
- if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !is_slave) {
- /* This vhost was configured for optional SSL, just tell the
- * client that we need to upgrade.
- */
- apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
- apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
-
- return HTTP_UPGRADE_REQUIRED;
- }
-
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
- "access to %s failed, reason: %s",
- r->filename, "SSL connection required");
-
- /* remember forbidden access for strict require option */
- apr_table_setn(r->notes, "ssl-access-forbidden", "1");
-
- return HTTP_FORBIDDEN;
- }
-
- /*
- * Check to see whether SSL is in use; if it's not, then no
- * further access control checks are relevant. (the test for
- * sc->enabled is probably strictly unnecessary)
- */
- if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
- return DECLINED;
- }
-
#ifdef HAVE_SRP
/*
* Support for per-directory reconfigured SSL connection parameters
@@ -581,7 +564,7 @@ int ssl_hook_Access(request_rec *r)
}
/* configure new state */
- if (is_slave) {
+ if (r->connection->master) {
/* TODO: this categorically fails changed cipher suite settings
* on slave connections. We could do better by
* - create a new SSL* from our SSL_CTX and set cipher suite there,
@@ -659,7 +642,7 @@ int ssl_hook_Access(request_rec *r)
}
if (renegotiate) {
- if (is_slave) {
+ if (r->connection->master) {
/* The request causes renegotiation on a slave connection.
* This is not allowed since we might have concurrent requests
* on this connection.
@@ -732,7 +715,7 @@ int ssl_hook_Access(request_rec *r)
(verify & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)))
{
renegotiate = TRUE;
- if (is_slave) {
+ if (r->connection->master) {
/* The request causes renegotiation on a slave connection.
* This is not allowed since we might have concurrent requests
* on this connection.
@@ -1048,30 +1031,8 @@ int ssl_hook_Access(request_rec *r)
/*
* Finally check for acceptable renegotiation results
*/
- if ((dc->nVerifyClient != SSL_CVERIFY_NONE) ||
- (sc->server->auth.verify_mode != SSL_CVERIFY_NONE)) {
- BOOL do_verify = ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
- (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE));
-
- if (do_verify && (SSL_get_verify_result(ssl) != X509_V_OK)) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02262)
- "Re-negotiation handshake failed: "
- "Client verification failed");
-
- return HTTP_FORBIDDEN;
- }
-
- if (do_verify) {
- if ((peercert = SSL_get_peer_certificate(ssl)) == NULL) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263)
- "Re-negotiation handshake failed: "
- "Client certificate missing");
-
- return HTTP_FORBIDDEN;
- }
-
- X509_free(peercert);
- }
+ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ return rc;
}
/*
@@ -1165,6 +1126,195 @@ int ssl_hook_Access(request_rec *r)
return DECLINED;
}
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+/*
+ * Access Handler, modern flavour, for SSL/TLS v1.3 and onward.
+ * Only client certificates can be requested, everything else stays.
+ */
+static int ssl_hook_Access_modern(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc,
+ SSLConnRec *sslconn, SSL *ssl)
+{
+ if ((dc->nVerifyClient != SSL_CVERIFY_UNSET) ||
+ (sc->server->auth.verify_mode != SSL_CVERIFY_UNSET)) {
+ int vmode_inplace, vmode_needed;
+ int change_vmode = FALSE;
+ int old_state, n, rc;
+
+ vmode_inplace = SSL_get_verify_mode(ssl);
+ vmode_needed = SSL_VERIFY_NONE;
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_REQUIRE) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_REQUIRE)) {
+ vmode_needed |= SSL_VERIFY_PEER_STRICT;
+ }
+
+ if ((dc->nVerifyClient == SSL_CVERIFY_OPTIONAL) ||
+ (dc->nVerifyClient == SSL_CVERIFY_OPTIONAL_NO_CA) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL) ||
+ (sc->server->auth.verify_mode == SSL_CVERIFY_OPTIONAL_NO_CA))
+ {
+ vmode_needed |= SSL_VERIFY_PEER;
+ }
+
+ if (vmode_needed == SSL_VERIFY_NONE) {
+ return DECLINED;
+ }
+
+ vmode_needed |= SSL_VERIFY_CLIENT_ONCE;
+ if (vmode_inplace != vmode_needed) {
+ /* Need to change, if new setting is more restrictive than existing one */
+
+ if ((vmode_inplace == SSL_VERIFY_NONE)
+ || (!(vmode_inplace & SSL_VERIFY_PEER)
+ && (vmode_needed & SSL_VERIFY_PEER))
+ || (!(vmode_inplace & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
+ && (vmode_inplace & SSL_VERIFY_FAIL_IF_NO_PEER_CERT))) {
+ /* need to change the effective verify mode */
+ change_vmode = TRUE;
+ }
+ else {
+ /* FIXME: does this work with TLSv1.3? Is this more than re-inspecting
+ * the certificate we should already have? */
+ /*
+ * override of SSLVerifyDepth
+ *
+ * The depth checks are handled by us manually inside the
+ * verify callback function and not by OpenSSL internally
+ * (and our function is aware of both the per-server and
+ * per-directory contexts). So we cannot ask OpenSSL about
+ * the currently verify depth. Instead we remember it in our
+ * SSLConnRec attached to the SSL* of OpenSSL. We've to force
+ * the renegotiation if the reconfigured/new verify depth is
+ * less than the currently active/remembered verify depth
+ * (because this means more restriction on the certificate
+ * chain).
+ */
+ n = (sslconn->verify_depth != UNSET)?
+ sslconn->verify_depth : sc->server->auth.verify_depth;
+ /* determine the new depth */
+ sslconn->verify_depth = (dc->nVerifyDepth != UNSET)
+ ? dc->nVerifyDepth
+ : sc->server->auth.verify_depth;
+ if (sslconn->verify_depth < n) {
+ change_vmode = TRUE;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO()
+ "Reduced client verification depth will "
+ "force renegotiation");
+ }
+ }
+ }
+
+ if (change_vmode) {
+ char peekbuf[1];
+
+ if (r->connection->master) {
+ /* FIXME: modifying the SSL on a slave connection is no good.
+ * We would need to push this back to the master connection
+ * somehow.
+ */
+ apr_table_setn(r->notes, "ssl-renegotiate-forbidden", "verify-client");
+ return HTTP_FORBIDDEN;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() "verify client post handshake");
+
+ SSL_set_verify(ssl, vmode_needed, ssl_callback_SSLVerify);
+ SSL_verify_client_post_handshake(ssl);
+
+ old_state = sslconn->reneg_state;
+ sslconn->reneg_state = RENEG_ALLOW;
+ modssl_set_app_data2(ssl, r);
+
+ SSL_do_handshake(ssl);
+ /* Need to trigger renegotiation handshake by reading.
+ * Peeking 0 bytes actually works.
+ * See: http://marc.info/?t=145493359200002&r=1&w=2
+ */
+ SSL_peek(ssl, peekbuf, 0);
+
+ sslconn->reneg_state = old_state;
+ modssl_set_app_data2(ssl, NULL);
+
+ /*
+ * Finally check for acceptable renegotiation results
+ */
+ if (OK != (rc = ssl_check_post_client_verify(r, sc, dc, sslconn, ssl))) {
+ return rc;
+ }
+ }
+ }
+
+ return DECLINED;
+}
+#endif
+
+int ssl_hook_Access(request_rec *r)
+{
+ SSLDirConfigRec *dc = myDirConfig(r);
+ SSLSrvConfigRec *sc = mySrvConfig(r->server);
+ SSLConnRec *sslconn = myConnConfig(r->connection);
+ SSL *ssl = sslconn ? sslconn->ssl : NULL;
+
+ /* On a slave connection, we do not expect to have an SSLConnRec, but
+ * our master connection might have one. */
+ if (!(sslconn && ssl) && r->connection->master) {
+ sslconn = myConnConfig(r->connection->master);
+ ssl = sslconn ? sslconn->ssl : NULL;
+ }
+
+ /*
+ * We should have handshaken here, otherwise we are being
+ * redirected (ErrorDocument) from a renegotiation failure below.
+ * The access is still forbidden in the latter case, let ap_die() handle
+ * this recursive (same) error.
+ */
+ if (ssl && !SSL_is_init_finished(ssl)) {
+ return HTTP_FORBIDDEN;
+ }
+
+ /*
+ * Support for SSLRequireSSL directive
+ */
+ if (dc->bSSLRequired && !ssl) {
+ if ((sc->enabled == SSL_ENABLED_OPTIONAL) && !r->connection->master) {
+ /* This vhost was configured for optional SSL, just tell the
+ * client that we need to upgrade.
+ */
+ apr_table_setn(r->err_headers_out, "Upgrade", "TLS/1.0, HTTP/1.1");
+ apr_table_setn(r->err_headers_out, "Connection", "Upgrade");
+
+ return HTTP_UPGRADE_REQUIRED;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02219)
+ "access to %s failed, reason: %s",
+ r->filename, "SSL connection required");
+
+ /* remember forbidden access for strict require option */
+ apr_table_setn(r->notes, "ssl-access-forbidden", "1");
+
+ return HTTP_FORBIDDEN;
+ }
+
+ /*
+ * Check to see whether SSL is in use; if it's not, then no
+ * further access control checks are relevant. (the test for
+ * sc->enabled is probably strictly unnecessary)
+ */
+ if (sc->enabled == SSL_ENABLED_FALSE || !ssl) {
+ return DECLINED;
+ }
+
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ /* TLSv1.3+ is less complicated here. Branch off into a new codeline
+ * and avoid messing with the past. */
+ if (SSL_version(ssl) >= TLS1_3_VERSION) {
+ return ssl_hook_Access_modern(r, sc, dc, sslconn, ssl);
+ }
+#endif
+ return ssl_hook_Access_classic(r, sc, dc, sslconn, ssl);
+}
+
/*
* Authentication Handler:
* Fake a Basic authentication from the X509 client certificate.
@@ -2078,31 +2228,43 @@ void ssl_callback_Info(const SSL *ssl, int where, int rc)
{
conn_rec *c;
server_rec *s;
- SSLConnRec *scr;
/* Retrieve the conn_rec and the associated SSLConnRec. */
if ((c = (conn_rec *)SSL_get_app_data((SSL *)ssl)) == NULL) {
return;
}
- if ((scr = myConnConfig(c)) == NULL) {
- return;
- }
+ /* With TLS 1.3 this callback may be called multiple times on the first
+ * negotiation, so the below logic to detect renegotiations can't work.
+ * Fortunately renegotiations are forbidden starting with TLS 1.3, and
+ * this is enforced by OpenSSL so there's nothing to be done here.
+ */
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+ if (SSL_version(ssl) < TLS1_3_VERSION)
+#endif
+ {
+ SSLConnRec *sslconn;
- /* If the reneg state is to reject renegotiations, check the SSL
- * state machine and move to ABORT if a Client Hello is being
- * read. */
- if (!scr->is_proxy &&
- (where & SSL_CB_HANDSHAKE_START) &&
- scr->reneg_state == RENEG_REJECT) {
- scr->reneg_state = RENEG_ABORT;
+ if ((sslconn = myConnConfig(c)) == NULL) {
+ return;
+ }
+
+ /* If the reneg state is to reject renegotiations, check the SSL
+ * state machine and move to ABORT if a Client Hello is being
+ * read. */
+ if (!sslconn->is_proxy &&
+ (where & SSL_CB_HANDSHAKE_START) &&
+ sslconn->reneg_state == RENEG_REJECT) {
+ sslconn->reneg_state = RENEG_ABORT;
ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02042)
"rejecting client initiated renegotiation");
- }
- /* If the first handshake is complete, change state to reject any
- * subsequent client-initiated renegotiation. */
- else if ((where & SSL_CB_HANDSHAKE_DONE) && scr->reneg_state == RENEG_INIT) {
- scr->reneg_state = RENEG_REJECT;
+ }
+ /* If the first handshake is complete, change state to reject any
+ * subsequent client-initiated renegotiation. */
+ else if ((where & SSL_CB_HANDSHAKE_DONE)
+ && sslconn->reneg_state == RENEG_INIT) {
+ sslconn->reneg_state = RENEG_REJECT;
+ }
}
s = mySrvFromConn(c);
diff --git a/modules/ssl/ssl_private.h b/modules/ssl/ssl_private.h
index 9683a1ae89..160640384d 100644
--- a/modules/ssl/ssl_private.h
+++ b/modules/ssl/ssl_private.h
@@ -132,13 +132,14 @@
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MIN_PROTO_VERSION, version, NULL)
#define SSL_CTX_set_max_proto_version(ctx, version) \
SSL_CTX_ctrl(ctx, SSL_CTRL_SET_MAX_PROTO_VERSION, version, NULL)
-#endif
-/* LibreSSL declares OPENSSL_VERSION_NUMBER == 2.0 but does not include most
- * changes from OpenSSL >= 1.1 (new functions, macros, deprecations, ...), so
- * we have to work around this...
+#elif LIBRESSL_VERSION_NUMBER < 0x2070000f
+/* LibreSSL before 2.7 declares OPENSSL_VERSION_NUMBER == 2.0 but does not
+ * include most changes from OpenSSL >= 1.1 (new functions, macros,
+ * deprecations, ...), so we have to work around this...
*/
#define MODSSL_USE_OPENSSL_PRE_1_1_API (1)
-#else
+#endif /* LIBRESSL_VERSION_NUMBER < 0x2060000f */
+#else /* defined(LIBRESSL_VERSION_NUMBER) */
#define MODSSL_USE_OPENSSL_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif
@@ -238,7 +239,8 @@ void init_bio_methods(void);
void free_bio_methods(void);
#endif
-#if OPENSSL_VERSION_NUMBER < 0x10002000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10002000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x2070000f)
#define X509_STORE_CTX_get0_store(x) (x->ctx)
#endif
@@ -372,8 +374,17 @@ typedef int ssl_opt_t;
#ifdef HAVE_TLSV1_X
#define SSL_PROTOCOL_TLSV1_1 (1<<3)
#define SSL_PROTOCOL_TLSV1_2 (1<<4)
+#define SSL_PROTOCOL_TLSV1_3 (1<<5)
+
+#ifdef SSL_OP_NO_TLSv1_3
+#define SSL_HAVE_PROTOCOL_TLSV1_3 (1)
+#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
+ SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2|SSL_PROTOCOL_TLSV1_3)
+#else
+#define SSL_HAVE_PROTOCOL_TLSV1_3 (0)
#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC| \
SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2)
+#endif
#else
#define SSL_PROTOCOL_ALL (SSL_PROTOCOL_BASIC)
#endif
@@ -646,6 +657,11 @@ typedef struct {
/** for client or downstream server authentication */
int verify_depth;
ssl_verify_t verify_mode;
+
+ /** TLSv1.3 has its separate cipher list, separate from the
+ settings for older TLS protocol versions. Since which one takes
+ effect is a matter of negotiation, we need separate settings */
+ const char *tls13_ciphers;
} modssl_auth_ctx_t;
#ifdef HAVE_TLS_SESSION_TICKETS
@@ -801,7 +817,7 @@ const char *ssl_cmd_SSLPassPhraseDialog(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCryptoDevice(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLRandomSeed(cmd_parms *, void *, const char *, const char *, const char *);
const char *ssl_cmd_SSLEngine(cmd_parms *, void *, const char *);
-const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLCipherSuite(cmd_parms *, void *, const char *, const char *);
const char *ssl_cmd_SSLCertificateFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateKeyFile(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLCertificateChainFile(cmd_parms *, void *, const char *);
@@ -830,7 +846,7 @@ const char *ssl_cmd_SSLInsecureRenegotiation(cmd_parms *cmd, void *dcfg, int fla
const char *ssl_cmd_SSLProxyEngine(cmd_parms *cmd, void *dcfg, int flag);
const char *ssl_cmd_SSLProxyProtocol(cmd_parms *, void *, const char *);
-const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *);
+const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *, void *, const char *, const char *);
const char *ssl_cmd_SSLProxyVerify(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyVerifyDepth(cmd_parms *, void *, const char *);
const char *ssl_cmd_SSLProxyCACertificatePath(cmd_parms *, void *, const char *);