(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.
pull/155/head
rubidium 17 years ago
parent 2880047689
commit 49e6247de8

@ -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);

@ -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'

@ -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;

@ -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

Loading…
Cancel
Save