# HG changeset patch # User rubidium # Date 1196608339 0 # Node ID 51009cfec3deee6779541ffcf29170d89c9daf93 # Parent 88f26cafc8582e922ce354faac447426221da2a8 (svn r11557) -Codechange: send and store the passwords a little more secure to/in the servers. Each server and game yield a (usually) different 'salt'. This salt is used by the clients to hash their passwords. This way the passwords are not sent in clear text and it is not trivial to use those hashes on other servers. NOTE: It is still NOT safe to use your trusted passwords and it will not stop people from being able to 'hijack' your password, it only makes it harder to do and certainly much less trivial than just dumping passwords from the memory. diff -r 88f26cafc858 -r 51009cfec3de src/console_cmds.cpp --- a/src/console_cmds.cpp Sun Dec 02 14:48:26 2007 +0000 +++ b/src/console_cmds.cpp Sun Dec 02 15:12:19 2007 +0000 @@ -1326,6 +1326,8 @@ 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 @@ 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 -r 88f26cafc858 -r 51009cfec3de src/network/core/config.h --- a/src/network/core/config.h Sun Dec 02 14:48:26 2007 +0000 +++ b/src/network/core/config.h Sun Dec 02 15:12:19 2007 +0000 @@ -29,7 +29,7 @@ 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 -r 88f26cafc858 -r 51009cfec3de src/network/network_client.cpp --- a/src/network/network_client.cpp Sun Dec 02 14:48:26 2007 +0000 +++ b/src/network/network_client.cpp Sun Dec 02 15:12:19 2007 +0000 @@ -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 @@ // 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 @@ // 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 @@ NetworkPasswordType type = (NetworkPasswordType)p->Recv_uint8(); switch (type) { + 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: - case NETWORK_COMPANY_PASSWORD: ShowNetworkNeedPassword(type); return NETWORK_RECV_STATUS_OKAY; @@ -471,6 +530,10 @@ { _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 -r 88f26cafc858 -r 51009cfec3de src/network/network_server.cpp --- a/src/network/network_server.cpp Sun Dec 02 14:48:26 2007 +0000 +++ b/src/network/network_server.cpp Sun Dec 02 15:12:19 2007 +0000 @@ -224,6 +224,8 @@ 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 @@ 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