Add passworded mechanism to change server game settings from client

This commit is contained in:
Jonathan G Rennison
2019-08-20 20:42:17 +01:00
parent 17c054a650
commit ec892879f4
17 changed files with 155 additions and 16 deletions

View File

@@ -79,6 +79,8 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet *p)
case PACKET_SERVER_NEED_COMPANY_PASSWORD: return this->Receive_SERVER_NEED_COMPANY_PASSWORD(p);
case PACKET_CLIENT_GAME_PASSWORD: return this->Receive_CLIENT_GAME_PASSWORD(p);
case PACKET_CLIENT_COMPANY_PASSWORD: return this->Receive_CLIENT_COMPANY_PASSWORD(p);
case PACKET_CLIENT_SETTINGS_PASSWORD: return this->Receive_CLIENT_SETTINGS_PASSWORD(p);
case PACKET_SERVER_SETTINGS_ACCESS: return this->Receive_SERVER_SETTINGS_ACCESS(p);
case PACKET_SERVER_WELCOME: return this->Receive_SERVER_WELCOME(p);
case PACKET_CLIENT_GETMAP: return this->Receive_CLIENT_GETMAP(p);
case PACKET_SERVER_WAIT: return this->Receive_SERVER_WAIT(p);
@@ -166,6 +168,8 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Pa
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SETTINGS_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SETTINGS_ACCESS); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WAIT(Packet *p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WAIT); }

View File

@@ -63,6 +63,8 @@ enum PacketGameType {
PACKET_CLIENT_GAME_PASSWORD, ///< Clients sends the (hashed) game password.
PACKET_SERVER_NEED_COMPANY_PASSWORD, ///< Server requests the (hashed) company password.
PACKET_CLIENT_COMPANY_PASSWORD, ///< Client sends the (hashed) company password.
PACKET_CLIENT_SETTINGS_PASSWORD, ///< Client sends the (hashed) settings password.
PACKET_SERVER_SETTINGS_ACCESS, ///< Server sends the settings access state.
/* The server welcomes the authenticated client and sends information of other clients. */
PACKET_SERVER_WELCOME, ///< Server welcomes you and gives you your #ClientID.
@@ -262,6 +264,21 @@ protected:
*/
virtual NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p);
/**
* Send a password to the server to authorize
* uint8 Password type (see NetworkPasswordType).
* string The password.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_CLIENT_SETTINGS_PASSWORD(Packet *p);
/**
* Indication to the client that the setting access state has changed
* bool setting access state
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_SETTINGS_ACCESS(Packet *p);
/**
* The client is joined and ready to receive his map:
* uint32 Own client ID.

View File

@@ -57,6 +57,7 @@ bool _network_server; ///< network-server is active
bool _network_available; ///< is network mode available?
bool _network_dedicated; ///< are we a dedicated server?
bool _is_network_server; ///< Does this client wants to be a network-server?
bool _network_settings_access; ///< Can this client change server settings?
NetworkServerGameInfo _network_game_info; ///< Information about our game.
NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies.
ClientID _network_own_client_id; ///< Our client identifier.

View File

@@ -22,5 +22,6 @@ extern bool _network_server; ///< network-server is active
extern bool _network_available; ///< is network mode available?
extern bool _network_dedicated; ///< are we a dedicated server?
extern bool _is_network_server; ///< Does this client wants to be a network-server?
extern bool _network_settings_access; ///< Can this client change server settings?
#endif /* NETWORK_H */

View File

