diff --git a/package/grpc/0003-Add-support-for-OpenSSL-4.patch b/package/grpc/0003-Add-support-for-OpenSSL-4.patch new file mode 100644 index 0000000000..cc517a2461 --- /dev/null +++ b/package/grpc/0003-Add-support-for-OpenSSL-4.patch @@ -0,0 +1,327 @@ +From fb056ab0bb3ed003febe82f069ff41514288a4f3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Bj=C3=B6rn=20Svensson?= +Date: Mon, 31 Aug 2026 09:52:46 -0700 +Subject: [PATCH] [OpenSSL] Add support for OpenSSL 4 (#41932) + +OpenSSL 4.0 removes several deprecated APIs and changes return types of various getter functions to `const`. +This PR fixes all affected code in gRPC's SSL transport layer, credentials, and tests. + +To handle the `const` return type differences across OpenSSL versions without version-specific `#if` guards at every call site, `auto*` is used to let the compiler deduce the correct type. +Where downstream serialization and copy functions (`i2d_X509_NAME`, `i2d_ASN1_OCTET_STRING`, `X509_NAME_dup`) have not yet been constified, `const_cast` is used at the call site to bridge the gap. These functions only read the data, so the cast is safe. + +Tested by building and running SSL tests against BoringSSL and OpenSSL 1.0.2u, 1.1.1w, 3.0.13, and 4.0.0. +Each commit message in this PR has some more details. + +RELEASE NOTES: + +* Add OpenSSL 4.0 compatibility. + +Closes #41932 + +COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/41932 from Nordix:openssl4 c2edc3d46ab7e87ced5d9d95710885a8a8c7a3e2 +PiperOrigin-RevId: 973935241 + +Upstream: https://github.com/grpc/grpc/commit/fb056ab0bb3ed003febe82f069ff41514288a4f3 + +Signed-off-by: Bernd Kuhls +--- + .../call/external/aws_request_signer.cc | 16 ++++++++++ + .../transport/tls/grpc_tls_crl_provider.cc | 4 +-- + src/core/tsi/ssl_transport_security.cc | 32 +++++++++++++------ + src/core/tsi/ssl_transport_security_utils.cc | 14 ++++---- + test/core/handshake/client_ssl.cc | 4 ++- + test/core/handshake/server_ssl_common.cc | 4 ++- + test/core/tsi/ssl_transport_security_test.cc | 10 ++++++ + 7 files changed, 65 insertions(+), 19 deletions(-) + +diff --git a/src/core/credentials/call/external/aws_request_signer.cc b/src/core/credentials/call/external/aws_request_signer.cc +index f7fb7a25b1d0b..d3f506d095abf 100644 +--- a/src/core/credentials/call/external/aws_request_signer.cc ++++ b/src/core/credentials/call/external/aws_request_signer.cc +@@ -18,12 +18,15 @@ + #include + #include + #include ++#if OPENSSL_VERSION_NUMBER < 0x30000000L + #include ++#endif + #include + + #include + #include + ++#include "src/core/util/grpc_check.h" + #include "absl/status/statusor.h" + #include "absl/strings/ascii.h" + #include "absl/strings/escaping.h" +@@ -41,6 +44,7 @@ namespace { + + #if OPENSSL_VERSION_NUMBER >= 0x30000000L + const char kSha256[] = "SHA256"; ++const char kHmacName[] = "HMAC"; + #endif + const char kAlgorithm[] = "AWS4-HMAC-SHA256"; + const char kDateFormat[] = "%a, %d %b %E4Y %H:%M:%S %Z"; +@@ -67,12 +71,24 @@ std::string SHA256Hex(const std::string& str) { + } + + std::string HMAC(const std::string& key, const std::string& msg) { ++#if OPENSSL_VERSION_NUMBER < 0x30000000L + unsigned int len; + unsigned char digest[EVP_MAX_MD_SIZE]; + HMAC(EVP_sha256(), key.c_str(), key.length(), + reinterpret_cast(msg.c_str()), msg.length(), + digest, &len); + return std::string(digest, digest + len); ++#else ++ size_t len = 0; ++ unsigned char digest[EVP_MAX_MD_SIZE]; ++ GRPC_CHECK_NE(EVP_Q_mac(nullptr, kHmacName, nullptr, kSha256, nullptr, ++ reinterpret_cast(key.c_str()), ++ key.length(), ++ reinterpret_cast(msg.c_str()), ++ msg.length(), digest, sizeof(digest), &len), ++ nullptr); ++ return std::string(digest, digest + len); ++#endif + } + + } // namespace +diff --git a/src/core/credentials/transport/tls/grpc_tls_crl_provider.cc b/src/core/credentials/transport/tls/grpc_tls_crl_provider.cc +index 5e24c18ee6127..dc95ba8457121 100644 +--- a/src/core/credentials/transport/tls/grpc_tls_crl_provider.cc ++++ b/src/core/credentials/transport/tls/grpc_tls_crl_provider.cc +@@ -56,12 +56,12 @@ absl::StatusOr IssuerFromCrl(X509_CRL* crl) { + if (crl == nullptr) { + return absl::InvalidArgumentError("crl cannot be null"); + } +- X509_NAME* issuer = X509_CRL_get_issuer(crl); ++ auto* issuer = X509_CRL_get_issuer(crl); + if (issuer == nullptr) { + return absl::InvalidArgumentError("crl cannot have null issuer"); + } + unsigned char* buf = nullptr; +- int len = i2d_X509_NAME(issuer, &buf); ++ int len = i2d_X509_NAME(const_cast(issuer), &buf); + if (len < 0 || buf == nullptr) { + return absl::InvalidArgumentError("crl cannot have null issuer"); + } +diff --git a/src/core/tsi/ssl_transport_security.cc b/src/core/tsi/ssl_transport_security.cc +index 6c1f20ebb5374..29ec47eeeef41 100644 +--- a/src/core/tsi/ssl_transport_security.cc ++++ b/src/core/tsi/ssl_transport_security.cc +@@ -49,7 +49,9 @@ + #include + #include + #include // For OPENSSL_free ++#if !defined(OPENSSL_NO_ENGINE) + #include ++#endif + #include + #include + #include +@@ -1021,9 +1023,7 @@ static int looks_like_ip_address(absl::string_view name) { + static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, + size_t* utf8_size) { + int common_name_index = -1; +- X509_NAME_ENTRY* common_name_entry = nullptr; +- ASN1_STRING* common_name_asn1 = nullptr; +- X509_NAME* subject_name = X509_get_subject_name(cert); ++ auto* subject_name = X509_get_subject_name(cert); + int utf8_returned_size = 0; + if (subject_name == nullptr) { + VLOG(2) << "Could not get subject name from certificate."; +@@ -1035,12 +1035,13 @@ static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, + VLOG(2) << "Could not get common name of subject from certificate."; + return TSI_NOT_FOUND; + } +- common_name_entry = X509_NAME_get_entry(subject_name, common_name_index); ++ auto* common_name_entry = ++ X509_NAME_get_entry(subject_name, common_name_index); + if (common_name_entry == nullptr) { + LOG(ERROR) << "Could not get common name entry from certificate."; + return TSI_INTERNAL_ERROR; + } +- common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); ++ auto* common_name_asn1 = X509_NAME_ENTRY_get_data(common_name_entry); + if (common_name_asn1 == nullptr) { + LOG(ERROR) << "Could not get common name entry asn1 from certificate."; + return TSI_INTERNAL_ERROR; +@@ -1081,7 +1082,7 @@ static tsi_result peer_property_from_x509_common_name( + static tsi_result peer_property_from_x509_subject(X509* cert, + tsi_peer_property* property, + bool is_verified_root_cert) { +- X509_NAME* subject_name = X509_get_subject_name(cert); ++ auto* subject_name = X509_get_subject_name(cert); + if (subject_name == nullptr) { + GRPC_TRACE_LOG(tsi, INFO) << "Could not get subject name from certificate."; + return TSI_NOT_FOUND; +@@ -1177,17 +1178,30 @@ static tsi_result add_subject_alt_names_properties_to_peer( + char ntop_buf[INET6_ADDRSTRLEN]; + int af; + ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ if (ASN1_STRING_length(subject_alt_name->d.iPAddress) == 4) { ++ af = AF_INET; ++ } else if (ASN1_STRING_length(subject_alt_name->d.iPAddress) == 16) { ++ af = AF_INET6; ++#else + if (subject_alt_name->d.iPAddress->length == 4) { + af = AF_INET; + } else if (subject_alt_name->d.iPAddress->length == 16) { + af = AF_INET6; ++#endif + } else { + LOG(ERROR) << "SAN IP Address contained invalid IP"; + result = TSI_INTERNAL_ERROR; + break; + } ++#if OPENSSL_VERSION_NUMBER >= 0x10100000L ++ const char* name = ++ inet_ntop(af, ASN1_STRING_get0_data(subject_alt_name->d.iPAddress), ++ ntop_buf, INET6_ADDRSTRLEN); ++#else + const char* name = inet_ntop(af, subject_alt_name->d.iPAddress->data, + ntop_buf, INET6_ADDRSTRLEN); ++#endif + if (name == nullptr) { + LOG(ERROR) << "Could not get IP string from asn1 octet."; + result = TSI_INTERNAL_ERROR; +@@ -1472,13 +1486,13 @@ static tsi_result x509_store_load_certs(X509_STORE* cert_store, + break; // We're at the end of stream. + } + if (root_names != nullptr) { +- root_name = X509_get_subject_name(root); +- if (root_name == nullptr) { ++ auto* root_subject = X509_get_subject_name(root); ++ if (root_subject == nullptr) { + LOG(ERROR) << "Could not get name from root certificate."; + result = TSI_INVALID_ARGUMENT; + break; + } +- root_name = X509_NAME_dup(root_name); ++ root_name = X509_NAME_dup(const_cast(root_subject)); + if (root_name == nullptr) { + result = TSI_OUT_OF_RESOURCES; + break; +diff --git a/src/core/tsi/ssl_transport_security_utils.cc b/src/core/tsi/ssl_transport_security_utils.cc +index 7318bc3f78057..9bf292821565c 100644 +--- a/src/core/tsi/ssl_transport_security_utils.cc ++++ b/src/core/tsi/ssl_transport_security_utils.cc +@@ -282,11 +282,11 @@ bool VerifyCrlCertIssuerNamesMatch(X509_CRL* crl, X509* cert) { + if (cert == nullptr || crl == nullptr) { + return false; + } +- X509_NAME* cert_issuer_name = X509_get_issuer_name(cert); ++ auto* cert_issuer_name = X509_get_issuer_name(cert); + if (cert_issuer_name == nullptr) { + return false; + } +- X509_NAME* crl_issuer_name = X509_CRL_get_issuer(crl); ++ auto* crl_issuer_name = X509_CRL_get_issuer(crl); + if (crl_issuer_name == nullptr) { + return false; + } +@@ -317,9 +317,9 @@ absl::StatusOr IssuerFromCert(X509* cert) { + if (cert == nullptr) { + return absl::InvalidArgumentError("cert cannot be null"); + } +- X509_NAME* issuer = X509_get_issuer_name(cert); ++ auto* issuer = X509_get_issuer_name(cert); + unsigned char* buf = nullptr; +- int len = i2d_X509_NAME(issuer, &buf); ++ int len = i2d_X509_NAME(const_cast(issuer), &buf); + if (len < 0 || buf == nullptr) { + return absl::InvalidArgumentError("could not read issuer name from cert"); + } +@@ -339,7 +339,8 @@ absl::StatusOr AkidFromCertificate(X509* cert) { + if (X509_get_ext_by_NID(cert, NID_authority_key_identifier, j) != -1) { + return absl::InvalidArgumentError("Could not get AKID from certificate."); + } +- akid = X509_EXTENSION_get_data(X509_get_ext(cert, j)); ++ akid = const_cast( ++ X509_EXTENSION_get_data(X509_get_ext(cert, j))); + } else { + return absl::InvalidArgumentError("Could not get AKID from certificate."); + } +@@ -364,7 +365,8 @@ absl::StatusOr AkidFromCrl(X509_CRL* crl) { + if (X509_CRL_get_ext_by_NID(crl, NID_authority_key_identifier, j) != -1) { + return absl::InvalidArgumentError("Could not get AKID from crl."); + } +- akid = X509_EXTENSION_get_data(X509_CRL_get_ext(crl, j)); ++ akid = const_cast( ++ X509_EXTENSION_get_data(X509_CRL_get_ext(crl, j))); + } else { + return absl::InvalidArgumentError("Could not get AKID from crl."); + } +diff --git a/test/core/handshake/client_ssl.cc b/test/core/handshake/client_ssl.cc +index 1952c6055094f..c985c0de926f1 100644 +--- a/test/core/handshake/client_ssl.cc ++++ b/test/core/handshake/client_ssl.cc +@@ -197,13 +197,15 @@ static void server_thread(void* arg) { + OpenSSL_add_ssl_algorithms(); + args->ssl_library_info->Notify(); + +- const SSL_METHOD* method = TLSv1_2_server_method(); ++ const SSL_METHOD* method = TLS_server_method(); + SSL_CTX* ctx = SSL_CTX_new(method); + if (!ctx) { + perror("Unable to create SSL context"); + ERR_print_errors_fp(stderr); + abort(); + } ++ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); ++ SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); + + // Load key pair. + if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { +diff --git a/test/core/handshake/server_ssl_common.cc b/test/core/handshake/server_ssl_common.cc +index ece1b6f50109e..7cb8f95e578f3 100644 +--- a/test/core/handshake/server_ssl_common.cc ++++ b/test/core/handshake/server_ssl_common.cc +@@ -187,13 +187,15 @@ bool server_ssl_test(const char* alpn_list[], unsigned int alpn_list_len, + // server port. + s.Await(); + +- const SSL_METHOD* method = TLSv1_2_client_method(); ++ const SSL_METHOD* method = TLS_client_method(); + SSL_CTX* ctx = SSL_CTX_new(method); + if (!ctx) { + perror("Unable to create SSL context"); + ERR_print_errors_fp(stderr); + abort(); + } ++ SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION); ++ SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION); + + // Load key pair. + if (SSL_CTX_use_certificate_file(ctx, SSL_CERT_PATH, SSL_FILETYPE_PEM) < 0) { +diff --git a/test/core/tsi/ssl_transport_security_test.cc b/test/core/tsi/ssl_transport_security_test.cc +index 806495f87ccca..8c3f4677495ec 100644 +--- a/test/core/tsi/ssl_transport_security_test.cc ++++ b/test/core/tsi/ssl_transport_security_test.cc +@@ -23,6 +23,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -985,7 +986,16 @@ TEST_P(SslTransportSecurityTest, DoRoundTripForAllConfigs) { + TEST_P(SslTransportSecurityTest, DoRoundTripWithErrorOnStack) { + // Invoke an SSL function that causes an error, and ensure the error + // makes it to the stack. ++#if OPENSSL_VERSION_NUMBER < 0x30000000L + ASSERT_FALSE(EC_KEY_new_by_curve_name(NID_rsa)); ++#else ++ // Use EVP_PKEY_CTX with an invalid operation to push an error. ++ EVP_PKEY_CTX* err_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr); ++ ASSERT_NE(err_ctx, nullptr); ++ // Calling sign_init without a key will fail and push an error. ++ ASSERT_LE(EVP_PKEY_sign_init(err_ctx), 0); ++ EVP_PKEY_CTX_free(err_ctx); ++#endif + ASSERT_NE(ERR_peek_error(), 0); + SetUpSslFixture(/*tls_version=*/std::get<0>(GetParam()), + /*send_client_ca_list=*/std::get<1>(GetParam()));