1515#include " google/cloud/internal/curl_handle_factory.h"
1616#include " google/cloud/credentials.h"
1717#include " google/cloud/internal/curl_options.h"
18+ #include " google/cloud/internal/make_status.h"
19+ #include " google/cloud/log.h"
20+ #include < openssl/err.h>
21+ #include < openssl/ssl.h>
1822#include < algorithm>
1923#include < iterator>
2024
2125namespace google {
2226namespace cloud {
2327namespace rest_internal {
2428GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
29+ namespace {
30+
31+ #ifndef _WIN32
32+ struct BIOPtrCleanup {
33+ int operator ()(BIO* b) const { return BIO_free (b); }
34+ };
35+
36+ using BioPtr = std::unique_ptr<BIO, BIOPtrCleanup>;
37+
38+ struct X509InfoPtrCleanup {
39+ void operator ()(STACK_OF(X509_INFO) * i) const {
40+ return sk_X509_INFO_pop_free (i, X509_INFO_free);
41+ }
42+ };
43+
44+ using X509InfoPtr = std::unique_ptr<STACK_OF(X509_INFO), X509InfoPtrCleanup>;
45+
46+ #endif
47+
48+ Status SetCurlCAInMemory (CurlHandleFactory const & factory, SSL_CTX* ssl_ctx) {
49+ #if _WIN32
50+ return internal::InternalError (
51+ " SSL callback function currently not supported in windows" ,
52+ GCP_ERROR_INFO ());
53+ #else
54+ X509_STORE* cert_store = SSL_CTX_get_cert_store (ssl_ctx);
55+ if (!cert_store) {
56+ return internal::InternalError (" SSL_CTX_get_cert_store returned NULL" ,
57+ GCP_ERROR_INFO ());
58+ }
59+
60+ // Add each of the provided certs to the store.
61+ for (auto const & cert : factory.ca_certs ()) {
62+ BioPtr buf{BIO_new_mem_buf (cert.data (), static_cast <int >(cert.length ()))};
63+ if (!buf) {
64+ return internal::InternalError (" BIO_new_mem_buf returned NULL" ,
65+ GCP_ERROR_INFO ());
66+ }
67+ X509InfoPtr info{
68+ PEM_X509_INFO_read_bio (buf.get (), nullptr , nullptr , nullptr )};
69+ if (!info) {
70+ return internal::InternalError (" PEM_X509_INFO_read_bio returned NULL" ,
71+ GCP_ERROR_INFO ());
72+ }
73+
74+ for (decltype (sk_X509_INFO_num (info.get ())) i = 0 ;
75+ i < sk_X509_INFO_num (info.get ()); ++i) {
76+ X509_INFO* value = sk_X509_INFO_value (info.get (), i);
77+ if (value->x509 ) {
78+ X509_STORE_add_cert (cert_store, value->x509 );
79+ }
80+ if (value->crl ) {
81+ X509_STORE_add_crl (cert_store, value->crl );
82+ }
83+ }
84+ }
85+
86+ return {};
87+ #endif
88+ }
89+
90+ } // namespace
91+
92+ extern " C" {
93+
94+ static CURLcode SslCtxFunction ( // NOLINT(misc-use-anonymous-namespace)
95+ CURL*, void * ssl_ctx, void * userdata) {
96+ auto * handle_factory = reinterpret_cast <CurlHandleFactory*>(userdata);
97+ auto result = SetCurlCAInMemory (*handle_factory, (SSL_CTX*)ssl_ctx);
98+ if (!result.ok ()) {
99+ GCP_LOG (ERROR) << result << " \n " ;
100+ return CURLE_ABORTED_BY_CALLBACK;
101+ }
102+ return CURLE_OK;
103+ }
104+ }
25105
26106void CurlHandleFactory::SetCurlStringOption (CURL* handle, CURLoption option_tag,
27107 char const * value) {
@@ -36,15 +116,27 @@ std::shared_ptr<CurlHandleFactory> GetDefaultCurlHandleFactory() {
36116
37117std::shared_ptr<CurlHandleFactory> GetDefaultCurlHandleFactory (
38118 Options const & options) {
39- if (!options.get <CARootsFilePathOption>().empty ()) {
119+ if (!options.get <CARootsFilePathOption>().empty () ||
120+ !options.get <experimental::CAInMemoryOption>().empty ()) {
40121 return std::make_shared<DefaultCurlHandleFactory>(options);
41122 }
123+
42124 return GetDefaultCurlHandleFactory ();
43125}
44126
45127DefaultCurlHandleFactory::DefaultCurlHandleFactory (Options const & o) {
46- if (o.has <CARootsFilePathOption>()) cainfo_ = o.get <CARootsFilePathOption>();
47- if (o.has <CAPathOption>()) capath_ = o.get <CAPathOption>();
128+ if (o.has <experimental::CAInMemoryOption>()) {
129+ ca_certs_ = o.get <experimental::CAInMemoryOption>();
130+ if (ca_certs_.empty ()) {
131+ GCP_LOG (FATAL) << internal::InvalidArgumentError (
132+ " No CA certificates specified" , GCP_ERROR_INFO ());
133+ }
134+ } else {
135+ if (o.has <CARootsFilePathOption>()) {
136+ cainfo_ = o.get <CARootsFilePathOption>();
137+ }
138+ if (o.has <CAPathOption>()) capath_ = o.get <CAPathOption>();
139+ }
48140}
49141
50142CurlPtr DefaultCurlHandleFactory::CreateHandle () {
@@ -74,17 +166,36 @@ void DefaultCurlHandleFactory::CleanupMultiHandle(CurlMulti m,
74166}
75167
76168void DefaultCurlHandleFactory::SetCurlOptions (CURL* handle) {
77- if (cainfo_) {
78- SetCurlStringOption (handle, CURLOPT_CAINFO, cainfo_->c_str ());
79- }
80- if (capath_) {
81- SetCurlStringOption (handle, CURLOPT_CAPATH, capath_->c_str ());
169+ if (!ca_certs_.empty ()) {
170+ SetCurlStringOption (handle, CURLOPT_CAINFO, nullptr );
171+ SetCurlStringOption (handle, CURLOPT_CAPATH, nullptr );
172+ auto result = curl_easy_setopt (handle, CURLOPT_SSL_CTX_DATA, this );
173+ if (result != CURLE_OK) {
174+ GCP_LOG (FATAL) << internal::InternalError (curl_easy_strerror (result),
175+ GCP_ERROR_INFO ());
176+ }
177+ result =
178+ curl_easy_setopt (handle, CURLOPT_SSL_CTX_FUNCTION, &SslCtxFunction);
179+ if (result != CURLE_OK) {
180+ GCP_LOG (FATAL) << internal::InternalError (curl_easy_strerror (result),
181+ GCP_ERROR_INFO ());
182+ }
183+ } else {
184+ if (cainfo_) {
185+ SetCurlStringOption (handle, CURLOPT_CAINFO, cainfo_->c_str ());
186+ }
187+ if (capath_) {
188+ SetCurlStringOption (handle, CURLOPT_CAPATH, capath_->c_str ());
189+ }
82190 }
83191}
84192
85193PooledCurlHandleFactory::PooledCurlHandleFactory (std::size_t maximum_size,
86194 Options const & o)
87- : maximum_size_(maximum_size), cainfo_(CAInfo(o)), capath_(CAPath(o)) {}
195+ : maximum_size_(maximum_size),
196+ cainfo_ (CAInfo(o)),
197+ capath_(CAPath(o)),
198+ ca_certs_(CACerts(o)) {}
88199
89200PooledCurlHandleFactory::~PooledCurlHandleFactory () = default ;
90201
@@ -192,11 +303,27 @@ void PooledCurlHandleFactory::CleanupMultiHandle(CurlMulti m,
192303}
193304
194305void PooledCurlHandleFactory::SetCurlOptions (CURL* handle) {
195- if (cainfo_) {
196- SetCurlStringOption (handle, CURLOPT_CAINFO, cainfo_->c_str ());
197- }
198- if (capath_) {
199- SetCurlStringOption (handle, CURLOPT_CAPATH, capath_->c_str ());
306+ if (!ca_certs_.empty ()) {
307+ SetCurlStringOption (handle, CURLOPT_CAINFO, nullptr );
308+ SetCurlStringOption (handle, CURLOPT_CAPATH, nullptr );
309+ auto result = curl_easy_setopt (handle, CURLOPT_SSL_CTX_DATA, this );
310+ if (result != CURLE_OK) {
311+ GCP_LOG (FATAL) << internal::InternalError (curl_easy_strerror (result),
312+ GCP_ERROR_INFO ());
313+ }
314+ result =
315+ curl_easy_setopt (handle, CURLOPT_SSL_CTX_FUNCTION, &SslCtxFunction);
316+ if (result != CURLE_OK) {
317+ GCP_LOG (FATAL) << internal::InternalError (curl_easy_strerror (result),
318+ GCP_ERROR_INFO ());
319+ }
320+ } else {
321+ if (cainfo_) {
322+ SetCurlStringOption (handle, CURLOPT_CAINFO, cainfo_->c_str ());
323+ }
324+ if (capath_) {
325+ SetCurlStringOption (handle, CURLOPT_CAPATH, capath_->c_str ());
326+ }
200327 }
201328}
202329
@@ -210,6 +337,16 @@ absl::optional<std::string> PooledCurlHandleFactory::CAPath(Options const& o) {
210337 return o.get <CAPathOption>();
211338}
212339
340+ std::vector<absl::string_view> PooledCurlHandleFactory::CACerts (
341+ Options const & o) {
342+ if (!o.has <experimental::CAInMemoryOption>()) return {};
343+ if (o.get <experimental::CAInMemoryOption>().empty ()) {
344+ GCP_LOG (FATAL) << internal::InvalidArgumentError (
345+ " No CA certificates specified" , GCP_ERROR_INFO ());
346+ }
347+ return o.get <experimental::CAInMemoryOption>();
348+ }
349+
213350GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
214351} // namespace rest_internal
215352} // namespace cloud
0 commit comments