Netcode: Identify controller client via a secret key

The 'controller' of an MP game (the host in general, though dedicated
servers would change that) is currently whoever first tells the server
that it is. This can be abused since it relies on trusting the clients.

This changes that logic: the server defines a 'controller secret', and
the first client to sent the correct controller secret is the
controller. This is safe assuming the secret is unknowable enough (the
current solution wouldn't pass strict cryptography tests, but it's
likely good enough).

Reverts 1a3fb29ff3, which introduced the 'trust the clients' mechanic,
as a change over 'the first local IP is controller'.

Necessary step towards dedicated server, if we want to use the regular
gamesetup (Refs #3556)

Differential Revision: https://code.wildfiregames.com/D3075
This was SVN commit r24952.
This commit is contained in:
wraitii
2021-02-27 17:44:59 +00:00
parent 32c3f4fb90
commit 113fefeeb7
14 changed files with 110 additions and 73 deletions
@@ -29,6 +29,7 @@
#include "network/StunClient.h"
#include "ps/CLogger.h"
#include "ps/Game.h"
#include "ps/GUID.h"
#include "ps/Util.h"
#include "scriptinterface/ScriptInterface.h"
@@ -39,6 +40,11 @@ u16 JSI_Network::GetDefaultPort(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivat
return PS_DEFAULT_PORT;
}
bool JSI_Network::IsNetController(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivate))
{
return !!g_NetClient && g_NetClient->IsController();
}
bool JSI_Network::HasNetServer(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivate))
{
return !!g_NetServer;
@@ -120,16 +126,21 @@ void JSI_Network::StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, c
return;
}
// Generate a secret to identify the host client.
std::string secret = ps_generate_guid();
// We will get hashed password from clients, so hash it once for server
CStr hashedPass = HashPassword(password);
g_NetServer->SetPassword(hashedPass);
g_NetServer->SetControllerSecret(secret);
g_Game = new CGame(true);
g_NetClient = new CNetClient(g_Game, true);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
g_NetClient->SetHostingPlayerName(hostLobbyName);
g_NetClient->SetGamePassword(hashedPass);
g_NetClient->SetupServerData("127.0.0.1", serverPort, false);
g_NetClient->SetControllerSecret(secret);
if (!g_NetClient->SetupConnection(nullptr))
{
@@ -147,7 +158,7 @@ void JSI_Network::StartNetworkJoin(ScriptInterface::CmptPrivate* pCmptPrivate, c
ENSURE(!g_Game);
g_Game = new CGame(true);
g_NetClient = new CNetClient(g_Game, false);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
g_NetClient->SetHostingPlayerName(hostJID.substr(0, hostJID.find("@")));
g_NetClient->SetupServerData(serverAddress, serverPort, useSTUN);
@@ -170,7 +181,7 @@ void JSI_Network::StartNetworkJoinLobby(ScriptInterface::CmptPrivate* UNUSED(pCm
CStr hashedPass = HashPassword(password);
g_Game = new CGame(true);
g_NetClient = new CNetClient(g_Game, false);
g_NetClient = new CNetClient(g_Game);
g_NetClient->SetUserName(playerName);
g_NetClient->SetHostingPlayerName(hostJID.substr(0, hostJID.find("@")));
g_NetClient->SetGamePassword(hashedPass);
@@ -269,6 +280,7 @@ void JSI_Network::SetTurnLength(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivat
void JSI_Network::RegisterScriptFunctions(const ScriptInterface& scriptInterface)
{
scriptInterface.RegisterFunction<u16, &GetDefaultPort>("GetDefaultPort");
scriptInterface.RegisterFunction<bool, &IsNetController>("IsNetController");
scriptInterface.RegisterFunction<bool, &HasNetServer>("HasNetServer");
scriptInterface.RegisterFunction<bool, &HasNetClient>("HasNetClient");
scriptInterface.RegisterFunction<void, CStrW, u16, CStr, bool, CStr, &StartNetworkHost>("StartNetworkHost");