1
0
forked from 0ad/0ad

Net Server: Verify password in Authenticate

Follows 1a8de6d2b8.
Validate the password when a client joins a game, so even a player that
knows the connection data cannot join.

Refs #3556, Refs #5913

Differential Revision: https://code.wildfiregames.com/D3438
This was SVN commit r24775.
This commit is contained in:
wraitii 2021-01-23 18:04:36 +00:00
parent 498f5eb083
commit 4cc824d620
8 changed files with 68 additions and 8 deletions

View File

@ -29,6 +29,7 @@
#undef START_NMT_CLASS_DERIVED
#undef NMT_FIELD_INT
#undef NMT_FIELD
#undef NMT_FIELD_SECRET
#undef NMT_START_ARRAY
#undef NMT_END_ARRAY
#undef END_NMT_CLASS
@ -106,6 +107,13 @@ public: \
#define NMT_FIELD(_tp, _nm) \
_tp _nm;
/**
* Likewise, but the string representation is hidden.
* NB: the data length is not hidden, so make sure to use fixed-length data if confidentiality is desirable.
*/
#define NMT_FIELD_SECRET(_tp, _nm) \
_tp _nm;
#define NMT_START_ARRAY(_nm) \
struct ARRAY_STRUCT_PREFIX(_nm); \
std::vector <ARRAY_STRUCT_PREFIX(_nm)> _nm; \
@ -155,6 +163,9 @@ size_t _nm::GetSerializedLength() const \
#define NMT_FIELD(_tp, _nm) \
ret += thiz->_nm.GetSerializedLength();
#define NMT_FIELD_SECRET(_tp, _nm) \
ret += thiz->_nm.GetSerializedLength();
#define END_NMT_CLASS() \
return ret; \
};
@ -197,6 +208,9 @@ u8 *_nm::Serialize(u8 *buffer) const \
#define NMT_FIELD(_tp, _nm) \
pos=thiz->_nm.Serialize(pos);
#define NMT_FIELD_SECRET(_tp, _nm) \
pos=thiz->_nm.Serialize(pos);
#define END_NMT_CLASS() \
return pos; \
}
@ -244,6 +258,9 @@ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \
#define NMT_FIELD(_tp, _nm) \
if ((pos=thiz->_nm.Deserialize(pos, end)) == NULL) BAIL_DESERIALIZER;
#define NMT_FIELD_SECRET(_tp, _nm) \
if ((pos=thiz->_nm.Deserialize(pos, end)) == NULL) BAIL_DESERIALIZER;
#define END_NMT_CLASS() \
return pos; \
}
@ -309,6 +326,9 @@ CStr _nm::ToStringRaw() const \
ret += NetMessageStringConvert(thiz->_nm); \
ret += ", ";
#define NMT_FIELD_SECRET(_tp, _nm) \
ret += #_nm ": [secret], ";
#define END_NMT_CLASS() \
return ret.substr(0, ret.length()-2); \
}

View File

