Use a more sophiscated server-client token validation

This commit is contained in:
Benau
2018-03-27 01:02:31 +08:00
parent c8dadf76f1
commit 70def13ca2
9 changed files with 112 additions and 40 deletions

View File

@@ -63,15 +63,6 @@ Event::Event(ENetEvent* event, std::shared_ptr<STKPeer> peer)
}
m_peer = peer;
if(m_type == EVENT_TYPE_MESSAGE && m_peer->isClientServerTokenSet() &&
m_data->getToken()!=m_peer->getClientServerToken() )
{
Log::error("Event", "Received event with invalid token!");
Log::error("Event", "HostID %d Token %d message token %d",
m_peer->getHostId(), m_peer->getClientServerToken(),
m_data->getToken());
Log::error("Event", m_data->getLogMessage().c_str());
}
} // Event(ENetEvent)
// ----------------------------------------------------------------------------

View File

@@ -435,7 +435,8 @@ void ClientLobby::connectionAccepted(Event* event)
NetworkConfig::get()->getNetworkPlayers().size());
// connection token
uint32_t token = data.getToken();
peer->setClientServerToken(token);
if (!peer->isClientServerTokenSet())
peer->setClientServerToken(token);
m_state = CONNECTED;
} // connectionAccepted
@@ -656,7 +657,6 @@ void ClientLobby::exitResultScreen(Event *event)
setup();
RaceResultGUI::getInstance()->backToLobby();
STKHost::get()->getServerPeerForClient()->unsetClientServerToken();
} // exitResultScreen
//-----------------------------------------------------------------------------

View File

