Merge commit '800f1c0bc5bd4632bd0f246c756283cc47d31a34' into pullstream
This commit is contained in:
commit
11c57356a4
@ -20,11 +20,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"
|
||||
|
||||
|
||||
|
||||
@ -32,7 +33,44 @@
|
||||
|
||||
// fwd:
|
||||
class cSchemeHandler;
|
||||
typedef std::shared_ptr<cSchemeHandler> cSchemeHandlerPtr;
|
||||
using cSchemeHandlerPtr = std::shared_ptr<cSchemeHandler>;
|
||||
|
||||
|
||||
/** 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<cEvent> 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<const char *>(a_Data), a_Size);
|
||||
}
|
||||
|
||||
std::shared_ptr<cEvent> m_Event;
|
||||
|
||||
/** The accumulator for the partial body data, so that OnBodyFinished() can send the entire thing at once. */
|
||||
AString & m_ResponseBody;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -277,7 +315,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
|
||||
{
|
||||
@ -387,7 +425,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));
|
||||
}
|
||||
|
||||
|
||||
@ -629,12 +667,12 @@ std::pair<bool, AString> 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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -645,13 +683,13 @@ std::pair<bool, AString> cUrlClient::Request(
|
||||
std::pair<bool, AString> 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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -663,12 +701,12 @@ std::pair<bool, AString> 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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -680,12 +718,12 @@ std::pair<bool, AString> 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)
|
||||
);
|
||||
}
|
||||
|
||||
@ -693,3 +731,64 @@ std::pair<bool, AString> cUrlClient::Put(
|
||||
|
||||
|
||||
|
||||
std::pair<bool, AString> 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<cEvent>();
|
||||
AString Response;
|
||||
auto Callbacks = std::make_unique<cSimpleHTTPCallbacks>(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<bool, AString> 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<bool, AString> 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<bool, AString> 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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -102,7 +102,7 @@ public:
|
||||
for such a response; instead, the redirect is silently attempted. */
|
||||
virtual void OnRedirecting(const AString & a_NewLocation) {}
|
||||
};
|
||||
typedef std::unique_ptr<cCallbacks> cCallbacksPtr;
|
||||
using cCallbacksPtr = std::unique_ptr<cCallbacks>;
|
||||
|
||||
|
||||
/** Used for HTTP status codes. */
|
||||
@ -131,7 +131,7 @@ public:
|
||||
const AString & a_URL,
|
||||
cCallbacksPtr && a_Callbacks,
|
||||
AStringMap && a_Headers,
|
||||
AString && a_Body,
|
||||
const AString & a_Body,
|
||||
AStringMap && a_Options
|
||||
);
|
||||
|
||||
@ -139,9 +139,9 @@ public:
|
||||
static std::pair<bool, AString> 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", ...) */
|
||||
@ -149,7 +149,7 @@ public:
|
||||
const AString & a_URL,
|
||||
cCallbacksPtr && a_Callbacks,
|
||||
AStringMap && a_Headers,
|
||||
AString && a_Body,
|
||||
const AString & a_Body,
|
||||
AStringMap && a_Options
|
||||
);
|
||||
|
||||
@ -158,7 +158,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<bool, AString> 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<bool, AString> 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<bool, AString> BlockingPost(
|
||||
const AString & a_URL,
|
||||
AStringMap && a_Headers,
|
||||
const AString & a_Body,
|
||||
AStringMap && a_Options
|
||||
);
|
||||
|
||||
/** Alias for BlockingRequest("PUT", ...) */
|
||||
static std::pair<bool, AString> BlockingPut(
|
||||
const AString & a_URL,
|
||||
AStringMap && a_Headers,
|
||||
const AString & a_Body,
|
||||
AStringMap && a_Options
|
||||
);
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
// Implements the cUrlParser class that parses string URL into individual parts
|
||||
|
||||
#include "Globals.h"
|
||||
|
||||
#include "UrlParser.h"
|
||||
|
||||
|
||||
@ -214,3 +215,13 @@ std::pair<bool, AString> cUrlParser::Parse(
|
||||
|
||||
|
||||
|
||||
std::pair<bool, AString> 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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -67,6 +67,9 @@ public:
|
||||
AString & a_Query,
|
||||
AString & a_Fragment
|
||||
);
|
||||
|
||||
/** Checks if the supplied URL is valid */
|
||||
static std::pair<bool, AString> Validate(const AString & a_Url);
|
||||
};
|
||||
|
||||
|
||||
|
@ -129,7 +129,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.
|
||||
|
@ -253,7 +253,8 @@ void cTCPLinkImpl::Close(void)
|
||||
|
||||
AString cTCPLinkImpl::StartTLSClient(
|
||||
cX509CertPtr a_OwnCert,
|
||||
cCryptoKeyPtr a_OwnPrivKey
|
||||
cCryptoKeyPtr a_OwnPrivKey,
|
||||
const std::string_view hostname
|
||||
)
|
||||
{
|
||||
// Check preconditions:
|
||||
@ -279,6 +280,8 @@ AString cTCPLinkImpl::StartTLSClient(
|
||||
m_TlsContext->Initialize(true);
|
||||
}
|
||||
|
||||
m_TlsContext->SetExpectedPeerName(hostname);
|
||||
|
||||
m_TlsContext->SetSelf(cLinkTlsContextWPtr(m_TlsContext));
|
||||
|
||||
// Start the handshake:
|
||||
|
@ -84,7 +84,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,
|
||||
|
@ -17,25 +17,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 "Protocol/Authenticator.h"
|
||||
|
||||
#include "../IniFile.h"
|
||||
#include "../JsonUtils.h"
|
||||
#include "ClientHandle.h"
|
||||
#include "HTTP/UrlClient.h"
|
||||
#include "HTTP/UrlParser.h"
|
||||
#include "IniFile.h"
|
||||
#include "JsonUtils.h"
|
||||
#include "json/json.h"
|
||||
|
||||
#include "../mbedTLS++/BlockingSslClientSocket.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%";
|
||||
|
||||
|
||||
|
||||
@ -67,6 +67,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -159,7 +189,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());
|
||||
|
||||
@ -168,39 +198,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())
|
||||
{
|
||||
@ -209,14 +213,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;
|
||||
}
|
||||
|
||||
@ -228,9 +232,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%"
|
||||
|
||||
@ -241,43 +245,13 @@ 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))
|
||||
// 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("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)
|
||||
{
|
||||
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())
|
||||
{
|
||||
@ -295,7 +269,7 @@ bool cAuthenticator::GetPlayerProperties(const AString & a_UUID, Json::Value & a
|
||||
a_Properties = root["properties"];
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
@ -82,7 +82,7 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::deque<cUser> cUserList;
|
||||
using cUserList = std::deque<cUser>;
|
||||
|
||||
cCriticalSection m_CS;
|
||||
cUserList m_Queue;
|
||||
@ -105,7 +105,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;
|
||||
};
|
||||
|
||||
|
||||
|
@ -21,17 +21,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"
|
||||
|
||||
|
||||
|
||||
|
||||
@ -46,122 +47,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<cX509Cert>();
|
||||
VERIFY(0 == Cert->Parse(CertString, sizeof(CertString)));
|
||||
return Cert;
|
||||
}();
|
||||
|
||||
return X509Cert;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Returns the config to be used for secure requests. */
|
||||
static std::shared_ptr<const cSslConfig> GetSslConfig()
|
||||
{
|
||||
static const std::shared_ptr<const cSslConfig> 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";
|
||||
|
||||
|
||||
|
||||
@ -204,8 +93,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;
|
||||
}
|
||||
@ -448,61 +336,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<const char *>(buf), static_cast<size_t>(ret));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cMojangAPI::LoadCachesFromDisk(void)
|
||||
{
|
||||
try
|
||||
@ -657,7 +490,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);
|
||||
@ -666,43 +500,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;
|
||||
@ -778,49 +582,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;
|
||||
}
|
||||
|
||||
@ -907,7 +685,7 @@ void cMojangAPI::Update(void)
|
||||
std::vector<cUUID> ProfileUUIDs;
|
||||
{
|
||||
cCSLock Lock(m_CSUUIDToProfile);
|
||||
for (auto & UUIDToProfile : m_UUIDToProfile)
|
||||
for (const auto & UUIDToProfile : m_UUIDToProfile)
|
||||
{
|
||||
if (UUIDToProfile.second.m_DateTime < LimitDateTime)
|
||||
{
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "../UUID.h"
|
||||
#include "UUID.h"
|
||||
|
||||
|
||||
|
||||
@ -58,11 +58,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.
|
||||
@ -147,8 +142,8 @@ protected:
|
||||
Int64 a_DateTime
|
||||
);
|
||||
};
|
||||
typedef std::map<AString, sProfile> cProfileMap;
|
||||
typedef std::map<cUUID, sProfile> cUUIDProfileMap;
|
||||
using cProfileMap = std::map<AString, sProfile>;
|
||||
using cUUIDProfileMap = std::map<cUUID, sProfile>;
|
||||
|
||||
|
||||
/** The server to connect to when converting player names to UUIDs. For example "api.mojang.com". */
|
||||
|
@ -25,6 +25,7 @@ target_sources(
|
||||
EntropyContext.h
|
||||
ErrorCodes.h
|
||||
RsaPrivateKey.h
|
||||
RootCA.h
|
||||
SslConfig.h
|
||||
SslContext.h
|
||||
Sha1Checksum.h
|
||||
|
97
src/mbedTLS++/RootCA.h
Normal file
97
src/mbedTLS++/RootCA.h
Normal file
@ -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<cX509Cert>();
|
||||
VERIFY(0 == Cert->Parse(CertString, sizeof(CertString)));
|
||||
return Cert;
|
||||
}();
|
||||
|
||||
return X509Cert;
|
||||
}
|
@ -17,11 +17,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,
|
||||
@ -241,7 +241,6 @@ void cSslConfig::SetCACerts(cX509CertPtr a_CACert)
|
||||
|
||||
std::shared_ptr<cSslConfig> cSslConfig::MakeDefaultConfig(bool a_IsClient)
|
||||
{
|
||||
// TODO: Default CA chain and SetAuthMode(eSslAuthMode::Required)
|
||||
auto Ret = std::make_shared<cSslConfig>();
|
||||
|
||||
Ret->InitDefaults(a_IsClient);
|
||||
@ -252,7 +251,8 @@ std::shared_ptr<cSslConfig> 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
|
||||
|
@ -98,10 +98,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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -70,7 +70,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.
|
||||
|
@ -133,7 +133,7 @@ int TestRequest1()
|
||||
auto callbacks = std::make_unique<cCallbacks>(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();
|
||||
@ -179,7 +179,7 @@ int TestRequest3()
|
||||
auto callbacks = std::make_unique<cCallbacks>(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();
|
||||
|
Loading…
x
Reference in New Issue
Block a user