Network: Change protocol for game/rcon/settings password auth

Instead of sending a hash, do a DH/X25519 key exchange
using the password.
This also allows authenticating the associated rcon payload and response.
pull/556/head
Jonathan G Rennison 1 year ago
parent 9042eb338f
commit 3d2dc77aa2

@ -1412,6 +1412,23 @@ void NetworkRandomBytesWithFallback(void *buf, size_t bytes)
}
}
void NetworkGameKeys::Initialise()
{
assert(!this->inited);
this->inited = true;
static_assert(sizeof(this->x25519_priv_key) == 32);
NetworkRandomBytesWithFallback(this->x25519_priv_key, sizeof(this->x25519_priv_key));
crypto_x25519_public_key(this->x25519_pub_key, this->x25519_priv_key);
}
NetworkSharedSecrets::~NetworkSharedSecrets()
{
static_assert(sizeof(*this) == 64);
crypto_wipe(this, sizeof(*this));
}
#ifdef __EMSCRIPTEN__
extern "C" {

@ -33,6 +33,7 @@
#include "../core/checksum_func.hpp"
#include "../fileio_func.h"
#include "../debug_settings.h"
#include "../3rdparty/monocypher/monocypher.h"
#include "table/strings.h"
@ -40,6 +41,8 @@
/* This file handles all the client-commands */
static void ResetClientConnectionKeyStates();
/** Read some packets, and when do use that data as initial load filter. */
struct PacketReader : LoadFilter {
static const size_t CHUNK = 32 * 1024; ///< 32 KiB chunks of memory.
@ -174,6 +177,8 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
FioFCloseFile(this->desync_log_file);
this->desync_log_file = nullptr;
}
ResetClientConnectionKeyStates();
}
NetworkRecvStatus ClientNetworkGameSocketHandler::CloseConnection(NetworkRecvStatus status)
@ -380,12 +385,10 @@ static uint32 last_ack_frame;
/** One bit of 'entropy' used to generate a salt for the company passwords. */
static uint32 _company_password_game_seed;
/** One bit of 'entropy' used to generate a salt for the server passwords. */
static uint64 _server_password_game_seed;
/** One bit of 'entropy' used to generate a salt for the rcon passwords. */
static uint64 _rcon_password_game_seed;
/** One bit of 'entropy' used to generate a salt for the settings passwords. */
static uint64 _settings_password_game_seed;
/** Network server's x25519 public key, used for key derivation */
static byte _server_x25519_pub_key[32];
/** Key message ID counter */
static uint64 _next_key_message_id;
/** The other bit of 'entropy' used to generate a salt for the server, rcon, and settings passwords. */
static std::string _password_server_id;
/** The other bit of 'entropy' used to generate a salt for the company passwords. */
@ -402,6 +405,60 @@ NetworkJoinInfo _network_join;
/** Make sure the server ID length is the same as a md5 hash. */
static_assert(NETWORK_SERVER_ID_LENGTH == 16 * 2 + 1);
NetworkRecvStatus ClientNetworkGameSocketHandler::SendKeyPasswordPacket(PacketType packet_type, NetworkSharedSecrets &ss, const std::string &password, const std::string *payload)
{
const NetworkGameKeys &keys = this->GetKeys();
byte shared_secret[32]; // Shared secret
crypto_x25519(shared_secret, keys.x25519_priv_key, _server_x25519_pub_key);
if (std::count(shared_secret, shared_secret + 32, 0) == 32) {
/* Secret is all 0 because public key is all 0, just give up at this point */
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
}
crypto_blake2b_ctx ctx;
crypto_blake2b_init (&ctx, 64);
crypto_blake2b_update(&ctx, shared_secret, 32); // Shared secret
crypto_blake2b_update(&ctx, keys.x25519_pub_key, 32); // Client pub key
crypto_blake2b_update(&ctx, _server_x25519_pub_key, 32); // Server pub key
crypto_blake2b_update(&ctx, (const byte *)password.data(), password.size()); // Password
crypto_blake2b_final (&ctx, ss.shared_data);
/* NetworkSharedSecrets::shared_data now contains 2 keys worth of hash, first key is used for up direction, second key for down direction (if any) */
crypto_wipe(shared_secret, 32);
std::vector<byte> message;
BufferSerialiser buffer(message);
/* Put monotonically increasing counter in message */
buffer.Send_uint64(_next_key_message_id);
/* Put actual payload in message, if there is one */
if (payload != nullptr) buffer.Send_string(*payload);
/* Message authentication code */
uint8 mac[16];
/* Use only once per key: random */
uint8 nonce[24];
NetworkRandomBytesWithFallback(nonce, 24);
/* Encrypt in place, use first half of hash as key */
crypto_aead_lock(message.data(), mac, ss.shared_data, nonce, keys.x25519_pub_key, 32, message.data(), message.size());
Packet *p = new Packet(packet_type, SHRT_MAX);
p->Send_binary(keys.x25519_pub_key, 32);
p->Send_binary(nonce, 24);
p->Send_binary(mac, 16);
p->Send_binary(message.data(), message.size());
_next_key_message_id++;
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/***********
* Sending functions
* DEF_CLIENT_SEND_COMMAND has no parameters
@ -438,10 +495,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendNewGRFsOk()
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const std::string &password)
{
Packet *p = new Packet(PACKET_CLIENT_GAME_PASSWORD, SHRT_MAX);
p->Send_buffer(GenerateGeneralPasswordHash(password, _password_server_id, _server_password_game_seed));
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
NetworkSharedSecrets ss;
return my_client->SendKeyPasswordPacket(PACKET_CLIENT_GAME_PASSWORD, ss, password, nullptr);
}
/**
@ -462,14 +517,14 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const std:
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSettingsPassword(const std::string &password)
{
Packet *p = new Packet(PACKET_CLIENT_SETTINGS_PASSWORD, SHRT_MAX);
if (password.empty()) {
p->Send_buffer(nullptr, 0);
Packet *p = new Packet(PACKET_CLIENT_SETTINGS_PASSWORD, SHRT_MAX);
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
} else {
p->Send_buffer(GenerateGeneralPasswordHash(password, _password_server_id, _settings_password_game_seed));
NetworkSharedSecrets ss;
return my_client->SendKeyPasswordPacket(PACKET_CLIENT_SETTINGS_PASSWORD, ss, password, nullptr);
}
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/** Request the map from the server. */
@ -637,11 +692,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendQuit()
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendRCon(const std::string &pass, const std::string &command)
{
Packet *p = new Packet(PACKET_CLIENT_RCON, SHRT_MAX);
p->Send_buffer(GenerateGeneralPasswordHash(pass, _password_server_id, _rcon_password_game_seed));
p->Send_string(command);
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
return my_client->SendKeyPasswordPacket(PACKET_CLIENT_RCON, my_client->last_rcon_shared_secrets, pass, &command);
}
/**
@ -836,7 +887,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSW
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
this->status = STATUS_AUTH_GAME;
_server_password_game_seed = p->Recv_uint64();
static_assert(sizeof(_server_x25519_pub_key) == 32);
p->Recv_binary(_server_x25519_pub_key, sizeof(_server_x25519_pub_key));
_password_server_id = p->Recv_string(NETWORK_SERVER_ID_LENGTH);
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
@ -876,9 +928,8 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet
/* Initialize the password hash salting variables, even if they were previously. */
_company_password_game_seed = p->Recv_uint32();
_server_password_game_seed = p->Recv_uint64();
_rcon_password_game_seed = p->Recv_uint64();
_settings_password_game_seed = p->Recv_uint64();
static_assert(sizeof(_server_x25519_pub_key) == 32);
p->Recv_binary(_server_x25519_pub_key, sizeof(_server_x25519_pub_key));
_password_server_id = p->Recv_string(NETWORK_SERVER_ID_LENGTH);
_company_password_server_id = p->Recv_string(NETWORK_SERVER_ID_LENGTH);
@ -1250,12 +1301,26 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_RCON(Packet *p)
{
if (this->status < STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
TextColour colour_code = (TextColour)p->Recv_uint16();
if (!IsValidConsoleColour(colour_code)) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (!p->CanReadFromPacket(1)) {
IConsolePrint(CC_ERROR, "Access Denied");
return NETWORK_RECV_STATUS_OKAY;
}
std::string rcon_out = p->Recv_string(NETWORK_RCONCOMMAND_LENGTH);
byte nonce[24];
byte mac[16];
p->Recv_binary(nonce, 24);
p->Recv_binary(mac, 16);
IConsolePrint(colour_code, rcon_out.c_str());
std::vector<byte> message = p->Recv_binary(p->RemainingBytesToTransfer());
if (crypto_aead_unlock(message.data(), mac, this->last_rcon_shared_secrets.shared_data + 32, nonce, nullptr, 0, message.data(), message.size()) == 0) {
SubPacketDeserialiser spd(p, message);
TextColour colour_code = (TextColour)spd.Recv_uint16();
if (!IsValidConsoleColour(colour_code)) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
std::string rcon_out = spd.Recv_string(NETWORK_RCONCOMMAND_LENGTH);
IConsolePrint(colour_code, rcon_out.c_str());
}
return NETWORK_RECV_STATUS_OKAY;
}
@ -1373,6 +1438,12 @@ std::string ClientNetworkGameSocketHandler::GetDebugInfo() const
return stdstr_fmt("status: %d (%s)", this->status, GetServerStatusName(this->status));
}
static void ResetClientConnectionKeyStates()
{
_next_key_message_id = 0;
crypto_wipe(_server_x25519_pub_key, sizeof(_server_x25519_pub_key));
}
/** Is called after a client is connected to the server */
void NetworkClient_Connected()
@ -1381,6 +1452,7 @@ void NetworkClient_Connected()
_frame_counter = 0;
_frame_counter_server = 0;
last_ack_frame = 0;
ResetClientConnectionKeyStates();
/* Request the game-info */
MyClient::SendJoin();
}

