From 97c49c6f294a0b7e931be2692c124bd78fc79946 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Tue, 9 May 2023 19:59:15 +0200 Subject: cTCPLink and cUrlClient accept list of trusted root CAs for TLS. --- src/HTTP/UrlClient.cpp | 85 +++++++++++++++++++++++++++++++++++--------------- src/HTTP/UrlClient.h | 29 ++++++++--------- 2 files changed, 74 insertions(+), 40 deletions(-) (limited to 'src/HTTP') diff --git a/src/HTTP/UrlClient.cpp b/src/HTTP/UrlClient.cpp index ed47341c3..eb52acfee 100644 --- a/src/HTTP/UrlClient.cpp +++ b/src/HTTP/UrlClient.cpp @@ -20,15 +20,18 @@ class cSchemeHandler; using cSchemeHandlerPtr = std::shared_ptr; -/** This is a basic set of callbacks to enable quick implementation of HTTP request. */ + + + namespace { - class cSimpleHTTPCallbacks : + /** Callbacks implementing the blocking UrlClient behavior. */ + class cBlockingHTTPCallbacks : public cUrlClient::cCallbacks { public: - explicit cSimpleHTTPCallbacks(std::shared_ptr a_Event, AString & a_ResponseBody) : + explicit cBlockingHTTPCallbacks(std::shared_ptr a_Event, AString & a_ResponseBody) : m_Event(std::move(a_Event)), m_ResponseBody(a_ResponseBody) { } @@ -73,13 +76,13 @@ public: cUrlClient::cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ) { // Create a new instance of cUrlClientRequest, wrapped in a SharedPtr, so that it has a controlled lifetime. // Cannot use std::make_shared, because the constructor is not public std::shared_ptr ptr (new cUrlClientRequest( - a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) + a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, a_Options )); return ptr->DoRequest(ptr); } @@ -138,6 +141,24 @@ public: return key; } + /** Returns the parsed TrustedRootCAs from the options, or an empty pointer if the option is not set. + Throws a std::runtime_error if CAs are provided, but parsing them fails. */ + cX509CertPtr GetTrustedRootCAs() const + { + auto itr = m_Options.find("TrustedRootCAs"); + if (itr == m_Options.end()) + { + return nullptr; + } + auto Cert = std::make_shared(); + auto Res = Cert->Parse(itr->second.data(), itr->second.size()); + if (Res != 0) + { + throw std::runtime_error(fmt::format("Failed to parse the TrustedRootCAs certificate: {}", Res)); + } + return Cert; + } + protected: /** Method to be used for the request */ @@ -184,14 +205,14 @@ protected: cUrlClient::cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ): m_Method(a_Method), m_Url(a_Url), m_Callbacks(std::move(a_Callbacks)), m_Headers(std::move(a_Headers)), m_Body(a_Body), - m_Options(std::move(a_Options)) + m_Options(a_Options) { m_NumRemainingRedirects = GetStringMapInteger(m_Options, "MaxRedirects", 30); } @@ -299,7 +320,7 @@ public: m_Link = &a_Link; if (m_IsTls) { - m_Link->StartTLSClient(m_ParentRequest.GetOwnCert(), m_ParentRequest.GetOwnPrivKey()); + m_Link->StartTLSClient(m_ParentRequest.GetOwnCert(), m_ParentRequest.GetOwnPrivKey(), m_ParentRequest.GetTrustedRootCAs()); } else { @@ -652,11 +673,11 @@ std::pair cUrlClient::Request( cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ) { return cUrlClientRequest::Request( - a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) + a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, a_Options ); } @@ -669,11 +690,11 @@ std::pair cUrlClient::Get( cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ) { return cUrlClientRequest::Request( - "GET", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) + "GET", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, a_Options ); } @@ -686,11 +707,11 @@ std::pair cUrlClient::Post( cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ) { return cUrlClientRequest::Request( - "POST", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) + "POST", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, a_Options ); } @@ -703,11 +724,11 @@ std::pair cUrlClient::Put( cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ) { return cUrlClientRequest::Request( - "PUT", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) + "PUT", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, a_Options ); } @@ -715,15 +736,24 @@ std::pair cUrlClient::Put( -std::pair cUrlClient::BlockingRequest(const AString & a_Method, const AString & a_URL, AStringMap && a_Headers, const AString & a_Body, AStringMap && a_Options) +std::pair cUrlClient::BlockingRequest( + const AString & a_Method, + const AString & a_URL, + AStringMap && a_Headers, + const AString & a_Body, + const AStringMap & a_Options +) { auto EvtFinished = std::make_shared(); AString Response; - auto Callbacks = std::make_unique(EvtFinished, Response); - auto [Success, ErrorMessage] = cUrlClient::Request(a_Method, a_URL, std::move(Callbacks), std::move(a_Headers), a_Body, std::move(a_Options)); + auto Callbacks = std::make_unique(EvtFinished, Response); + auto [Success, ErrorMessage] = cUrlClient::Request(a_Method, a_URL, std::move(Callbacks), std::move(a_Headers), a_Body, a_Options); if (Success) { - EvtFinished->Wait(); + if (!EvtFinished->Wait(10000)) + { + return std::make_pair(false, "Timeout"); + } } else { @@ -741,9 +771,10 @@ std::pair cUrlClient::BlockingGet( const AString & a_URL, AStringMap a_Headers, const AString & a_Body, - AStringMap a_Options) + const AStringMap & a_Options +) { - return BlockingRequest("GET", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); + return BlockingRequest("GET", a_URL, std::move(a_Headers), a_Body, a_Options); } @@ -754,9 +785,10 @@ std::pair cUrlClient::BlockingPost( const AString & a_URL, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options) + const AStringMap & a_Options +) { - return BlockingRequest("POST", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); + return BlockingRequest("POST", a_URL, std::move(a_Headers), a_Body, a_Options); } @@ -767,9 +799,10 @@ std::pair cUrlClient::BlockingPut( const AString & a_URL, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options) + const AStringMap & a_Options +) { - return BlockingRequest("PUT", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); + return BlockingRequest("PUT", a_URL, std::move(a_Headers), a_Body, a_Options); } diff --git a/src/HTTP/UrlClient.h b/src/HTTP/UrlClient.h index aaff60a87..a73f22521 100644 --- a/src/HTTP/UrlClient.h +++ b/src/HTTP/UrlClient.h @@ -9,6 +9,7 @@ Options that can be set via the Options parameter to the cUrlClient calls: "OwnCert": The client certificate to use, if requested by the server. Any string that can be parsed by cX509Cert. "OwnPrivKey": The private key appropriate for OwnCert. Any string that can be parsed by cCryptoKey. "OwnPrivKeyPassword": The password for OwnPrivKey. If not present or empty, no password is assumed. +"TrustedRootCAs": The trusted root CA certificates (\n-delimited concatenated PEM format) to be used for peer cert verification. If not present, peer cert is not verified. Behavior: - If a redirect is received, and redirection is allowed, the redirection is reported via OnRedirecting() callback @@ -116,16 +117,16 @@ public: cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options ); /** Alias for Request("GET", ...) */ static std::pair Get( const AString & a_URL, cCallbacksPtr && a_Callbacks, - AStringMap && a_Headers = AStringMap(), - const AString & a_Body = AString(), - AStringMap && a_Options = AStringMap() + AStringMap && a_Headers = {}, + const AString & a_Body = {}, + const AStringMap & a_Options = {} ); /** Alias for Request("POST", ...) */ @@ -134,7 +135,7 @@ public: cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options = {} ); /** Alias for Request("PUT", ...) */ @@ -143,7 +144,7 @@ public: cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options = {} ); /** The method will run a thread blocking HTTP request. Any error handling @@ -153,17 +154,17 @@ public: static std::pair BlockingRequest( const AString & a_Method, const AString & a_URL, - AStringMap && a_Headers = AStringMap(), - const AString & a_Body = AString(), - AStringMap && a_Options = AStringMap() + AStringMap && a_Headers = {}, + const AString & a_Body = {}, + const AStringMap & a_Options = {} ); /** Alias for BlockingRequest("GET", ...) */ static std::pair BlockingGet( const AString & a_URL, - AStringMap a_Headers = AStringMap(), - const AString & a_Body = AString(), - AStringMap a_Options = AStringMap() + AStringMap a_Headers = {}, + const AString & a_Body = {}, + const AStringMap & a_Options = {} ); /** Alias for BlockingRequest("POST", ...) */ @@ -171,7 +172,7 @@ public: const AString & a_URL, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options = {} ); /** Alias for BlockingRequest("PUT", ...) */ @@ -179,7 +180,7 @@ public: const AString & a_URL, AStringMap && a_Headers, const AString & a_Body, - AStringMap && a_Options + const AStringMap & a_Options = {} ); }; -- cgit v1.2.3