diff options
-rw-r--r-- | src/console_cmds.cpp | 7 | ||||
-rw-r--r-- | src/network/core/config.h | 2 | ||||
-rw-r--r-- | src/network/network_client.cpp | 69 | ||||
-rw-r--r-- | src/network/network_server.cpp | 4 |
4 files changed, 77 insertions, 5 deletions
diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index af5feb4fb..96b6a9735 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -1326,6 +1326,8 @@ DEF_CONSOLE_HOOK(ConHookRconPW) return true; } +extern void HashCurrentCompanyPassword(); + /* Also use from within player_gui to change the password graphically */ bool NetworkChangeCompanyPassword(byte argc, char *argv[]) { @@ -1346,8 +1348,11 @@ bool NetworkChangeCompanyPassword(byte argc, char *argv[]) ttd_strlcpy(_network_player_info[_local_player].password, argv[0], sizeof(_network_player_info[_local_player].password)); - if (!_network_server) + if (!_network_server) { SEND_COMMAND(PACKET_CLIENT_SET_PASSWORD)(_network_player_info[_local_player].password); + } else { + HashCurrentCompanyPassword(); + } IConsolePrintF(_icolour_warn, "'company_pw' changed to: %s", _network_player_info[_local_player].password); diff --git a/src/network/core/config.h b/src/network/core/config.h index 6fcbf7da2..f2f21c59d 100644 --- a/src/network/core/config.h +++ b/src/network/core/config.h @@ -29,7 +29,7 @@ enum { NETWORK_HOSTNAME_LENGTH = 80, ///< The maximum length of the host name, in bytes including '\0' NETWORK_UNIQUE_ID_LENGTH = 33, ///< The maximum length of the unique id of the clients, in bytes including '\0' NETWORK_REVISION_LENGTH = 15, ///< The maximum length of the revision, in bytes including '\0' - NETWORK_PASSWORD_LENGTH = 20, ///< The maximum length of the password, in bytes including '\0' + NETWORK_PASSWORD_LENGTH = 33, ///< The maximum length of the password, in bytes including '\0' (must be >= NETWORK_UNIQUE_ID_LENGTH) NETWORK_PLAYERS_LENGTH = 200, ///< The maximum length for the list of players that controls a company, in bytes including '\0' NETWORK_CLIENT_NAME_LENGTH = 25, ///< The maximum length of a player, in bytes including '\0' NETWORK_RCONCOMMAND_LENGTH = 500, ///< The maximum length of a rconsole command, in bytes including '\0' diff --git a/src/network/network_client.cpp b/src/network/network_client.cpp index d7f7a7ee8..7c27e0b46 100644 --- a/src/network/network_client.cpp +++ b/src/network/network_client.cpp @@ -23,6 +23,7 @@ #include "../ai/ai.h" #include "../helpers.hpp" #include "../fileio.h" +#include "../md5.h" // This file handles all the client-commands @@ -32,6 +33,59 @@ static uint32 last_ack_frame; +/** One bit of 'entropy' used to generate a salt for the company passwords. */ +static uint32 _password_game_seed; +/** The other bit of 'entropy' used to generate a salt for the company passwords. */ +static char _password_server_unique_id[NETWORK_UNIQUE_ID_LENGTH]; + +/** Make sure the unique ID length is the same as a md5 hash. */ +assert_compile(NETWORK_UNIQUE_ID_LENGTH == 16 * 2 + 1); + +/** + * Generates a hashed password for the company name. + * @param password the password to 'encrypt'. + * @return the hashed password. + */ +static const char *GenerateCompanyPasswordHash(const char *password) +{ + if (StrEmpty(password)) return password; + + char salted_password[NETWORK_UNIQUE_ID_LENGTH]; + + memset(salted_password, 0, sizeof(salted_password)); + snprintf(salted_password, sizeof(salted_password), "%s", password); + /* Add the game seed and the server's unique ID as the salt. */ + for (uint i = 0; i < NETWORK_UNIQUE_ID_LENGTH; i++) salted_password[i] ^= _password_server_unique_id[i] ^ (_password_game_seed >> i); + + md5_state_t state; + md5_byte_t digest[16]; + static char hashed_password[NETWORK_UNIQUE_ID_LENGTH]; + + /* Generate the MD5 hash */ + md5_init(&state); + md5_append(&state, (const md5_byte_t*)salted_password, sizeof(salted_password)); + md5_finish(&state, digest); + + for (int di = 0; di < 16; di++) sprintf(hashed_password + di * 2, "%02x", digest[di]); + + return hashed_password; +} + +/** + * Hash the current company password; used when the server 'player' sets his/her password. + */ +void HashCurrentCompanyPassword() +{ + if (StrEmpty(_network_player_info[_local_player].password)) return; + + _password_game_seed = _patches.generation_seed; + snprintf(_password_server_unique_id, sizeof(_password_server_unique_id), _network_unique_id); + + const char *new_pw = GenerateCompanyPasswordHash(_network_player_info[_local_player].password); + snprintf(_network_player_info[_local_player].password, sizeof(_network_player_info[_local_player].password), new_pw); +} + + // ********** // Sending functions // DEF_CLIENT_SEND_COMMAND has no parameters @@ -103,7 +157,7 @@ DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_PASSWORD)(NetworkPasswordType type, // Packet *p = NetworkSend_Init(PACKET_CLIENT_PASSWORD); p->Send_uint8 (type); - p->Send_string(password); + p->Send_string(type == NETWORK_GAME_PASSWORD ? password : GenerateCompanyPasswordHash(password)); MY_CLIENT->Send_Packet(p); } @@ -224,7 +278,7 @@ DEF_CLIENT_SEND_COMMAND_PARAM(PACKET_CLIENT_SET_PASSWORD)(const char *password) // Packet *p = NetworkSend_Init(PACKET_CLIENT_SET_PASSWORD); - p->Send_string(password); + p->Send_string(GenerateCompanyPasswordHash(password)); MY_CLIENT->Send_Packet(p); } @@ -458,8 +512,13 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_NEED_PASSWORD) NetworkPasswordType type = (NetworkPasswordType)p->Recv_uint8(); switch (type) { - case NETWORK_GAME_PASSWORD: case NETWORK_COMPANY_PASSWORD: + /* Initialize the password hash salting variables. */ + _password_game_seed = p->Recv_uint32(); + p->Recv_string(_password_server_unique_id, sizeof(_password_server_unique_id)); + if (MY_CLIENT->has_quit) return NETWORK_RECV_STATUS_MALFORMED_PACKET; + + case NETWORK_GAME_PASSWORD: ShowNetworkNeedPassword(type); return NETWORK_RECV_STATUS_OKAY; @@ -471,6 +530,10 @@ DEF_CLIENT_RECEIVE_COMMAND(PACKET_SERVER_WELCOME) { _network_own_client_index = p->Recv_uint16(); + /* Initialize the password hash salting variables, even if they were previously. */ + _password_game_seed = p->Recv_uint32(); + p->Recv_string(_password_server_unique_id, sizeof(_password_server_unique_id)); + // Start receiving the map SEND_COMMAND(PACKET_CLIENT_GETMAP)(); return NETWORK_RECV_STATUS_OKAY; diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index 0c484d516..1c10996db 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -224,6 +224,8 @@ DEF_SERVER_SEND_COMMAND_PARAM(PACKET_SERVER_NEED_PASSWORD)(NetworkTCPSocketHandl Packet *p = NetworkSend_Init(PACKET_SERVER_NEED_PASSWORD); p->Send_uint8(type); + p->Send_uint32(_patches.generation_seed); + p->Send_string(_network_unique_id); cs->Send_Packet(p); } @@ -247,6 +249,8 @@ DEF_SERVER_SEND_COMMAND(PACKET_SERVER_WELCOME) p = NetworkSend_Init(PACKET_SERVER_WELCOME); p->Send_uint16(cs->index); + p->Send_uint32(_patches.generation_seed); + p->Send_string(_network_unique_id); cs->Send_Packet(p); // Transmit info about all the active clients |