From: Joe Orton <jorton@redhat.com>
Date: Mon, 26 Jul 2021 12:24:46 +0100
Subject: * modules/ssl/ssl_engine_init.c (ssl_init_server_certs): Fix use of
   encrypted private keys with OpenSSL 3.0.

* test/travis_run_linux.sh: For TEST_SSL, test loading encrypted
  private keys.

Github: closes #{197}

Submitted by: jorton


Forwarded: yes, https://github.com/apache/httpd/pull/258
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/apache2/+bug/1951476
---
 modules/ssl/ssl_engine_init.c | 19 +++++++++++++++++--
 1 file changed, 17 insertions(+), 2 deletions(-)

diff --git a/modules/ssl/ssl_engine_init.c b/modules/ssl/ssl_engine_init.c
index a2da916..2f3a120 100644
--- a/modules/ssl/ssl_engine_init.c
+++ b/modules/ssl/ssl_engine_init.c
@@ -1280,6 +1280,22 @@ static APR_INLINE int modssl_DH_bits(DH *dh)
 #endif
 }
 
+/* SSL_CTX_use_PrivateKey_file() can fail either because the private
+ * key was encrypted, or due to a mismatch between an already-loaded
+ * cert and the key - a common misconfiguration - from calling
+ * X509_check_private_key().  This macro is passed the last error code
+ * off the OpenSSL stack and evaluates to true only for the first
+ * case.  With OpenSSL < 3 the second case is identifiable by the
+ * function code, but function codes are not used from 3.0. */
+#if OPENSSL_VERSION_NUMBER < 0x30000000L
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_FUNC(ec) != X509_F_X509_CHECK_PRIVATE_KEY)
+#else
+#define CHECK_PRIVKEY_ERROR(ec) (ERR_GET_LIB != ERR_LIB_X509            \
+                                 || (ERR_GET_REASON(ec) != X509_R_KEY_TYPE_MISMATCH \
+                                     && ERR_GET_REASON(ec) != X509_R_KEY_VALUES_MISMATCH \
+                                     && ERR_GET_REASON(ec) != X509_R_UNKNOWN_KEY_TYPE))
+#endif
+
 static apr_status_t ssl_init_server_certs(server_rec *s,
                                           apr_pool_t *p,
                                           apr_pool_t *ptemp,
@@ -1385,8 +1401,7 @@ static apr_status_t ssl_init_server_certs(server_rec *s,
         }
         else if ((SSL_CTX_use_PrivateKey_file(mctx->ssl_ctx, keyfile,
                                               SSL_FILETYPE_PEM) < 1)
-                 && (ERR_GET_FUNC(ERR_peek_last_error())
-                     != X509_F_X509_CHECK_PRIVATE_KEY)) {
+                 && CHECK_PRIVKEY_ERROR(ERR_peek_last_error())) {
             ssl_asn1_t *asn1;
             const unsigned char *ptr;
 
