From 800f1c0bc5bd4632bd0f246c756283cc47d31a34 Mon Sep 17 00:00:00 2001 From: x12xx12x <44411062+12xx12@users.noreply.github.com> Date: Sun, 26 Mar 2023 14:48:06 +0200 Subject: Auth SSL Fixes - Fixed Login Breaking bug - Auth and MojangAPI now use UrlClient - fixed bug in UrlClient where one letter was missing in the HTTP Header - added function to verify Urls from config files and error handling on bad Urls in config for Auth --- src/Bindings/LuaTCPLink.cpp | 4 +- src/HTTP/UrlClient.cpp | 133 +++++++++++++++++--- src/HTTP/UrlClient.h | 48 ++++++- src/HTTP/UrlParser.cpp | 11 ++ src/HTTP/UrlParser.h | 3 + src/OSSupport/Network.h | 3 +- src/OSSupport/TCPLinkImpl.cpp | 5 +- src/OSSupport/TCPLinkImpl.h | 3 +- src/Protocol/Authenticator.cpp | 136 ++++++++------------ src/Protocol/Authenticator.h | 4 +- src/Protocol/MojangAPI.cpp | 276 ++++------------------------------------- src/Protocol/MojangAPI.h | 11 +- src/mbedTLS++/CMakeLists.txt | 1 + src/mbedTLS++/RootCA.h | 97 +++++++++++++++ src/mbedTLS++/SslConfig.cpp | 14 +-- src/mbedTLS++/SslContext.cpp | 4 +- src/mbedTLS++/SslContext.h | 2 +- tests/HTTP/UrlClientTest.cpp | 4 +- 18 files changed, 380 insertions(+), 379 deletions(-) create mode 100644 src/mbedTLS++/RootCA.h diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index 498691806..1e8f99410 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -192,7 +192,9 @@ AString cLuaTCPLink::StartTLSClient( return fmt::format(FMT_STRING("Cannot parse client private key: -0x{:x}"), -res); } } - return link->StartTLSClient(ownCert, ownPrivKey); + + // TODO : Provide a way to pass SNI from Lua too. + return link->StartTLSClient(ownCert, ownPrivKey, ""); } return ""; } diff --git a/src/HTTP/UrlClient.cpp b/src/HTTP/UrlClient.cpp index 13a882205..3985e0707 100644 --- a/src/HTTP/UrlClient.cpp +++ b/src/HTTP/UrlClient.cpp @@ -4,11 +4,12 @@ // Implements the cUrlClient class for high-level URL interaction #include "Globals.h" -#include "UrlClient.h" -#include "UrlParser.h" -#include "HTTPMessageParser.h" -#include "../mbedTLS++/X509Cert.h" -#include "../mbedTLS++/CryptoKey.h" + +#include "HTTP/UrlClient.h" +#include "HTTP/UrlParser.h" +#include "HTTP/HTTPMessageParser.h" +#include "mbedTLS++/X509Cert.h" +#include "mbedTLS++/CryptoKey.h" @@ -16,7 +17,44 @@ // fwd: class cSchemeHandler; -typedef std::shared_ptr cSchemeHandlerPtr; +using cSchemeHandlerPtr = std::shared_ptr; + + +/** This is a basic set of callbacks to enable quick implementation of HTTP request. */ +namespace +{ + class cSimpleHTTPCallbacks : + public cUrlClient::cCallbacks + { + public: + + explicit cSimpleHTTPCallbacks(std::shared_ptr a_Event, AString & a_ResponseBody) : + m_Event(std::move(a_Event)), m_ResponseBody(a_ResponseBody) + { + } + + void OnBodyFinished() override + { + m_Event->Set(); + } + + void OnError(const AString & a_ErrorMsg) override + { + LOGERROR("%s %d: HTTP Error: %s", __FILE__, __LINE__, a_ErrorMsg.c_str()); + m_Event->Set(); + } + + void OnBodyData(const void * a_Data, size_t a_Size) override + { + m_ResponseBody.append(static_cast(a_Data), a_Size); + } + + std::shared_ptr m_Event; + + /** The accumulator for the partial body data, so that OnBodyFinished() can send the entire thing at once. */ + AString & m_ResponseBody; + }; +} @@ -261,7 +299,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.m_UrlHost); } else { @@ -371,7 +409,7 @@ public: return; } } - m_ParentRequest.GetCallbacks().OnStatusLine(a_FirstLine.substr(1, idxFirstSpace), resultCode, a_FirstLine.substr(idxSecondSpace + 1)); + m_ParentRequest.GetCallbacks().OnStatusLine(a_FirstLine.substr(0, idxFirstSpace), resultCode, a_FirstLine.substr(idxSecondSpace + 1)); } @@ -613,12 +651,12 @@ std::pair cUrlClient::Request( const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, AStringMap && a_Options ) { return cUrlClientRequest::Request( - a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), std::move(a_Body), std::move(a_Options) + a_Method, a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) ); } @@ -629,13 +667,13 @@ std::pair cUrlClient::Request( std::pair cUrlClient::Get( const AString & a_URL, cCallbacksPtr && a_Callbacks, - AStringMap a_Headers, + AStringMap && a_Headers, const AString & a_Body, - AStringMap a_Options + AStringMap && a_Options ) { return cUrlClientRequest::Request( - "GET", a_URL, std::move(a_Callbacks), std::move(a_Headers), std::move(a_Body), std::move(a_Options) + "GET", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) ); } @@ -647,12 +685,12 @@ std::pair cUrlClient::Post( const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, AStringMap && a_Options ) { return cUrlClientRequest::Request( - "POST", a_URL, std::move(a_Callbacks), std::move(a_Headers), std::move(a_Body), std::move(a_Options) + "POST", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) ); } @@ -664,12 +702,12 @@ std::pair cUrlClient::Put( const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, AStringMap && a_Options ) { return cUrlClientRequest::Request( - "PUT", a_URL, std::move(a_Callbacks), std::move(a_Headers), std::move(a_Body), std::move(a_Options) + "PUT", a_URL, std::move(a_Callbacks), std::move(a_Headers), a_Body, std::move(a_Options) ); } @@ -677,3 +715,64 @@ 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) +{ + 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)); + if (Success) + { + EvtFinished->Wait(); + } + else + { + LOGWARNING("%s: HTTP error: %s", __FUNCTION__, ErrorMessage.c_str()); + return std::make_pair(false, AString()); + } + return std::make_pair(true, Response); +} + + + + + +std::pair cUrlClient::BlockingGet( + const AString & a_URL, + AStringMap a_Headers, + const AString & a_Body, + AStringMap a_Options) +{ + return BlockingRequest("GET", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); +} + + + + + +std::pair cUrlClient::BlockingPost( + const AString & a_URL, + AStringMap && a_Headers, + const AString & a_Body, + AStringMap && a_Options) +{ + return BlockingRequest("POST", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); +} + + + + + +std::pair cUrlClient::BlockingPut( + const AString & a_URL, + AStringMap && a_Headers, + const AString & a_Body, + AStringMap && a_Options) +{ + return BlockingRequest("PUT", a_URL, std::move(a_Headers), a_Body, std::move(a_Options)); +} + + + + + diff --git a/src/HTTP/UrlClient.h b/src/HTTP/UrlClient.h index 5f737b057..aaff60a87 100644 --- a/src/HTTP/UrlClient.h +++ b/src/HTTP/UrlClient.h @@ -86,7 +86,7 @@ public: for such a response; instead, the redirect is silently attempted. */ virtual void OnRedirecting(const AString & a_NewLocation) {} }; - typedef std::unique_ptr cCallbacksPtr; + using cCallbacksPtr = std::unique_ptr; /** Used for HTTP status codes. */ @@ -115,7 +115,7 @@ public: const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, AStringMap && a_Options ); @@ -123,9 +123,9 @@ public: static std::pair Get( const AString & a_URL, cCallbacksPtr && a_Callbacks, - AStringMap a_Headers = AStringMap(), + AStringMap && a_Headers = AStringMap(), const AString & a_Body = AString(), - AStringMap a_Options = AStringMap() + AStringMap && a_Options = AStringMap() ); /** Alias for Request("POST", ...) */ @@ -133,7 +133,7 @@ public: const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, AStringMap && a_Options ); @@ -142,7 +142,43 @@ public: const AString & a_URL, cCallbacksPtr && a_Callbacks, AStringMap && a_Headers, - AString && a_Body, + const AString & a_Body, + AStringMap && a_Options + ); + + /** The method will run a thread blocking HTTP request. Any error handling + is done inside the functions. Check the LOG or stdout for any occurring + errors. Other parameters are the same as for the regular request method. + The return value is if the request was successful and the response. */ + 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() + ); + + /** 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() + ); + + /** Alias for BlockingRequest("POST", ...) */ + static std::pair BlockingPost( + const AString & a_URL, + AStringMap && a_Headers, + const AString & a_Body, + AStringMap && a_Options + ); + + /** Alias for BlockingRequest("PUT", ...) */ + static std::pair BlockingPut( + const AString & a_URL, + AStringMap && a_Headers, + const AString & a_Body, AStringMap && a_Options ); }; diff --git a/src/HTTP/UrlParser.cpp b/src/HTTP/UrlParser.cpp index 85b1cd216..f89aaad45 100644 --- a/src/HTTP/UrlParser.cpp +++ b/src/HTTP/UrlParser.cpp @@ -4,6 +4,7 @@ // Implements the cUrlParser class that parses string URL into individual parts #include "Globals.h" + #include "UrlParser.h" @@ -198,3 +199,13 @@ std::pair cUrlParser::Parse( +std::pair cUrlParser::Validate(const AString & a_Url) +{ + AString UrlScheme, UrlUsername, UrlPassword, UrlHost, UrlPath, UrlQuery, UrlFragment; + UInt16 Port; + return Parse(a_Url, UrlScheme, UrlUsername, UrlPassword, UrlHost, Port, UrlPath, UrlQuery, UrlFragment); +} + + + + diff --git a/src/HTTP/UrlParser.h b/src/HTTP/UrlParser.h index 15a63e05d..76018460a 100644 --- a/src/HTTP/UrlParser.h +++ b/src/HTTP/UrlParser.h @@ -51,6 +51,9 @@ public: AString & a_Query, AString & a_Fragment ); + + /** Checks if the supplied URL is valid */ + static std::pair Validate(const AString & a_Url); }; diff --git a/src/OSSupport/Network.h b/src/OSSupport/Network.h index 32163b710..32c7ecdd0 100644 --- a/src/OSSupport/Network.h +++ b/src/OSSupport/Network.h @@ -113,7 +113,8 @@ public: Returns empty string on success, non-empty error description on failure. */ virtual AString StartTLSClient( cX509CertPtr a_OwnCert, - cCryptoKeyPtr a_OwnPrivKey + cCryptoKeyPtr a_OwnPrivKey, + const std::string_view hostname ) = 0; /** Starts a TLS handshake as a server connection. diff --git a/src/OSSupport/TCPLinkImpl.cpp b/src/OSSupport/TCPLinkImpl.cpp index c93a1879d..86fa24a63 100644 --- a/src/OSSupport/TCPLinkImpl.cpp +++ b/src/OSSupport/TCPLinkImpl.cpp @@ -237,7 +237,8 @@ void cTCPLinkImpl::Close(void) AString cTCPLinkImpl::StartTLSClient( cX509CertPtr a_OwnCert, - cCryptoKeyPtr a_OwnPrivKey + cCryptoKeyPtr a_OwnPrivKey, + const std::string_view hostname ) { // Check preconditions: @@ -263,6 +264,8 @@ AString cTCPLinkImpl::StartTLSClient( m_TlsContext->Initialize(true); } + m_TlsContext->SetExpectedPeerName(hostname); + m_TlsContext->SetSelf(cLinkTlsContextWPtr(m_TlsContext)); // Start the handshake: diff --git a/src/OSSupport/TCPLinkImpl.h b/src/OSSupport/TCPLinkImpl.h index 4b8c6f308..d26b1e358 100644 --- a/src/OSSupport/TCPLinkImpl.h +++ b/src/OSSupport/TCPLinkImpl.h @@ -68,7 +68,8 @@ public: virtual void Close(void) override; virtual AString StartTLSClient( cX509CertPtr a_OwnCert, - cCryptoKeyPtr a_OwnPrivKey + cCryptoKeyPtr a_OwnPrivKey, + const std::string_view hostname ) override; virtual AString StartTLSServer( cX509CertPtr a_OwnCert, diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index b0669a354..00b09c30d 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -1,25 +1,25 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules -#include "Authenticator.h" -#include "MojangAPI.h" -#include "../Root.h" -#include "../Server.h" -#include "../ClientHandle.h" -#include "../UUID.h" - -#include "../IniFile.h" -#include "../JsonUtils.h" -#include "json/json.h" +#include "Protocol/Authenticator.h" -#include "../mbedTLS++/BlockingSslClientSocket.h" +#include "ClientHandle.h" +#include "HTTP/UrlClient.h" +#include "HTTP/UrlParser.h" +#include "IniFile.h" +#include "JsonUtils.h" +#include "json/json.h" +#include "Protocol/MojangAPI.h" +#include "Root.h" +#include "Server.h" +#include "UUID.h" -#define DEFAULT_AUTH_SERVER "sessionserver.mojang.com" -#define DEFAULT_AUTH_ADDRESS "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%" +constexpr char DEFAULT_AUTH_SERVER[] = "sessionserver.mojang.com"; +constexpr char DEFAULT_AUTH_ADDRESS[] = "/session/minecraft/hasJoined?username=%USERNAME%&serverId=%SERVERID%"; @@ -51,6 +51,36 @@ void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings) m_Server = a_Settings.GetValueSet ("Authentication", "Server", DEFAULT_AUTH_SERVER); m_Address = a_Settings.GetValueSet ("Authentication", "Address", DEFAULT_AUTH_ADDRESS); m_ShouldAuthenticate = a_Settings.GetValueSetB("Authentication", "Authenticate", true); + + // prepend https:// if missing + constexpr std::string_view HttpPrefix = "http://"; + constexpr std::string_view HttpsPrefix = "https://"; + + if ( + (std::string_view(m_Server).substr(0, HttpPrefix.size()) != HttpPrefix) && + (std::string_view(m_Server).substr(0, HttpsPrefix.size()) != HttpsPrefix) + ) + { + m_Server = "https://" + m_Server; + } + + { + auto [IsSuccessfull, ErrorMessage] = cUrlParser::Validate(m_Server); + if (!IsSuccessfull) + { + LOGWARNING("%s %d: Supplied invalid URL for configuration value [Authentication: Server]: \"%s\", using default! Error: %s", __FUNCTION__, __LINE__, m_Server.c_str(), ErrorMessage.c_str()); + m_Server = DEFAULT_AUTH_SERVER; + } + } + + { + auto [IsSuccessfull, ErrorMessage] = cUrlParser::Validate(m_Server); + if (!IsSuccessfull) + { + LOGWARNING("%s %d: Supplied invalid URL for configuration value [Authentication: Address]: \"%s\", using default! Error: %s", __FUNCTION__, __LINE__, m_Address.c_str(), ErrorMessage.c_str()); + m_Address = DEFAULT_AUTH_ADDRESS; + } + } } @@ -143,7 +173,7 @@ void cAuthenticator::Execute(void) -bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties) +bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties) const { LOGD("Trying to authenticate user %s", a_UserName.c_str()); @@ -152,39 +182,13 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S ReplaceURL(ActualAddress, "%USERNAME%", a_UserName); ReplaceURL(ActualAddress, "%SERVERID%", a_ServerId); - AString Request; - Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; - Request += "Host: " + m_Server + "\r\n"; - Request += "User-Agent: Cuberite\r\n"; - Request += "Connection: close\r\n"; - Request += "\r\n"; - - AString Response; - if (!cMojangAPI::SecureRequest(m_Server, Request, Response)) + // Create and send the HTTP request + auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); + if (!IsSuccessfull) { return false; } - // Check the HTTP status line: - const AString Prefix("HTTP/1.1 200 OK"); - AString HexDump; - if (Response.compare(0, Prefix.size(), Prefix)) - { - LOGINFO("User %s failed to auth, bad HTTP status line received", a_UserName.c_str()); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - return false; - } - - // Erase the HTTP headers from the response: - size_t idxHeadersEnd = Response.find("\r\n\r\n"); - if (idxHeadersEnd == AString::npos) - { - LOGINFO("User %s failed to authenticate, bad HTTP response header received", a_UserName.c_str()); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - return false; - } - Response.erase(0, idxHeadersEnd + 4); - // Parse the Json response: if (Response.empty()) { @@ -193,14 +197,14 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S Json::Value root; if (!JsonUtils::ParseString(Response, root)) { - LOGWARNING("cAuthenticator: Cannot parse received data (authentication) to JSON!"); + LOGWARNING("%s: Cannot parse received data (authentication) to JSON!", __FUNCTION__); return false; } a_UserName = root.get("name", "Unknown").asString(); a_Properties = root["properties"]; if (!a_UUID.FromString(root.get("id", "").asString())) { - LOGWARNING("cAuthenticator: Recieved invalid UUID format"); + LOGWARNING("%s: Received invalid UUID format", __FUNCTION__); return false; } @@ -212,9 +216,9 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S +#ifdef ENABLE_PROPERTIES - -/* In case we want to export this function to the plugin API later - don't forget to add the relevant INI configuration lines for DEFAULT_PROPERTIES_ADDRESS +/* In case we want to export this function to the plugin API later - don't forget to add the relevant INI configuration lines for DEFAULT_PROPERTIES_ADDRESS */ #define DEFAULT_PROPERTIES_ADDRESS "/session/minecraft/profile/%UUID%" @@ -225,42 +229,12 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a { LOGD("Trying to get properties for user %s", a_UUID.c_str()); - // Create the GET request: - AString ActualAddress = m_PropertiesAddress; - ReplaceString(ActualAddress, "%UUID%", a_UUID); - - AString Request; - Request += "GET " + ActualAddress + " HTTP/1.0\r\n"; - Request += "Host: " + m_Server + "\r\n"; - Request += "User-Agent: Cuberite\r\n"; - Request += "Connection: close\r\n"; - Request += "\r\n"; - - AString Response; - if (!ConnectSecurelyToAddress(StarfieldCACert(), m_Server, Request, Response)) - { - return false; - } - - // Check the HTTP status line: - const AString Prefix("HTTP/1.1 200 OK"); - AString HexDump; - if (Response.compare(0, Prefix.size(), Prefix)) - { - LOGINFO("Failed to get properties for user %s, bad HTTP status line received", a_UUID.c_str()); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - return false; - } - - // Erase the HTTP headers from the response: - size_t idxHeadersEnd = Response.find("\r\n\r\n"); - if (idxHeadersEnd == AString::npos) + // Create and send the HTTP request + auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); + if (!IsSuccessfull) { - LOGINFO("Failed to get properties for user %s, bad HTTP response header received", a_UUID.c_str()); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); return false; } - Response.erase(0, idxHeadersEnd + 4); // Parse the Json response: if (Response.empty()) @@ -279,7 +253,7 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a a_Properties = root["properties"]; return true; } -*/ +#endif diff --git a/src/Protocol/Authenticator.h b/src/Protocol/Authenticator.h index 66dcf20b4..1987de8b3 100644 --- a/src/Protocol/Authenticator.h +++ b/src/Protocol/Authenticator.h @@ -66,7 +66,7 @@ private: } }; - typedef std::deque cUserList; + using cUserList = std::deque; cCriticalSection m_CS; cUserList m_Queue; @@ -89,7 +89,7 @@ private: /** Returns true if the user authenticated okay, false on error Returns the case-corrected username, UUID, and properties (eg. skin). */ - bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties); + bool AuthWithYggdrasil(AString & a_UserName, const AString & a_ServerId, cUUID & a_UUID, Json::Value & a_Properties) const; }; diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 6cedf66d2..37c1b0911 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -5,17 +5,18 @@ #include "Globals.h" #include "MojangAPI.h" + +#include "HTTP/UrlClient.h" +#include "IniFile.h" +#include "JsonUtils.h" +#include "json/json.h" +#include "mbedTLS++/BlockingSslClientSocket.h" +#include "mbedTLS++/SslConfig.h" +#include "OSSupport/IsThread.h" +#include "RankManager.h" +#include "Root.h" #include "SQLiteCpp/Database.h" #include "SQLiteCpp/Statement.h" -#include "../IniFile.h" -#include "../JsonUtils.h" -#include "json/json.h" -#include "../mbedTLS++/BlockingSslClientSocket.h" -#include "../mbedTLS++/SslConfig.h" -#include "../RankManager.h" -#include "../OSSupport/IsThread.h" -#include "../Root.h" - @@ -30,122 +31,10 @@ const int MAX_PER_QUERY = 100; -#define DEFAULT_NAME_TO_UUID_SERVER "api.mojang.com" -#define DEFAULT_NAME_TO_UUID_ADDRESS "/profiles/minecraft" -#define DEFAULT_UUID_TO_PROFILE_SERVER "sessionserver.mojang.com" -#define DEFAULT_UUID_TO_PROFILE_ADDRESS "/session/minecraft/profile/%UUID%?unsigned=false" - - - - - -/** Returns the CA certificates that should be trusted for Mojang-related connections. */ -static cX509CertPtr GetCACerts(void) -{ - static const char CertString[] = - // DigiCert Global Root CA (sessionserver.mojang.com) - // Downloaded from https://www.digicert.com/kb/digicert-root-certificates.htm - "-----BEGIN CERTIFICATE-----\n" - "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" - "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" - "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" - "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" - "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" - "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" - "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" - "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" - "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" - "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" - "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" - "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" - "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" - "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" - "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" - "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" - "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" - "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" - "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" - "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" - "-----END CERTIFICATE-----\n" - - // Amazon Root CA 1 (api.mojang.com) - // Downloaded from https://www.amazontrust.com/repository/ - "-----BEGIN CERTIFICATE-----\n" - "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" - "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" - "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" - "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" - "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" - "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" - "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" - "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" - "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" - "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" - "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" - "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" - "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" - "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" - "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" - "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" - "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" - "rqXRfboQnoZsG4q5WTP468SQvvG5\n" - "-----END CERTIFICATE-----\n" - - // AAA Certificate Services (authserver.ely.by GH#4832) - // Downloaded from https://www.tbs-certificates.co.uk/FAQ/en/Comodo_AAA_Certificate_Services.html - "-----BEGIN CERTIFICATE-----\n" - "MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n" - "MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n" - "GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n" - "YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n" - "MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" - "BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n" - "GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" - "ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n" - "BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n" - "3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n" - "YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n" - "rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n" - "ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n" - "oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n" - "MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n" - "QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n" - "b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n" - "AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n" - "GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n" - "Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n" - "G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n" - "l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n" - "smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n" - "-----END CERTIFICATE-----\n" - ; - - static auto X509Cert = [&]() - { - auto Cert = std::make_shared(); - VERIFY(0 == Cert->Parse(CertString, sizeof(CertString))); - return Cert; - }(); - - return X509Cert; -} - - - - - -/** Returns the config to be used for secure requests. */ -static std::shared_ptr GetSslConfig() -{ - static const std::shared_ptr Config = []() - { - auto Conf = cSslConfig::MakeDefaultConfig(true); - Conf->SetCACerts(GetCACerts()); - Conf->SetAuthMode(eSslAuthMode::Required); - return Conf; - }(); - return Config; -} +constexpr char DEFAULT_NAME_TO_UUID_SERVER[] = "api.mojang.com"; +constexpr char DEFAULT_NAME_TO_UUID_ADDRESS[] = "/profiles/minecraft"; +constexpr char DEFAULT_UUID_TO_PROFILE_SERVER[] = "sessionserver.mojang.com"; +constexpr char DEFAULT_UUID_TO_PROFILE_ADDRESS[] = "/session/minecraft/profile/%UUID%?unsigned=false"; @@ -188,8 +77,7 @@ cMojangAPI::sProfile::sProfile( for (Json::UInt i = 0; i < Size; i++) { const Json::Value & Prop = a_Properties[i]; - AString PropName = Prop.get("name", "").asString(); - if (PropName != "textures") + if (Prop.get("name", "").asString() != "textures") { continue; } @@ -432,61 +320,6 @@ void cMojangAPI::AddPlayerProfile(const AString & a_PlayerName, const cUUID & a_ -bool cMojangAPI::SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response) -{ - // Connect the socket: - cBlockingSslClientSocket Socket; - Socket.SetSslConfig(GetSslConfig()); - Socket.SetExpectedPeerName(a_ServerName); - if (!Socket.Connect(a_ServerName, 443)) - { - LOGWARNING("%s: Can't connect to %s: %s", __FUNCTION__, a_ServerName.c_str(), Socket.GetLastErrorText().c_str()); - return false; - } - - if (!Socket.Send(a_Request.c_str(), a_Request.size())) - { - LOGWARNING("%s: Writing SSL data failed: %s", __FUNCTION__, Socket.GetLastErrorText().c_str()); - return false; - } - - // Read the HTTP response: - unsigned char buf[1024]; - - for (;;) - { - int ret = Socket.Receive(buf, sizeof(buf)); - - if ((ret == MBEDTLS_ERR_SSL_WANT_READ) || (ret == MBEDTLS_ERR_SSL_WANT_WRITE)) - { - // This value should never be returned, it is handled internally by cBlockingSslClientSocket - LOGWARNING("%s: SSL reading failed internally", __FUNCTION__); - return false; - } - if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) - { - break; - } - if (ret < 0) - { - LOGWARNING("%s: SSL reading failed: -0x%x", __FUNCTION__, -ret); - return false; - } - if (ret == 0) - { - break; - } - - a_Response.append(reinterpret_cast(buf), static_cast(ret)); - } - - return true; -} - - - - - void cMojangAPI::LoadCachesFromDisk(void) { try @@ -641,7 +474,8 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) // Create the request body - a JSON containing up to MAX_PER_QUERY playernames: Json::Value root; int Count = 0; - AStringVector::iterator itr = a_NamesToQuery.begin(), end = a_NamesToQuery.end(); + auto itr = a_NamesToQuery.begin(); + auto end = a_NamesToQuery.end(); for (; (itr != end) && (Count < MAX_PER_QUERY); ++itr, ++Count) { Json::Value req(*itr); @@ -650,43 +484,13 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) a_NamesToQuery.erase(a_NamesToQuery.begin(), itr); auto RequestBody = JsonUtils::WriteFastString(root); - // Create the HTTP request: - AString Request; - Request += "POST " + m_NameToUUIDAddress + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding - Request += "Host: " + m_NameToUUIDServer + "\r\n"; - Request += "User-Agent: Cuberite\r\n"; - Request += "Connection: close\r\n"; - Request += "Content-Type: application/json\r\n"; - Request += fmt::format(FMT_STRING("Content-Length: {}\r\n"), RequestBody.length()); - Request += "\r\n"; - Request += RequestBody; - - // Get the response from the server: - AString Response; - if (!SecureRequest(m_NameToUUIDServer, Request, Response)) + // Create and send the HTTP request + auto [IsSuccessfull, Response] = cUrlClient::BlockingPost(m_NameToUUIDAddress, AStringMap(), std::move(RequestBody), AStringMap()); + if (!IsSuccessfull) { continue; } - - // Check the HTTP status line: - const AString Prefix("HTTP/1.1 200 OK"); AString HexDump; - if (Response.compare(0, Prefix.size(), Prefix)) - { - LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - continue; - } - - // Erase the HTTP headers from the response: - size_t idxHeadersEnd = Response.find("\r\n\r\n"); - if (idxHeadersEnd == AString::npos) - { - LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - continue; - } - Response.erase(0, idxHeadersEnd + 4); // Parse the returned string into Json: AString ParseError; @@ -762,49 +566,23 @@ void cMojangAPI::QueryUUIDToProfile(const cUUID & a_UUID) AString Address = m_UUIDToProfileAddress; ReplaceURL(Address, "%UUID%", a_UUID.ToShortString()); - // Create the HTTP request: - AString Request; - Request += "GET " + Address + " HTTP/1.0\r\n"; // We need to use HTTP 1.0 because we don't handle Chunked transfer encoding - Request += "Host: " + m_UUIDToProfileServer + "\r\n"; - Request += "User-Agent: Cuberite\r\n"; - Request += "Connection: close\r\n"; - Request += "Content-Length: 0\r\n"; - Request += "\r\n"; - - // Get the response from the server: - AString Response; - if (!SecureRequest(m_UUIDToProfileServer, Request, Response)) + // Create and send the HTTP request + auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(Address); + if (!IsSuccessfull) { return; } - // Check the HTTP status line: - const AString Prefix("HTTP/1.1 200 OK"); - AString HexDump; - if (Response.compare(0, Prefix.size(), Prefix)) - { - LOGINFO("%s failed: bad HTTP status line received", __FUNCTION__); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - return; - } - - // Erase the HTTP headers from the response: - size_t idxHeadersEnd = Response.find("\r\n\r\n"); - if (idxHeadersEnd == AString::npos) - { - LOGINFO("%s failed: bad HTTP response header received", __FUNCTION__); - LOGD("Response: \n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); - return; - } - Response.erase(0, idxHeadersEnd + 4); - // Parse the returned string into Json: Json::Value root; AString ParseError; if (!JsonUtils::ParseString(Response, root, &ParseError) || !root.isObject()) { LOGWARNING("%s failed: Cannot parse received data (NameToUUID) to JSON: \"%s\"", __FUNCTION__, ParseError); +#ifdef NDEBUG + AString HexDump; LOGD("Response body:\n%s", CreateHexDump(HexDump, Response.data(), Response.size(), 16).c_str()); +#endif return; } @@ -891,7 +669,7 @@ void cMojangAPI::Update(void) std::vector ProfileUUIDs; { cCSLock Lock(m_CSUUIDToProfile); - for (auto & UUIDToProfile : m_UUIDToProfile) + for (const auto & UUIDToProfile : m_UUIDToProfile) { if (UUIDToProfile.second.m_DateTime < LimitDateTime) { diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h index 4d1751f1c..f9267fefe 100644 --- a/src/Protocol/MojangAPI.h +++ b/src/Protocol/MojangAPI.h @@ -11,7 +11,7 @@ #include -#include "../UUID.h" +#include "UUID.h" @@ -42,11 +42,6 @@ public: Loads cached results from disk. */ void Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth); - /** Connects to the specified server using SSL, sends the given request and receives the response. - Checks Mojang certificates using the hard-coded Starfield root CA certificate. - Returns true if all was successful, false on failure. */ - static bool SecureRequest(const AString & a_ServerName, const AString & a_Request, AString & a_Response); - /** Converts a player name into a UUID. The UUID will be nil on error. If a_UseOnlyCached is true, the function only consults the cached values. @@ -131,8 +126,8 @@ protected: Int64 a_DateTime ); }; - typedef std::map cProfileMap; - typedef std::map cUUIDProfileMap; + using cProfileMap = std::map; + using cUUIDProfileMap = std::map; /** The server to connect to when converting player names to UUIDs. For example "api.mojang.com". */ diff --git a/src/mbedTLS++/CMakeLists.txt b/src/mbedTLS++/CMakeLists.txt index 42e0fc8b2..dcb5d23a0 100644 --- a/src/mbedTLS++/CMakeLists.txt +++ b/src/mbedTLS++/CMakeLists.txt @@ -25,6 +25,7 @@ target_sources( EntropyContext.h ErrorCodes.h RsaPrivateKey.h + RootCA.h SslConfig.h SslContext.h Sha1Checksum.h diff --git a/src/mbedTLS++/RootCA.h b/src/mbedTLS++/RootCA.h new file mode 100644 index 000000000..3e0d654bd --- /dev/null +++ b/src/mbedTLS++/RootCA.h @@ -0,0 +1,97 @@ + +// This file contains the public keys for different root CAs + +#include "Globals.h" +#include "mbedTLS++/X509Cert.h" + +static cX509CertPtr GetCACerts(void) +{ + static const char CertString[] = + // DigiCert Global Root CA (sessionserver.mojang.com) + // Downloaded from https://www.digicert.com/kb/digicert-root-certificates.htm + + // DigiCert Global Root CA + "-----BEGIN CERTIFICATE-----\n" + "MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n" + "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n" + "d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n" + "QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n" + "MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n" + "b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n" + "9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n" + "CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n" + "nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n" + "43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n" + "T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n" + "gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n" + "BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n" + "TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n" + "DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n" + "hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n" + "06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n" + "PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n" + "YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n" + "CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n" + "-----END CERTIFICATE-----\n" + + // Amazon Root CA 1 (api.mojang.com) + // Downloaded from https://www.amazontrust.com/repository/ + "-----BEGIN CERTIFICATE-----\n" + "MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\n" + "ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\n" + "b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\n" + "MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\n" + "b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\n" + "ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\n" + "9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\n" + "IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\n" + "VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\n" + "93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\n" + "jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\n" + "AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\n" + "A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\n" + "U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\n" + "N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\n" + "o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\n" + "5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\n" + "rqXRfboQnoZsG4q5WTP468SQvvG5\n" + "-----END CERTIFICATE-----\n" + + // AAA Certificate Services (authserver.ely.by GH#4832) + // Downloaded from https://www.tbs-certificates.co.uk/FAQ/en/Comodo_AAA_Certificate_Services.html + "-----BEGIN CERTIFICATE-----\n" + "MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\n" + "MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\n" + "GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\n" + "YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\n" + "MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\n" + "BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\n" + "GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\n" + "ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\n" + "BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\n" + "3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\n" + "YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\n" + "rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\n" + "ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\n" + "oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\n" + "MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\n" + "QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\n" + "b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\n" + "AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\n" + "GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\n" + "Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\n" + "G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\n" + "l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\n" + "smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\n" + "-----END CERTIFICATE-----\n" + ; + +static auto X509Cert = [&]() +{ + auto Cert = std::make_shared(); + VERIFY(0 == Cert->Parse(CertString, sizeof(CertString))); + return Cert; +}(); + +return X509Cert; +} diff --git a/src/mbedTLS++/SslConfig.cpp b/src/mbedTLS++/SslConfig.cpp index 8ea850c9f..054d63980 100644 --- a/src/mbedTLS++/SslConfig.cpp +++ b/src/mbedTLS++/SslConfig.cpp @@ -1,11 +1,11 @@ #include "Globals.h" -#include "SslConfig.h" -#include "EntropyContext.h" -#include "CtrDrbgContext.h" -#include "CryptoKey.h" -#include "X509Cert.h" +#include "mbedTLS++/SslConfig.h" + +#include "mbedTLS++/CryptoKey.h" +#include "mbedTLS++/EntropyContext.h" +#include "mbedTLS++/RootCA.h" // This allows us to debug SSL and certificate problems, but produce way too much output, @@ -225,7 +225,6 @@ void cSslConfig::SetCACerts(cX509CertPtr a_CACert) std::shared_ptr cSslConfig::MakeDefaultConfig(bool a_IsClient) { - // TODO: Default CA chain and SetAuthMode(eSslAuthMode::Required) auto Ret = std::make_shared(); Ret->InitDefaults(a_IsClient); @@ -236,7 +235,8 @@ std::shared_ptr cSslConfig::MakeDefaultConfig(bool a_IsClient) Ret->SetRng(std::move(CtrDrbg)); } - Ret->SetAuthMode(eSslAuthMode::None); // We cannot verify because we don't have a CA chain + Ret->SetAuthMode(eSslAuthMode::Required); + Ret->SetCACerts(GetCACerts()); #ifndef NDEBUG #ifdef ENABLE_SSL_DEBUG_MSG diff --git a/src/mbedTLS++/SslContext.cpp b/src/mbedTLS++/SslContext.cpp index 4847e9322..83bb1955e 100644 --- a/src/mbedTLS++/SslContext.cpp +++ b/src/mbedTLS++/SslContext.cpp @@ -82,10 +82,10 @@ int cSslContext::Initialize(bool a_IsClient) -void cSslContext::SetExpectedPeerName(const AString & a_ExpectedPeerName) +void cSslContext::SetExpectedPeerName(const std::string_view a_ExpectedPeerName) { ASSERT(m_IsValid); // Call Initialize() first - mbedtls_ssl_set_hostname(&m_Ssl, a_ExpectedPeerName.c_str()); + mbedtls_ssl_set_hostname(&m_Ssl, a_ExpectedPeerName.data()); } diff --git a/src/mbedTLS++/SslContext.h b/src/mbedTLS++/SslContext.h index 033749bf3..6343f7e43 100644 --- a/src/mbedTLS++/SslContext.h +++ b/src/mbedTLS++/SslContext.h @@ -54,7 +54,7 @@ public: /** Sets the SSL peer name expected for this context. Must be called after Initialize(). \param a_ExpectedPeerName CommonName that we expect the SSL peer to have in its cert, if it is different, the verification will fail. An empty string will disable the CN check. */ - void SetExpectedPeerName(const AString & a_ExpectedPeerName); + void SetExpectedPeerName(const std::string_view a_ExpectedPeerName); /** Writes data to be encrypted and sent to the SSL peer. Will perform SSL handshake, if needed. Returns the number of bytes actually written, or mbedTLS error code. diff --git a/tests/HTTP/UrlClientTest.cpp b/tests/HTTP/UrlClientTest.cpp index 17f98ab70..41d61152e 100644 --- a/tests/HTTP/UrlClientTest.cpp +++ b/tests/HTTP/UrlClientTest.cpp @@ -117,7 +117,7 @@ int TestRequest1() auto callbacks = std::make_unique(evtFinished); AStringMap options; options["MaxRedirects"] = "0"; - auto res = cUrlClient::Get("http://github.com", std::move(callbacks), AStringMap(), AString(), options); + auto res = cUrlClient::Get("http://github.com", std::move(callbacks), AStringMap(), AString(), std::move(options)); if (res.first) { evtFinished->Wait(); @@ -163,7 +163,7 @@ int TestRequest3() auto callbacks = std::make_unique(evtFinished); AStringMap options; options["MaxRedirects"] = "0"; - auto res = cUrlClient::Get("https://github.com", std::move(callbacks), AStringMap(), AString(), options); + auto res = cUrlClient::Get("https://github.com", std::move(callbacks), AStringMap(), AString(), std::move(options)); if (res.first) { evtFinished->Wait(); -- cgit v1.2.3