@ -18,6 +18,7 @@ private:
std::string connection_string; ///< Address we are connected to.
struct PacketReader *savegame; ///< Packet reader for reading the savegame.
byte token; ///< The token we need to send back to the server to prove we're the right client.
NetworkSharedSecrets last_rcon_shared_secrets; ///< Keys for last rcon (and incoming replies)
/** Status of the connection with the server. */
enum ServerStatus {
@ -40,6 +41,8 @@ private:
std::string server_desync_log;
bool emergency_save_done = false;
NetworkGameKeys intl_keys;
static const char *GetServerStatusName(ServerStatus status);
protected:
@ -81,6 +84,9 @@ protected:
static NetworkRecvStatus SendGetMap();
static NetworkRecvStatus SendMapOk();
void CheckConnection();
NetworkRecvStatus SendKeyPasswordPacket(PacketType packet_type, NetworkSharedSecrets &ss, const std::string &password, const std::string *payload);
public:
ClientNetworkGameSocketHandler(SOCKET s, std::string connection_string);
~ClientNetworkGameSocketHandler();
@ -90,6 +96,12 @@ public:
std::string GetDebugInfo() const override;
const NetworkGameKeys &GetKeys()
{
if (!this->intl_keys.inited) this->intl_keys.Initialise();
return this->intl_keys;
}
static NetworkRecvStatus SendJoin();
static NetworkRecvStatus SendCommand(const CommandPacket *cp);
static NetworkRecvStatus SendError(NetworkErrorCode errorno, NetworkRecvStatus recvstatus = NETWORK_RECV_STATUS_OKAY);

@ -84,6 +84,7 @@ bool NetworkServerChangeClientName(ClientID client_id, const std::string &new_na
void NetworkServerDoMove(ClientID client_id, CompanyID company_id);
void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const std::string &string);
void NetworkServerSendRconDenied(ClientID client_id);
void NetworkServerSendChat(NetworkAction action, DestType type, int dest, const std::string &msg, ClientID from_id, NetworkTextMessageData data = NetworkTextMessageData(), bool from_admin = false);
void NetworkServerSendExternalChat(const std::string &source, TextColour colour, const std::string &user, const std::string &msg);

@ -122,6 +122,20 @@ struct NetworkGameList *NetworkAddServer(const std::string &connection_string, b
void NetworkRebuildHostList();
void UpdateNetworkGameWindow();
struct NetworkGameKeys {
byte x25519_priv_key[32]; ///< x25519 key: private part
byte x25519_pub_key[32]; ///< x25519 key: public part
bool inited = false;
void Initialise();
};
struct NetworkSharedSecrets {
byte shared_data[64];
~NetworkSharedSecrets();
};
/* From network_command.cpp */
/**
* Everything we need to know about a command to be able to execute it.
@ -148,6 +162,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err);
bool NetworkMakeClientNameUnique(std::string &new_name);
std::string GenerateCompanyPasswordHash(const std::string &password, const std::string &password_server_id, uint32 password_game_seed);
std::vector<uint8> GenerateGeneralPasswordHash(const std::string &password, const std::string &password_server_id, uint64 password_game_seed);
std::string BytesToHexString(const byte *data, uint length);
std::string NetworkGenerateRandomKeyString(uint bytes);
std::string_view ParseCompanyFromConnectionString(const std::string &connection_string, CompanyID *company_id);

@ -31,6 +31,7 @@
#include "../rev.h"
#include "../crashlog.h"
#include "../3rdparty/randombytes/randombytes.h"
#include "../3rdparty/monocypher/monocypher.h"
#include <mutex>
#include <condition_variable>
#if defined(__MINGW32__)
@ -193,16 +194,6 @@ struct PacketWriter : SaveFilter {
};
const std::vector<byte> &ServerNetworkGameSocketHandler::CachedPassword::GetHash(const std::string &password, uint64 password_game_seed)
{
if (password != this->source) {
this->source = password;
this->cached_hash = GenerateGeneralPasswordHash(password, _settings_client.network.network_id, password_game_seed);
}
return this->cached_hash;
}
/**
* Create a new socket for the server side of the game connection.
* @param s The socket to connect with.
@ -213,14 +204,6 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
this->client_id = _network_client_id++;
this->receive_limit = _settings_client.network.bytes_per_frame_burst;
uint64 seeds[3];
static_assert(sizeof(seeds) == 24);
NetworkRandomBytesWithFallback(seeds, sizeof(seeds));
this->server_hash_bits = seeds[0];
this->rcon_hash_bits = seeds[1];
this->settings_hash_bits = seeds[2];
/* The Socket and Info pools need to be the same in size. After all,
* each Socket will be associated with at most one Info object. As
* such if the Socket was allocated the Info object can as well. */
@ -244,6 +227,61 @@ ServerNetworkGameSocketHandler::~ServerNetworkGameSocketHandler()
}
}
bool ServerNetworkGameSocketHandler::ParseKeyPasswordPacket(Packet *p, NetworkSharedSecrets &ss, const std::string &password, std::string *payload, size_t length)
{
byte client_pub_key[32];
byte nonce[24];
byte mac[16];
p->Recv_binary(client_pub_key, 32);
p->Recv_binary(nonce, 24);
p->Recv_binary(mac, 16);
const NetworkGameKeys &keys = this->GetKeys();
byte shared_secret[32]; // Shared secret
crypto_x25519(shared_secret, keys.x25519_priv_key, client_pub_key);
if (std::count(shared_secret, shared_secret + 32, 0) == 32) {
/* Secret is all 0 because public key is all 0, just reject it */
return false;
}
crypto_blake2b_ctx ctx;
crypto_blake2b_init (&ctx, 64);
crypto_blake2b_update(&ctx, shared_secret, 32); // Shared secret
crypto_blake2b_update(&ctx, client_pub_key, 32); // Client pub key
crypto_blake2b_update(&ctx, keys.x25519_pub_key, 32); // Server pub key
crypto_blake2b_update(&ctx, (const byte *)password.data(), password.size()); // Password
crypto_blake2b_final (&ctx, ss.shared_data);
/* NetworkSharedSecrets::shared_data now contains 2 keys worth of hash, first key is used for up direction, second key for down direction (if any) */
crypto_wipe(shared_secret, 32);
std::vector<byte> message = p->Recv_binary(p->RemainingBytesToTransfer());
if (message.size() < 8) return false;
if ((message.size() == 8) != (payload == nullptr)) {
/* Payload expected but not present, or vice versa, just give up */
return false;
}
/* Decrypt in place, use first half of hash as key */
if (crypto_aead_unlock(message.data(), mac, ss.shared_data, nonce, client_pub_key, 32, message.data(), message.size()) == 0) {
SubPacketDeserialiser spd(p, message);
uint64 message_id = spd.Recv_uint64();
if (message_id < this->min_key_message_id) {
/* ID has not increased monotonically, reject the message */
return false;
}
this->min_key_message_id = message_id + 1;
if (payload != nullptr) {
*payload = spd.Recv_string(length);
}
return true;
}
return false;
}
std::unique_ptr<Packet> ServerNetworkGameSocketHandler::ReceivePacket()
{
/* Only allow receiving when we have some buffer free; this value
@ -487,8 +525,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
/* Reset 'lag' counters */
this->last_frame = this->last_frame_server = _frame_counter;
const NetworkGameKeys &keys = this->GetKeys();
Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD, SHRT_MAX);
p->Send_uint64(this->server_hash_bits);
static_assert(sizeof(keys.x25519_pub_key) == 32);
p->Send_binary(keys.x25519_pub_key, sizeof(keys.x25519_pub_key));
p->Send_string(_settings_client.network.network_id);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
@ -525,12 +566,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
_network_game_info.clients_on++;
const NetworkGameKeys &keys = this->GetKeys();
p = new Packet(PACKET_SERVER_WELCOME, SHRT_MAX);
p->Send_uint32(this->client_id);
p->Send_uint32(_settings_game.game_creation.generation_seed);
p->Send_uint64(this->server_hash_bits);
p->Send_uint64(this->rcon_hash_bits);
p->Send_uint64(this->settings_hash_bits);
static_assert(sizeof(keys.x25519_pub_key) == 32);
p->Send_binary(keys.x25519_pub_key, sizeof(keys.x25519_pub_key));
p->Send_string(_settings_client.network.network_id);
p->Send_string(_network_company_server_id);
this->SendPacket(p);
@ -804,10 +846,38 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGame()
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConResult(uint16 colour, const std::string &command)
{
assert(this->rcon_reply_key != nullptr);
std::vector<byte> message;
BufferSerialiser buffer(message);
buffer.Send_uint16(colour);
buffer.Send_string(command);
/* Message authentication code */
uint8 mac[16];
/* Use only once per key: random */
uint8 nonce[24];
NetworkRandomBytesWithFallback(nonce, 24);
/* Encrypt in place, use first half of hash as key */
crypto_aead_lock(message.data(), mac, this->rcon_reply_key, nonce, nullptr, 0, message.data(), message.size());
Packet *p = new Packet(PACKET_SERVER_RCON, SHRT_MAX);
p->Send_binary(nonce, 24);
p->Send_binary(mac, 16);
p->Send_binary(message.data(), message.size());
p->Send_uint16(colour);
p->Send_string(command);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/**
* Send a deny result of a console action.
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConDenied()
{
Packet *p = new Packet(PACKET_SERVER_RCON, SHRT_MAX);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
@ -979,13 +1049,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(P
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
std::vector<byte> password = p->Recv_buffer();
/* Check game password. Allow joining if we cleared the password meanwhile */
if (!_settings_client.network.server_password.empty() &&
password != this->game_password_hash_cache.GetHash(_settings_client.network.server_password, this->server_hash_bits)) {
/* Password is invalid */
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
if (!_settings_client.network.server_password.empty()) {
NetworkSharedSecrets ss;
if (!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.server_password, nullptr, 0)) {
/* Password is invalid */
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
}
}
const NetworkClientInfo *ci = this->GetInfo();
@ -1025,16 +1095,16 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWO
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
std::vector<byte> password = p->Recv_buffer();
NetworkSharedSecrets ss;
/* Check settings password. Deny if no password is set */
if (password.empty()) {
if (!p->CanReadFromPacket(1)) {
if (this->settings_authed) DEBUG(net, 0, "[settings-ctrl] client-id %d deauthed", this->client_id);
this->settings_authed = false;
} else if (_settings_client.network.settings_password.empty() ||
password != this->settings_password_hash_cache.GetHash(_settings_client.network.settings_password, this->settings_hash_bits)) {
!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.settings_password, nullptr, 0)) {
DEBUG(net, 0, "[settings-ctrl] wrong password from client-id %d", this->client_id);
NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied");
NetworkServerSendRconDenied(this->client_id);
this->settings_authed = false;
NetworkRecvStatus status = this->HandleAuthFailure(this->settings_auth_failures);
if (status != NETWORK_RECV_STATUS_OKAY) return status;
@ -1591,25 +1661,26 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p)
if (this->status != STATUS_ACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
if (_settings_client.network.rcon_password.empty()) {
NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied");
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
}
std::vector<byte> password = p->Recv_buffer();
std::string command = p->Recv_string(NETWORK_RCONCOMMAND_LENGTH);
if (password != this->rcon_password_hash_cache.GetHash(_settings_client.network.rcon_password, this->rcon_hash_bits)) {
std::string command;
NetworkSharedSecrets ss;
if (!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.rcon_password, &command, NETWORK_RCONCOMMAND_LENGTH)) {
DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied");
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
}
DEBUG(net, 3, "[rcon] Client-id %d executed: %s", this->client_id, command.c_str());
_redirect_console_to_client = this->client_id;
this->rcon_reply_key = ss.shared_data + 32; /* second key */
IConsoleCmdExec(command.c_str());
_redirect_console_to_client = INVALID_CLIENT_ID;
this->rcon_auth_failures = 0;
this->rcon_reply_key = nullptr;
return NETWORK_RECV_STATUS_OKAY;
}
@ -2204,6 +2275,17 @@ void NetworkServerSendRcon(ClientID client_id, TextColour colour_code, const std
NetworkClientSocket::GetByClientID(client_id)->SendRConResult(colour_code, string);
}
/**
* Send an rcon reply to the client.
* @param client_id The identifier of the client.
* @param colour_code The colour of the text.
* @param string The actual reply.
*/
void NetworkServerSendRconDenied(ClientID client_id)
{
NetworkClientSocket::GetByClientID(client_id)->SendRConDenied();
}
/**
* Kick a single client.
* @param client_id The client to kick.

@ -22,15 +22,9 @@ extern NetworkClientSocketPool _networkclientsocket_pool;
/** Class for handling the server side of the game connection. */
class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED> {
struct CachedPassword {
std::string source;
std::vector<byte> cached_hash;
const std::vector<byte> &GetHash(const std::string &password, uint64 password_game_seed);
};
CachedPassword game_password_hash_cache;
CachedPassword rcon_password_hash_cache;
CachedPassword settings_password_hash_cache;
NetworkGameKeys intl_keys;
uint64 min_key_message_id = 0;
byte *rcon_reply_key = nullptr;
protected:
NetworkRecvStatus Receive_CLIENT_JOIN(Packet *p) override;
@ -61,6 +55,8 @@ protected:
NetworkRecvStatus SendNeedGamePassword();
NetworkRecvStatus SendNeedCompanyPassword();
bool ParseKeyPasswordPacket(Packet *p, NetworkSharedSecrets &ss, const std::string &password, std::string *payload, size_t length);
public:
/** Status of a client */
enum ClientStatus {
@ -86,9 +82,6 @@ public:
ClientStatus status; ///< Status of this client
CommandQueue outgoing_queue; ///< The command-queue awaiting delivery
size_t receive_limit; ///< Amount of bytes that we can receive at this moment
uint64 server_hash_bits; ///< Server password hash entropy bits
uint64 rcon_hash_bits; ///< Rcon password hash entropy bits
uint64 settings_hash_bits; ///< Settings password hash entropy bits
bool settings_authed = false;///< Authorised to control all game settings
bool supports_zstd = false; ///< Client supports zstd compression
@ -119,6 +112,7 @@ public:
NetworkRecvStatus SendShutdown();
NetworkRecvStatus SendNewGame();
NetworkRecvStatus SendRConResult(uint16 colour, const std::string &command);
NetworkRecvStatus SendRConDenied();
NetworkRecvStatus SendMove(ClientID client_id, CompanyID company_id);
NetworkRecvStatus SendClientInfo(NetworkClientInfo *ci);
@ -138,6 +132,12 @@ public:
std::string GetDebugInfo() const override;
const NetworkGameKeys &GetKeys()
{
if (!this->intl_keys.inited) this->intl_keys.Initialise();
return this->intl_keys;
}
static void Send();
static void AcceptConnection(SOCKET s, const NetworkAddress &address);
static bool AllowConnection();

Loading…
Cancel
Save