Add support of setting CAPath and relevant test (#4982)

* Add support of setting CAPath and relevant test

* Renaming macro and update when will it get define
This commit is contained in:
Phoebus Mak 2023-09-29 17:32:47 +01:00 committed by GitHub
parent daa36f1650
commit bf652dcd47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 94 additions and 6 deletions

View File

@ -4,6 +4,9 @@
### Features Added
- [[#4983]](https://github.com/Azure/azure-sdk-for-cpp/issues/4983) Added support for setting `CURLOPT_CAPATH` libcurl option on Linux.
### Breaking Changes
### Bugs Fixed

View File

@ -12,6 +12,14 @@
#include "azure/core/http/http.hpp"
#include "azure/core/http/policies/policy.hpp"
#include "azure/core/http/transport.hpp"
#include "azure/core/platform.hpp"
#if defined(AZ_PLATFORM_LINUX)
#include <openssl/opensslv.h>
#if OPENSSL_VERSION_NUMBER >= 0x00905100L
#define _azure_SUPPORT_SETTING_CAPATH
#endif // OPENSSL_VERSION_NUMBER >= 0x00905100L
#endif // defined(AZ_PLATFORM_LINUX)
namespace Azure { namespace Core { namespace Http {
class CurlNetworkConnection;
@ -122,6 +130,20 @@ namespace Azure { namespace Core { namespace Http {
*/
std::string CAInfo;
#if defined(_azure_SUPPORT_SETTING_CAPATH)
/**
* @brief Path to a directory which holds PEM encoded file, containing the certificate
* authorities sent to libcurl handle directly.
*
* @remark The Azure SDK will not check if the path is valid or not.
*
* @remark The default is the built-in system specific path. More about this option:
* https://curl.se/libcurl/c/CURLOPT_CAPATH.html
*
*/
std::string CAPath;
#endif
/**
* @brief All HTTP requests will keep the connection channel open to the service.
*

View File

@ -1283,6 +1283,12 @@ inline std::string GetConnectionKey(std::string const& host, CurlTransportOption
key.append(",");
key.append(!options.CAInfo.empty() ? options.CAInfo : "0");
key.append(",");
#if defined(_azure_SUPPORT_SETTING_CAPATH)
key.append(!options.CAPath.empty() ? options.CAPath : "0");
#else
key.append("0"); // CAPath is always empty on Windows;
#endif
key.append(",");
key.append(
options.Proxy.HasValue() ? (options.Proxy.Value().empty() ? "NoProxy" : options.Proxy.Value())
: "0");
@ -2314,6 +2320,19 @@ CurlConnection::CurlConnection(
}
}
#if defined(_azure_SUPPORT_SETTING_CAPATH)
if (!options.CAPath.empty())
{
if (!SetLibcurlOption(m_handle, CURLOPT_CAPATH, options.CAPath.c_str(), &result))
{
throw Azure::Core::Http::TransportException(
_detail::DefaultFailedToGetNewConnectionTemplate + hostDisplayName
+ ". Failed to set CA path to:" + options.CAPath + ". "
+ std::string(curl_easy_strerror(result)));
}
}
#endif
#if LIBCURL_VERSION_NUM >= 0x074D00 // 7.77.0
if (!options.SslOptions.PemEncodedExpectedRootCertificates.empty())
{

View File

@ -56,7 +56,9 @@ namespace Azure { namespace Core { namespace Test {
Azure::Core::Http::Request req(
Azure::Core::Http::HttpMethod::Get, Azure::Core::Url(AzureSdkHttpbinServer::Get()));
std::string const expectedConnectionKey(CreateConnectionKey(
AzureSdkHttpbinServer::Schema(), AzureSdkHttpbinServer::Host(), ",0,0,0,0,1,1,0,0,0,0"));
AzureSdkHttpbinServer::Schema(),
AzureSdkHttpbinServer::Host(),
",0,0,0,0,0,1,1,0,0,0,0"));
{
// Creating a new connection with default options
@ -125,7 +127,7 @@ namespace Azure { namespace Core { namespace Test {
// Now test that using a different connection config won't re-use the same connection
std::string const secondExpectedKey = AzureSdkHttpbinServer::Schema() + "://"
+ AzureSdkHttpbinServer::Host() + ",0,0,0,0,1,0,0,0,0,200000";
+ AzureSdkHttpbinServer::Host() + ",0,0,0,0,0,1,0,0,0,0,200000";
{
// Creating a new connection with options
Azure::Core::Http::CurlTransportOptions options;
@ -436,7 +438,7 @@ namespace Azure { namespace Core { namespace Test {
std::string const expectedConnectionKey(CreateConnectionKey(
AzureSdkHttpbinServer::Schema(),
AzureSdkHttpbinServer::Host(),
",0,0,0,0,1,1,0,0,0,0"));
",0,0,0,0,0,1,1,0,0,0,0"));
// Creating a new connection with default options
auto connection = Azure::Core::Http::_detail::CurlConnectionPool::g_curlConnectionPool
@ -474,7 +476,7 @@ namespace Azure { namespace Core { namespace Test {
std::string const expectedConnectionKey(CreateConnectionKey(
AzureSdkHttpbinServer::Schema(),
AzureSdkHttpbinServer::Host(),
":443,0,0,0,0,1,1,0,0,0,0"));
":443,0,0,0,0,0,1,1,0,0,0,0"));
// Creating a new connection with default options
auto connection = Azure::Core::Http::_detail::CurlConnectionPool::g_curlConnectionPool
@ -513,7 +515,7 @@ namespace Azure { namespace Core { namespace Test {
std::string const expectedConnectionKey(CreateConnectionKey(
AzureSdkHttpbinServer::Schema(),
AzureSdkHttpbinServer::Host(),
",0,0,0,0,1,1,0,0,0,0"));
",0,0,0,0,0,1,1,0,0,0,0"));
// Creating a new connection with default options
auto connection = Azure::Core::Http::_detail::CurlConnectionPool::g_curlConnectionPool
@ -550,7 +552,7 @@ namespace Azure { namespace Core { namespace Test {
std::string const expectedConnectionKey(CreateConnectionKey(
AzureSdkHttpbinServer::Schema(),
AzureSdkHttpbinServer::Host(),
":443,0,0,0,0,1,1,0,0,0,0"));
":443,0,0,0,0,0,1,1,0,0,0,0"));
// Creating a new connection with default options
auto connection = Azure::Core::Http::_detail::CurlConnectionPool::g_curlConnectionPool

View File

@ -12,6 +12,7 @@
#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER)
#include "azure/core/http/curl_transport.hpp"
#include "openssl/x509.h"
#endif
#include "transport_adapter_base_test.hpp"
@ -234,6 +235,47 @@ namespace Azure { namespace Core { namespace Test {
.ConnectionPoolIndex.clear());
}
#if defined(_azure_SUPPORT_SETTING_CAPATH)
TEST(CurlTransportOptions, setCADirectory)
{
Azure::Core::Http::CurlTransportOptions curlOptions;
// openssl default cert location will be used only if environment variable SSL_CERT_DIR
// is not set
const char* ca = getenv(X509_get_default_cert_dir_env());
if (ca)
{
curlOptions.CAPath = ca;
}
else
{
curlOptions.CAPath = X509_get_default_cert_dir();
}
auto transportAdapter = std::make_shared<Azure::Core::Http::CurlTransport>(curlOptions);
Azure::Core::Http::Policies::TransportOptions options;
options.Transport = transportAdapter;
auto transportPolicy
= std::make_unique<Azure::Core::Http::Policies::_internal::TransportPolicy>(options);
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
policies.emplace_back(std::move(transportPolicy));
Azure::Core::Http::_internal::HttpPipeline pipeline(policies);
// Use HTTPS
Azure::Core::Url url(AzureSdkHttpbinServer::Get());
Azure::Core::Http::Request request(Azure::Core::Http::HttpMethod::Get, url);
std::unique_ptr<Azure::Core::Http::RawResponse> response;
EXPECT_NO_THROW(response = pipeline.Send(request, Azure::Core::Context::ApplicationContext));
EXPECT_EQ(response->GetStatusCode(), Azure::Core::Http::HttpStatusCode::Ok);
// Clean the connection from the pool *Windows fails to clean if we leave to be clean upon
// app-destruction
EXPECT_NO_THROW(Azure::Core::Http::_detail::CurlConnectionPool::g_curlConnectionPool
.ConnectionPoolIndex.clear());
}
#endif
TEST(CurlTransportOptions, httpsDefault)
{
auto transportAdapter = std::make_shared<Azure::Core::Http::CurlTransport>();