From 4395feafcadc4b74f749ce421a530922e5f58047 Mon Sep 17 00:00:00 2001
From: ucko <ucko@ncbi.nlm.nih.gov>
Date: Tue, 23 Apr 2024 19:21:48 +0000
Subject: [PATCH] Accommodate Mbed TLS 3.x (specifically, 3.6.0 LTS).

- Conditionally pass mbedtls_pk_parse_key the existing pRNG and its state.
- Call psa_crypto_init during setup, as PSA may be in play even when not
  explicitly enabled.  Documentation suggests calling it as early as
  possible; in practice, though, holding off until after pRNG
  initialization avoids "insufficient entropy" errors under 2.x (at least
  2.28.8 LTS on Linux) and still works fine as of 3.6.0.
- At least for now, ensure that MBEDTLS_SSL_VERIFY_NONE remains effective
  by additionally capping the TLS version at 1.2; as of 1.3, certificate
  checking has become mandatory, and Mbed TLS respects that requirement.
- Additional tuneups to ncbi_mbedtls.c-only code:
-- Account for error-code macro repertoire changes by conditionalizing
   major-version-specific macros on their availability and specifically
   checking for or emitting MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED (new)
   as appropriate,
-- x_ErrorToStatus: Conditionally use MBEDTLS_PRIVATE for p_bio, which has
   no accessor.
-- x_MbedTlsWrite: Substitute mbedtls_ssl_get_max_out_record_payload for
   mbedtls_ssl_get_output_max_frag_len, which didn't account for other
   relevant considerations and is now fully private.

https://github.com/Mbed-TLS/mbedtls/blob/development/docs/3.0-migration-guide.md
supplied useful guidance.  JIRA: CXX-13565.

git-svn-id: https://anonsvn.ncbi.nlm.nih.gov/repos/v1/trunk/c++@102329 78c7ea69-d796-4a43-9a09-de51944f1b03
---
 c++/src/connect/ncbi_mbedtls.c      | 44 ++++++++++++++++++++++++++++++---
 c++/src/connect/ncbi_priv.h         |  2 +-
 c++/src/connect/ncbi_uv_nghttp2.cpp | 22 ++++++++++++++++-
 3 files changed, 63 insertions(+), 5 deletions(-)

--- a/c++/src/connect/ncbi_mbedtls.c
+++ b/c++/src/connect/ncbi_mbedtls.c
@@ -52,6 +52,7 @@
 #    include <mbedtls/ssl.h>
 #    include <mbedtls/threading.h>
 #    include <mbedtls/version.h>
+#    include <psa/crypto.h>
 
 #  if   defined(ENOTSUP)
 #    define NCBI_NOTSUPPORTED  ENOTSUP