@ -175,6 +175,11 @@ void CNetClient::SetHostingPlayerName(const CStr& hostingPlayerName)
m_HostingPlayerName = hostingPlayerName;
}
void CNetClient::SetGamePassword(const CStr& hashedPassword)
{
m_Password = hashedPassword;
}
bool CNetClient::SetupConnection(ENetHost* enetClient)
{
CNetClientSession* session = new CNetClientSession(*this);
@ -570,7 +575,7 @@ void CNetClient::SendAuthenticateMessage()
{
CAuthenticateMessage authenticate;
authenticate.m_Name = m_UserName;
authenticate.m_Password = L""; // TODO
authenticate.m_Password = m_Password;
authenticate.m_IsLocalClient = m_IsLocalClient;
SendMessage(&authenticate);
}

View File

@ -99,6 +99,11 @@ public:
*/
void SetHostingPlayerName(const CStr& hostingPlayerName);
/**
* Set the game password.
*/
void SetGamePassword(const CStr& hashedPassword);
/**
* Returns the GUID of the local client.
* Used for distinguishing observers.
@ -293,6 +298,11 @@ private:
u16 m_ServerPort;
bool m_UseSTUN;
/**
* Password to join the game.
*/
CStr m_Password;
/// Current network session (or NULL if not connected)
CNetClientSession* m_Session;

View File

@ -28,7 +28,7 @@
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!'
#define PS_PROTOCOL_VERSION 0x01010015 // Arbitrary protocol
#define PS_PROTOCOL_VERSION 0x01010016 // Arbitrary protocol
#define PS_DEFAULT_PORT 0x5073 // 'P', 's'
// Set when lobby authentication is required. Used in the SrvHandshakeResponseMessage.
@ -121,8 +121,7 @@ END_NMT_CLASS()
START_NMT_CLASS_(Authenticate, NMT_AUTHENTICATE)
NMT_FIELD(CStrW, m_Name)
// TODO: The password should not be printed to logfiles
NMT_FIELD(CStrW, m_Password)
NMT_FIELD_SECRET(CStr, m_Password)
NMT_FIELD_INT(m_IsLocalClient, u8, 1)
END_NMT_CLASS()

View File

@ -181,6 +181,11 @@ CNetServerWorker::~CNetServerWorker()
delete m_ServerTurnManager;
}
void CNetServerWorker::SetPassword(const CStr& hashedPassword)
{
m_Password = hashedPassword;
}
bool CNetServerWorker::SetupConnection(const u16 port)
{
ENSURE(m_State == SERVER_STATE_UNCONNECTED);
@ -979,6 +984,18 @@ bool CNetServerWorker::OnAuthenticate(void* context, CFsmEvent* event)
return true;
}
// Check the password before anything else.
if (server.m_Password != message->m_Password)
{
// Noisy logerror because players are not supposed to be able to get the IP,
// so this might be someone targeting the host for some reason
// (or TODO a dedicated server and we do want to log anyways)
LOGERROR("Net server: user %s tried joining with the wrong password",
session->GetUserName().ToUTF8());
session->Disconnect(NDR_SERVER_REFUSED);
return true;
}
// Either deduplicate or prohibit join if name is in use
bool duplicatePlayernames = false;
CFG_GET_VAL("network.duplicateplayernames", duplicatePlayernames);
@ -1088,8 +1105,6 @@ bool CNetServerWorker::OnAuthenticate(void* context, CFsmEvent* event)
return true;
}
// TODO: check server password etc?
u32 newHostID = server.m_NextHostID++;
session->SetUserName(username);
@ -1622,6 +1637,8 @@ bool CNetServer::CheckPassword(const CStr& password) const
void CNetServer::SetPassword(const CStr& password)
{
m_Password = password;
std::lock_guard<std::mutex> lock(m_Worker->m_WorkerMutex);
m_Worker->SetPassword(password);
}
void CNetServer::StartGame()

View File

@ -209,6 +209,8 @@ private:
CNetServerWorker(bool useLobbyAuth, int autostartPlayers);
~CNetServerWorker();
void SetPassword(const CStr& hashedPassword);
/**
* Begin listening for network connections.
* @return true on success, false on error (e.g. port already in use)
@ -341,6 +343,8 @@ private:
std::vector<u32> m_BannedIPs;
std::vector<CStrW> m_BannedPlayers;
CStr m_Password;
/**
* Holds the GUIDs of all currently paused players.
*/

View File

@ -225,6 +225,7 @@ private:
CStr m_GUID;
CStrW m_UserName;
u32 m_HostID;
CStr m_Password;
bool m_IsLocalClient;
};

View File

@ -128,12 +128,14 @@ void JSI_Network::StartNetworkHost(ScriptInterface::CmptPrivate* pCmptPrivate, c
}
// We will get hashed password from clients, so hash it once for server
g_NetServer->SetPassword(HashPassword(password));
CStr hashedPass = HashPassword(password);
g_NetServer->SetPassword(hashedPass);
g_Game = new CGame(true);
g_NetClient = new CNetClient(g_Game, true);
g_NetClient->SetUserName(playerName);
g_NetClient->SetHostingPlayerName(hostLobbyName);
g_NetClient->SetGamePassword(hashedPass);
g_NetClient->SetupServerData("127.0.0.1", serverPort, false);
if (!g_NetClient->SetupConnection(nullptr))
@ -173,11 +175,13 @@ void JSI_Network::StartNetworkJoinLobby(ScriptInterface::CmptPrivate* pCmptPriva
ENSURE(!g_NetServer);
ENSURE(!g_Game);
CStr hashedPass = HashPassword(password);
g_Game = new CGame(true);
g_NetClient = new CNetClient(g_Game, false);
g_NetClient->SetUserName(playerName);
g_NetClient->SetHostingPlayerName(hostJID.substr(0, hostJID.find("@")));
g_XmppClient->SendIqGetConnectionData(hostJID, HashPassword(password).c_str());
g_NetClient->SetGamePassword(hashedPass);
g_XmppClient->SendIqGetConnectionData(hostJID, hashedPass.c_str());
}
void JSI_Network::DisconnectNetworkGame(ScriptInterface::CmptPrivate* UNUSED(pCmptPrivate))