@@ -159,6 +159,7 @@ ClientNetworkGameSocketHandler::~ClientNetworkGameSocketHandler()
{
assert(ClientNetworkGameSocketHandler::my_client == this);
ClientNetworkGameSocketHandler::my_client = nullptr;
_network_settings_access = false;
delete this->savegame;
}
@@ -331,6 +332,8 @@ static uint32 _server_password_game_seed;
/** One bit of 'entropy' used to generate a salt for the rcon passwords. */
static uint32 _rcon_password_game_seed;
/** One bit of 'entropy' used to generate a salt for the settings passwords. */
static uint32 _settings_password_game_seed;
/** The other bit of 'entropy' used to generate a salt for the company, server, rcon, and settings passwords. */
static char _password_server_id[NETWORK_SERVER_ID_LENGTH];
/** Maximum number of companies of the currently joined server. */
@@ -415,6 +418,18 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const char
return NETWORK_RECV_STATUS_OKAY;
}
/**
* Set the game password as requested.
* @param password The game password.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSettingsPassword(const char *password)
{
Packet *p = new Packet(PACKET_CLIENT_SETTINGS_PASSWORD);
p->Send_string(GenerateCompanyPasswordHash(password, _password_server_id, _settings_password_game_seed));
my_client->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/** Request the map from the server. */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendGetMap()
{
@@ -821,6 +836,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet
_company_password_game_seed = p->Recv_uint32();
_server_password_game_seed = p->Recv_uint32();
_rcon_password_game_seed = p->Recv_uint32();
_settings_password_game_seed = p->Recv_uint32();
p->Recv_string(_password_server_id, sizeof(_password_server_id));
/* Start receiving the map */
@@ -1209,6 +1225,17 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet *p)
{
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
_network_settings_access = p->Recv_bool();
ReInitAllWindows();
return NETWORK_RECV_STATUS_OKAY;
}
/**
* Check the connection's state, i.e. is the connection still up?
*/
@@ -1263,6 +1290,16 @@ void NetworkClientSendRcon(const char *password, const char *command)
MyClient::SendRCon(password, command);
}
/**
* Send settings password.
* @param password The password.
* @param command The command to execute.
*/
void NetworkClientSendSettingsPassword(const char *password)
{
MyClient::SendSettingsPassword(password);
}
/**
* Notify the server of this client wanting to be moved to another company.
* @param company_id id of the company the client wishes to be moved to.

View File

@@ -49,6 +49,7 @@ protected:
NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet *p) override;
NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_SERVER_SETTINGS_ACCESS(Packet *p) override;
NetworkRecvStatus Receive_SERVER_WELCOME(Packet *p) override;
NetworkRecvStatus Receive_SERVER_WAIT(Packet *p) override;
NetworkRecvStatus Receive_SERVER_MAP_BEGIN(Packet *p) override;
@@ -92,6 +93,7 @@ public:
static NetworkRecvStatus SendGamePassword(const char *password);
static NetworkRecvStatus SendCompanyPassword(const char *password);
static NetworkRecvStatus SendSettingsPassword(const char *password);
static NetworkRecvStatus SendChat(NetworkAction action, DestType type, int dest, const char *msg, NetworkTextMessageData data);
static NetworkRecvStatus SendSetPassword(const char *password);

View File

@@ -54,6 +54,7 @@ void NetworkClientsToSpectators(CompanyID cid);
void NetworkClientConnectGame(NetworkAddress address, CompanyID join_as, const char *join_server_password = nullptr, const char *join_company_password = nullptr);
void NetworkClientRequestMove(CompanyID company, const char *pass = "");
void NetworkClientSendRcon(const char *password, const char *command);
void NetworkClientSendSettingsPassword(const char *password);
void NetworkClientSendChat(NetworkAction action, DestType type, int dest, const char *msg, NetworkTextMessageData data = NetworkTextMessageData());
bool NetworkClientPreferTeamChat(const NetworkClientInfo *cio);
bool NetworkCompanyIsPassworded(CompanyID company_id);

View File

@@ -218,6 +218,7 @@ ServerNetworkGameSocketHandler::ServerNetworkGameSocketHandler(SOCKET s) : Netwo
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();
/* 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
@@ -537,6 +538,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
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_string(_settings_client.network.network_id);
this->SendPacket(p);
@@ -860,6 +862,15 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ServerNetworkGameSocketHandler::SendSettingsAccessUpdate(bool ok)
{
Packet *p = new Packet(PACKET_SERVER_SETTINGS_ACCESS);
p->Send_bool(ok);
this->SendPacket(p);
return NETWORK_RECV_STATUS_OKAY;
}
/***********
* Receiving functions
* DEF_SERVER_RECEIVE_COMMAND has parameter: NetworkClientSocket *cs, Packet *p
@@ -1014,6 +1025,29 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWOR
return this->SendWelcome();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet *p)
{
if (this->status != STATUS_ACTIVE) {
/* Illegal call, return error and ignore the packet */
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
char password[NETWORK_PASSWORD_LENGTH];
p->Recv_string(password, sizeof(password));
/* Check settings password. Deny if no password is set */
if (StrEmpty(_settings_client.network.settings_password) ||
strcmp(password, GenerateCompanyPasswordHash(_settings_client.network.settings_password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed ^ this->settings_hash_bits)) != 0) {
DEBUG(net, 0, "[settings-ctrl] wrong password from client-id %d", this->client_id);
this->settings_authed = false;
} else {
DEBUG(net, 0, "[settings-ctrl] client-id %d", this->client_id);
this->settings_authed = true;
}
return this->SendSettingsAccessUpdate(this->settings_authed);
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet *p)
{
NetworkClientSocket *new_cs;
@@ -1108,12 +1142,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
}
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER) {
if ((GetCommandFlags(cp.cmd) & CMD_SERVER) && ci->client_id != CLIENT_ID_SERVER && !this->settings_authed) {
IConsolePrintF(CC_ERROR, "WARNING: server only command from: client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
return this->SendError(NETWORK_ERROR_KICKED);
}
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER) {
if ((GetCommandFlags(cp.cmd) & CMD_SPECTATOR) == 0 && !Company::IsValidID(cp.company) && ci->client_id != CLIENT_ID_SERVER && !this->settings_authed) {
IConsolePrintF(CC_ERROR, "WARNING: spectator issuing command from client %d (IP: %s), kicking...", ci->client_id, this->GetClientIP());
return this->SendError(NETWORK_ERROR_KICKED);
}
@@ -1123,7 +1157,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet
* to match the company in the packet. If it doesn't, the client has done
* something pretty naughty (or a bug), and will be kicked
*/
if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company) {
if (!(cp.cmd == CMD_COMPANY_CTRL && cp.p1 == 0 && ci->client_playas == COMPANY_NEW_COMPANY) && ci->client_playas != cp.company &&
!((GetCommandFlags(cp.cmd) & CMD_SERVER) && this->settings_authed)) {
IConsolePrintF(CC_ERROR, "WARNING: client %d (IP: %s) tried to execute a command as company %d, kicking...",
ci->client_playas + 1, this->GetClientIP(), cp.company + 1);
return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);

View File

@@ -29,6 +29,7 @@ protected:
NetworkRecvStatus Receive_CLIENT_COMPANY_INFO(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_SETTINGS_PASSWORD(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_GETMAP(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_MAP_OK(Packet *p) override;
NetworkRecvStatus Receive_CLIENT_ACK(Packet *p) override;
@@ -74,6 +75,8 @@ public:
int receive_limit; ///< Amount of bytes that we can receive at this moment
uint32 server_hash_bits; ///< Server password hash entropy bits
uint32 rcon_hash_bits; ///< Rcon password hash entropy bits
uint32 settings_hash_bits; ///< Settings password hash entropy bits
bool settings_authed = false;///< Authorised to control all game settings
struct PacketWriter *savegame; ///< Writer used to write the savegame.
NetworkAddress client_address; ///< IP-address of the client (so he can be banned)
@@ -104,6 +107,7 @@ public:
NetworkRecvStatus SendCommand(const CommandPacket *cp);
NetworkRecvStatus SendCompanyUpdate();
NetworkRecvStatus SendConfigUpdate();
NetworkRecvStatus SendSettingsAccessUpdate(bool ok);
static void Send();
static void AcceptConnection(SOCKET s, const NetworkAddress &address);