@@ -101,7 +102,11 @@ static int mbtls_user_mutex_lock(MT_LOCK
     if (lock) {
         switch (MT_LOCK_Do(*lock, eMT_Lock)) {
         case -1:
+#ifdef MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE
             return MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE;
+#else
+            return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+#endif
         case  0:
             return MBEDTLS_ERR_THREADING_MUTEX_ERROR;
         case  1:
@@ -117,7 +122,11 @@ static int mbtls_user_mutex_unlock(MT_LO
     if (lock) {
         switch (MT_LOCK_Do(*lock, eMT_Unlock)) {
         case -1:
+#ifdef MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE
             return MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE;
+#else
+            return MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED;
+#endif
         case  0:
             return MBEDTLS_ERR_THREADING_MUTEX_ERROR;
         case  1:
@@ -214,7 +223,11 @@ static EIO_Status x_ErrorToStatus(int er
 
     if (!error)
         return eIO_Success;
+#if MBEDTLS_VERSION_MAJOR >= 3
+    sock = ((SNcbiSSLctx*)(session->MBEDTLS_PRIVATE(p_bio)))->sock;
+#else
     sock = ((SNcbiSSLctx*)(session->p_bio))->sock;
+#endif
     switch (error) {
     case MBEDTLS_ERR_SSL_WANT_READ:
         status = x_RetryStatus(sock, direction);
@@ -225,11 +238,20 @@ static EIO_Status x_ErrorToStatus(int er
     case MBEDTLS_ERR_SSL_TIMEOUT:
         status = eIO_Timeout;
         break;
+#ifdef MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE
     case MBEDTLS_ERR_THREADING_FEATURE_UNAVAILABLE:
+#endif
+#ifdef MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE
     case MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE:
+#endif
     case MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE:
+#ifdef MBEDTLS_ERR_SSL_UNKNOWN_CIPHER
     case MBEDTLS_ERR_SSL_UNKNOWN_CIPHER:
+#endif
     case MBEDTLS_ERR_SSL_INTERNAL_ERROR:
+#ifdef MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED
+    case MBEDTLS_ERR_PLATFORM_FEATURE_UNSUPPORTED:
+#endif
         status = eIO_NotSupported;
         break;
     case MBEDTLS_ERR_THREADING_BAD_INPUT_DATA:
@@ -574,8 +596,8 @@ static EIO_Status x_MbedTlsWrite(void* s
 static EIO_Status s_MbedTlsWrite(void* session, const void* data,
                                  size_t n_todo, size_t* n_done, int* error)
 {
-    size_t max_size
-        = mbedtls_ssl_get_output_max_frag_len((mbedtls_ssl_context*) session);
+    size_t max_size = mbedtls_ssl_get_max_out_record_payload
+        ((mbedtls_ssl_context*) session);
     EIO_Status status;
 
     *n_done = 0;
@@ -714,6 +736,7 @@ static EIO_Status s_MbedTlsInit(FSSLPull
     char version[80];
     const char* val;
     char buf[32];
+    psa_status_t psa_status;
 
     mbedtls_version_get_string(version);
     if (strcasecmp(MBEDTLS_VERSION_STRING, version) != 0) {
@@ -734,6 +757,11 @@ static EIO_Status s_MbedTlsInit(FSSLPull
                                 MBEDTLS_SSL_TRANSPORT_STREAM,
                                 MBEDTLS_SSL_PRESET_DEFAULT);
     mbedtls_ssl_conf_authmode(&s_MbedTlsConf, MBEDTLS_SSL_VERIFY_NONE);
+#if MBEDTLS_VERSION_MAJOR >= 3
+    /* The above line can otherwise be ineffective. */
+    mbedtls_ssl_conf_max_tls_version(&s_MbedTlsConf,
+                                     MBEDTLS_SSL_VERSION_TLS1_2);
+#endif
 
     /* Check CONN_[MBED]TLS_LOGLEVEL or [CONN][MBED]TLS_LOGLEVEL */
     val = ConnNetInfo_GetValueInternal(0, "MBED" REG_CONN_TLS_LOGLEVEL,
@@ -781,6 +809,12 @@ static EIO_Status s_MbedTlsInit(FSSLPull
     mbedtls_ssl_conf_rng(&s_MbedTlsConf,
                          mbedtls_ctr_drbg_random, &s_MbedTlsCtrDrbg);
 
+    if ((psa_status = psa_crypto_init()) != PSA_SUCCESS) {
+        CORE_LOGF_X(51, eLOG_Error,
+                    ("psa_crypto_init failed with status %d", psa_status));
+        return eIO_NotSupported;
+    }
+
     s_Pull = pull;
     s_Push = push;
 
@@ -957,7 +991,11 @@ NCBI_CRED NcbiCreateMbedTlsCertCredentia
 
     err = mbedtls_pk_parse_key(xcred->pkey,
                                (const unsigned char*) pkey, pkeysz ? pkeysz
-                               : strlen((const char*) pkey) + 1, 0, 0);
+                               : strlen((const char*) pkey) + 1, 0, 0
+#if MBEDTLS_VERSION_MAJOR >= 3
+                               , mbedtls_ctr_drbg_random, &s_MbedTlsCtrDrbg
+#endif
+                               );
     if (err) {
         mbedtls_strerror(err, errbuf, sizeof(errbuf) - 1);
         CORE_LOG_ERRNO_EXX(12, eLOG_Error, err, errbuf,
--- a/c++/src/connect/ncbi_priv.h
+++ b/c++/src/connect/ncbi_priv.h
@@ -101,7 +101,7 @@ NCBI_C_DEFINE_ERRCODE_X(Connect_SMTP,
 NCBI_C_DEFINE_ERRCODE_X(Connect_HTTP,          307,  26);
 NCBI_C_DEFINE_ERRCODE_X(Connect_Service,       308,  10);
 NCBI_C_DEFINE_ERRCODE_X(Connect_HeapMgr,       309,  34);
-NCBI_C_DEFINE_ERRCODE_X(Connect_TLS,           310,  50);
+NCBI_C_DEFINE_ERRCODE_X(Connect_TLS,           310,  51);
 NCBI_C_DEFINE_ERRCODE_X(Connect_Mghbn,         311,  16);
 NCBI_C_DEFINE_ERRCODE_X(Connect_Crypt,         312,   5);
 NCBI_C_DEFINE_ERRCODE_X(Connect_LocalIP,       313,   5);
--- a/c++/src/connect/ncbi_uv_nghttp2.cpp
+++ b/c++/src/connect/ncbi_uv_nghttp2.cpp
@@ -40,6 +40,8 @@
 #include <corelib/ncbiapp.hpp>
 #include <corelib/request_ctx.hpp>
 
+#include <psa/crypto.h>
+
 BEGIN_NCBI_SCOPE
 
 #define NCBI_UV_WRITE_TRACE(message)        _TRACE(message)
@@ -701,6 +703,10 @@ SUvNgHttp2_TlsImpl::SUvNgHttp2_TlsImpl(c
     }
 
     mbedtls_ssl_conf_authmode(&m_Conf, MBEDTLS_SSL_VERIFY_NONE);
+#if MBEDTLS_VERSION_MAJOR >= 3
+    /* The above line can otherwise be ineffective. */
+    mbedtls_ssl_conf_max_tls_version(&m_Conf, MBEDTLS_SSL_VERSION_TLS1_2);
+#endif
     mbedtls_entropy_init(&m_Entropy);
     mbedtls_ctr_drbg_init(&m_CtrDrbg);
     mbedtls_x509_crt_init(&m_Cert);
@@ -714,6 +720,14 @@ SUvNgHttp2_TlsImpl::SUvNgHttp2_TlsImpl(c
     }
 
     mbedtls_ssl_conf_rng(&m_Conf, mbedtls_ctr_drbg_random, &m_CtrDrbg);
+    auto p_rv = psa_crypto_init();
+
+    if (p_rv != PSA_SUCCESS) {
+        NCBI_UVNGHTTP2_TLS_TRACE(this << " psa_crypto_init: error code"
+                                 << p_rv);
+        return;
+    }
+
     mbedtls_ssl_conf_alpn_protocols(&m_Conf, m_Protocols.data());
     mbedtls_ssl_init(&m_Ssl);
 
@@ -748,7 +762,13 @@ SUvNgHttp2_TlsImpl::SUvNgHttp2_TlsImpl(c
         return;
     }
 
-    auto pk_rv = mbedtls_pk_parse_key(&m_Pkey, reinterpret_cast<const unsigned char*>(pkey.data()), pkey.size() + 1, nullptr, 0);
+    auto pk_rv = mbedtls_pk_parse_key(
+        &m_Pkey, reinterpret_cast<const unsigned char*>(pkey.data()),
+        pkey.size() + 1, nullptr, 0
+#if MBEDTLS_VERSION_MAJOR >= 3
+        , mbedtls_ctr_drbg_random, &m_CtrDrbg
+#endif
+        );
 
     if (pk_rv) {
         NCBI_UVNGHTTP2_TLS_TRACE(this << " mbedtls_pk_parse_key: " << SUvNgHttp2_Error::MbedTlsStr(pk_rv));
