11#include < functional>
22#ifdef OPENSSL_BACKEND_USED
33#include < iostream>
4+ #include < sstream>
45#endif // OPENSSL_BACKEND_USED
56
67#include " cpr/callback.h"
910
1011#ifdef OPENSSL_BACKEND_USED
1112#include < openssl/bio.h>
13+ #include < openssl/err.h>
1214#include < openssl/pem.h>
15+ #include < openssl/pemerr.h>
1316#include < openssl/ssl.h>
1417#include < openssl/x509.h>
1518#include < openssl/x509_vfy.h>
@@ -38,6 +41,31 @@ bool CancellationCallback::operator()(cpr_pf_arg_t dltotal, cpr_pf_arg_t dlnow,
3841
3942#ifdef OPENSSL_BACKEND_USED
4043namespace ssl {
44+ template <auto fn>
45+ struct deleter_from_fn {
46+ template <typename T>
47+ constexpr void operator ()(T* arg) const {
48+ fn (arg);
49+ }
50+ };
51+
52+ template <typename T, auto fn>
53+ using custom_unique_ptr = std::unique_ptr<T, deleter_from_fn<fn>>;
54+ using x509_ptr = custom_unique_ptr<X509, X509_free>;
55+ using bio_ptr = custom_unique_ptr<BIO, BIO_free>;
56+
57+ inline std::string get_openssl_print_errors () {
58+ std::ostringstream oss;
59+ ERR_print_errors_cb (
60+ [](char const * str, size_t len, void * data) -> int {
61+ auto & oss = *static_cast <std::ostringstream*>(data);
62+ oss << str;
63+ return static_cast <int >(len);
64+ },
65+ &oss);
66+ return oss.str ();
67+ }
68+
4169/* *
4270 * The ssl_ctx parameter is actually a pointer to the SSL library's SSL_CTX for OpenSSL.
4371 * If an error is returned from the callback no attempt to establish a connection is made and
@@ -49,43 +77,38 @@ namespace ssl {
4977CURLcode tryLoadCaCertFromBuffer (CURL* /* curl*/ , void * sslctx, void * raw_cert_buf) {
5078 // Check arguments
5179 if (raw_cert_buf == nullptr || sslctx == nullptr ) {
52- std::cerr << " CPR SSL context invalid callback arguments!\n " ;
80+ std::cerr << " Invalid callback arguments!\n " ;
5381 return CURLE_ABORTED_BY_CALLBACK;
5482 }
5583
56- // Setup pointer
57- X509_STORE* store = nullptr ;
58- X509* cert = nullptr ;
59- BIO* bio = nullptr ;
60- char * cert_buf = static_cast <char *>(raw_cert_buf);
84+ // Get a pointer to the current certificate verification storage
85+ auto * store = SSL_CTX_get_cert_store (static_cast <SSL_CTX*>(sslctx));
6186
6287 // Create a memory BIO using the data of cert_buf.
6388 // Note: It is assumed, that cert_buf is nul terminated and its length is determined by strlen.
64- bio = BIO_new_mem_buf (cert_buf , -1 );
89+ const bio_ptr bio{ BIO_new_mem_buf (static_cast < char *>(raw_cert_buf) , -1 )} ;
6590
66- // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use.
67- PEM_read_bio_X509 (bio, &cert, nullptr , nullptr );
68- if (cert == nullptr ) {
69- std::cerr << " CPR SSL context PEM_read_bio_X509 failed!\n " ;
70- return CURLE_ABORTED_BY_CALLBACK;
71- }
72-
73- // Get a pointer to the current certificate verification storage
74- store = SSL_CTX_get_cert_store (static_cast <SSL_CTX*>(sslctx));
91+ bool at_least_got_one = false ;
92+ for (;;) {
93+ // Load the PEM formatted certicifate into an X509 structure which OpenSSL can use.
94+ const x509_ptr x{PEM_read_bio_X509_AUX (bio.get (), nullptr , nullptr , nullptr )};
95+ if (x == nullptr ) {
96+ if ((ERR_GET_REASON (ERR_peek_last_error ()) == PEM_R_NO_START_LINE) && at_least_got_one) {
97+ ERR_clear_error ();
98+ break ;
99+ }
100+ std::cerr << " PEM_read_bio_X509_AUX failed: \n " << get_openssl_print_errors () << ' \n ' ;
101+ return CURLE_ABORTED_BY_CALLBACK;
102+ }
75103
76- // Add the loaded certificate to the verification storage
77- const int status = X509_STORE_add_cert (store, cert);
78- if (status == 0 ) {
79- std::cerr << " CPR SSL context error adding certificate!\n " ;
80- return CURLE_ABORTED_BY_CALLBACK;
104+ // Add the loaded certificate to the verification storage
105+ if (X509_STORE_add_cert (store, x.get ()) == 0 ) {
106+ std::cerr << " X509_STORE_add_cert failed: \n " << get_openssl_print_errors () << ' \n ' ;
107+ return CURLE_ABORTED_BY_CALLBACK;
108+ }
109+ at_least_got_one = true ;
81110 }
82111
83- // Decrement the reference count of the X509 structure cert and frees it up
84- X509_free (cert);
85-
86- // Free the entire bio chain
87- BIO_free (bio);
88-
89112 // The CA certificate was loaded successfully into the verification storage
90113 return CURLE_OK;
91114}
0 commit comments