Network: Change hash function for non-company passwords

Increase size of per-connection salts, simplify management
Cache per-connection hashes at server end
Send hashes as binary rather than bothering to stringify them
This commit is contained in:
Jonathan G Rennison
2023-06-12 22:41:09 +01:00
parent 79255c0294
commit c285413853
5 changed files with 97 additions and 27 deletions

View File

@@ -30,6 +30,7 @@
#include "../core/random_func.hpp"
#include "../rev.h"
#include "../crashlog.h"
#include "../3rdparty/randombytes/randombytes.h"
#include <mutex>
#include <condition_variable>
#if defined(__MINGW32__)
@@ -192,6 +193,16 @@ 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.
@@ -201,9 +212,17 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
this->status = STATUS_INACTIVE;
this->client_id = _network_client_id++;
this->receive_limit = _settings_client.network.bytes_per_frame_burst;
this->server_hash_bits = InteractiveRandom();
this->rcon_hash_bits = InteractiveRandom();
this->settings_hash_bits = InteractiveRandom();
uint64 seeds[3];
if (randombytes(&seeds, sizeof(uint64) * lengthof(seeds)) < 0) {
/* Can't get random data, use InteractiveRandom */
for (uint64 &seed : seeds) {
seed = (uint64)(InteractiveRandom()) | (((uint64)(InteractiveRandom())) << 32);
}
}
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
@@ -472,7 +491,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
this->last_frame = this->last_frame_server = _frame_counter;
Packet *p = new Packet(PACKET_SERVER_NEED_GAME_PASSWORD, SHRT_MAX);
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->server_hash_bits);
p->Send_uint64(this->server_hash_bits);
p->Send_string(_settings_client.network.network_id);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
@@ -512,9 +531,9 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
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_uint32(_settings_game.game_creation.generation_seed ^ this->server_hash_bits);
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->rcon_hash_bits);
p->Send_uint32(_settings_game.game_creation.generation_seed ^ this->settings_hash_bits);
p->Send_uint64(this->server_hash_bits);
p->Send_uint64(this->rcon_hash_bits);
p->Send_uint64(this->settings_hash_bits);
p->Send_string(_settings_client.network.network_id);
p->Send_string(_network_company_server_id);
this->SendPacket(p);
@@ -963,11 +982,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(P
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
std::string password = p->Recv_string(NETWORK_PASSWORD_LENGTH);
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 != GenerateCompanyPasswordHash(_settings_client.network.server_password.c_str(), _settings_client.network.network_id.c_str(), _settings_game.game_creation.generation_seed ^ this->server_hash_bits)) {
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);
}
@@ -1009,14 +1028,14 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWO
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
std::string password = p->Recv_string(NETWORK_PASSWORD_LENGTH);
std::vector<byte> password = p->Recv_buffer();
/* Check settings password. Deny if no password is set */
if (password.empty()) {
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 != GenerateCompanyPasswordHash(_settings_client.network.settings_password.c_str(), _settings_client.network.network_id.c_str(), _settings_game.game_creation.generation_seed ^ this->settings_hash_bits)) {
password != this->settings_password_hash_cache.GetHash(_settings_client.network.settings_password, this->settings_hash_bits)) {
DEBUG(net, 0, "[settings-ctrl] wrong password from client-id %d", this->client_id);
NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied");
this->settings_authed = false;
@@ -1576,10 +1595,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p)
return NETWORK_RECV_STATUS_OKAY;
}
std::string password = p->Recv_string(NETWORK_PASSWORD_LENGTH);
std::vector<byte> password = p->Recv_buffer();
std::string command = p->Recv_string(NETWORK_RCONCOMMAND_LENGTH);
if (password != GenerateCompanyPasswordHash(_settings_client.network.rcon_password.c_str(), _settings_client.network.network_id.c_str(), _settings_game.game_creation.generation_seed ^ this->rcon_hash_bits)) {
if (password != this->rcon_password_hash_cache.GetHash(_settings_client.network.rcon_password, this->rcon_hash_bits)) {
DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied");
return NETWORK_RECV_STATUS_OKAY;