mirror of https://github.com/OpenTTD/OpenTTD
Change: remove company passwords over client allow lists
parent
a002803d1c
commit
71fc907584
|
@ -67,7 +67,6 @@ bool _network_server; ///< network-server is active
|
||||||
bool _network_available; ///< is network mode available?
|
bool _network_available; ///< is network mode available?
|
||||||
bool _network_dedicated; ///< are we a dedicated server?
|
bool _network_dedicated; ///< are we a dedicated server?
|
||||||
bool _is_network_server; ///< Does this client wants to be a network-server?
|
bool _is_network_server; ///< Does this client wants to be a network-server?
|
||||||
NetworkCompanyState *_network_company_states = nullptr; ///< Statistics about some companies.
|
|
||||||
ClientID _network_own_client_id; ///< Our client identifier.
|
ClientID _network_own_client_id; ///< Our client identifier.
|
||||||
ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client.
|
ClientID _redirect_console_to_client; ///< If not invalid, redirect the console output to a client.
|
||||||
uint8_t _network_reconnect; ///< Reconnect timeout
|
uint8_t _network_reconnect; ///< Reconnect timeout
|
||||||
|
@ -85,7 +84,6 @@ uint32_t _sync_seed_2; ///< Second part of the seed.
|
||||||
#endif
|
#endif
|
||||||
uint32_t _sync_frame; ///< The frame to perform the sync check.
|
uint32_t _sync_frame; ///< The frame to perform the sync check.
|
||||||
bool _network_first_time; ///< Whether we have finished joining or not.
|
bool _network_first_time; ///< Whether we have finished joining or not.
|
||||||
CompanyMask _network_company_passworded; ///< Bitmask of the password status of all companies.
|
|
||||||
|
|
||||||
static_assert((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH);
|
static_assert((int)NETWORK_COMPANY_NAME_LENGTH == MAX_LENGTH_COMPANY_NAME_CHARS * MAX_CHAR_LENGTH);
|
||||||
|
|
||||||
|
@ -287,9 +285,9 @@ std::string GenerateCompanyPasswordHash(const std::string &password, const std::
|
||||||
* @param company_id id of the company we want to check the 'passworded' flag for.
|
* @param company_id id of the company we want to check the 'passworded' flag for.
|
||||||
* @return true if the company requires a password.
|
* @return true if the company requires a password.
|
||||||
*/
|
*/
|
||||||
bool NetworkCompanyIsPassworded(CompanyID company_id)
|
bool NetworkCompanyIsPassworded([[maybe_unused]] CompanyID company_id)
|
||||||
{
|
{
|
||||||
return HasBit(_network_company_passworded, company_id);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This puts a text-message to the console, or in the future, the chat-box,
|
/* This puts a text-message to the console, or in the future, the chat-box,
|
||||||
|
@ -687,10 +685,6 @@ void NetworkClose(bool close_admins)
|
||||||
|
|
||||||
NetworkFreeLocalCommandQueue();
|
NetworkFreeLocalCommandQueue();
|
||||||
|
|
||||||
delete[] _network_company_states;
|
|
||||||
_network_company_states = nullptr;
|
|
||||||
_network_company_passworded = 0;
|
|
||||||
|
|
||||||
InitializeNetworkPools(close_admins);
|
InitializeNetworkPools(close_admins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -985,7 +979,6 @@ bool NetworkServerStart()
|
||||||
Debug(net, 5, "Starting listeners for incoming server queries");
|
Debug(net, 5, "Starting listeners for incoming server queries");
|
||||||
NetworkUDPServerListen();
|
NetworkUDPServerListen();
|
||||||
|
|
||||||
_network_company_states = new NetworkCompanyState[MAX_COMPANIES];
|
|
||||||
_network_server = true;
|
_network_server = true;
|
||||||
_networking = true;
|
_networking = true;
|
||||||
_frame_counter = 0;
|
_frame_counter = 0;
|
||||||
|
@ -995,7 +988,6 @@ bool NetworkServerStart()
|
||||||
_network_own_client_id = CLIENT_ID_SERVER;
|
_network_own_client_id = CLIENT_ID_SERVER;
|
||||||
|
|
||||||
_network_clients_connected = 0;
|
_network_clients_connected = 0;
|
||||||
_network_company_passworded = 0;
|
|
||||||
|
|
||||||
NetworkInitGameInfo();
|
NetworkInitGameInfo();
|
||||||
|
|
||||||
|
|
|
@ -1261,12 +1261,10 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(P
|
||||||
return NETWORK_RECV_STATUS_OKAY;
|
return NETWORK_RECV_STATUS_OKAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet &p)
|
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet &)
|
||||||
{
|
{
|
||||||
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
if (this->status < STATUS_ACTIVE) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
|
||||||
|
|
||||||
static_assert(sizeof(_network_company_passworded) <= sizeof(uint16_t));
|
|
||||||
_network_company_passworded = p.Recv_uint16();
|
|
||||||
SetWindowClassesDirty(WC_COMPANY);
|
SetWindowClassesDirty(WC_COMPANY);
|
||||||
|
|
||||||
Debug(net, 9, "Client::Receive_SERVER_COMPANY_UPDATE()");
|
Debug(net, 9, "Client::Receive_SERVER_COMPANY_UPDATE()");
|
||||||
|
|
|
@ -24,8 +24,6 @@
|
||||||
#include "../company_type.h"
|
#include "../company_type.h"
|
||||||
#include "../string_type.h"
|
#include "../string_type.h"
|
||||||
|
|
||||||
extern NetworkCompanyState *_network_company_states;
|
|
||||||
|
|
||||||
extern ClientID _network_own_client_id;
|
extern ClientID _network_own_client_id;
|
||||||
extern ClientID _redirect_console_to_client;
|
extern ClientID _redirect_console_to_client;
|
||||||
extern uint8_t _network_reconnect;
|
extern uint8_t _network_reconnect;
|
||||||
|
|
|
@ -1423,7 +1423,6 @@ using ClientButton = Button<ClientID>;
|
||||||
struct NetworkClientListWindow : Window {
|
struct NetworkClientListWindow : Window {
|
||||||
private:
|
private:
|
||||||
ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query.
|
ClientListWidgets query_widget; ///< During a query this tracks what widget caused the query.
|
||||||
CompanyID join_company; ///< During query for company password, this stores what company we wanted to join.
|
|
||||||
|
|
||||||
ClientID dd_client_id; ///< During admin dropdown, track which client this was for.
|
ClientID dd_client_id; ///< During admin dropdown, track which client this was for.
|
||||||
CompanyID dd_company_id; ///< During admin dropdown, track which company this was for.
|
CompanyID dd_company_id; ///< During admin dropdown, track which company this was for.
|
||||||
|
@ -1459,10 +1458,6 @@ private:
|
||||||
if (_network_server) {
|
if (_network_server) {
|
||||||
NetworkServerDoMove(CLIENT_ID_SERVER, company_id);
|
NetworkServerDoMove(CLIENT_ID_SERVER, company_id);
|
||||||
MarkWholeScreenDirty();
|
MarkWholeScreenDirty();
|
||||||
} else if (NetworkCompanyIsPassworded(company_id)) {
|
|
||||||
w->query_widget = WID_CL_COMPANY_JOIN;
|
|
||||||
w->join_company = company_id;
|
|
||||||
ShowQueryString(STR_EMPTY, STR_NETWORK_NEED_COMPANY_PASSWORD_CAPTION, NETWORK_PASSWORD_LENGTH, w, CS_ALPHANUMERAL, QSF_PASSWORD);
|
|
||||||
} else {
|
} else {
|
||||||
NetworkClientRequestMove(company_id);
|
NetworkClientRequestMove(company_id);
|
||||||
}
|
}
|
||||||
|
@ -1546,14 +1541,15 @@ private:
|
||||||
* Part of RebuildList() to create the information for a single company.
|
* Part of RebuildList() to create the information for a single company.
|
||||||
* @param company_id The company to build the list for.
|
* @param company_id The company to build the list for.
|
||||||
* @param client_playas The company the client is joined as.
|
* @param client_playas The company the client is joined as.
|
||||||
|
* @param can_join_company Whether this company can be joined by us.
|
||||||
*/
|
*/
|
||||||
void RebuildListCompany(CompanyID company_id, CompanyID client_playas)
|
void RebuildListCompany(CompanyID company_id, CompanyID client_playas, bool can_join_company)
|
||||||
{
|
{
|
||||||
ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat);
|
ButtonCommon *chat_button = new CompanyButton(SPR_CHAT, company_id == COMPANY_SPECTATOR ? STR_NETWORK_CLIENT_LIST_CHAT_SPECTATOR_TOOLTIP : STR_NETWORK_CLIENT_LIST_CHAT_COMPANY_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyChat);
|
||||||
|
|
||||||
if (_network_server) this->buttons[line_count].push_back(std::make_unique<CompanyButton>(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin, company_id == COMPANY_SPECTATOR));
|
if (_network_server) this->buttons[line_count].push_back(std::make_unique<CompanyButton>(SPR_ADMIN, STR_NETWORK_CLIENT_LIST_ADMIN_COMPANY_TOOLTIP, COLOUR_RED, company_id, &NetworkClientListWindow::OnClickCompanyAdmin, company_id == COMPANY_SPECTATOR));
|
||||||
this->buttons[line_count].emplace_back(chat_button);
|
this->buttons[line_count].emplace_back(chat_button);
|
||||||
if (client_playas != company_id) this->buttons[line_count].push_back(std::make_unique<CompanyButton>(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin, company_id != COMPANY_SPECTATOR && Company::Get(company_id)->is_ai));
|
if (can_join_company) this->buttons[line_count].push_back(std::make_unique<CompanyButton>(SPR_JOIN, STR_NETWORK_CLIENT_LIST_JOIN_TOOLTIP, COLOUR_ORANGE, company_id, &NetworkClientListWindow::OnClickCompanyJoin, company_id != COMPANY_SPECTATOR && Company::Get(company_id)->is_ai));
|
||||||
|
|
||||||
this->line_count += 1;
|
this->line_count += 1;
|
||||||
|
|
||||||
|
@ -1599,18 +1595,18 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_playas != COMPANY_SPECTATOR) {
|
if (client_playas != COMPANY_SPECTATOR) {
|
||||||
this->RebuildListCompany(client_playas, client_playas);
|
this->RebuildListCompany(client_playas, client_playas, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Companies */
|
/* Companies */
|
||||||
for (const Company *c : Company::Iterate()) {
|
for (const Company *c : Company::Iterate()) {
|
||||||
if (c->index == client_playas) continue;
|
if (c->index == client_playas) continue;
|
||||||
|
|
||||||
this->RebuildListCompany(c->index, client_playas);
|
this->RebuildListCompany(c->index, client_playas, (own_ci != nullptr && c->allow_list.Contains(own_ci->public_key)) || _network_server);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Spectators */
|
/* Spectators */
|
||||||
this->RebuildListCompany(COMPANY_SPECTATOR, client_playas);
|
this->RebuildListCompany(COMPANY_SPECTATOR, client_playas, client_playas != COMPANY_SPECTATOR);
|
||||||
|
|
||||||
this->vscroll->SetCount(this->line_count);
|
this->vscroll->SetCount(this->line_count);
|
||||||
}
|
}
|
||||||
|
@ -1914,10 +1910,6 @@ public:
|
||||||
this->InvalidateData();
|
this->InvalidateData();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case WID_CL_COMPANY_JOIN:
|
|
||||||
NetworkClientRequestMove(this->join_company, str);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,8 +80,6 @@ extern std::string _network_server_name;
|
||||||
|
|
||||||
extern uint8_t _network_reconnect;
|
extern uint8_t _network_reconnect;
|
||||||
|
|
||||||
extern CompanyMask _network_company_passworded;
|
|
||||||
|
|
||||||
void NetworkQueryServer(const std::string &connection_string);
|
void NetworkQueryServer(const std::string &connection_string);
|
||||||
|
|
||||||
void GetBindAddresses(NetworkAddressList *addresses, uint16_t port);
|
void GetBindAddresses(NetworkAddressList *addresses, uint16_t port);
|
||||||
|
|
|
@ -485,7 +485,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
|
||||||
this->status = STATUS_AUTH_COMPANY;
|
this->status = STATUS_AUTH_COMPANY;
|
||||||
|
|
||||||
NetworkClientInfo *ci = this->GetInfo();
|
NetworkClientInfo *ci = this->GetInfo();
|
||||||
if (!Company::IsValidID(ci->client_playas) || _network_company_states[ci->client_playas].password.empty()) {
|
if (!Company::IsValidID(ci->client_playas)) {
|
||||||
return this->SendWelcome();
|
return this->SendWelcome();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -847,8 +847,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
|
||||||
|
|
||||||
auto p = std::make_unique<Packet>(this, PACKET_SERVER_COMPANY_UPDATE);
|
auto p = std::make_unique<Packet>(this, PACKET_SERVER_COMPANY_UPDATE);
|
||||||
|
|
||||||
static_assert(sizeof(_network_company_passworded) <= sizeof(uint16_t));
|
p->Send_uint16(0);
|
||||||
p->Send_uint16(_network_company_passworded);
|
|
||||||
this->SendPacket(std::move(p));
|
this->SendPacket(std::move(p));
|
||||||
return NETWORK_RECV_STATUS_OKAY;
|
return NETWORK_RECV_STATUS_OKAY;
|
||||||
}
|
}
|
||||||
|
@ -939,6 +938,11 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet
|
||||||
if (!Company::IsValidHumanID(playas)) {
|
if (!Company::IsValidHumanID(playas)) {
|
||||||
return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
|
return this->SendError(NETWORK_ERROR_COMPANY_MISMATCH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!Company::Get(playas)->allow_list.Contains(this->peer_public_key)) {
|
||||||
|
/* When we're not authorized, just bump us to a spectator. */
|
||||||
|
playas = COMPANY_SPECTATOR;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1021,7 +1025,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_AUTH_RESPONSE(P
|
||||||
return NETWORK_RECV_STATUS_OKAY;
|
return NETWORK_RECV_STATUS_OKAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &p)
|
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &)
|
||||||
{
|
{
|
||||||
if (this->status != STATUS_AUTH_COMPANY) {
|
if (this->status != STATUS_AUTH_COMPANY) {
|
||||||
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
|
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
|
||||||
|
@ -1029,18 +1033,6 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWOR
|
||||||
|
|
||||||
Debug(net, 9, "client[{}] Receive_CLIENT_COMPANY_PASSWORD()", this->client_id);
|
Debug(net, 9, "client[{}] Receive_CLIENT_COMPANY_PASSWORD()", this->client_id);
|
||||||
|
|
||||||
std::string password = p.Recv_string(NETWORK_PASSWORD_LENGTH);
|
|
||||||
|
|
||||||
/* Check company password. Allow joining if we cleared the password meanwhile.
|
|
||||||
* Also, check the company is still valid - client could be moved to spectators
|
|
||||||
* in the middle of the authorization process */
|
|
||||||
CompanyID playas = this->GetInfo()->client_playas;
|
|
||||||
if (Company::IsValidID(playas) && !_network_company_states[playas].password.empty() &&
|
|
||||||
_network_company_states[playas].password.compare(password) != 0) {
|
|
||||||
/* Password is invalid */
|
|
||||||
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this->SendWelcome();
|
return this->SendWelcome();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1559,17 +1551,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet &p)
|
||||||
/* Check if the company is valid, we don't allow moving to AI companies */
|
/* Check if the company is valid, we don't allow moving to AI companies */
|
||||||
if (company_id != COMPANY_SPECTATOR && !Company::IsValidHumanID(company_id)) return NETWORK_RECV_STATUS_OKAY;
|
if (company_id != COMPANY_SPECTATOR && !Company::IsValidHumanID(company_id)) return NETWORK_RECV_STATUS_OKAY;
|
||||||
|
|
||||||
/* Check if we require a password for this company */
|
if (company_id != COMPANY_SPECTATOR && !Company::Get(company_id)->allow_list.Contains(this->peer_public_key)) {
|
||||||
if (company_id != COMPANY_SPECTATOR && !Company::Get(company_id)->allow_list.Contains(this->peer_public_key) && !_network_company_states[company_id].password.empty()) {
|
Debug(net, 2, "Wrong public key from client-id #{} for company #{}", this->client_id, company_id + 1);
|
||||||
/* we need a password from the client - should be in this packet */
|
|
||||||
std::string password = p.Recv_string(NETWORK_PASSWORD_LENGTH);
|
|
||||||
|
|
||||||
/* Incorrect password sent, return! */
|
|
||||||
if (_network_company_states[company_id].password.compare(password) != 0) {
|
|
||||||
Debug(net, 2, "Wrong password from client-id #{} for company #{}", this->client_id, company_id + 1);
|
|
||||||
return NETWORK_RECV_STATUS_OKAY;
|
return NETWORK_RECV_STATUS_OKAY;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* if we get here we can move the client */
|
/* if we get here we can move the client */
|
||||||
NetworkServerDoMove(this->client_id, company_id);
|
NetworkServerDoMove(this->client_id, company_id);
|
||||||
|
@ -1758,17 +1743,8 @@ bool NetworkServerChangeClientName(ClientID client_id, const std::string &new_na
|
||||||
* @param password The new password.
|
* @param password The new password.
|
||||||
* @param already_hashed Is the given password already hashed?
|
* @param already_hashed Is the given password already hashed?
|
||||||
*/
|
*/
|
||||||
void NetworkServerSetCompanyPassword(CompanyID company_id, const std::string &password, bool already_hashed)
|
void NetworkServerSetCompanyPassword([[maybe_unused]] CompanyID company_id, [[maybe_unused]] const std::string &password, [[maybe_unused]] bool already_hashed)
|
||||||
{
|
{
|
||||||
if (!Company::IsValidHumanID(company_id)) return;
|
|
||||||
|
|
||||||
if (already_hashed) {
|
|
||||||
_network_company_states[company_id].password = password;
|
|
||||||
} else {
|
|
||||||
_network_company_states[company_id].password = GenerateCompanyPasswordHash(password, _settings_client.network.network_id, _settings_game.game_creation.generation_seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkServerUpdateCompanyPassworded(company_id, !_network_company_states[company_id].password.empty());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2088,7 +2064,6 @@ void NetworkServerUpdateCompanyPassworded(CompanyID company_id, bool passworded)
|
||||||
{
|
{
|
||||||
if (NetworkCompanyIsPassworded(company_id) == passworded) return;
|
if (NetworkCompanyIsPassworded(company_id) == passworded) return;
|
||||||
|
|
||||||
SB(_network_company_passworded, company_id, 1, !!passworded);
|
|
||||||
SetWindowClassesDirty(WC_COMPANY);
|
SetWindowClassesDirty(WC_COMPANY);
|
||||||
|
|
||||||
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
|
for (NetworkClientSocket *cs : NetworkClientSocket::Iterate()) {
|
||||||
|
@ -2278,9 +2253,6 @@ void NetworkServerNewCompany(const Company *c, NetworkClientInfo *ci)
|
||||||
|
|
||||||
if (!_network_server) return;
|
if (!_network_server) return;
|
||||||
|
|
||||||
_network_company_states[c->index].password.clear();
|
|
||||||
NetworkServerUpdateCompanyPassworded(c->index, false);
|
|
||||||
|
|
||||||
if (ci != nullptr) {
|
if (ci != nullptr) {
|
||||||
/* ci is nullptr when replaying, or for AIs. In neither case there is a client. */
|
/* ci is nullptr when replaying, or for AIs. In neither case there is a client. */
|
||||||
ci->client_playas = c->index;
|
ci->client_playas = c->index;
|
||||||
|
|
|
@ -70,11 +70,6 @@ struct NetworkCompanyStats {
|
||||||
bool ai; ///< Is this company an AI
|
bool ai; ///< Is this company an AI
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Some state information of a company, especially for servers */
|
|
||||||
struct NetworkCompanyState {
|
|
||||||
std::string password; ///< The password for the company
|
|
||||||
};
|
|
||||||
|
|
||||||
struct NetworkClientInfo;
|
struct NetworkClientInfo;
|
||||||
|
|
||||||
/** The type of password we're asking for. */
|
/** The type of password we're asking for. */
|
||||||
|
|
Loading…
Reference in New Issue