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/Protocol/Authenticator.cpp | 16 +++--- src/Protocol/MojangAPI.cpp | 123 +++++++++++++++++++++++++++++++++++------ src/Protocol/MojangAPI.h | 21 +++---- 3 files changed, 122 insertions(+), 38 deletions(-) (limited to 'src/Protocol') diff --git a/src/Protocol/Authenticator.cpp b/src/Protocol/Authenticator.cpp index 00b09c30d..41eac82d3 100644 --- a/src/Protocol/Authenticator.cpp +++ b/src/Protocol/Authenticator.cpp @@ -65,8 +65,8 @@ void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings) } { - auto [IsSuccessfull, ErrorMessage] = cUrlParser::Validate(m_Server); - if (!IsSuccessfull) + auto [IsSuccessful, ErrorMessage] = cUrlParser::Validate(m_Server); + if (!IsSuccessful) { 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; @@ -74,8 +74,8 @@ void cAuthenticator::ReadSettings(cSettingsRepositoryInterface & a_Settings) } { - auto [IsSuccessfull, ErrorMessage] = cUrlParser::Validate(m_Server); - if (!IsSuccessfull) + auto [IsSuccessful, ErrorMessage] = cUrlParser::Validate(m_Server); + if (!IsSuccessful) { 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; @@ -183,8 +183,8 @@ bool cAuthenticator::AuthWithYggdrasil(AString & a_UserName, const AString & a_S ReplaceURL(ActualAddress, "%SERVERID%", a_ServerId); // Create and send the HTTP request - auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); - if (!IsSuccessfull) + auto [IsSuccessful, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); + if (!IsSuccessful) { return false; } @@ -230,8 +230,8 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a LOGD("Trying to get properties for user %s", a_UUID.c_str()); // Create and send the HTTP request - auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); - if (!IsSuccessfull) + auto [IsSuccessful, Response] = cUrlClient::BlockingGet(m_Server + ActualAddress); + if (!IsSuccessful) { return false; } diff --git a/src/Protocol/MojangAPI.cpp b/src/Protocol/MojangAPI.cpp index 37c1b0911..57becce62 100644 --- a/src/Protocol/MojangAPI.cpp +++ b/src/Protocol/MojangAPI.cpp @@ -40,6 +40,99 @@ constexpr char DEFAULT_UUID_TO_PROFILE_ADDRESS[] = "/session/minecraft/profile/% +namespace MojangTrustedRootCAs +{ + /** Returns the Options that should be used for cUrlClient queries to the Mojang APIs. */ + static const AStringMap & UrlClientOptions() + { + static const AString 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 const AStringMap UrlClientOptions = {{"TrustedRootCAs", CertString}}; + return UrlClientOptions; + } +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cMojangAPI::sProfile: @@ -143,11 +236,7 @@ protected: //////////////////////////////////////////////////////////////////////////////// // cMojangAPI: -cMojangAPI::cMojangAPI(void) : - m_NameToUUIDServer(DEFAULT_NAME_TO_UUID_SERVER), - m_NameToUUIDAddress(DEFAULT_NAME_TO_UUID_ADDRESS), - m_UUIDToProfileServer(DEFAULT_UUID_TO_PROFILE_SERVER), - m_UUIDToProfileAddress(DEFAULT_UUID_TO_PROFILE_ADDRESS), +cMojangAPI::cMojangAPI(): m_RankMgr(nullptr), m_UpdateThread(new cUpdateThread(*this)) { @@ -168,10 +257,12 @@ cMojangAPI::~cMojangAPI() void cMojangAPI::Start(cSettingsRepositoryInterface & a_Settings, bool a_ShouldAuth) { - m_NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); - m_NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); - m_UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); - m_UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); + auto NameToUUIDServer = a_Settings.GetValueSet("MojangAPI", "NameToUUIDServer", DEFAULT_NAME_TO_UUID_SERVER); + auto NameToUUIDAddress = a_Settings.GetValueSet("MojangAPI", "NameToUUIDAddress", DEFAULT_NAME_TO_UUID_ADDRESS); + auto UUIDToProfileServer = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileServer", DEFAULT_UUID_TO_PROFILE_SERVER); + auto UUIDToProfileAddress = a_Settings.GetValueSet("MojangAPI", "UUIDToProfileAddress", DEFAULT_UUID_TO_PROFILE_ADDRESS); + m_NameToUUIDUrl = "https://" + NameToUUIDServer + NameToUUIDAddress; + m_UUIDToProfileUrl = "https://" + UUIDToProfileServer + UUIDToProfileAddress; LoadCachesFromDisk(); if (a_ShouldAuth) { @@ -485,8 +576,8 @@ void cMojangAPI::QueryNamesToUUIDs(AStringVector & a_NamesToQuery) auto RequestBody = JsonUtils::WriteFastString(root); // Create and send the HTTP request - auto [IsSuccessfull, Response] = cUrlClient::BlockingPost(m_NameToUUIDAddress, AStringMap(), std::move(RequestBody), AStringMap()); - if (!IsSuccessfull) + auto [IsSuccessful, Response] = cUrlClient::BlockingPost(m_NameToUUIDUrl, {}, std::move(RequestBody), MojangTrustedRootCAs::UrlClientOptions()); + if (!IsSuccessful) { continue; } @@ -562,13 +653,11 @@ void cMojangAPI::CacheUUIDToProfile(const cUUID & a_UUID) void cMojangAPI::QueryUUIDToProfile(const cUUID & a_UUID) { - // Create the request address: - AString Address = m_UUIDToProfileAddress; - ReplaceURL(Address, "%UUID%", a_UUID.ToShortString()); - // Create and send the HTTP request - auto [IsSuccessfull, Response] = cUrlClient::BlockingGet(Address); - if (!IsSuccessfull) + auto Url = m_UUIDToProfileUrl; + ReplaceString(Url, "%UUID%", URLEncode(a_UUID.ToShortString())); + auto [IsSuccessful, Response] = cUrlClient::BlockingGet(Url, {}, {}, MojangTrustedRootCAs::UrlClientOptions()); + if (!IsSuccessful) { return; } diff --git a/src/Protocol/MojangAPI.h b/src/Protocol/MojangAPI.h index f9267fefe..6d550662c 100644 --- a/src/Protocol/MojangAPI.h +++ b/src/Protocol/MojangAPI.h @@ -130,19 +130,14 @@ protected: using cUUIDProfileMap = std::map; - /** The server to connect to when converting player names to UUIDs. For example "api.mojang.com". */ - AString m_NameToUUIDServer; - - /** The URL to use for converting player names to UUIDs, without server part. - For example "/profiles/page/1". */ - AString m_NameToUUIDAddress; - - /** The server to connect to when converting UUID to profile. For example "sessionserver.mojang.com". */ - AString m_UUIDToProfileServer; - - /** The URL to use for converting UUID to profile, without the server part. - Will replace %UUID% with the actual UUID. For example "session/minecraft/profile/%UUID%?unsigned=false". */ - AString m_UUIDToProfileAddress; + /** The full URL to check when converting player names to UUIDs. + For example: "https://api.mojang.com/profiles/page/1". */ + AString m_NameToUUIDUrl; + + /** The full URL to use for converting UUID to profile. + %UUID% will get replaced with the actual UUID. + For example "https://sessionserver.mojang.com/session/minecraft/profile/%UUID%?unsigned=false". */ + AString m_UUIDToProfileUrl; /** Cache for the Name-to-UUID lookups. The map key is lowercased PlayerName. Protected by m_CSNameToUUID. */ cProfileMap m_NameToUUID; -- cgit v1.2.3