From 0d5e69aae60c5dbf3c6c746b42705642c8362711 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Fri, 26 Jun 2026 17:09:10 +0800 Subject: [PATCH 1/5] Add Tongsuo NTLS support to C++ TSSLSocket Enable building libthrift against Tongsuo via -DWITH_TONGSUO and expose NTLS dual-certificate APIs on TSSLSocketFactory for TLCP workloads. --- build/cmake/ConfigureChecks.cmake | 28 +++ build/cmake/DefineOptions.cmake | 35 +++- build/cmake/FindTongsuo.cmake | 80 ++++++++ build/cmake/config.h.in | 3 + lib/cpp/src/thrift/transport/TSSLSocket.cpp | 195 ++++++++++++++++++ lib/cpp/src/thrift/transport/TSSLSocket.h | 12 ++ lib/cpp/test/CMakeLists.txt | 19 ++ lib/cpp/test/TNTLSSocketTest.cpp | 213 ++++++++++++++++++++ test/keys/ntls/chain-ca.crt | 26 +++ test/keys/ntls/client_enc.crt | 13 ++ test/keys/ntls/client_enc.key | 5 + test/keys/ntls/client_sign.crt | 13 ++ test/keys/ntls/client_sign.key | 5 + test/keys/ntls/server_enc.crt | 13 ++ test/keys/ntls/server_enc.key | 5 + test/keys/ntls/server_sign.crt | 13 ++ test/keys/ntls/server_sign.key | 5 + 17 files changed, 681 insertions(+), 2 deletions(-) create mode 100644 build/cmake/FindTongsuo.cmake create mode 100644 lib/cpp/test/TNTLSSocketTest.cpp create mode 100644 test/keys/ntls/chain-ca.crt create mode 100644 test/keys/ntls/client_enc.crt create mode 100644 test/keys/ntls/client_enc.key create mode 100644 test/keys/ntls/client_sign.crt create mode 100644 test/keys/ntls/client_sign.key create mode 100644 test/keys/ntls/server_enc.crt create mode 100644 test/keys/ntls/server_enc.key create mode 100644 test/keys/ntls/server_sign.crt create mode 100644 test/keys/ntls/server_sign.key diff --git a/build/cmake/ConfigureChecks.cmake b/build/cmake/ConfigureChecks.cmake index 29b8bbea859..1151ca8b729 100644 --- a/build/cmake/ConfigureChecks.cmake +++ b/build/cmake/ConfigureChecks.cmake @@ -86,6 +86,34 @@ set(PACKAGE ${PACKAGE_NAME}) set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(VERSION ${thrift_VERSION}) +if(WITH_OPENSSL AND OPENSSL_FOUND) + set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") + if(TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto) + set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) + else() + set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") + endif() + + check_cxx_source_compiles( + " + #include + #if defined(OPENSSL_NO_NTLS) + # error ntls disabled + #endif + int main() { + SSL_CTX *ctx = SSL_CTX_new(NTLS_method()); + if (ctx == nullptr) { + return 1; + } + SSL_CTX_enable_ntls(ctx); + SSL_CTX_free(ctx); + return 0; + } + " + THRIFT_HAVE_NTLS + ) +endif() + # generate a config.h file configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/thrift/config.h") diff --git a/build/cmake/DefineOptions.cmake b/build/cmake/DefineOptions.cmake index 7c1dddb5e61..67ada4830da 100644 --- a/build/cmake/DefineOptions.cmake +++ b/build/cmake/DefineOptions.cmake @@ -78,7 +78,33 @@ CMAKE_DEPENDENT_OPTION(BUILD_C_GLIB "Build C (GLib) library" ON # OpenSSL if(WITH_CPP OR WITH_C_GLIB) - find_package(OpenSSL) + option(WITH_TONGSUO "Build with Tongsuo instead of OpenSSL" OFF) + set(TONGSUO_ROOT_DIR "" CACHE PATH "Root directory of a Tongsuo installation") + + if(WITH_TONGSUO) + find_package(Tongsuo REQUIRED) + set(OPENSSL_FOUND TRUE) + set(OPENSSL_INCLUDE_DIR "${TONGSUO_INCLUDE_DIR}") + set(OPENSSL_SSL_LIBRARY "${TONGSUO_SSL_LIBRARY}") + set(OPENSSL_CRYPTO_LIBRARY "${TONGSUO_CRYPTO_LIBRARY}") + set(OPENSSL_LIBRARIES "${TONGSUO_SSL_LIBRARY}" "${TONGSUO_CRYPTO_LIBRARY}") + if(NOT TARGET OpenSSL::SSL) + add_library(OpenSSL::SSL UNKNOWN IMPORTED) + set_target_properties(OpenSSL::SSL PROPERTIES + IMPORTED_LOCATION "${TONGSUO_SSL_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" + ) + endif() + if(NOT TARGET OpenSSL::Crypto) + add_library(OpenSSL::Crypto UNKNOWN IMPORTED) + set_target_properties(OpenSSL::Crypto PROPERTIES + IMPORTED_LOCATION "${TONGSUO_CRYPTO_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" + ) + endif() + else() + find_package(OpenSSL) + endif() CMAKE_DEPENDENT_OPTION(WITH_OPENSSL "Build with OpenSSL support" ON "OPENSSL_FOUND" OFF) endif() @@ -164,7 +190,12 @@ message(STATUS "Language libraries:") message(STATUS) message(STATUS " Build with OpenSSL: ${WITH_OPENSSL}") if(WITH_OPENSSL) - message(STATUS " Version: ${OPENSSL_VERSION}") + if(WITH_TONGSUO) + message(STATUS " Backend: Tongsuo") + message(STATUS " Root: ${TONGSUO_ROOT_DIR}") + else() + message(STATUS " Version: ${OPENSSL_VERSION}") + endif() endif() message(STATUS) message(STATUS " Build C++ library: ${BUILD_CPP}") diff --git a/build/cmake/FindTongsuo.cmake b/build/cmake/FindTongsuo.cmake new file mode 100644 index 00000000000..c678b73731c --- /dev/null +++ b/build/cmake/FindTongsuo.cmake @@ -0,0 +1,80 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# + +# Find Tongsuo (OpenSSL-compatible TLS library) +# +# TONGSUO_INCLUDE_DIR, TONGSUO_SSL_LIBRARY, TONGSUO_CRYPTO_LIBRARY +# Tongsuo_FOUND + +include(FindPackageHandleStandardArgs) + +find_path(TONGSUO_INCLUDE_DIR + NAMES openssl/ssl.h + HINTS + ${TONGSUO_ROOT_DIR} + ENV TONGSUO_ROOT + PATH_SUFFIXES include +) + +find_library(TONGSUO_SSL_LIBRARY + NAMES ssl libssl + HINTS + ${TONGSUO_ROOT_DIR} + ENV TONGSUO_ROOT + PATH_SUFFIXES lib lib64 +) + +find_library(TONGSUO_CRYPTO_LIBRARY + NAMES crypto libcrypto + HINTS + ${TONGSUO_ROOT_DIR} + ENV TONGSUO_ROOT + PATH_SUFFIXES lib lib64 +) + +find_package_handle_standard_args(Tongsuo + REQUIRED_VARS + TONGSUO_INCLUDE_DIR + TONGSUO_SSL_LIBRARY + TONGSUO_CRYPTO_LIBRARY +) + +if(TONGSUO_FOUND) + if(NOT TARGET Tongsuo::SSL) + add_library(Tongsuo::SSL UNKNOWN IMPORTED) + set_target_properties(Tongsuo::SSL PROPERTIES + IMPORTED_LOCATION "${TONGSUO_SSL_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" + ) + endif() + + if(NOT TARGET Tongsuo::Crypto) + add_library(Tongsuo::Crypto UNKNOWN IMPORTED) + set_target_properties(Tongsuo::Crypto PROPERTIES + IMPORTED_LOCATION "${TONGSUO_CRYPTO_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" + ) + endif() +endif() + +mark_as_advanced( + TONGSUO_INCLUDE_DIR + TONGSUO_SSL_LIBRARY + TONGSUO_CRYPTO_LIBRARY +) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index afcdc1e6a7e..4bb060d6253 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -157,4 +157,7 @@ /* Define to 1 if strerror_r returns char *. */ #cmakedefine STRERROR_R_CHAR_P 1 +/* Define to 1 if NTLS support is available. */ +#cmakedefine THRIFT_HAVE_NTLS 1 + #endif diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.cpp b/lib/cpp/src/thrift/transport/TSSLSocket.cpp index ac88acd9266..260edffbdc4 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.cpp +++ b/lib/cpp/src/thrift/transport/TSSLSocket.cpp @@ -178,6 +178,12 @@ static void buildErrors(string& message, int errno_copy = 0, int sslerrno = 0); static bool matchName(const char* host, const char* pattern, int size); static char uppercase(char c); +static void requireNtlsSupport() { +#ifndef THRIFT_HAVE_NTLS + throw TSSLException("NTLS is not available in this build"); +#endif +} + // SSLContext implementation SSLContext::SSLContext(const SSLProtocol& protocol) { if (protocol == SSLTLS) { @@ -192,6 +198,10 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { ctx_ = SSL_CTX_new(TLSv1_1_method()); } else if (protocol == TLSv1_2) { ctx_ = SSL_CTX_new(TLSv1_2_method()); +#ifdef THRIFT_HAVE_NTLS + } else if (protocol == NTLS) { + ctx_ = SSL_CTX_new(NTLS_method()); +#endif } else { /// UNKNOWN PROTOCOL! throw TSSLException("SSL_CTX_new: Unknown protocol"); @@ -202,6 +212,11 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { buildErrors(errors); throw TSSLException("SSL_CTX_new: " + errors); } +#ifdef THRIFT_HAVE_NTLS + if (protocol == NTLS) { + SSL_CTX_enable_ntls(ctx_); + } +#endif SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY); // Keep version-flexible negotiation for current protocol versions while setting @@ -1092,6 +1107,186 @@ void TSSLSocketFactory::loadPrivateKeyFromBuffer(const char* aPrivateKey, const } } +void TSSLSocketFactory::loadSignCertificate(const char* path, const char* format) { + requireNtlsSupport(); + if (path == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadSignCertificate: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_sign_certificate_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_sign_certificate_file: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +#endif +} + +void TSSLSocketFactory::loadSignCertificateFromBuffer(const char* aCertificate, const char* format) { + requireNtlsSupport(); + if (aCertificate == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadSignCertificateFromBuffer: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") != 0) { + throw TSSLException("Unsupported certificate format: " + string(format)); + } + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aCertificate); + X509* cert = PEM_read_bio_X509(mem, nullptr, nullptr, nullptr); + BIO_free(mem); + const int status = SSL_CTX_use_sign_certificate(ctx_->get(), cert); + X509_free(cert); + if (status != 1) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_sign_certificate: " + errors); + } +#endif +} + +void TSSLSocketFactory::loadSignPrivateKey(const char* path, const char* format) { + requireNtlsSupport(); + if (path == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadSignPrivateKey: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_sign_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_sign_PrivateKey_file: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +#endif +} + +void TSSLSocketFactory::loadSignPrivateKeyFromBuffer(const char* aPrivateKey, const char* format) { + requireNtlsSupport(); + if (aPrivateKey == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadSignPrivateKeyFromBuffer: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") != 0) { + throw TSSLException("Unsupported certificate format: " + string(format)); + } + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aPrivateKey); + EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr); + BIO_free(mem); + const int status = SSL_CTX_use_sign_PrivateKey(ctx_->get(), key); + EVP_PKEY_free(key); + if (status == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_sign_PrivateKey: " + errors); + } +#endif +} + +void TSSLSocketFactory::loadEncCertificate(const char* path, const char* format) { + requireNtlsSupport(); + if (path == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadEncCertificate: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_enc_certificate_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_enc_certificate_file: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +#endif +} + +void TSSLSocketFactory::loadEncCertificateFromBuffer(const char* aCertificate, const char* format) { + requireNtlsSupport(); + if (aCertificate == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadEncCertificateFromBuffer: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") != 0) { + throw TSSLException("Unsupported certificate format: " + string(format)); + } + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aCertificate); + X509* cert = PEM_read_bio_X509(mem, nullptr, nullptr, nullptr); + BIO_free(mem); + const int status = SSL_CTX_use_enc_certificate(ctx_->get(), cert); + X509_free(cert); + if (status != 1) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_enc_certificate: " + errors); + } +#endif +} + +void TSSLSocketFactory::loadEncPrivateKey(const char* path, const char* format) { + requireNtlsSupport(); + if (path == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadEncPrivateKey: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") == 0) { + if (SSL_CTX_use_enc_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_enc_PrivateKey_file: " + errors); + } + } else { + throw TSSLException("Unsupported certificate format: " + string(format)); + } +#endif +} + +void TSSLSocketFactory::loadEncPrivateKeyFromBuffer(const char* aPrivateKey, const char* format) { + requireNtlsSupport(); + if (aPrivateKey == nullptr || format == nullptr) { + throw TTransportException(TTransportException::BAD_ARGS, + "loadEncPrivateKeyFromBuffer: either or is nullptr"); + } +#ifdef THRIFT_HAVE_NTLS + if (strcmp(format, "PEM") != 0) { + throw TSSLException("Unsupported certificate format: " + string(format)); + } + BIO* mem = BIO_new(BIO_s_mem()); + BIO_puts(mem, aPrivateKey); + EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr); + BIO_free(mem); + const int status = SSL_CTX_use_enc_PrivateKey(ctx_->get(), key); + EVP_PKEY_free(key); + if (status == 0) { + int errno_copy = THRIFT_GET_SOCKET_ERROR; + string errors; + buildErrors(errors, errno_copy); + throw TSSLException("SSL_CTX_use_enc_PrivateKey: " + errors); + } +#endif +} + void TSSLSocketFactory::loadTrustedCertificates(const char* path, const char* capath) { if (path == nullptr) { throw TTransportException(TTransportException::BAD_ARGS, diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.h b/lib/cpp/src/thrift/transport/TSSLSocket.h index e6b8992451a..04d50599dbc 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.h +++ b/lib/cpp/src/thrift/transport/TSSLSocket.h @@ -43,6 +43,7 @@ enum SSLProtocol { TLSv1_0 = 3, // Supports TLSv1_0 or later. TLSv1_1 = 4, // Supports TLSv1_1 or later. TLSv1_2 = 5, // Supports TLSv1_2 or later. + NTLS = 6, // Tongsuo NTLS/TLCP; requires dual signing and encryption certificates. LATEST = TLSv1_2 }; @@ -281,6 +282,17 @@ class TSSLSocketFactory { */ virtual void loadPrivateKey(const char* path, const char* format = "PEM"); virtual void loadPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); + /** + * Load NTLS signing or encryption certificate/key (Tongsuo only). + */ + virtual void loadSignCertificate(const char* path, const char* format = "PEM"); + virtual void loadSignCertificateFromBuffer(const char* aCertificate, const char* format = "PEM"); + virtual void loadSignPrivateKey(const char* path, const char* format = "PEM"); + virtual void loadSignPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); + virtual void loadEncCertificate(const char* path, const char* format = "PEM"); + virtual void loadEncCertificateFromBuffer(const char* aCertificate, const char* format = "PEM"); + virtual void loadEncPrivateKey(const char* path, const char* format = "PEM"); + virtual void loadEncPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); /** * Load trusted certificates from specified file. * diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index f015bfdb9be..9a006701c37 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -371,6 +371,25 @@ if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTE endif () add_test(NAME SecurityFromBufferTest COMMAND SecurityFromBufferTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys") +if(THRIFT_HAVE_NTLS) + add_executable(TNTLSSocketTest TNTLSSocketTest.cpp) + target_link_libraries(TNTLSSocketTest + ${OPENSSL_LIBRARIES} + ${Boost_LIBRARIES} + thrift + ) + set(TNTLSSocketTest_ENV "THRIFT_NTLS_CERT_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys/ntls") + if(TONGSUO_ROOT_DIR) + list(APPEND TNTLSSocketTest_ENV + "LD_LIBRARY_PATH=${TONGSUO_ROOT_DIR}/lib64:${TONGSUO_ROOT_DIR}/lib") + set_target_properties(TNTLSSocketTest PROPERTIES + BUILD_RPATH "${TONGSUO_ROOT_DIR}/lib64;${TONGSUO_ROOT_DIR}/lib" + ) + endif() + add_test(NAME TNTLSSocketTest + COMMAND ${CMAKE_COMMAND} -E env ${TNTLSSocketTest_ENV} $) +endif() + endif() if(WITH_QT5) diff --git a/lib/cpp/test/TNTLSSocketTest.cpp b/lib/cpp/test/TNTLSSocketTest.cpp new file mode 100644 index 00000000000..2e2a60d05d4 --- /dev/null +++ b/lib/cpp/test/TNTLSSocketTest.cpp @@ -0,0 +1,213 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#define BOOST_TEST_MODULE TNTLSSocketTest +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SIGNAL_H +#include +#endif + +using apache::thrift::transport::AccessManager; +using apache::thrift::transport::Decision; +using apache::thrift::transport::NTLS; +using apache::thrift::transport::TSSLServerSocket; +using apache::thrift::transport::TSSLSocket; +using apache::thrift::transport::TSSLSocketFactory; +using apache::thrift::transport::TTransport; +using std::shared_ptr; +using std::string; + +namespace { + +const char* kNtlsCipher = "ECC-SM2-WITH-SM4-SM3"; + +boost::filesystem::path keyDir; + +boost::filesystem::path certFile(const string& filename) { + return keyDir / filename; +} + +string readFile(const boost::filesystem::path& path) { + std::ifstream input(path.string().c_str(), std::ios::binary); + BOOST_REQUIRE_MESSAGE(input.good(), "Unable to read " + path.string()); + std::ostringstream contents; + contents << input.rdbuf(); + return contents.str(); +} + +class AllowAllAccessManager : public AccessManager { +public: + Decision verify(const sockaddr_storage&) noexcept override { return ALLOW; } + Decision verify(const string&, const char*, int) noexcept override { return ALLOW; } + Decision verify(const sockaddr_storage&, const char*, int) noexcept override { return ALLOW; } +}; + +struct GlobalFixtureNtls { + GlobalFixtureNtls() { +#ifdef __linux__ + signal(SIGPIPE, SIG_IGN); +#endif + + TSSLSocketFactory::setManualOpenSSLInitialization(true); + apache::thrift::transport::initializeOpenSSL(); + + const char* envDir = std::getenv("THRIFT_NTLS_CERT_DIR"); + if (envDir != nullptr && *envDir != '\0') { + keyDir = boost::filesystem::path(envDir); + } + if (!boost::filesystem::exists(certFile("server_sign.crt"))) { + keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys" / "ntls"; + } + if (!boost::filesystem::exists(certFile("server_sign.crt"))) { + keyDir = boost::filesystem::path( + boost::unit_test::framework::master_test_suite().argv[ + boost::unit_test::framework::master_test_suite().argc - 1]); + } + if (!boost::filesystem::exists(certFile("server_sign.crt"))) { + throw std::invalid_argument( + "The last argument to this test must be the directory containing the NTLS test certificate(s)."); + } + } + + ~GlobalFixtureNtls() { + apache::thrift::transport::cleanupOpenSSL(); +#ifdef __linux__ + signal(SIGPIPE, SIG_DFL); +#endif + } +}; + +#if (BOOST_VERSION >= 105900) +BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls); +#else +BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls) +#endif + +void loadServerDualCerts(const shared_ptr& factory) { + factory->loadSignCertificate(certFile("server_sign.crt").string().c_str()); + factory->loadSignPrivateKey(certFile("server_sign.key").string().c_str()); + factory->loadEncCertificate(certFile("server_enc.crt").string().c_str()); + factory->loadEncPrivateKey(certFile("server_enc.key").string().c_str()); +} + +void loadClientDualCerts(const shared_ptr& factory) { + factory->loadSignCertificate(certFile("client_sign.crt").string().c_str()); + factory->loadSignPrivateKey(certFile("client_sign.key").string().c_str()); + factory->loadEncCertificate(certFile("client_enc.crt").string().c_str()); + factory->loadEncPrivateKey(certFile("client_enc.key").string().c_str()); +} + +shared_ptr createServerFactory(bool requireClientAuth) { + shared_ptr factory(new TSSLSocketFactory(NTLS)); + factory->ciphers(kNtlsCipher); + loadServerDualCerts(factory); + factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); + factory->authenticate(requireClientAuth); + if (requireClientAuth) { + factory->access(shared_ptr(new AllowAllAccessManager())); + } + factory->server(true); + return factory; +} + +shared_ptr createClientFactory(bool authenticatePeer) { + shared_ptr factory(new TSSLSocketFactory(NTLS)); + factory->ciphers(kNtlsCipher); + loadClientDualCerts(factory); + factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); + factory->authenticate(authenticatePeer); + factory->access(shared_ptr(new AllowAllAccessManager())); + return factory; +} + +shared_ptr createServerFactoryFromBuffer(bool requireClientAuth) { + shared_ptr factory(new TSSLSocketFactory(NTLS)); + factory->ciphers(kNtlsCipher); + factory->loadSignCertificateFromBuffer(readFile(certFile("server_sign.crt")).c_str()); + factory->loadSignPrivateKeyFromBuffer(readFile(certFile("server_sign.key")).c_str()); + factory->loadEncCertificateFromBuffer(readFile(certFile("server_enc.crt")).c_str()); + factory->loadEncPrivateKeyFromBuffer(readFile(certFile("server_enc.key")).c_str()); + factory->loadTrustedCertificatesFromBuffer(readFile(certFile("chain-ca.crt")).c_str()); + factory->authenticate(requireClientAuth); + if (requireClientAuth) { + factory->access(shared_ptr(new AllowAllAccessManager())); + } + factory->server(true); + return factory; +} + +void echoOnce(shared_ptr transport) { + uint8_t request[5] = {'h', 'e', 'l', 'l', 'o'}; + uint8_t response[5] = {}; + transport->write(request, 5); + transport->flush(); + uint32_t received = transport->read(response, 5); + BOOST_CHECK_EQUAL(received, 5U); + BOOST_CHECK_EQUAL_COLLECTIONS(request, request + 5, response, response + 5); +} + +void runEchoTest(const shared_ptr& serverFactory, + const shared_ptr& clientFactory) { + TSSLServerSocket server("127.0.0.1", 0, serverFactory); + server.listen(); + const int port = server.getPort(); + + bool serverFailed = false; + boost::thread serverThread([&]() { + try { + echoOnce(server.accept()); + } catch (...) { + serverFailed = true; + } + }); + + shared_ptr client = clientFactory->createSocket("127.0.0.1", port); + client->open(); + echoOnce(client); + client->close(); + serverThread.join(); + server.close(); + BOOST_CHECK(!serverFailed); +} + +} // namespace + +BOOST_AUTO_TEST_SUITE(TNTLSSocketTest) + +BOOST_AUTO_TEST_CASE(test_ntls_handshake_and_echo) { + runEchoTest(createServerFactory(false), createClientFactory(false)); +} + +BOOST_AUTO_TEST_CASE(test_ntls_mutual_auth_echo) { + runEchoTest(createServerFactory(true), createClientFactory(true)); +} + +BOOST_AUTO_TEST_CASE(test_ntls_dual_cert_from_buffer) { + runEchoTest(createServerFactoryFromBuffer(false), createClientFactory(false)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/keys/ntls/chain-ca.crt b/test/keys/ntls/chain-ca.crt new file mode 100644 index 00000000000..a46055e2e71 --- /dev/null +++ b/test/keys/ntls/chain-ca.crt @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIB4DCCAYagAwIBAgIBADAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG +A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v +dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRjELMAkGA1UE +BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEDAO +BgNVBAMMB3Jvb3QgY2EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASN55Ju2pvU +Bi8UrWHc4ZaKnsqiFPWfcM/6H2Gu/VQ7I1oVnyPktvlTrtwhSy6K43JoCnjVPHrq +jOXxnkOtGVDVo2MwYTAdBgNVHQ4EFgQUxu7mMmVaB3vq7JRi8UEFHcxVFY4wHwYD +VR0jBBgwFoAUxu7mMmVaB3vq7JRi8UEFHcxVFY4wDwYDVR0TAQH/BAUwAwEB/zAO +BgNVHQ8BAf8EBAMCAYYwCgYIKoEcz1UBg3UDSAAwRQIhAIz7tgrp7LmOQEJGPAU3 +8m9PNzMOTqGWZqux8CxIuEGjAiB4cFVYQ4sTCYb/4fNayKYO1FH+Q2Cc7xGq7WPd +knwWpw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIB4zCCAYigAwIBAgIBATAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG +A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v +dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRTELMAkGA1UE +BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxDzAN +BgNVBAMMBnN1YiBjYTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABH0feWwae0S0 +w4QQA5cBGYwaQPaxZFcLzIqph+I6BQQCGXaIAabqpO0zjAyf1twYmoM3ZRLJgbZz +HE/2rRMPBiajZjBkMB0GA1UdDgQWBBSsYesigGJZCD6WyNF/znRcAq88mTAfBgNV +HSMEGDAWgBTG7uYyZVoHe+rslGLxQQUdzFUVjjASBgNVHRMBAf8ECDAGAQH/AgEA +MA4GA1UdDwEB/wQEAwIBhjAKBggqgRzPVQGDdQNJADBGAiEApoHDue1bzGukE97O +BqQbboU1d3jqNg4gAgpMe5fFIosCIQDwndSp7Tc3DZ0QCifXKNqgykjepsWTPZ3R +NrMzM0rflg== +-----END CERTIFICATE----- diff --git a/test/keys/ntls/client_enc.crt b/test/keys/ntls/client_enc.crt new file mode 100644 index 00000000000..d42effc29ee --- /dev/null +++ b/test/keys/ntls/client_enc.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7TCCAZKgAwIBAgIUWy6/ole1R8GwTrzoOtZebFla3aowCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y13MXG1Zsx+NiEZ7Bb +OFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncMo1owWDAJBgNVHRMEAjAAMAsG +A1UdDwQEAwIDODAdBgNVHQ4EFgQUfPE8T3aPRzOi/+LiWTRrkM+0dKgwHwYDVR0j +BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDSQAwRgIhAOIT +GEUHnILUpLCbSZCyG8TigYmbg7ImyZFtXF/uEhOfAiEA59PnEVYaegvpI5Ltn5T2 +PKKqiZ2QOWEfRHJIi/FFZeo= +-----END CERTIFICATE----- diff --git a/test/keys/ntls/client_enc.key b/test/keys/ntls/client_enc.key new file mode 100644 index 00000000000..86aa5b3d809 --- /dev/null +++ b/test/keys/ntls/client_enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQguz0M4/6qUhgHAxuG +WI2NPtNciIwmYAN4AUDoBEka1tehRANCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y1 +3MXG1Zsx+NiEZ7BbOFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncM +-----END PRIVATE KEY----- diff --git a/test/keys/ntls/client_sign.crt b/test/keys/ntls/client_sign.crt new file mode 100644 index 00000000000..b4f335f7a85 --- /dev/null +++ b/test/keys/ntls/client_sign.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7jCCAZOgAwIBAgIUbEstbqUWeJMWK3rlwLXE9YdtJ8QwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0C +AQYIKoEcz1UBgi0DQgAELWF+dNVYbd4j0kkPvUaOMPEdAS1QqPOSzRhJsQsWfpoG +YffjoqAO5+xHGO2Te0qyxQqg00HRXkCVdDs4UK9tPKNaMFgwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFFRpNXBAPyjFwT7w8EBNJLMVaFgdMB8GA1Ud +IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQDH +LBaKDJFhHYRNLhYXFBtZH6BIa6cJfewLyVaH0oDaMgIhAMgZQDmGvHCzJ9vdgL2P +7upTf3I28uj+3pq7ZiwKRBlO +-----END CERTIFICATE----- diff --git a/test/keys/ntls/client_sign.key b/test/keys/ntls/client_sign.key new file mode 100644 index 00000000000..d4b4ecdbcef --- /dev/null +++ b/test/keys/ntls/client_sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5zLNmBvXdesfATxu +gMMpvEHfuxhUCr6L8rS2EEBr6SGhRANCAAQtYX501Vht3iPSSQ+9Ro4w8R0BLVCo +85LNGEmxCxZ+mgZh9+OioA7n7EcY7ZN7SrLFCqDTQdFeQJV0OzhQr208 +-----END PRIVATE KEY----- diff --git a/test/keys/ntls/server_enc.crt b/test/keys/ntls/server_enc.crt new file mode 100644 index 00000000000..1d9ac5e5b93 --- /dev/null +++ b/test/keys/ntls/server_enc.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB6zCCAZKgAwIBAgIUaNiS6WOsoEViDnmdb8Mdk3Qz5XwwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIB +BggqgRzPVQGCLQNCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY7JQUmflKUKWMZ11v +mtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNko1owWDAJBgNVHRMEAjAAMAsG +A1UdDwQEAwIDODAdBgNVHQ4EFgQUZ6Wt1ZR24FqcXla4hg/xOyju7FQwHwYDVR0j +BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDRwAwRAIgR1k1 +ecSt7I2335jEquFmHBE5pe8Sk/IqOqQS0Jvs1uYCIG5XMB0XeUaVb9OctaxgOQLN +F8dRftiUHsyYXqfbaVjI +-----END CERTIFICATE----- diff --git a/test/keys/ntls/server_enc.key b/test/keys/ntls/server_enc.key new file mode 100644 index 00000000000..b3e6dc511e9 --- /dev/null +++ b/test/keys/ntls/server_enc.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgLrRk3CWTe+WZOFSf +TMYwbOocLs3MSRpOO0/AvSmvH5mhRANCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY +7JQUmflKUKWMZ11vmtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNk +-----END PRIVATE KEY----- diff --git a/test/keys/ntls/server_sign.crt b/test/keys/ntls/server_sign.crt new file mode 100644 index 00000000000..ac7f633b178 --- /dev/null +++ b/test/keys/ntls/server_sign.crt @@ -0,0 +1,13 @@ +-----BEGIN CERTIFICATE----- +MIIB7jCCAZOgAwIBAgIUcbKTlc6+CNoHglmEk+xm+WIqZcAwCgYIKoEcz1UBg3Uw +RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE +CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy +OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD +QzELMAkGA1UECwwCREQxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0C +AQYIKoEcz1UBgi0DQgAEBb/67sQGyPP1gKbjnFKEdsDfK2EGXUp09HavD7ZokPiW +rMSyHYsDbRPxe9TTgjSQi+23f44+rocGVPxvqASNDKNaMFgwCQYDVR0TBAIwADAL +BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFH3uBqkdowIvk//P7n5UtnpV9TR6MB8GA1Ud +IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQCz +W/6Z/d/IJUTrO0o8nCxNle6R0AkRCKUFhW9zbIRlNwIhAJZxg4gs2cV2QF37oHs6 +9TD+MkRbql4Yb47+jLf8f247 +-----END CERTIFICATE----- diff --git a/test/keys/ntls/server_sign.key b/test/keys/ntls/server_sign.key new file mode 100644 index 00000000000..acec32681da --- /dev/null +++ b/test/keys/ntls/server_sign.key @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgeQTrKtO8mNXn/yvg +R+pdbCgH5sl+WCFfXcqGl64soU2hRANCAAQFv/ruxAbI8/WApuOcUoR2wN8rYQZd +SnT0dq8PtmiQ+JasxLIdiwNtE/F71NOCNJCL7bd/jj6uhwZU/G+oBI0M +-----END PRIVATE KEY----- From 10c5ba88392ae357b34f418fc1241f7490b5ab10 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 30 Jun 2026 15:33:38 +0800 Subject: [PATCH 2/5] Reshape C++ TLS support to backend-neutral SSLContext injection. Replace Tongsuo-specific build options, NTLS protocol enum, and dual-certificate factory APIs with SSLContext(SSL_CTX*, bool takeOwnership) so applications can inject a pre-configured OpenSSL-compatible context through SSLContextFactory. --- build/cmake/ConfigureChecks.cmake | 1 + build/cmake/DefineOptions.cmake | 35 +-- build/cmake/FindTongsuo.cmake | 80 ------- build/cmake/config.h.in | 2 +- lib/cpp/README.md | 7 +- lib/cpp/src/thrift/transport/TSSLSocket.cpp | 206 +----------------- lib/cpp/src/thrift/transport/TSSLSocket.h | 20 +- lib/cpp/test/CMakeLists.txt | 18 +- lib/cpp/test/SecurityTest.cpp | 29 +++ ...etTest.cpp => TNTLSInjectedSocketTest.cpp} | 106 +++++---- test/keys/ntls/chain-ca.crt | 26 --- test/keys/ntls/client_enc.crt | 13 -- test/keys/ntls/client_enc.key | 5 - test/keys/ntls/client_sign.crt | 13 -- test/keys/ntls/client_sign.key | 5 - test/keys/ntls/server_enc.crt | 13 -- test/keys/ntls/server_enc.key | 5 - test/keys/ntls/server_sign.crt | 13 -- test/keys/ntls/server_sign.key | 5 - 19 files changed, 112 insertions(+), 490 deletions(-) delete mode 100644 build/cmake/FindTongsuo.cmake rename lib/cpp/test/{TNTLSSocketTest.cpp => TNTLSInjectedSocketTest.cpp} (62%) delete mode 100644 test/keys/ntls/chain-ca.crt delete mode 100644 test/keys/ntls/client_enc.crt delete mode 100644 test/keys/ntls/client_enc.key delete mode 100644 test/keys/ntls/client_sign.crt delete mode 100644 test/keys/ntls/client_sign.key delete mode 100644 test/keys/ntls/server_enc.crt delete mode 100644 test/keys/ntls/server_enc.key delete mode 100644 test/keys/ntls/server_sign.crt delete mode 100644 test/keys/ntls/server_sign.key diff --git a/build/cmake/ConfigureChecks.cmake b/build/cmake/ConfigureChecks.cmake index 1151ca8b729..c757edd8e2b 100644 --- a/build/cmake/ConfigureChecks.cmake +++ b/build/cmake/ConfigureChecks.cmake @@ -87,6 +87,7 @@ set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(VERSION ${thrift_VERSION}) if(WITH_OPENSSL AND OPENSSL_FOUND) + unset(THRIFT_HAVE_NTLS CACHE) set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") if(TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto) set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) diff --git a/build/cmake/DefineOptions.cmake b/build/cmake/DefineOptions.cmake index 67ada4830da..7c1dddb5e61 100644 --- a/build/cmake/DefineOptions.cmake +++ b/build/cmake/DefineOptions.cmake @@ -78,33 +78,7 @@ CMAKE_DEPENDENT_OPTION(BUILD_C_GLIB "Build C (GLib) library" ON # OpenSSL if(WITH_CPP OR WITH_C_GLIB) - option(WITH_TONGSUO "Build with Tongsuo instead of OpenSSL" OFF) - set(TONGSUO_ROOT_DIR "" CACHE PATH "Root directory of a Tongsuo installation") - - if(WITH_TONGSUO) - find_package(Tongsuo REQUIRED) - set(OPENSSL_FOUND TRUE) - set(OPENSSL_INCLUDE_DIR "${TONGSUO_INCLUDE_DIR}") - set(OPENSSL_SSL_LIBRARY "${TONGSUO_SSL_LIBRARY}") - set(OPENSSL_CRYPTO_LIBRARY "${TONGSUO_CRYPTO_LIBRARY}") - set(OPENSSL_LIBRARIES "${TONGSUO_SSL_LIBRARY}" "${TONGSUO_CRYPTO_LIBRARY}") - if(NOT TARGET OpenSSL::SSL) - add_library(OpenSSL::SSL UNKNOWN IMPORTED) - set_target_properties(OpenSSL::SSL PROPERTIES - IMPORTED_LOCATION "${TONGSUO_SSL_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" - ) - endif() - if(NOT TARGET OpenSSL::Crypto) - add_library(OpenSSL::Crypto UNKNOWN IMPORTED) - set_target_properties(OpenSSL::Crypto PROPERTIES - IMPORTED_LOCATION "${TONGSUO_CRYPTO_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" - ) - endif() - else() - find_package(OpenSSL) - endif() + find_package(OpenSSL) CMAKE_DEPENDENT_OPTION(WITH_OPENSSL "Build with OpenSSL support" ON "OPENSSL_FOUND" OFF) endif() @@ -190,12 +164,7 @@ message(STATUS "Language libraries:") message(STATUS) message(STATUS " Build with OpenSSL: ${WITH_OPENSSL}") if(WITH_OPENSSL) - if(WITH_TONGSUO) - message(STATUS " Backend: Tongsuo") - message(STATUS " Root: ${TONGSUO_ROOT_DIR}") - else() - message(STATUS " Version: ${OPENSSL_VERSION}") - endif() + message(STATUS " Version: ${OPENSSL_VERSION}") endif() message(STATUS) message(STATUS " Build C++ library: ${BUILD_CPP}") diff --git a/build/cmake/FindTongsuo.cmake b/build/cmake/FindTongsuo.cmake deleted file mode 100644 index c678b73731c..00000000000 --- a/build/cmake/FindTongsuo.cmake +++ /dev/null @@ -1,80 +0,0 @@ -# -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# - -# Find Tongsuo (OpenSSL-compatible TLS library) -# -# TONGSUO_INCLUDE_DIR, TONGSUO_SSL_LIBRARY, TONGSUO_CRYPTO_LIBRARY -# Tongsuo_FOUND - -include(FindPackageHandleStandardArgs) - -find_path(TONGSUO_INCLUDE_DIR - NAMES openssl/ssl.h - HINTS - ${TONGSUO_ROOT_DIR} - ENV TONGSUO_ROOT - PATH_SUFFIXES include -) - -find_library(TONGSUO_SSL_LIBRARY - NAMES ssl libssl - HINTS - ${TONGSUO_ROOT_DIR} - ENV TONGSUO_ROOT - PATH_SUFFIXES lib lib64 -) - -find_library(TONGSUO_CRYPTO_LIBRARY - NAMES crypto libcrypto - HINTS - ${TONGSUO_ROOT_DIR} - ENV TONGSUO_ROOT - PATH_SUFFIXES lib lib64 -) - -find_package_handle_standard_args(Tongsuo - REQUIRED_VARS - TONGSUO_INCLUDE_DIR - TONGSUO_SSL_LIBRARY - TONGSUO_CRYPTO_LIBRARY -) - -if(TONGSUO_FOUND) - if(NOT TARGET Tongsuo::SSL) - add_library(Tongsuo::SSL UNKNOWN IMPORTED) - set_target_properties(Tongsuo::SSL PROPERTIES - IMPORTED_LOCATION "${TONGSUO_SSL_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" - ) - endif() - - if(NOT TARGET Tongsuo::Crypto) - add_library(Tongsuo::Crypto UNKNOWN IMPORTED) - set_target_properties(Tongsuo::Crypto PROPERTIES - IMPORTED_LOCATION "${TONGSUO_CRYPTO_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${TONGSUO_INCLUDE_DIR}" - ) - endif() -endif() - -mark_as_advanced( - TONGSUO_INCLUDE_DIR - TONGSUO_SSL_LIBRARY - TONGSUO_CRYPTO_LIBRARY -) diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index 4bb060d6253..62103e55ff7 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -157,7 +157,7 @@ /* Define to 1 if strerror_r returns char *. */ #cmakedefine STRERROR_R_CHAR_P 1 -/* Define to 1 if NTLS support is available. */ +/* Define to 1 if the linked TLS library provides NTLS (optional local test). */ #cmakedefine THRIFT_HAVE_NTLS 1 #endif diff --git a/lib/cpp/README.md b/lib/cpp/README.md index a9959af8c21..f9e9167ebbc 100644 --- a/lib/cpp/README.md +++ b/lib/cpp/README.md @@ -138,7 +138,12 @@ TSSLSocket are always created from TSSLSocketFactory. The default TSSLSocketFactory context uses OpenSSL's version-flexible TLS method and sets TLS 1.2 as the minimum negotiated protocol version. Applications that need a different protocol range can provide a custom SSLContext factory and -adjust the OpenSSL context options before creating sockets. +adjust the OpenSSL context options before creating sockets. Applications that +link against an OpenSSL-compatible TLS library can also create and configure an +SSL_CTX externally, wrap it with SSLContext, and pass it through the factory. +For example, NTLS/TLCP (Tongsuo) dual-certificate setup is done on the external +SSL_CTX before wrapping; see `TNTLSInjectedSocketTest.cpp` when built against a +TLS library with NTLS support. ## How to use SSL APIs diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.cpp b/lib/cpp/src/thrift/transport/TSSLSocket.cpp index 260edffbdc4..ac0b85945fc 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.cpp +++ b/lib/cpp/src/thrift/transport/TSSLSocket.cpp @@ -178,14 +178,8 @@ static void buildErrors(string& message, int errno_copy = 0, int sslerrno = 0); static bool matchName(const char* host, const char* pattern, int size); static char uppercase(char c); -static void requireNtlsSupport() { -#ifndef THRIFT_HAVE_NTLS - throw TSSLException("NTLS is not available in this build"); -#endif -} - // SSLContext implementation -SSLContext::SSLContext(const SSLProtocol& protocol) { +SSLContext::SSLContext(const SSLProtocol& protocol) : takeOwnership_(true) { if (protocol == SSLTLS) { ctx_ = SSL_CTX_new(SSLv23_method()); #ifndef OPENSSL_NO_SSL3 @@ -198,10 +192,6 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { ctx_ = SSL_CTX_new(TLSv1_1_method()); } else if (protocol == TLSv1_2) { ctx_ = SSL_CTX_new(TLSv1_2_method()); -#ifdef THRIFT_HAVE_NTLS - } else if (protocol == NTLS) { - ctx_ = SSL_CTX_new(NTLS_method()); -#endif } else { /// UNKNOWN PROTOCOL! throw TSSLException("SSL_CTX_new: Unknown protocol"); @@ -212,11 +202,6 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { buildErrors(errors); throw TSSLException("SSL_CTX_new: " + errors); } -#ifdef THRIFT_HAVE_NTLS - if (protocol == NTLS) { - SSL_CTX_enable_ntls(ctx_); - } -#endif SSL_CTX_set_mode(ctx_, SSL_MODE_AUTO_RETRY); // Keep version-flexible negotiation for current protocol versions while setting @@ -228,8 +213,15 @@ SSLContext::SSLContext(const SSLProtocol& protocol) { } } +SSLContext::SSLContext(SSL_CTX* ctx, bool takeOwnership) + : ctx_(ctx), takeOwnership_(takeOwnership) { + if (ctx_ == nullptr) { + throw TSSLException("SSLContext: ctx must not be null"); + } +} + SSLContext::~SSLContext() { - if (ctx_ != nullptr) { + if (ctx_ != nullptr && takeOwnership_) { SSL_CTX_free(ctx_); ctx_ = nullptr; } @@ -1107,186 +1099,6 @@ void TSSLSocketFactory::loadPrivateKeyFromBuffer(const char* aPrivateKey, const } } -void TSSLSocketFactory::loadSignCertificate(const char* path, const char* format) { - requireNtlsSupport(); - if (path == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadSignCertificate: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") == 0) { - if (SSL_CTX_use_sign_certificate_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_sign_certificate_file: " + errors); - } - } else { - throw TSSLException("Unsupported certificate format: " + string(format)); - } -#endif -} - -void TSSLSocketFactory::loadSignCertificateFromBuffer(const char* aCertificate, const char* format) { - requireNtlsSupport(); - if (aCertificate == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadSignCertificateFromBuffer: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") != 0) { - throw TSSLException("Unsupported certificate format: " + string(format)); - } - BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts(mem, aCertificate); - X509* cert = PEM_read_bio_X509(mem, nullptr, nullptr, nullptr); - BIO_free(mem); - const int status = SSL_CTX_use_sign_certificate(ctx_->get(), cert); - X509_free(cert); - if (status != 1) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_sign_certificate: " + errors); - } -#endif -} - -void TSSLSocketFactory::loadSignPrivateKey(const char* path, const char* format) { - requireNtlsSupport(); - if (path == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadSignPrivateKey: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") == 0) { - if (SSL_CTX_use_sign_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_sign_PrivateKey_file: " + errors); - } - } else { - throw TSSLException("Unsupported certificate format: " + string(format)); - } -#endif -} - -void TSSLSocketFactory::loadSignPrivateKeyFromBuffer(const char* aPrivateKey, const char* format) { - requireNtlsSupport(); - if (aPrivateKey == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadSignPrivateKeyFromBuffer: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") != 0) { - throw TSSLException("Unsupported certificate format: " + string(format)); - } - BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts(mem, aPrivateKey); - EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr); - BIO_free(mem); - const int status = SSL_CTX_use_sign_PrivateKey(ctx_->get(), key); - EVP_PKEY_free(key); - if (status == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_sign_PrivateKey: " + errors); - } -#endif -} - -void TSSLSocketFactory::loadEncCertificate(const char* path, const char* format) { - requireNtlsSupport(); - if (path == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadEncCertificate: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") == 0) { - if (SSL_CTX_use_enc_certificate_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_enc_certificate_file: " + errors); - } - } else { - throw TSSLException("Unsupported certificate format: " + string(format)); - } -#endif -} - -void TSSLSocketFactory::loadEncCertificateFromBuffer(const char* aCertificate, const char* format) { - requireNtlsSupport(); - if (aCertificate == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadEncCertificateFromBuffer: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") != 0) { - throw TSSLException("Unsupported certificate format: " + string(format)); - } - BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts(mem, aCertificate); - X509* cert = PEM_read_bio_X509(mem, nullptr, nullptr, nullptr); - BIO_free(mem); - const int status = SSL_CTX_use_enc_certificate(ctx_->get(), cert); - X509_free(cert); - if (status != 1) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_enc_certificate: " + errors); - } -#endif -} - -void TSSLSocketFactory::loadEncPrivateKey(const char* path, const char* format) { - requireNtlsSupport(); - if (path == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadEncPrivateKey: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") == 0) { - if (SSL_CTX_use_enc_PrivateKey_file(ctx_->get(), path, SSL_FILETYPE_PEM) == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_enc_PrivateKey_file: " + errors); - } - } else { - throw TSSLException("Unsupported certificate format: " + string(format)); - } -#endif -} - -void TSSLSocketFactory::loadEncPrivateKeyFromBuffer(const char* aPrivateKey, const char* format) { - requireNtlsSupport(); - if (aPrivateKey == nullptr || format == nullptr) { - throw TTransportException(TTransportException::BAD_ARGS, - "loadEncPrivateKeyFromBuffer: either or is nullptr"); - } -#ifdef THRIFT_HAVE_NTLS - if (strcmp(format, "PEM") != 0) { - throw TSSLException("Unsupported certificate format: " + string(format)); - } - BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts(mem, aPrivateKey); - EVP_PKEY* key = PEM_read_bio_PrivateKey(mem, nullptr, nullptr, nullptr); - BIO_free(mem); - const int status = SSL_CTX_use_enc_PrivateKey(ctx_->get(), key); - EVP_PKEY_free(key); - if (status == 0) { - int errno_copy = THRIFT_GET_SOCKET_ERROR; - string errors; - buildErrors(errors, errno_copy); - throw TSSLException("SSL_CTX_use_enc_PrivateKey: " + errors); - } -#endif -} - void TSSLSocketFactory::loadTrustedCertificates(const char* path, const char* capath) { if (path == nullptr) { throw TTransportException(TTransportException::BAD_ARGS, diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.h b/lib/cpp/src/thrift/transport/TSSLSocket.h index 04d50599dbc..2485bcdce09 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.h +++ b/lib/cpp/src/thrift/transport/TSSLSocket.h @@ -43,7 +43,6 @@ enum SSLProtocol { TLSv1_0 = 3, // Supports TLSv1_0 or later. TLSv1_1 = 4, // Supports TLSv1_1 or later. TLSv1_2 = 5, // Supports TLSv1_2 or later. - NTLS = 6, // Tongsuo NTLS/TLCP; requires dual signing and encryption certificates. LATEST = TLSv1_2 }; @@ -282,17 +281,6 @@ class TSSLSocketFactory { */ virtual void loadPrivateKey(const char* path, const char* format = "PEM"); virtual void loadPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); - /** - * Load NTLS signing or encryption certificate/key (Tongsuo only). - */ - virtual void loadSignCertificate(const char* path, const char* format = "PEM"); - virtual void loadSignCertificateFromBuffer(const char* aCertificate, const char* format = "PEM"); - virtual void loadSignPrivateKey(const char* path, const char* format = "PEM"); - virtual void loadSignPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); - virtual void loadEncCertificate(const char* path, const char* format = "PEM"); - virtual void loadEncCertificateFromBuffer(const char* aCertificate, const char* format = "PEM"); - virtual void loadEncPrivateKey(const char* path, const char* format = "PEM"); - virtual void loadEncPrivateKeyFromBuffer(const char* aPrivateKey, const char* format = "PEM"); /** * Load trusted certificates from specified file. * @@ -377,12 +365,20 @@ class TSSLException : public TTransportException { class SSLContext { public: SSLContext(const SSLProtocol& protocol = SSLTLS); + /** + * Wrap an existing OpenSSL SSL_CTX. + * + * @param ctx OpenSSL context to wrap + * @param takeOwnership If true (default), SSLContext frees ctx on destruction + */ + SSLContext(SSL_CTX* ctx, bool takeOwnership = true); virtual ~SSLContext(); SSL* createSSL(); SSL_CTX* get() { return ctx_; } private: SSL_CTX* ctx_; + bool takeOwnership_; }; /** diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index 9a006701c37..7de43b133a5 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -372,22 +372,16 @@ endif () add_test(NAME SecurityFromBufferTest COMMAND SecurityFromBufferTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys") if(THRIFT_HAVE_NTLS) - add_executable(TNTLSSocketTest TNTLSSocketTest.cpp) - target_link_libraries(TNTLSSocketTest + add_executable(TNTLSInjectedSocketTest TNTLSInjectedSocketTest.cpp) + target_link_libraries(TNTLSInjectedSocketTest ${OPENSSL_LIBRARIES} ${Boost_LIBRARIES} thrift ) - set(TNTLSSocketTest_ENV "THRIFT_NTLS_CERT_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys/ntls") - if(TONGSUO_ROOT_DIR) - list(APPEND TNTLSSocketTest_ENV - "LD_LIBRARY_PATH=${TONGSUO_ROOT_DIR}/lib64:${TONGSUO_ROOT_DIR}/lib") - set_target_properties(TNTLSSocketTest PROPERTIES - BUILD_RPATH "${TONGSUO_ROOT_DIR}/lib64;${TONGSUO_ROOT_DIR}/lib" - ) - endif() - add_test(NAME TNTLSSocketTest - COMMAND ${CMAKE_COMMAND} -E env ${TNTLSSocketTest_ENV} $) + set(TNTLSInjectedSocketTest_ENV + "THRIFT_NTLS_CERT_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../../../../Tongsuo/test/certs/sm2") + add_test(NAME TNTLSInjectedSocketTest + COMMAND ${CMAKE_COMMAND} -E env ${TNTLSInjectedSocketTest_ENV} $) endif() endif() diff --git a/lib/cpp/test/SecurityTest.cpp b/lib/cpp/test/SecurityTest.cpp index d64b0da8449..3fb1f18be36 100644 --- a/lib/cpp/test/SecurityTest.cpp +++ b/lib/cpp/test/SecurityTest.cpp @@ -34,6 +34,7 @@ #endif using apache::thrift::transport::TSSLServerSocket; +using apache::thrift::transport::SSLContext; using apache::thrift::transport::SSLContextFactory; using apache::thrift::transport::TSSLException; using apache::thrift::transport::TServerTransport; @@ -274,6 +275,34 @@ BOOST_AUTO_TEST_CASE(custom_ssl_context_options) context.reset(); } +BOOST_AUTO_TEST_CASE(wrapped_ssl_context) +{ + SSL_CTX* raw = SSL_CTX_new(TLS_method()); + BOOST_REQUIRE(raw != nullptr); + SSL_CTX_set_mode(raw, SSL_MODE_AUTO_RETRY); + + std::shared_ptr context; + TSSLSocketFactory factory([&context, raw]() { + context = std::make_shared(raw, true); + return context; + }); + BOOST_CHECK(context->get() == raw); + context.reset(); +} + +BOOST_AUTO_TEST_CASE(wrapped_ssl_context_null) +{ + try + { + std::make_shared(nullptr); + BOOST_FAIL("Expected null SSL_CTX to throw"); + } + catch (const TSSLException& ex) + { + BOOST_CHECK_EQUAL("SSLContext: ctx must not be null", std::string(ex.what())); + } +} + BOOST_AUTO_TEST_CASE(custom_ssl_context_factory_validation) { try diff --git a/lib/cpp/test/TNTLSSocketTest.cpp b/lib/cpp/test/TNTLSInjectedSocketTest.cpp similarity index 62% rename from lib/cpp/test/TNTLSSocketTest.cpp rename to lib/cpp/test/TNTLSInjectedSocketTest.cpp index 2e2a60d05d4..7b2426445c6 100644 --- a/lib/cpp/test/TNTLSSocketTest.cpp +++ b/lib/cpp/test/TNTLSInjectedSocketTest.cpp @@ -17,14 +17,13 @@ * under the License. */ -#define BOOST_TEST_MODULE TNTLSSocketTest +#define BOOST_TEST_MODULE TNTLSInjectedSocketTest #include #include #include #include -#include #include -#include +#include #include #include #ifdef HAVE_SIGNAL_H @@ -33,7 +32,8 @@ using apache::thrift::transport::AccessManager; using apache::thrift::transport::Decision; -using apache::thrift::transport::NTLS; +using apache::thrift::transport::SSLContext; +using apache::thrift::transport::TSSLException; using apache::thrift::transport::TSSLServerSocket; using apache::thrift::transport::TSSLSocket; using apache::thrift::transport::TSSLSocketFactory; @@ -51,14 +51,6 @@ boost::filesystem::path certFile(const string& filename) { return keyDir / filename; } -string readFile(const boost::filesystem::path& path) { - std::ifstream input(path.string().c_str(), std::ios::binary); - BOOST_REQUIRE_MESSAGE(input.good(), "Unable to read " + path.string()); - std::ostringstream contents; - contents << input.rdbuf(); - return contents.str(); -} - class AllowAllAccessManager : public AccessManager { public: Decision verify(const sockaddr_storage&) noexcept override { return ALLOW; } @@ -79,9 +71,6 @@ struct GlobalFixtureNtls { if (envDir != nullptr && *envDir != '\0') { keyDir = boost::filesystem::path(envDir); } - if (!boost::filesystem::exists(certFile("server_sign.crt"))) { - keyDir = boost::filesystem::current_path().parent_path().parent_path().parent_path() / "test" / "keys" / "ntls"; - } if (!boost::filesystem::exists(certFile("server_sign.crt"))) { keyDir = boost::filesystem::path( boost::unit_test::framework::master_test_suite().argv[ @@ -89,7 +78,7 @@ struct GlobalFixtureNtls { } if (!boost::filesystem::exists(certFile("server_sign.crt"))) { throw std::invalid_argument( - "The last argument to this test must be the directory containing the NTLS test certificate(s)."); + "Set THRIFT_NTLS_CERT_DIR or pass the SM2 dual-cert directory as the last argument."); } } @@ -107,24 +96,49 @@ BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls); BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls) #endif -void loadServerDualCerts(const shared_ptr& factory) { - factory->loadSignCertificate(certFile("server_sign.crt").string().c_str()); - factory->loadSignPrivateKey(certFile("server_sign.key").string().c_str()); - factory->loadEncCertificate(certFile("server_enc.crt").string().c_str()); - factory->loadEncPrivateKey(certFile("server_enc.key").string().c_str()); +void throwOpenSslError(const string& label) { + throw TSSLException(label); } -void loadClientDualCerts(const shared_ptr& factory) { - factory->loadSignCertificate(certFile("client_sign.crt").string().c_str()); - factory->loadSignPrivateKey(certFile("client_sign.key").string().c_str()); - factory->loadEncCertificate(certFile("client_enc.crt").string().c_str()); - factory->loadEncPrivateKey(certFile("client_enc.key").string().c_str()); +void loadDualCerts(SSL_CTX* ctx, const string& prefix) { + const string signCert = certFile(prefix + "_sign.crt").string(); + const string signKey = certFile(prefix + "_sign.key").string(); + const string encCert = certFile(prefix + "_enc.crt").string(); + const string encKey = certFile(prefix + "_enc.key").string(); + + if (SSL_CTX_use_sign_certificate_file(ctx, signCert.c_str(), SSL_FILETYPE_PEM) <= 0) { + throwOpenSslError("SSL_CTX_use_sign_certificate_file"); + } + if (SSL_CTX_use_sign_PrivateKey_file(ctx, signKey.c_str(), SSL_FILETYPE_PEM) <= 0) { + throwOpenSslError("SSL_CTX_use_sign_PrivateKey_file"); + } + if (SSL_CTX_use_enc_certificate_file(ctx, encCert.c_str(), SSL_FILETYPE_PEM) <= 0) { + throwOpenSslError("SSL_CTX_use_enc_certificate_file"); + } + if (SSL_CTX_use_enc_PrivateKey_file(ctx, encKey.c_str(), SSL_FILETYPE_PEM) <= 0) { + throwOpenSslError("SSL_CTX_use_enc_PrivateKey_file"); + } +} + +shared_ptr createInjectedNtlsContext(const string& prefix) { + SSL_CTX* ctx = SSL_CTX_new(NTLS_method()); + if (ctx == nullptr) { + throwOpenSslError("SSL_CTX_new(NTLS_method)"); + } + SSL_CTX_enable_ntls(ctx); + SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); + if (SSL_CTX_set_cipher_list(ctx, kNtlsCipher) <= 0) { + SSL_CTX_free(ctx); + throwOpenSslError("SSL_CTX_set_cipher_list"); + } + loadDualCerts(ctx, prefix); + return std::make_shared(ctx, true); } shared_ptr createServerFactory(bool requireClientAuth) { - shared_ptr factory(new TSSLSocketFactory(NTLS)); - factory->ciphers(kNtlsCipher); - loadServerDualCerts(factory); + shared_ptr factory(new TSSLSocketFactory([]() { + return createInjectedNtlsContext("server"); + })); factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); factory->authenticate(requireClientAuth); if (requireClientAuth) { @@ -135,37 +149,21 @@ shared_ptr createServerFactory(bool requireClientAuth) { } shared_ptr createClientFactory(bool authenticatePeer) { - shared_ptr factory(new TSSLSocketFactory(NTLS)); - factory->ciphers(kNtlsCipher); - loadClientDualCerts(factory); + shared_ptr factory(new TSSLSocketFactory([]() { + return createInjectedNtlsContext("client"); + })); factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); factory->authenticate(authenticatePeer); factory->access(shared_ptr(new AllowAllAccessManager())); return factory; } -shared_ptr createServerFactoryFromBuffer(bool requireClientAuth) { - shared_ptr factory(new TSSLSocketFactory(NTLS)); - factory->ciphers(kNtlsCipher); - factory->loadSignCertificateFromBuffer(readFile(certFile("server_sign.crt")).c_str()); - factory->loadSignPrivateKeyFromBuffer(readFile(certFile("server_sign.key")).c_str()); - factory->loadEncCertificateFromBuffer(readFile(certFile("server_enc.crt")).c_str()); - factory->loadEncPrivateKeyFromBuffer(readFile(certFile("server_enc.key")).c_str()); - factory->loadTrustedCertificatesFromBuffer(readFile(certFile("chain-ca.crt")).c_str()); - factory->authenticate(requireClientAuth); - if (requireClientAuth) { - factory->access(shared_ptr(new AllowAllAccessManager())); - } - factory->server(true); - return factory; -} - void echoOnce(shared_ptr transport) { uint8_t request[5] = {'h', 'e', 'l', 'l', 'o'}; uint8_t response[5] = {}; transport->write(request, 5); transport->flush(); - uint32_t received = transport->read(response, 5); + const uint32_t received = transport->read(response, 5); BOOST_CHECK_EQUAL(received, 5U); BOOST_CHECK_EQUAL_COLLECTIONS(request, request + 5, response, response + 5); } @@ -196,18 +194,14 @@ void runEchoTest(const shared_ptr& serverFactory, } // namespace -BOOST_AUTO_TEST_SUITE(TNTLSSocketTest) +BOOST_AUTO_TEST_SUITE(TNTLSInjectedSocketTest) -BOOST_AUTO_TEST_CASE(test_ntls_handshake_and_echo) { +BOOST_AUTO_TEST_CASE(test_injected_ntls_handshake_and_echo) { runEchoTest(createServerFactory(false), createClientFactory(false)); } -BOOST_AUTO_TEST_CASE(test_ntls_mutual_auth_echo) { +BOOST_AUTO_TEST_CASE(test_injected_ntls_mutual_auth_echo) { runEchoTest(createServerFactory(true), createClientFactory(true)); } -BOOST_AUTO_TEST_CASE(test_ntls_dual_cert_from_buffer) { - runEchoTest(createServerFactoryFromBuffer(false), createClientFactory(false)); -} - BOOST_AUTO_TEST_SUITE_END() diff --git a/test/keys/ntls/chain-ca.crt b/test/keys/ntls/chain-ca.crt deleted file mode 100644 index a46055e2e71..00000000000 --- a/test/keys/ntls/chain-ca.crt +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB4DCCAYagAwIBAgIBADAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG -A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v -dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRjELMAkGA1UE -BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxEDAO -BgNVBAMMB3Jvb3QgY2EwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAASN55Ju2pvU -Bi8UrWHc4ZaKnsqiFPWfcM/6H2Gu/VQ7I1oVnyPktvlTrtwhSy6K43JoCnjVPHrq -jOXxnkOtGVDVo2MwYTAdBgNVHQ4EFgQUxu7mMmVaB3vq7JRi8UEFHcxVFY4wHwYD -VR0jBBgwFoAUxu7mMmVaB3vq7JRi8UEFHcxVFY4wDwYDVR0TAQH/BAUwAwEB/zAO -BgNVHQ8BAf8EBAMCAYYwCgYIKoEcz1UBg3UDSAAwRQIhAIz7tgrp7LmOQEJGPAU3 -8m9PNzMOTqGWZqux8CxIuEGjAiB4cFVYQ4sTCYb/4fNayKYO1FH+Q2Cc7xGq7WPd -knwWpw== ------END CERTIFICATE----- ------BEGIN CERTIFICATE----- -MIIB4zCCAYigAwIBAgIBATAKBggqgRzPVQGDdTBGMQswCQYDVQQGEwJBQTELMAkG -A1UECAwCQkIxCzAJBgNVBAoMAkNDMQswCQYDVQQLDAJERDEQMA4GA1UEAwwHcm9v -dCBjYTAgFw0yMzAyMjIwMjMwMTNaGA8yMTIzMDEyOTAyMzAxM1owRTELMAkGA1UE -BhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UECwwCREQxDzAN -BgNVBAMMBnN1YiBjYTBZMBMGByqGSM49AgEGCCqBHM9VAYItA0IABH0feWwae0S0 -w4QQA5cBGYwaQPaxZFcLzIqph+I6BQQCGXaIAabqpO0zjAyf1twYmoM3ZRLJgbZz -HE/2rRMPBiajZjBkMB0GA1UdDgQWBBSsYesigGJZCD6WyNF/znRcAq88mTAfBgNV -HSMEGDAWgBTG7uYyZVoHe+rslGLxQQUdzFUVjjASBgNVHRMBAf8ECDAGAQH/AgEA -MA4GA1UdDwEB/wQEAwIBhjAKBggqgRzPVQGDdQNJADBGAiEApoHDue1bzGukE97O -BqQbboU1d3jqNg4gAgpMe5fFIosCIQDwndSp7Tc3DZ0QCifXKNqgykjepsWTPZ3R -NrMzM0rflg== ------END CERTIFICATE----- diff --git a/test/keys/ntls/client_enc.crt b/test/keys/ntls/client_enc.crt deleted file mode 100644 index d42effc29ee..00000000000 --- a/test/keys/ntls/client_enc.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB7TCCAZKgAwIBAgIUWy6/ole1R8GwTrzoOtZebFla3aowCgYIKoEcz1UBg3Uw -RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE -CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy -OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD -QzELMAkGA1UECwwCREQxEzARBgNVBAMMCmNsaWVudCBlbmMwWTATBgcqhkjOPQIB -BggqgRzPVQGCLQNCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y13MXG1Zsx+NiEZ7Bb -OFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncMo1owWDAJBgNVHRMEAjAAMAsG -A1UdDwQEAwIDODAdBgNVHQ4EFgQUfPE8T3aPRzOi/+LiWTRrkM+0dKgwHwYDVR0j -BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDSQAwRgIhAOIT -GEUHnILUpLCbSZCyG8TigYmbg7ImyZFtXF/uEhOfAiEA59PnEVYaegvpI5Ltn5T2 -PKKqiZ2QOWEfRHJIi/FFZeo= ------END CERTIFICATE----- diff --git a/test/keys/ntls/client_enc.key b/test/keys/ntls/client_enc.key deleted file mode 100644 index 86aa5b3d809..00000000000 --- a/test/keys/ntls/client_enc.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQguz0M4/6qUhgHAxuG -WI2NPtNciIwmYAN4AUDoBEka1tehRANCAAQpIN3pNIBB3oZ+SaXKoZNtMkH5t5y1 -3MXG1Zsx+NiEZ7BbOFBbEB99vyQry6c9rzlM8IedPw6OwIc58dsA+ncM ------END PRIVATE KEY----- diff --git a/test/keys/ntls/client_sign.crt b/test/keys/ntls/client_sign.crt deleted file mode 100644 index b4f335f7a85..00000000000 --- a/test/keys/ntls/client_sign.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB7jCCAZOgAwIBAgIUbEstbqUWeJMWK3rlwLXE9YdtJ8QwCgYIKoEcz1UBg3Uw -RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE -CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy -OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD -QzELMAkGA1UECwwCREQxFDASBgNVBAMMC2NsaWVudCBzaWduMFkwEwYHKoZIzj0C -AQYIKoEcz1UBgi0DQgAELWF+dNVYbd4j0kkPvUaOMPEdAS1QqPOSzRhJsQsWfpoG -YffjoqAO5+xHGO2Te0qyxQqg00HRXkCVdDs4UK9tPKNaMFgwCQYDVR0TBAIwADAL -BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFFRpNXBAPyjFwT7w8EBNJLMVaFgdMB8GA1Ud -IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQDH -LBaKDJFhHYRNLhYXFBtZH6BIa6cJfewLyVaH0oDaMgIhAMgZQDmGvHCzJ9vdgL2P -7upTf3I28uj+3pq7ZiwKRBlO ------END CERTIFICATE----- diff --git a/test/keys/ntls/client_sign.key b/test/keys/ntls/client_sign.key deleted file mode 100644 index d4b4ecdbcef..00000000000 --- a/test/keys/ntls/client_sign.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQg5zLNmBvXdesfATxu -gMMpvEHfuxhUCr6L8rS2EEBr6SGhRANCAAQtYX501Vht3iPSSQ+9Ro4w8R0BLVCo -85LNGEmxCxZ+mgZh9+OioA7n7EcY7ZN7SrLFCqDTQdFeQJV0OzhQr208 ------END PRIVATE KEY----- diff --git a/test/keys/ntls/server_enc.crt b/test/keys/ntls/server_enc.crt deleted file mode 100644 index 1d9ac5e5b93..00000000000 --- a/test/keys/ntls/server_enc.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB6zCCAZKgAwIBAgIUaNiS6WOsoEViDnmdb8Mdk3Qz5XwwCgYIKoEcz1UBg3Uw -RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE -CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy -OTAyMzAxNFowSTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD -QzELMAkGA1UECwwCREQxEzARBgNVBAMMCnNlcnZlciBlbmMwWTATBgcqhkjOPQIB -BggqgRzPVQGCLQNCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY7JQUmflKUKWMZ11v -mtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNko1owWDAJBgNVHRMEAjAAMAsG -A1UdDwQEAwIDODAdBgNVHQ4EFgQUZ6Wt1ZR24FqcXla4hg/xOyju7FQwHwYDVR0j -BBgwFoAUrGHrIoBiWQg+lsjRf850XAKvPJkwCgYIKoEcz1UBg3UDRwAwRAIgR1k1 -ecSt7I2335jEquFmHBE5pe8Sk/IqOqQS0Jvs1uYCIG5XMB0XeUaVb9OctaxgOQLN -F8dRftiUHsyYXqfbaVjI ------END CERTIFICATE----- diff --git a/test/keys/ntls/server_enc.key b/test/keys/ntls/server_enc.key deleted file mode 100644 index b3e6dc511e9..00000000000 --- a/test/keys/ntls/server_enc.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgLrRk3CWTe+WZOFSf -TMYwbOocLs3MSRpOO0/AvSmvH5mhRANCAAR9vqVFQ0WBcr07aI5QnC31RYas4AtY -7JQUmflKUKWMZ11vmtr/CJ6BN6djQ6zS81yjCopcz4G3zc5SZqAWueNk ------END PRIVATE KEY----- diff --git a/test/keys/ntls/server_sign.crt b/test/keys/ntls/server_sign.crt deleted file mode 100644 index ac7f633b178..00000000000 --- a/test/keys/ntls/server_sign.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB7jCCAZOgAwIBAgIUcbKTlc6+CNoHglmEk+xm+WIqZcAwCgYIKoEcz1UBg3Uw -RTELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJDQzELMAkGA1UE -CwwCREQxDzANBgNVBAMMBnN1YiBjYTAgFw0yMzAyMjIwMjMwMTRaGA8yMTIzMDEy -OTAyMzAxNFowSjELMAkGA1UEBhMCQUExCzAJBgNVBAgMAkJCMQswCQYDVQQKDAJD -QzELMAkGA1UECwwCREQxFDASBgNVBAMMC3NlcnZlciBzaWduMFkwEwYHKoZIzj0C -AQYIKoEcz1UBgi0DQgAEBb/67sQGyPP1gKbjnFKEdsDfK2EGXUp09HavD7ZokPiW -rMSyHYsDbRPxe9TTgjSQi+23f44+rocGVPxvqASNDKNaMFgwCQYDVR0TBAIwADAL -BgNVHQ8EBAMCBsAwHQYDVR0OBBYEFH3uBqkdowIvk//P7n5UtnpV9TR6MB8GA1Ud -IwQYMBaAFKxh6yKAYlkIPpbI0X/OdFwCrzyZMAoGCCqBHM9VAYN1A0kAMEYCIQCz -W/6Z/d/IJUTrO0o8nCxNle6R0AkRCKUFhW9zbIRlNwIhAJZxg4gs2cV2QF37oHs6 -9TD+MkRbql4Yb47+jLf8f247 ------END CERTIFICATE----- diff --git a/test/keys/ntls/server_sign.key b/test/keys/ntls/server_sign.key deleted file mode 100644 index acec32681da..00000000000 --- a/test/keys/ntls/server_sign.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBG0wawIBAQQgeQTrKtO8mNXn/yvg -R+pdbCgH5sl+WCFfXcqGl64soU2hRANCAAQFv/ruxAbI8/WApuOcUoR2wN8rYQZd -SnT0dq8PtmiQ+JasxLIdiwNtE/F71NOCNJCL7bd/jj6uhwZU/G+oBI0M ------END PRIVATE KEY----- From 8ffcd87df6e6a21811f161bce276c37165495d8d Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 30 Jun 2026 16:05:40 +0800 Subject: [PATCH 3/5] Mark injected SSLContext constructor explicit. Prevent implicit conversion from SSL_CTX* to avoid accidental ownership mistakes. --- lib/cpp/src/thrift/transport/TSSLSocket.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/cpp/src/thrift/transport/TSSLSocket.h b/lib/cpp/src/thrift/transport/TSSLSocket.h index 2485bcdce09..b331c624d95 100644 --- a/lib/cpp/src/thrift/transport/TSSLSocket.h +++ b/lib/cpp/src/thrift/transport/TSSLSocket.h @@ -371,7 +371,7 @@ class SSLContext { * @param ctx OpenSSL context to wrap * @param takeOwnership If true (default), SSLContext frees ctx on destruction */ - SSLContext(SSL_CTX* ctx, bool takeOwnership = true); + explicit SSLContext(SSL_CTX* ctx, bool takeOwnership = true); virtual ~SSLContext(); SSL* createSSL(); SSL_CTX* get() { return ctx_; } From 87ebd575d3796223bd419589cfc5032b17076895 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 30 Jun 2026 16:29:23 +0800 Subject: [PATCH 4/5] Document NTLS local test cert path and LD_LIBRARY_PATH notes. Clarify that CTest expects a sibling Tongsuo source checkout for SM2 fixtures and that shared TLS libraries from a build tree need LD_LIBRARY_PATH. --- lib/cpp/README.md | 12 +++++++++++- lib/cpp/test/CMakeLists.txt | 1 + lib/cpp/test/TNTLSInjectedSocketTest.cpp | 5 +++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/cpp/README.md b/lib/cpp/README.md index f9e9167ebbc..b79a169e0a2 100644 --- a/lib/cpp/README.md +++ b/lib/cpp/README.md @@ -143,7 +143,17 @@ link against an OpenSSL-compatible TLS library can also create and configure an SSL_CTX externally, wrap it with SSLContext, and pass it through the factory. For example, NTLS/TLCP (Tongsuo) dual-certificate setup is done on the external SSL_CTX before wrapping; see `TNTLSInjectedSocketTest.cpp` when built against a -TLS library with NTLS support. +TLS library with NTLS support (`THRIFT_HAVE_NTLS`). + +Local NTLS testing notes: + +- CTest sets `THRIFT_NTLS_CERT_DIR` to the Tongsuo SM2 fixture directory under + a sibling source checkout (`/Tongsuo/test/certs/sm2` next to the + Thrift tree). If you only have an installed TLS prefix, set + `THRIFT_NTLS_CERT_DIR` to your dual-cert directory before running the test. +- If CMake links against shared libraries from a TLS source/build tree (without + `make install`), set `LD_LIBRARY_PATH` to that library directory when running + `ctest` or the test binary. ## How to use SSL APIs diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index 7de43b133a5..c1213c23cc4 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -379,6 +379,7 @@ if(THRIFT_HAVE_NTLS) thrift ) set(TNTLSInjectedSocketTest_ENV + # Sibling Tongsuo source checkout; override via THRIFT_NTLS_CERT_DIR at runtime. "THRIFT_NTLS_CERT_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../../../../Tongsuo/test/certs/sm2") add_test(NAME TNTLSInjectedSocketTest COMMAND ${CMAKE_COMMAND} -E env ${TNTLSInjectedSocketTest_ENV} $) diff --git a/lib/cpp/test/TNTLSInjectedSocketTest.cpp b/lib/cpp/test/TNTLSInjectedSocketTest.cpp index 7b2426445c6..99ba2b183ca 100644 --- a/lib/cpp/test/TNTLSInjectedSocketTest.cpp +++ b/lib/cpp/test/TNTLSInjectedSocketTest.cpp @@ -17,6 +17,11 @@ * under the License. */ +// Optional local NTLS handshake test (built when THRIFT_HAVE_NTLS is set). +// Cert directory: set THRIFT_NTLS_CERT_DIR, or pass the SM2 cert dir after "--". +// CTest defaults to a sibling Tongsuo checkout (/Tongsuo/test/certs/sm2). +// When linking shared TLS libs from a source tree, set LD_LIBRARY_PATH accordingly. + #define BOOST_TEST_MODULE TNTLSInjectedSocketTest #include #include From 08700779e125daaf3ab9c97fba6ef2e3ef70fa20 Mon Sep 17 00:00:00 2001 From: 761417898 <761417898@qq.com> Date: Tue, 30 Jun 2026 16:59:01 +0800 Subject: [PATCH 5/5] Remove optional NTLS local test and related CMake/docs. Keep the PR focused on backend-neutral SSLContext injection; SecurityTest covers the wrapped-context API. --- build/cmake/ConfigureChecks.cmake | 29 ---- build/cmake/config.h.in | 3 - lib/cpp/README.md | 17 +- lib/cpp/test/CMakeLists.txt | 14 -- lib/cpp/test/TNTLSInjectedSocketTest.cpp | 212 ----------------------- 5 files changed, 3 insertions(+), 272 deletions(-) delete mode 100644 lib/cpp/test/TNTLSInjectedSocketTest.cpp diff --git a/build/cmake/ConfigureChecks.cmake b/build/cmake/ConfigureChecks.cmake index c757edd8e2b..29b8bbea859 100644 --- a/build/cmake/ConfigureChecks.cmake +++ b/build/cmake/ConfigureChecks.cmake @@ -86,35 +86,6 @@ set(PACKAGE ${PACKAGE_NAME}) set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(VERSION ${thrift_VERSION}) -if(WITH_OPENSSL AND OPENSSL_FOUND) - unset(THRIFT_HAVE_NTLS CACHE) - set(CMAKE_REQUIRED_INCLUDES "${OPENSSL_INCLUDE_DIR}") - if(TARGET OpenSSL::SSL AND TARGET OpenSSL::Crypto) - set(CMAKE_REQUIRED_LIBRARIES OpenSSL::SSL OpenSSL::Crypto) - else() - set(CMAKE_REQUIRED_LIBRARIES "${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") - endif() - - check_cxx_source_compiles( - " - #include - #if defined(OPENSSL_NO_NTLS) - # error ntls disabled - #endif - int main() { - SSL_CTX *ctx = SSL_CTX_new(NTLS_method()); - if (ctx == nullptr) { - return 1; - } - SSL_CTX_enable_ntls(ctx); - SSL_CTX_free(ctx); - return 0; - } - " - THRIFT_HAVE_NTLS - ) -endif() - # generate a config.h file configure_file("${CMAKE_CURRENT_SOURCE_DIR}/build/cmake/config.h.in" "${CMAKE_CURRENT_BINARY_DIR}/thrift/config.h") diff --git a/build/cmake/config.h.in b/build/cmake/config.h.in index 62103e55ff7..afcdc1e6a7e 100644 --- a/build/cmake/config.h.in +++ b/build/cmake/config.h.in @@ -157,7 +157,4 @@ /* Define to 1 if strerror_r returns char *. */ #cmakedefine STRERROR_R_CHAR_P 1 -/* Define to 1 if the linked TLS library provides NTLS (optional local test). */ -#cmakedefine THRIFT_HAVE_NTLS 1 - #endif diff --git a/lib/cpp/README.md b/lib/cpp/README.md index b79a169e0a2..7827dc385eb 100644 --- a/lib/cpp/README.md +++ b/lib/cpp/README.md @@ -140,20 +140,9 @@ method and sets TLS 1.2 as the minimum negotiated protocol version. Applications that need a different protocol range can provide a custom SSLContext factory and adjust the OpenSSL context options before creating sockets. Applications that link against an OpenSSL-compatible TLS library can also create and configure an -SSL_CTX externally, wrap it with SSLContext, and pass it through the factory. -For example, NTLS/TLCP (Tongsuo) dual-certificate setup is done on the external -SSL_CTX before wrapping; see `TNTLSInjectedSocketTest.cpp` when built against a -TLS library with NTLS support (`THRIFT_HAVE_NTLS`). - -Local NTLS testing notes: - -- CTest sets `THRIFT_NTLS_CERT_DIR` to the Tongsuo SM2 fixture directory under - a sibling source checkout (`/Tongsuo/test/certs/sm2` next to the - Thrift tree). If you only have an installed TLS prefix, set - `THRIFT_NTLS_CERT_DIR` to your dual-cert directory before running the test. -- If CMake links against shared libraries from a TLS source/build tree (without - `make install`), set `LD_LIBRARY_PATH` to that library directory when running - `ctest` or the test binary. +SSL_CTX externally, wrap it with `SSLContext`, and pass it through the factory +(for example, protocol-specific or dual-certificate setups that the default +factory methods cannot express). ## How to use SSL APIs diff --git a/lib/cpp/test/CMakeLists.txt b/lib/cpp/test/CMakeLists.txt index c1213c23cc4..f015bfdb9be 100644 --- a/lib/cpp/test/CMakeLists.txt +++ b/lib/cpp/test/CMakeLists.txt @@ -371,20 +371,6 @@ if (NOT MSVC AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT ${CMAKE_SYSTE endif () add_test(NAME SecurityFromBufferTest COMMAND SecurityFromBufferTest -- "${CMAKE_CURRENT_SOURCE_DIR}/../../../test/keys") -if(THRIFT_HAVE_NTLS) - add_executable(TNTLSInjectedSocketTest TNTLSInjectedSocketTest.cpp) - target_link_libraries(TNTLSInjectedSocketTest - ${OPENSSL_LIBRARIES} - ${Boost_LIBRARIES} - thrift - ) - set(TNTLSInjectedSocketTest_ENV - # Sibling Tongsuo source checkout; override via THRIFT_NTLS_CERT_DIR at runtime. - "THRIFT_NTLS_CERT_DIR=${CMAKE_CURRENT_SOURCE_DIR}/../../../../Tongsuo/test/certs/sm2") - add_test(NAME TNTLSInjectedSocketTest - COMMAND ${CMAKE_COMMAND} -E env ${TNTLSInjectedSocketTest_ENV} $) -endif() - endif() if(WITH_QT5) diff --git a/lib/cpp/test/TNTLSInjectedSocketTest.cpp b/lib/cpp/test/TNTLSInjectedSocketTest.cpp deleted file mode 100644 index 99ba2b183ca..00000000000 --- a/lib/cpp/test/TNTLSInjectedSocketTest.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// Optional local NTLS handshake test (built when THRIFT_HAVE_NTLS is set). -// Cert directory: set THRIFT_NTLS_CERT_DIR, or pass the SM2 cert dir after "--". -// CTest defaults to a sibling Tongsuo checkout (/Tongsuo/test/certs/sm2). -// When linking shared TLS libs from a source tree, set LD_LIBRARY_PATH accordingly. - -#define BOOST_TEST_MODULE TNTLSInjectedSocketTest -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SIGNAL_H -#include -#endif - -using apache::thrift::transport::AccessManager; -using apache::thrift::transport::Decision; -using apache::thrift::transport::SSLContext; -using apache::thrift::transport::TSSLException; -using apache::thrift::transport::TSSLServerSocket; -using apache::thrift::transport::TSSLSocket; -using apache::thrift::transport::TSSLSocketFactory; -using apache::thrift::transport::TTransport; -using std::shared_ptr; -using std::string; - -namespace { - -const char* kNtlsCipher = "ECC-SM2-WITH-SM4-SM3"; - -boost::filesystem::path keyDir; - -boost::filesystem::path certFile(const string& filename) { - return keyDir / filename; -} - -class AllowAllAccessManager : public AccessManager { -public: - Decision verify(const sockaddr_storage&) noexcept override { return ALLOW; } - Decision verify(const string&, const char*, int) noexcept override { return ALLOW; } - Decision verify(const sockaddr_storage&, const char*, int) noexcept override { return ALLOW; } -}; - -struct GlobalFixtureNtls { - GlobalFixtureNtls() { -#ifdef __linux__ - signal(SIGPIPE, SIG_IGN); -#endif - - TSSLSocketFactory::setManualOpenSSLInitialization(true); - apache::thrift::transport::initializeOpenSSL(); - - const char* envDir = std::getenv("THRIFT_NTLS_CERT_DIR"); - if (envDir != nullptr && *envDir != '\0') { - keyDir = boost::filesystem::path(envDir); - } - if (!boost::filesystem::exists(certFile("server_sign.crt"))) { - keyDir = boost::filesystem::path( - boost::unit_test::framework::master_test_suite().argv[ - boost::unit_test::framework::master_test_suite().argc - 1]); - } - if (!boost::filesystem::exists(certFile("server_sign.crt"))) { - throw std::invalid_argument( - "Set THRIFT_NTLS_CERT_DIR or pass the SM2 dual-cert directory as the last argument."); - } - } - - ~GlobalFixtureNtls() { - apache::thrift::transport::cleanupOpenSSL(); -#ifdef __linux__ - signal(SIGPIPE, SIG_DFL); -#endif - } -}; - -#if (BOOST_VERSION >= 105900) -BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls); -#else -BOOST_GLOBAL_FIXTURE(GlobalFixtureNtls) -#endif - -void throwOpenSslError(const string& label) { - throw TSSLException(label); -} - -void loadDualCerts(SSL_CTX* ctx, const string& prefix) { - const string signCert = certFile(prefix + "_sign.crt").string(); - const string signKey = certFile(prefix + "_sign.key").string(); - const string encCert = certFile(prefix + "_enc.crt").string(); - const string encKey = certFile(prefix + "_enc.key").string(); - - if (SSL_CTX_use_sign_certificate_file(ctx, signCert.c_str(), SSL_FILETYPE_PEM) <= 0) { - throwOpenSslError("SSL_CTX_use_sign_certificate_file"); - } - if (SSL_CTX_use_sign_PrivateKey_file(ctx, signKey.c_str(), SSL_FILETYPE_PEM) <= 0) { - throwOpenSslError("SSL_CTX_use_sign_PrivateKey_file"); - } - if (SSL_CTX_use_enc_certificate_file(ctx, encCert.c_str(), SSL_FILETYPE_PEM) <= 0) { - throwOpenSslError("SSL_CTX_use_enc_certificate_file"); - } - if (SSL_CTX_use_enc_PrivateKey_file(ctx, encKey.c_str(), SSL_FILETYPE_PEM) <= 0) { - throwOpenSslError("SSL_CTX_use_enc_PrivateKey_file"); - } -} - -shared_ptr createInjectedNtlsContext(const string& prefix) { - SSL_CTX* ctx = SSL_CTX_new(NTLS_method()); - if (ctx == nullptr) { - throwOpenSslError("SSL_CTX_new(NTLS_method)"); - } - SSL_CTX_enable_ntls(ctx); - SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY); - if (SSL_CTX_set_cipher_list(ctx, kNtlsCipher) <= 0) { - SSL_CTX_free(ctx); - throwOpenSslError("SSL_CTX_set_cipher_list"); - } - loadDualCerts(ctx, prefix); - return std::make_shared(ctx, true); -} - -shared_ptr createServerFactory(bool requireClientAuth) { - shared_ptr factory(new TSSLSocketFactory([]() { - return createInjectedNtlsContext("server"); - })); - factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); - factory->authenticate(requireClientAuth); - if (requireClientAuth) { - factory->access(shared_ptr(new AllowAllAccessManager())); - } - factory->server(true); - return factory; -} - -shared_ptr createClientFactory(bool authenticatePeer) { - shared_ptr factory(new TSSLSocketFactory([]() { - return createInjectedNtlsContext("client"); - })); - factory->loadTrustedCertificates(certFile("chain-ca.crt").string().c_str()); - factory->authenticate(authenticatePeer); - factory->access(shared_ptr(new AllowAllAccessManager())); - return factory; -} - -void echoOnce(shared_ptr transport) { - uint8_t request[5] = {'h', 'e', 'l', 'l', 'o'}; - uint8_t response[5] = {}; - transport->write(request, 5); - transport->flush(); - const uint32_t received = transport->read(response, 5); - BOOST_CHECK_EQUAL(received, 5U); - BOOST_CHECK_EQUAL_COLLECTIONS(request, request + 5, response, response + 5); -} - -void runEchoTest(const shared_ptr& serverFactory, - const shared_ptr& clientFactory) { - TSSLServerSocket server("127.0.0.1", 0, serverFactory); - server.listen(); - const int port = server.getPort(); - - bool serverFailed = false; - boost::thread serverThread([&]() { - try { - echoOnce(server.accept()); - } catch (...) { - serverFailed = true; - } - }); - - shared_ptr client = clientFactory->createSocket("127.0.0.1", port); - client->open(); - echoOnce(client); - client->close(); - serverThread.join(); - server.close(); - BOOST_CHECK(!serverFailed); -} - -} // namespace - -BOOST_AUTO_TEST_SUITE(TNTLSInjectedSocketTest) - -BOOST_AUTO_TEST_CASE(test_injected_ntls_handshake_and_echo) { - runEchoTest(createServerFactory(false), createClientFactory(false)); -} - -BOOST_AUTO_TEST_CASE(test_injected_ntls_mutual_auth_echo) { - runEchoTest(createServerFactory(true), createClientFactory(true)); -} - -BOOST_AUTO_TEST_SUITE_END()