2016-02-21 13:57:56 -05:00
|
|
|
#include "STDInclude.hpp"
|
|
|
|
|
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
Auth::AuthInfo Auth::ClientAuthInfo[18];
|
|
|
|
|
|
|
|
void Auth::Frame()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < *Game::svs_numclients; i++)
|
|
|
|
{
|
|
|
|
Game::client_t* client = &Game::svs_clients[i];
|
|
|
|
Auth::AuthInfo* info = &Auth::ClientAuthInfo[i];
|
|
|
|
|
|
|
|
// State must be 5 or greater here, as otherwise the client will crash when being kicked.
|
|
|
|
// That's due to the hunk being freed by that time, but it hasn't been reallocated, therefore all future allocations will cause a crash.
|
|
|
|
// Additionally, the game won't catch the errors and simply lose the connection, so we even have to add a delay to send the data.
|
|
|
|
|
|
|
|
// Not sure if that's potentially unsafe, though.
|
|
|
|
// Players faking their GUID will be connected for 5 seconds, which allows them to fuck up everything.
|
|
|
|
// I think we have to perform the verification when clients are still in state 3, but for now it works.
|
|
|
|
|
|
|
|
// I think we even have to lock the client into state 3 until the verification is done.
|
|
|
|
// Intercepting the entire connection process to perform the authentication within state 3 solely is necessary, due to having a timeout.
|
|
|
|
// Not sending a response might allow the player to connect for a few seconds (<= 5) until the timeout is reached.
|
|
|
|
if (client->state >= 5)
|
|
|
|
{
|
|
|
|
if (info->state == Auth::STATE_NEGOTIATING && (Game::Com_Milliseconds() - info->time) > 1000 * 5)
|
|
|
|
{
|
|
|
|
info->state = Auth::STATE_INVALID;
|
|
|
|
info->time = Game::Com_Milliseconds();
|
|
|
|
Game::SV_KickClientError(client, "XUID verification timeout!");
|
|
|
|
}
|
|
|
|
else if (info->state == Auth::STATE_UNKNOWN && info->time && (Game::Com_Milliseconds() - info->time) > 1000 * 5) // Wait 5 seconds (error delay)
|
|
|
|
{
|
|
|
|
Logger::Print("Sending XUID authentication request to %s\n", Network::Address(client->adr).GetString());
|
|
|
|
|
|
|
|
info->state = Auth::STATE_NEGOTIATING;
|
|
|
|
info->time = Game::Com_Milliseconds();
|
|
|
|
info->challenge = Utils::VA("%X", Utils::Cryptography::Rand::GenerateInt());
|
|
|
|
Network::SendCommand(client->adr, "xuidAuthReq", info->challenge);
|
|
|
|
}
|
|
|
|
else if (info->state == Auth::STATE_UNKNOWN && !info->time)
|
|
|
|
{
|
|
|
|
info->time = Game::Com_Milliseconds();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Auth::RegisterClient(int clientNum)
|
|
|
|
{
|
|
|
|
if (clientNum >= 18) return;
|
|
|
|
|
|
|
|
Network::Address address(Game::svs_clients[clientNum].adr);
|
|
|
|
|
|
|
|
if (address.GetType() == Game::netadrtype_t::NA_BOT)
|
|
|
|
{
|
|
|
|
Auth::ClientAuthInfo[clientNum].state = Auth::STATE_VALID;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Logger::Print("Registering client %s\n", address.GetString());
|
|
|
|
Auth::ClientAuthInfo[clientNum].time = 0;
|
|
|
|
Auth::ClientAuthInfo[clientNum].state = Auth::STATE_UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void __declspec(naked) Auth::RegisterClientStub()
|
|
|
|
{
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
push esi
|
|
|
|
call Auth::RegisterClient
|
|
|
|
pop esi
|
|
|
|
|
|
|
|
imul esi, 366Ch
|
|
|
|
mov eax, 478A18h
|
|
|
|
jmp eax
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Auth::Auth()
|
|
|
|
{
|
|
|
|
// Only clients receive the auth request
|
|
|
|
if (!Dedicated::IsDedicated())
|
|
|
|
{
|
|
|
|
Network::Handle("xuidAuthReq", [] (Network::Address address, std::string data)
|
|
|
|
{
|
|
|
|
Logger::Print("Received XUID authentication request from %s\n", address.GetString());
|
|
|
|
|
|
|
|
// Only accept requests from the server we're connected to
|
|
|
|
if (address != *Game::connectedHost) return;
|
|
|
|
if (!Steam::User::GuidKey.IsValid()) return;
|
|
|
|
|
|
|
|
Proto::Auth::Response response;
|
|
|
|
response.set_publickey(Steam::User::GuidKey.GetPublicKey());
|
2016-02-21 15:12:49 -05:00
|
|
|
response.set_signature(Utils::Cryptography::ECDSA::SignMessage(Steam::User::GuidKey, data));
|
2016-02-21 13:57:56 -05:00
|
|
|
|
|
|
|
Network::SendCommand(address, "xuidAuthResp", response.SerializeAsString());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Network::Handle("xuidAuthResp", [] (Network::Address address, std::string data)
|
|
|
|
{
|
|
|
|
Logger::Print("Received XUID authentication response from %s\n", address.GetString());
|
|
|
|
|
|
|
|
Proto::Auth::Response response;
|
|
|
|
response.ParseFromString(data);
|
|
|
|
|
|
|
|
if (response.signature().empty()) return;
|
|
|
|
if (response.publickey().empty()) return;
|
|
|
|
|
|
|
|
for (int i = 0; i < *Game::svs_numclients; i++)
|
|
|
|
{
|
|
|
|
Game::client_t* client = &Game::svs_clients[i];
|
|
|
|
Auth::AuthInfo* info = &Auth::ClientAuthInfo[i];
|
|
|
|
|
|
|
|
if (client->state >= 3 && address == client->adr && info->state == Auth::STATE_NEGOTIATING)
|
|
|
|
{
|
|
|
|
unsigned int id = static_cast<unsigned int>(~0x110000100000000 & client->steamid);
|
|
|
|
|
|
|
|
// Check if guid matches the certificate
|
|
|
|
if (id != (Utils::OneAtATime(response.publickey().data(), response.publickey().size()) & ~0x80000000))
|
|
|
|
{
|
|
|
|
info->state = Auth::STATE_INVALID;
|
|
|
|
Game::SV_KickClientError(client, "XUID doesn't match the certificate!");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->publicKey.Set(response.publickey());
|
|
|
|
|
|
|
|
if (Utils::Cryptography::ECDSA::VerifyMessage(info->publicKey, info->challenge, response.signature()))
|
|
|
|
{
|
|
|
|
info->state = Auth::STATE_VALID;
|
|
|
|
Logger::Print("Verified XUID from %s\n", address.GetString());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info->state = Auth::STATE_INVALID;
|
|
|
|
Game::SV_KickClientError(client, "Challenge signature was invalid!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Install frame handlers
|
|
|
|
Dedicated::OnFrame(Auth::Frame);
|
|
|
|
Renderer::OnFrame(Auth::Frame);
|
|
|
|
|
|
|
|
// Install registration hook
|
|
|
|
Utils::Hook(0x478A12, Auth::RegisterClientStub, HOOK_JUMP).Install()->Quick();
|
|
|
|
}
|
|
|
|
|
|
|
|
Auth::~Auth()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|