Index: src/console_cmds.cpp =================================================================== --- src/console_cmds.cpp (revision 21695) +++ src/console_cmds.cpp (working copy) @@ -1510,20 +1510,44 @@ DEF_CONSOLE_CMD(ConCompanyPassword) { if (argc == 0) { - IConsoleHelp("Change the password of your company. Usage: 'company_pw \"\"'"); + const char *helpmsg; + + if (_network_dedicated) { + helpmsg = "Change the password of a company. Usage: 'company_pw \"\""; + } else if (_network_server) { + helpmsg = "Change the password of your or any other company. Usage: 'company_pw [] \"\"'"; + } else { + helpmsg = "Change the password of your company. Usage: 'company_pw \"\"'"; + } + + IConsoleHelp(helpmsg); IConsoleHelp("Use \"*\" to disable the password."); return true; } - if (argc != 2) return false; + CompanyID company_id; + const char *password; + const char *errormsg; - if (!Company::IsValidID(_local_company)) { - IConsoleError("You have to own a company to make use of this command."); + if (argc == 2) { + company_id = _local_company; + password = argv[1]; + errormsg = "You have to own a company to make use of this command."; + } else if (argc == 3 && _network_server) { + company_id = (CompanyID)(atoi(argv[1]) - 1); + password = argv[2]; + errormsg = "You have to specify the ID of a valid human controlled company."; + } else { return false; } - const char *password = NetworkChangeCompanyPassword(argv[1]); + if (!Company::IsValidHumanID(company_id)) { + IConsoleError(errormsg); + return false; + } + password = NetworkChangeCompanyPassword(company_id, password, false); + if (StrEmpty(password)) { IConsolePrintF(CC_WARNING, "Company password cleared"); } else { Index: src/company_cmd.cpp =================================================================== --- src/company_cmd.cpp (revision 21695) +++ src/company_cmd.cpp (working copy) @@ -814,7 +814,7 @@ assert(_local_company == COMPANY_SPECTATOR); SetLocalCompany(c->index); if (!StrEmpty(_settings_client.network.default_company_pass)) { - NetworkChangeCompanyPassword(_settings_client.network.default_company_pass); + NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass); } /* Now that we have a new company, broadcast our company settings to Index: src/network/network_func.h =================================================================== --- src/network/network_func.h (revision 21695) +++ src/network/network_func.h (working copy) @@ -36,7 +36,7 @@ byte NetworkSpectatorCount(); void NetworkUpdateClientName(); bool NetworkCompanyHasClients(CompanyID company); -const char *NetworkChangeCompanyPassword(const char *); +const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password, bool already_hashed = true); void NetworkReboot(); void NetworkDisconnect(bool blocking = false, bool close_admins = true); void NetworkGameLoop(); Index: src/network/network_server.cpp =================================================================== --- src/network/network_server.cpp (revision 21695) +++ src/network/network_server.cpp (working copy) @@ -1287,10 +1287,7 @@ p->Recv_string(password, sizeof(password)); ci = this->GetInfo(); - if (Company::IsValidID(ci->client_playas)) { - strecpy(_network_company_states[ci->client_playas].password, password, lastof(_network_company_states[ci->client_playas].password)); - NetworkServerUpdateCompanyPassworded(ci->client_playas, !StrEmpty(_network_company_states[ci->client_playas].password)); - } + NetworkServerSetCompanyPassword(ci->client_playas, password); return NETWORK_RECV_STATUS_OKAY; } @@ -1629,6 +1626,24 @@ return true; } +/** + * Set/Reset a company password on the server end. + * @param company_id ID of the company the password should be changed for. + * @param password The new password. + * @param already_hashed Is the given password already hashed? + */ +void NetworkServerSetCompanyPassword(CompanyID company_id, const char *password, bool already_hashed) +{ + if (!Company::IsValidHumanID(company_id)) return; + + if (!already_hashed) { + password = NetworkGeneratePasswordHash(password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed); + } + + strecpy(_network_company_states[company_id].password, password, lastof(_network_company_states[company_id].password)); + NetworkServerUpdateCompanyPassworded(company_id, !StrEmpty(_network_company_states[company_id].password)); +} + /* Handle the local command-queue */ static void NetworkHandleCommandQueue(NetworkClientSocket *cs) { @@ -1810,6 +1825,8 @@ FOR_ALL_CLIENT_SOCKETS(cs) { if (cs->status >= NetworkClientSocket::STATUS_PRE_ACTIVE) cs->SendCompanyUpdate(); } + + NetworkAdminCompanyUpdate(Company::GetIfValid(company_id)); } /** Index: src/network/network_server.h =================================================================== --- src/network/network_server.h (revision 21695) +++ src/network/network_server.h (working copy) @@ -118,6 +118,7 @@ }; void NetworkServer_Tick(bool send_frame); +void NetworkServerSetCompanyPassword(CompanyID company_id, const char *password, bool already_hashed = true); #define FOR_ALL_CLIENT_SOCKETS_FROM(var, start) FOR_ALL_ITEMS_FROM(NetworkClientSocket, clientsocket_index, var, start) #define FOR_ALL_CLIENT_SOCKETS(var) FOR_ALL_CLIENT_SOCKETS_FROM(var, 0) Index: src/network/network_client.cpp =================================================================== --- src/network/network_client.cpp (revision 21695) +++ src/network/network_client.cpp (working copy) @@ -298,55 +298,6 @@ /** Make sure the server ID length is the same as a md5 hash. */ assert_compile(NETWORK_SERVER_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_SERVER_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 ID as the salt. */ - for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) { - salted_password[i] ^= _password_server_id[i] ^ (_password_game_seed >> (i % 32)); - } - - Md5 checksum; - uint8 digest[16]; - static char hashed_password[NETWORK_SERVER_ID_LENGTH]; - - /* Generate the MD5 hash */ - checksum.Append(salted_password, sizeof(salted_password) - 1); - checksum.Finish(digest); - - for (int di = 0; di < 16; di++) sprintf(hashed_password + di * 2, "%02x", digest[di]); - hashed_password[lengthof(hashed_password) - 1] = '\0'; - - return hashed_password; -} - -/** - * Hash the current company password; used when the server 'company' sets his/her password. - */ -static void HashCurrentCompanyPassword(const char *password) -{ - _password_game_seed = _settings_game.game_creation.generation_seed; - strecpy(_password_server_id, _settings_client.network.network_id, lastof(_password_server_id)); - - const char *new_pw = GenerateCompanyPasswordHash(password); - strecpy(_network_company_states[_local_company].password, new_pw, lastof(_network_company_states[_local_company].password)); - - if (_network_server) { - NetworkServerUpdateCompanyPassworded(_local_company, !StrEmpty(_network_company_states[_local_company].password)); - } -} - - /*********** * Sending functions * DEF_CLIENT_SEND_COMMAND has no parameters @@ -396,7 +347,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const char *password) { Packet *p = new Packet(PACKET_CLIENT_COMPANY_PASSWORD); - p->Send_string(GenerateCompanyPasswordHash(password)); + p->Send_string(NetworkGeneratePasswordHash(password, _password_server_id, _password_game_seed)); my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; } @@ -475,7 +426,7 @@ { Packet *p = new Packet(PACKET_CLIENT_SET_PASSWORD); - p->Send_string(GenerateCompanyPasswordHash(password)); + p->Send_string(NetworkGeneratePasswordHash(password, _password_server_id, _password_game_seed)); my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; } @@ -506,11 +457,11 @@ return NETWORK_RECV_STATUS_OKAY; } -NetworkRecvStatus ClientNetworkGameSocketHandler::SendMove(CompanyID company, const char *pass) +NetworkRecvStatus ClientNetworkGameSocketHandler::SendMove(CompanyID company, const char *password) { Packet *p = new Packet(PACKET_CLIENT_MOVE); p->Send_uint8(company); - p->Send_string(GenerateCompanyPasswordHash(pass)); + p->Send_string(NetworkGeneratePasswordHash(password, _password_server_id, _password_game_seed)); my_client->SendPacket(p); return NETWORK_RECV_STATUS_OKAY; } @@ -1227,7 +1178,11 @@ MyClient::SendChat(action, type, dest, msg, data); } -static void NetworkClientSetPassword(const char *password) +/** + * Set/Reset company password on the client side. + * @param password Password to be set. + */ +void NetworkClientSetCompanyPassword(const char *password) { MyClient::SendSetPassword(password); } @@ -1251,24 +1206,6 @@ } /** - * Sets/resets company password - * @param password new password, "" or "*" resets password - * @return new password - */ -const char *NetworkChangeCompanyPassword(const char *password) -{ - if (strcmp(password, "*") == 0) password = ""; - - if (!_network_server) { - NetworkClientSetPassword(password); - } else { - HashCurrentCompanyPassword(password); - } - - return password; -} - -/** * Check if max_companies has been reached on the server (local check only). * @return true if the max value has been reached or exceeded, false otherwise. */ Index: src/network/network.cpp =================================================================== --- src/network/network.cpp (revision 21695) +++ src/network/network.cpp (working copy) @@ -151,6 +151,59 @@ } /** + * Change the company password of a given company. + * @param company_id ID of the company the password should be changed for. + * @param password The unhashed password we like to set ('*' or '' resets the password) + * @return The password. + */ +const char *NetworkChangeCompanyPassword(CompanyID company_id, const char *password, bool already_hashed) +{ + if (strcmp(password, "*") == 0) password = ""; + + if (_network_server) { + NetworkServerSetCompanyPassword(company_id, password, already_hashed); + } else { + NetworkClientSetCompanyPassword(password); + } + + return password; +} + +/** + * Hash the given password using server ID and game seed. + * @param password Password to hash. + * @param password_server_id Server ID. + * @param password_game_seed Game seed. + * @return The hashed password. + */ +const char *NetworkGeneratePasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed) +{ + if (StrEmpty(password)) return password; + + char salted_password[NETWORK_SERVER_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 ID as the salt. */ + for (uint i = 0; i < NETWORK_SERVER_ID_LENGTH - 1; i++) { + salted_password[i] ^= password_server_id[i] ^ (password_game_seed >> (i % 32)); + } + + Md5 checksum; + uint8 digest[16]; + static char hashed_password[NETWORK_SERVER_ID_LENGTH]; + + /* Generate the MD5 hash */ + checksum.Append(salted_password, sizeof(salted_password) - 1); + checksum.Finish(digest); + + for (int di = 0; di < 16; di++) sprintf(hashed_password + di * 2, "%02x", digest[di]); + hashed_password[lengthof(hashed_password) - 1] = '\0'; + + return hashed_password; +} + +/** * Check if the company we want to join requires a password. * @param company_id id of the company we want to check the 'passworded' flag for. * @return true if the company requires a password. Index: src/network/network_internal.h =================================================================== --- src/network/network_internal.h (revision 21695) +++ src/network/network_internal.h (working copy) @@ -168,6 +168,7 @@ NetworkClientSocket *NetworkFindClientStateFromClientID(ClientID client_id); StringID GetNetworkErrorMsg(NetworkErrorCode err); bool NetworkFindName(char new_name[NETWORK_CLIENT_NAME_LENGTH]); +const char *NetworkGeneratePasswordHash(const char *password, const char *password_server_id, uint32 password_game_seed); #endif /* ENABLE_NETWORK */ #endif /* NETWORK_INTERNAL_H */ Index: src/network/network_client.h =================================================================== --- src/network/network_client.h (revision 21695) +++ src/network/network_client.h (working copy) @@ -108,6 +108,7 @@ typedef ClientNetworkGameSocketHandler MyClient; void NetworkClient_Connected(); +void NetworkClientSetCompanyPassword(const char *password); extern CompanyID _network_join_as; Index: src/network/network_gui.cpp =================================================================== --- src/network/network_gui.cpp (revision 21695) +++ src/network/network_gui.cpp (working copy) @@ -2311,7 +2311,7 @@ snprintf(_settings_client.network.default_company_pass, lengthof(_settings_client.network.default_company_pass), "%s", this->edit_str_buf); } - NetworkChangeCompanyPassword(this->edit_str_buf); + NetworkChangeCompanyPassword(_local_company, this->edit_str_buf); } virtual void OnPaint() Index: src/openttd.cpp =================================================================== --- src/openttd.cpp (revision 21695) +++ src/openttd.cpp (working copy) @@ -823,7 +823,7 @@ /* We are the server, we start a new company (not dedicated), * so set the default password *if* needed. */ if (_network_server && !StrEmpty(_settings_client.network.default_company_pass)) { - NetworkChangeCompanyPassword(_settings_client.network.default_company_pass); + NetworkChangeCompanyPassword(_local_company, _settings_client.network.default_company_pass); } #endif /* ENABLE_NETWORK */