@@ -38,8 +38,8 @@ class LobbyProtocol : public Protocol
{
public:
/** Lists all lobby events (LE). */
enum
{
enum : uint8_t
{
LE_CONNECTION_REQUESTED = 1, // a connection to the server
LE_CONNECTION_REFUSED, // Connection to server refused
LE_CONNECTION_ACCEPTED, // Connection to server accepted

View File

@@ -913,16 +913,19 @@ void ServerLobby::connectionRequested(Event* event)
m_available_kts.second.erase(track_erase);
}
// Now answer to the peer that just connected
// ------------------------------------------
RandomGenerator token_generator;
// use 4 random numbers because rand_max is probably 2^15-1.
uint32_t token = (uint32_t)((token_generator.get(RAND_MAX) & 0xff) << 24 |
(token_generator.get(RAND_MAX) & 0xff) << 16 |
(token_generator.get(RAND_MAX) & 0xff) << 8 |
(token_generator.get(RAND_MAX) & 0xff));
if (!peer->isClientServerTokenSet())
{
// Now answer to the peer that just connected
// ------------------------------------------
RandomGenerator token_generator;
// use 4 random numbers because rand_max is probably 2^15-1.
uint32_t token = (uint32_t)((token_generator.get(RAND_MAX) & 0xff) << 24 |
(token_generator.get(RAND_MAX) & 0xff) << 16 |
(token_generator.get(RAND_MAX) & 0xff) << 8 |
(token_generator.get(RAND_MAX) & 0xff));
peer->setClientServerToken(token);
peer->setClientServerToken(token);
}
// send a message to the one that asked to connect
NetworkString *message_ack = getNetworkString(4);
// connection success -- return the host id of peer

View File

@@ -707,10 +707,11 @@ void STKHost::mainLoop()
Log::info("STKHost", "Listening has been started.");
ENetEvent event;
ENetHost* host = m_network->getENetHost();
const bool is_server = NetworkConfig::get()->isServer();
// A separate network connection (socket) to handle LAN requests.
Network* direct_socket = NULL;
if ((NetworkConfig::get()->isLAN() && NetworkConfig::get()->isServer()) ||
if ((NetworkConfig::get()->isLAN() && is_server) ||
NetworkConfig::get()->isPublicServer())
{
TransportAddress address(0,
@@ -734,6 +735,32 @@ void STKHost::mainLoop()
handleDirectSocketRequest(direct_socket, sl);
} // if discovery host
if (is_server)
{
std::unique_lock<std::mutex> peer_lock(m_peers_mutex);
// Remove any peer which has no token for 7 seconds
// The token is set when the first connection request has happened
for (auto it = m_peers.begin(); it != m_peers.end();)
{
if (!it->second->isClientServerTokenSet() &&
(float)StkTime::getRealTime() >
it->second->getConnectedTime() + 7.0f)
{
Log::info("STKHost", "%s has no token for more than 7"
" seconds, disconnect it by force.",
it->second->getAddress().toString().c_str());
enet_host_flush(host);
enet_peer_reset(it->first);
it = m_peers.erase(it);
}
else
{
it++;
}
}
peer_lock.unlock();
}
std::list<std::tuple<ENetPeer*, ENetPacket*, uint32_t,
ENetCommandType> > copied_list;
std::unique_lock<std::mutex> lock(m_enet_cmd_mutex);
@@ -805,7 +832,39 @@ void STKHost::mainLoop()
if (!stk_event && m_peers.find(event.peer) != m_peers.end())
{
stk_event = new Event(&event, m_peers.at(event.peer));
auto& peer = m_peers.at(event.peer);
unsigned token = 0;
// Token is after the protocol type (1 byte) in stk network
// string (network order)
token += event.packet->data[1];
token <<= 8;
token += event.packet->data[2];
token <<= 8;
token += event.packet->data[3];
token <<= 8;
token += event.packet->data[4];
if (is_server && ((!peer->isClientServerTokenSet() &&
!isConnectionRequestPacket(event.packet->data,
(int)event.packet->dataLength)) ||
(token != peer->getClientServerToken())))
{
// For server discard all events from wrong or unset token
// peers if that is not a connection request
if (token != peer->getClientServerToken())
{
Log::error("STKHost", "Received event with invalid token!");
Log::error("STKHost", "HostID %d Token %d message token %d",
peer->getHostId(), peer->getClientServerToken(), token);
NetworkString wrong_event(event.packet->data,
(int)event.packet->dataLength);
Log::error("STKHost", wrong_event.getLogMessage().c_str());
peer->unsetClientServerToken();
}
enet_packet_destroy(event.packet);
continue;
}
stk_event = new Event(&event, peer);
}
else if (!stk_event)
{
@@ -980,7 +1039,8 @@ void STKHost::sendPacketToAllPeers(NetworkString *data, bool reliable)
std::lock_guard<std::mutex> lock(m_peers_mutex);
for (auto p : m_peers)
{
p.second->sendPacket(data, reliable);
if (p.second->isClientServerTokenSet())
p.second->sendPacket(data, reliable);
}
} // sendPacketExcept
@@ -997,7 +1057,7 @@ void STKHost::sendPacketExcept(STKPeer* peer, NetworkString *data,
for (auto p : m_peers)
{
STKPeer* stk_peer = p.second.get();
if (!stk_peer->isSamePeer(peer))
if (!stk_peer->isSamePeer(peer) && p.second->isClientServerTokenSet())
{
stk_peer->sendPacket(data, reliable);
}
@@ -1059,3 +1119,13 @@ void STKHost::replaceNetwork(ENetEvent& event, Network* network)
if (pm && !pm->isExiting())
pm->propagateEvent(new Event(&event, stk_peer));
} // replaceNetwork
//-----------------------------------------------------------------------------
bool STKHost::isConnectionRequestPacket(unsigned char* data, int length)
{
if (length < 6)
return false;
// Connection request is not synchronous
return (uint8_t)data[0] == PROTOCOL_LOBBY_ROOM &&
(uint8_t)data[5] == LobbyProtocol::LE_CONNECTION_REQUESTED;
} // isConnectionRequestPacket

View File

@@ -144,6 +144,8 @@ private:
std::shared_ptr<ServerLobby> sl);
// ------------------------------------------------------------------------
void mainLoop();
// ------------------------------------------------------------------------
bool isConnectionRequestPacket(unsigned char* data, int length);
public:
/** If a network console should be started. */

View File

@@ -34,10 +34,10 @@ STKPeer::STKPeer(ENetPeer *enet_peer, STKHost* host, uint32_t host_id)
: m_peer_address(enet_peer->address), m_host(host)
{
m_enet_peer = enet_peer;
m_client_server_token = 0;
m_host_id = host_id;
m_token_set = false;
m_connected_time = (float)StkTime::getRealTime();
m_token_set.store(false);
m_client_server_token.store(0);
} // STKPeer
//-----------------------------------------------------------------------------

View File

@@ -29,6 +29,7 @@
#include <enet/enet.h>
#include <atomic>
#include <memory>
#include <vector>
@@ -55,10 +56,10 @@ protected:
ENetPeer* m_enet_peer;
/** The token of this client. */
uint32_t m_client_server_token;
std::atomic_uint32_t m_client_server_token;
/** True if the token for this peer has been set. */
bool m_token_set;
std::atomic_bool m_token_set;
/** Host id of this peer. */
int m_host_id;
@@ -99,28 +100,31 @@ public:
{ m_players.push_back(p); }
// ------------------------------------------------------------------------
/** Sets the token for this client. */
void setClientServerToken(const uint32_t& token)
void setClientServerToken(const uint32_t token)
{
m_client_server_token = token;
m_token_set = true;
m_client_server_token.store(token);
m_token_set.store(true);
} // setClientServerToken
// ------------------------------------------------------------------------
/** Unsets the token for this client. (used in server to invalidate peer)
*/
void unsetClientServerToken()
{
m_token_set = false;
m_client_server_token = 0;
m_client_server_token.store(0);
m_token_set.store(false);
}
// ------------------------------------------------------------------------
/** Returns the token of this client. */
uint32_t getClientServerToken() const { return m_client_server_token; }
uint32_t getClientServerToken() const
{ return m_client_server_token.load(); }
// ------------------------------------------------------------------------
/** Returns if the token for this client is known. */
bool isClientServerTokenSet() const { return m_token_set; }
bool isClientServerTokenSet() const { return m_token_set.load(); }
// ------------------------------------------------------------------------
/** Returns the host id of this peer. */
uint32_t getHostId() const { return m_host_id; }
uint32_t getHostId() const { return m_host_id; }
// ------------------------------------------------------------------------
float getConnectedTime() const { return m_connected_time; }
float getConnectedTime() const { return m_connected_time; }
// ------------------------------------------------------------------------
uint32_t getPing() const;

View File

@@ -104,7 +104,9 @@ void ServerSelection::beforeAddingWidget()
m_server_list_widget->addColumn(_("Game mode"), 2);
if (NetworkConfig::get()->isWAN())
{
m_server_list_widget->addColumn(_("Server owner"), 1);
// I18N: In server selection screen, owner of server, only displayed
// if it's localhost or friends'
m_server_list_widget->addColumn(_("Owner"), 1);
// I18N: In server selection screen, distance to server
m_server_list_widget->addColumn(_("Distance"), 1);
}