Backport of:

From 223f607038e242e8f80045d39a1f0d608de05d11 Mon Sep 17 00:00:00 2001
From: Stefan Eissing <icing@apache.org>
Date: Wed, 5 Sep 2018 11:28:15 +0000
Subject: [PATCH] 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
---
 CHANGES                         |  13 ++
 docs/manual/mod/mod_ssl.xml     |  30 ++-
 modules/ssl/mod_ssl.c           |  12 +-
 modules/ssl/ssl_engine_config.c |  68 ++++--
 modules/ssl/ssl_engine_init.c   |  37 +++-
 modules/ssl/ssl_engine_kernel.c | 378 +++++++++++++++++++++++---------
 modules/ssl/ssl_private.h       |  32 ++-
 7 files changed, 422 insertions(+), 148 deletions(-)

#diff --git a/CHANGES b/CHANGES
#index 040296d511d..45814786e4c 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 5f7282760ec..588798cc3e4 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>
Index: apache2-2.4.29/modules/ssl/mod_ssl.c
===================================================================
--- apache2-2.4.29.orig/modules/ssl/mod_ssl.c	2019-10-10 07:42:38.540812167 -0400
+++ apache2-2.4.29/modules/ssl/mod_ssl.c	2019-10-10 08:17:40.222323439 -0400
@@ -87,9 +87,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)")
@@ -179,9 +179,9 @@ static const command_rec ssl_config_cmds
     SSL_CMD_SRV(ProxyProtocol, RAW_ARGS,
                "SSL Proxy: enable or disable SSL protocol flavors "
                 "('[+-][" SSL_PROTOCOLS "] ...' - see manual)")
-    SSL_CMD_SRV(ProxyCipherSuite, TAKE1,
+    SSL_CMD_SRV(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_SRV(ProxyVerify, TAKE1,
                "SSL Proxy: whether to verify the remote certificate "
                "('on' or 'off')")
@@ -392,7 +392,7 @@ static int ssl_hook_pre_config(apr_pool_
     /* 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();
Index: apache2-2.4.29/modules/ssl/ssl_engine_config.c
===================================================================
--- apache2-2.4.29.orig/modules/ssl/ssl_engine_config.c	2019-10-10 07:42:38.540812167 -0400
+++ apache2-2.4.29/modules/ssl/ssl_engine_config.c	2019-10-10 07:47:09.001800884 -0400
@@ -136,6 +136,7 @@ static void modssl_ctx_init(modssl_ctx_t
     mctx->auth.cipher_suite   = NULL;
     mctx->auth.verify_depth   = UNSET;
     mctx->auth.verify_mode    = SSL_CVERIFY_UNSET;
+    mctx->auth.tls13_ciphers = NULL;
 
     mctx->ocsp_enabled        = FALSE;
     mctx->ocsp_force_default  = FALSE;
@@ -296,6 +297,7 @@ static void modssl_ctx_cfg_merge(apr_poo
     cfgMergeString(auth.cipher_suite);
     cfgMergeInt(auth.verify_depth);
     cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
+    cfgMergeString(auth.tls13_ciphers);
 
     cfgMergeBool(ocsp_enabled);
     cfgMergeBool(ocsp_force_default);
@@ -729,22 +731,37 @@ const char *ssl_cmd_SSLFIPS(cmd_parms *c
 
 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 \
@@ -1402,6 +1419,9 @@ static const char *ssl_cmd_protocol_pars
         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;
@@ -1463,16 +1483,28 @@ const char *ssl_cmd_SSLProxyProtocol(cmd
 
 const char *ssl_cmd_SSLProxyCipherSuite(cmd_parms *cmd,
                                         void *dcfg,
-                                        const char *arg)
+                                        const char *arg1, const char *arg2)
 {
     SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
-
-    /* always disable null and export ciphers */
-    arg = apr_pstrcat(cmd->pool, arg, ":!aNULL:!eNULL:!EXP", NULL);
-
-    sc->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);
+        sc->proxy->auth.cipher_suite = arg2;
+        return NULL;
+    }
+#if SSL_HAVE_PROTOCOL_TLSV1_3
+    else if (!strcmp("TLSv1.3", arg1)) {
+        sc->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,
Index: apache2-2.4.29/modules/ssl/ssl_engine_init.c
===================================================================
--- apache2-2.4.29.orig/modules/ssl/ssl_engine_init.c	2019-10-10 07:42:38.540812167 -0400
+++ apache2-2.4.29/modules/ssl/ssl_engine_init.c	2019-10-10 07:42:38.536812152 -0400
@@ -581,6 +581,9 @@ static apr_status_t ssl_init_ctx_protoco
 #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;
@@ -613,6 +616,13 @@ static apr_status_t ssl_init_ctx_protoco
             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 ?
@@ -630,7 +640,8 @@ static apr_status_t ssl_init_ctx_protoco
 
     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);
 
@@ -652,10 +663,19 @@ static apr_status_t ssl_init_ctx_protoco
     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) {
@@ -677,6 +697,11 @@ static apr_status_t ssl_init_ctx_protoco
 
     /* 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;
     }
@@ -908,7 +933,15 @@ static apr_status_t ssl_init_ctx_cipher_
         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;
 }
 
Index: apache2-2.4.29/modules/ssl/ssl_engine_kernel.c
===================================================================
--- apache2-2.4.29.orig/modules/ssl/ssl_engine_kernel.c	2019-10-10 07:42:38.540812167 -0400
+++ apache2-2.4.29/modules/ssl/ssl_engine_kernel.c	2019-10-10 07:42:38.536812152 -0400
@@ -188,6 +188,12 @@ static int ssl_auth_compatible(modssl_au
             || 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_re
     }
 }
 
+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.
@@ -1050,30 +1033,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;
         }
 
         /*
@@ -1167,6 +1128,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, i
 {
     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);
Index: apache2-2.4.29/modules/ssl/ssl_private.h
===================================================================
--- apache2-2.4.29.orig/modules/ssl/ssl_private.h	2019-10-10 07:42:38.540812167 -0400
+++ apache2-2.4.29/modules/ssl/ssl_private.h	2019-10-10 07:42:38.536812152 -0400
@@ -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
 
@@ -368,8 +370,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
@@ -628,6 +639,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
@@ -782,7 +798,7 @@ const char  *ssl_cmd_SSLPassPhraseDialog
 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 *);
@@ -811,7 +827,7 @@ const char *ssl_cmd_SSLInsecureRenegotia
 
 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 *);
