Pull all changes from upstream integration branch: svn diff -r1840105:1841219 https://svn.apache.org/repos/asf/httpd/httpd/branches/tlsv1.3-for-2.4.x --- httpd-2.4.34/modules/ssl/mod_ssl.c.r1827912+ +++ httpd-2.4.34/modules/ssl/mod_ssl.c @@ -93,9 +93,9 @@ 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 @@ 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 @@ /* 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(); --- httpd-2.4.34/modules/ssl/ssl_engine_config.c.r1827912+ +++ httpd-2.4.34/modules/ssl/ssl_engine_config.c @@ -136,6 +136,7 @@ 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 @@ 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_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 \ @@ -1449,6 +1466,9 @@ 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; @@ -1510,16 +1530,28 @@ 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, --- httpd-2.4.34/modules/ssl/ssl_engine_init.c.r1827912+ +++ httpd-2.4.34/modules/ssl/ssl_engine_init.c @@ -568,6 +568,9 @@ #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 @@ 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 @@ 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 @@ 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 @@ /* 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; } @@ -736,6 +761,13 @@ SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS); #endif +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL + /* For OpenSSL >=1.1.1, disable auto-retry mode so it's possible + * to consume handshake records without blocking for app-data. + * https://github.com/openssl/openssl/issues/7178 */ + SSL_CTX_clear_mode(ctx, SSL_MODE_AUTO_RETRY); +#endif + return APR_SUCCESS; } @@ -888,7 +920,15 @@ 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; } @@ -1493,6 +1533,13 @@ X509_STORE_CTX *sctx; X509_STORE *store = SSL_CTX_get_cert_store(mctx->ssl_ctx); +#if OPENSSL_VERSION_NUMBER >= 0x1010100fL + /* For OpenSSL >=1.1.1, turn on client cert support which is + * otherwise turned off by default (by design). + * https://github.com/openssl/openssl/issues/6933 */ + SSL_CTX_set_post_handshake_auth(mctx->ssl_ctx, 1); +#endif + SSL_CTX_set_client_cert_cb(mctx->ssl_ctx, ssl_callback_proxy_cert); --- httpd-2.4.34/modules/ssl/ssl_engine_kernel.c.r1827912+ +++ httpd-2.4.34/modules/ssl/ssl_engine_kernel.c @@ -188,6 +188,12 @@ || 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,87 +430,70 @@ } } -/* - * Access Handler - */ -int ssl_hook_Access(request_rec *r) +static int ssl_check_post_client_verify(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; - BOOL renegotiate = FALSE, renegotiate_quick = FALSE; X509 *cert; - X509 *peercert; - X509_STORE *cert_store = NULL; - 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; - 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; + /* + * Remember the peer certificate's DN + */ + if ((cert = SSL_get_peer_certificate(ssl))) { + if (sslconn->client_cert) { + X509_free(sslconn->client_cert); } - ctx = SSL_get_SSL_CTX(ssl); + sslconn->client_cert = cert; + sslconn->client_dn = NULL; } - + /* - * Support for SSLRequireSSL directive + * Finally check for acceptable renegotiation results */ - 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"); + 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_UPGRADE_REQUIRED; + return HTTP_FORBIDDEN; } - 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"); + if (do_verify) { + if (cert == NULL) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02263) + "Re-negotiation handshake failed: " + "Client certificate missing"); - return HTTP_FORBIDDEN; + return HTTP_FORBIDDEN; + } + } } + return OK; +} - /* - * 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; - } +/* + * Access Handler, classic flavour, for SSL/TLS up to v1.2 + * where everything can be renegotiated and no one is happy. + */ +static int ssl_hook_Access_classic(request_rec *r, SSLSrvConfigRec *sc, SSLDirConfigRec *dc, + SSLConnRec *sslconn, SSL *ssl) +{ + server_rec *handshakeserver = sslconn ? sslconn->server : NULL; + SSLSrvConfigRec *hssc = handshakeserver? mySrvConfig(handshakeserver) : NULL; + SSL_CTX *ctx = NULL; + BOOL renegotiate = FALSE, renegotiate_quick = FALSE; + X509 *peercert; + X509_STORE *cert_store = NULL; + 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, rc; + const char *ncipher_suite; #ifdef HAVE_SRP /* @@ -581,7 +570,7 @@ } /* 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 +648,7 @@ } 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 +721,7 @@ (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. @@ -885,6 +874,7 @@ if (renegotiate_quick) { STACK_OF(X509) *cert_stack; + X509 *cert; /* perform just a manual re-verification of the peer */ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02258) @@ -1037,43 +1027,10 @@ } /* - * Remember the peer certificate's DN - */ - if ((cert = SSL_get_peer_certificate(ssl))) { - if (sslconn->client_cert) { - X509_free(sslconn->client_cert); - } - sslconn->client_cert = cert; - sslconn->client_dn = NULL; - } - - /* * 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; } /* @@ -1096,6 +1053,215 @@ } } + 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_needed & 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); + + if (SSL_verify_client_post_handshake(ssl) != 1) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10158) + "cannot perform post-handshake authentication"); + ssl_log_ssl_error(SSLLOG_MARK, APLOG_ERR, r->server); + apr_table_setn(r->notes, "error-notes", + "Reason: Cannot perform Post-Handshake Authentication.
"); + return HTTP_FORBIDDEN; + } + + 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; + apr_array_header_t *requires; + ssl_require_t *ssl_requires; + int ok, i, ret; + + /* 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) { + ret = ssl_hook_Access_modern(r, sc, dc, sslconn, ssl); + } + else +#endif + { + ret = ssl_hook_Access_classic(r, sc, dc, sslconn, ssl); + } + + if (ret != DECLINED) { + return ret; + } + /* If we're trying to have the user name set from a client * certificate then we need to set it here. This should be safe as * the user name probably isn't important from an auth checking point @@ -2080,31 +2246,43 @@ { 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 ((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 (!scr->is_proxy && - (where & SSL_CB_HANDSHAKE_START) && - scr->reneg_state == RENEG_REJECT) { - scr->reneg_state = RENEG_ABORT; + /* 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); --- httpd-2.4.34/modules/ssl/ssl_private.h.r1827912+ +++ httpd-2.4.34/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 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 @@ #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 @@ /** 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_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_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 *);