From 4b2f86c20d1e83b1416830efec6111bdee4fb961 Mon Sep 17 00:00:00 2001 From: Fabian Sauter Date: Fri, 24 May 2024 16:32:28 +0200 Subject: [PATCH] Fixed ssl context callback support --- cpr/callback.cpp | 77 ++++++++++++++++++++------------ cpr/ssl_ctx.cpp | 111 ----------------------------------------------- 2 files changed, 50 insertions(+), 138 deletions(-) delete mode 100644 cpr/ssl_ctx.cpp diff --git a/cpr/callback.cpp b/cpr/callback.cpp index bee2f8309..2b7d75abd 100644 --- a/cpr/callback.cpp +++ b/cpr/callback.cpp @@ -1,6 +1,7 @@ #include #ifdef OPENSSL_BACKEND_USED #include +#include #endif // OPENSSL_BACKEND_USED #include "cpr/callback.h" @@ -9,7 +10,9 @@ #ifdef OPENSSL_BACKEND_USED #include +#include #include +#include #include #include #include @@ -38,6 +41,31 @@ bool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow, #ifdef OPENSSL_BACKEND_USED namespace ssl { +template +struct deleter_from_fn { + template + constexpr void operator()(T* arg) const { + fn(arg); + } +}; + +template +using custom_unique_ptr = std::unique_ptr>; +using x509_ptr = custom_unique_ptr; +using bio_ptr = custom_unique_ptr; + +inline std::string get_openssl_print_errors() { + std::ostringstream oss; + ERR_print_errors_cb( + [](char const* str, size_t len, void* data) -> int { + auto& oss = *static_cast(data); + oss << str; + return static_cast(len); + }, + &oss); + return oss.str(); +} + /** * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL. * If an error is returned from the callback no attempt to establish a connection is made and @@ -49,43 +77,38 @@ namespace ssl { CURLcode tryLoadCaCertFromBuffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) { // Check arguments if (raw_cert_buf == nullptr || sslctx == nullptr) { - std::cerr << "CPR SSL context invalid callback arguments!\n"; + std::cerr << "Invalid callback arguments!\n"; return CURLE_ABORTED_BY_CALLBACK; } - // Setup pointer - X509_STORE* store = nullptr; - X509* cert = nullptr; - BIO* bio = nullptr; - char* cert_buf = static_cast(raw_cert_buf); + // Get a pointer to the current certificate verification storage + auto* store = SSL_CTX_get_cert_store(static_cast(sslctx)); // Create a memory BIO using the data of cert_buf. // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen. - bio = BIO_new_mem_buf(cert_buf, -1); + const bio_ptr bio{BIO_new_mem_buf(static_cast(raw_cert_buf), -1)}; - // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use. - PEM_read_bio_X509(bio, &cert, nullptr, nullptr); - if (cert == nullptr) { - std::cerr << "CPR SSL context PEM_read_bio_X509 failed!\n"; - return CURLE_ABORTED_BY_CALLBACK; - } - - // Get a pointer to the current certificate verification storage - store = SSL_CTX_get_cert_store(static_cast(sslctx)); + bool at_least_got_one = false; + for (;;) { + // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use. + const x509_ptr x{PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)}; + if (x == nullptr) { + if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && at_least_got_one) { + ERR_clear_error(); + break; + } + std::cerr << "PEM_read_bio_X509_AUX failed: \n" << get_openssl_print_errors() << '\n'; + return CURLE_ABORTED_BY_CALLBACK; + } - // Add the loaded certificate to the verification storage - const int status = X509_STORE_add_cert(store, cert); - if (status == 0) { - std::cerr << "CPR SSL context error adding certificate!\n"; - return CURLE_ABORTED_BY_CALLBACK; + // Add the loaded certificate to the verification storage + if (X509_STORE_add_cert(store, x.get()) == 0) { + std::cerr << "X509_STORE_add_cert failed: \n" << get_openssl_print_errors() << '\n'; + return CURLE_ABORTED_BY_CALLBACK; + } + at_least_got_one = true; } - // Decrement the reference count of the X509 structure cert and frees it up - X509_free(cert); - - // Free the entire bio chain - BIO_free(bio); - // The CA certificate was loaded successfully into the verification storage return CURLE_OK; } diff --git a/cpr/ssl_ctx.cpp b/cpr/ssl_ctx.cpp deleted file mode 100644 index 4a5ca3610..000000000 --- a/cpr/ssl_ctx.cpp +++ /dev/null @@ -1,111 +0,0 @@ - -#include "cpr/ssl_ctx.h" -#include "cpr/ssl_options.h" -#include -#include -#include -#include -#include -#include - -#if SUPPORT_CURLOPT_SSL_CTX_FUNCTION - -#ifdef OPENSSL_BACKEND_USED - -#include -#include -#include -#include -#include -#include -#include - -// openssl/types.h was added in later version of openssl and is therefore not always available. -// This is for example the case on Ubuntu 20.04. -// We try to include it if available to satisfy clang-tidy. -// Ref: https://github.com/openssl/openssl/commit/50cd4768c6b89c757645f28519236bb989216f8d -#if __has_include() -#include -#else -#include -#endif - -namespace cpr { - -template -struct deleter_from_fn { - template - constexpr void operator()(T* arg) const { - fn(arg); - } -}; - -template -using custom_unique_ptr = std::unique_ptr>; -using x509_ptr = custom_unique_ptr; -using bio_ptr = custom_unique_ptr; - -inline std::string get_openssl_print_errors() { - std::ostringstream oss; - ERR_print_errors_cb( - [](char const* str, size_t len, void* data) -> int { - auto& oss = *static_cast(data); - oss << str; - return static_cast(len); - }, - &oss); - return oss.str(); -} - -/** - * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL. - * If an error is returned from the callback no attempt to establish a connection is made and - * the perform operation will return the callback's error code. - * - * Sources: https://curl.se/libcurl/c/CURLOPT_SSL_CTX_FUNCTION.html - * https://curl.se/libcurl/c/CURLOPT_SSL_CTX_DATA.html - */ -CURLcode tryLoadCaCertFromBuffer(CURL* /*curl*/, void* sslctx, void* raw_cert_buf) { - // Check arguments - if (raw_cert_buf == nullptr || sslctx == nullptr) { - std::cerr << "Invalid callback arguments!\n"; - return CURLE_ABORTED_BY_CALLBACK; - } - - // Get a pointer to the current certificate verification storage - auto* store = SSL_CTX_get_cert_store(static_cast(sslctx)); - - // Create a memory BIO using the data of cert_buf. - // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen. - const bio_ptr bio{BIO_new_mem_buf(static_cast(raw_cert_buf), -1)}; - - bool at_least_got_one = false; - for (;;) { - // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use. - const x509_ptr x{PEM_read_bio_X509_AUX(bio.get(), nullptr, nullptr, nullptr)}; - if (x == nullptr) { - if ((ERR_GET_REASON(ERR_peek_last_error()) == PEM_R_NO_START_LINE) && at_least_got_one) { - ERR_clear_error(); - break; - } - std::cerr << "PEM_read_bio_X509_AUX failed: \n" << get_openssl_print_errors() << '\n'; - return CURLE_ABORTED_BY_CALLBACK; - } - - // Add the loaded certificate to the verification storage - if (X509_STORE_add_cert(store, x.get()) == 0) { - std::cerr << "X509_STORE_add_cert failed: \n" << get_openssl_print_errors() << '\n'; - return CURLE_ABORTED_BY_CALLBACK; - } - at_least_got_one = true; - } - - // The CA certificate was loaded successfully into the verification storage - return CURLE_OK; -} - -} // namespace cpr - -#endif // OPENSSL_BACKEND_USED - -#endif // SUPPORT_CURLOPT_SSL_CTX_FUNCTION