diff --git a/src/network/network_server.cpp b/src/network/network_server.cpp index ad65e00e2c..4b93d21457 100644 --- a/src/network/network_server.cpp +++ b/src/network/network_server.cpp @@ -1039,9 +1039,12 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWO DEBUG(net, 0, "[settings-ctrl] wrong password from client-id %d", this->client_id); NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied"); this->settings_authed = false; + NetworkRecvStatus status = this->HandleAuthFailure(this->settings_auth_failures); + if (status != NETWORK_RECV_STATUS_OKAY) return status; } else { DEBUG(net, 0, "[settings-ctrl] client-id %d", this->client_id); this->settings_authed = true; + this->settings_auth_failures = 0; } return this->SendSettingsAccessUpdate(this->settings_authed); @@ -1592,7 +1595,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p) if (_settings_client.network.rcon_password.empty()) { NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied"); - return NETWORK_RECV_STATUS_OKAY; + return this->HandleAuthFailure(this->rcon_auth_failures); } std::vector password = p->Recv_buffer(); @@ -1601,7 +1604,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p) if (password != this->rcon_password_hash_cache.GetHash(_settings_client.network.rcon_password, this->rcon_hash_bits)) { DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id); NetworkServerSendRcon(this->client_id, CC_ERROR, "Access Denied"); - return NETWORK_RECV_STATUS_OKAY; + return this->HandleAuthFailure(this->rcon_auth_failures); } DEBUG(net, 3, "[rcon] Client-id %d executed: %s", this->client_id, command.c_str()); @@ -1609,6 +1612,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet *p) _redirect_console_to_client = this->client_id; IConsoleCmdExec(command.c_str()); _redirect_console_to_client = INVALID_CLIENT_ID; + this->rcon_auth_failures = 0; return NETWORK_RECV_STATUS_OKAY; } @@ -1638,6 +1642,17 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet *p) return NETWORK_RECV_STATUS_OKAY; } +NetworkRecvStatus ServerNetworkGameSocketHandler::HandleAuthFailure(uint &failure_count) +{ + failure_count++; + if (_settings_client.network.max_auth_failures != 0 && failure_count >= _settings_client.network.max_auth_failures) { + DEBUG(net, 0, "Kicked client-id #%d due to too many failed authentication attempts", this->client_id); + return this->SendError(NETWORK_ERROR_KICKED); + } else { + return NETWORK_RECV_STATUS_OKAY; + } +} + const char *ServerNetworkGameSocketHandler::GetClientStatusName(ClientStatus status) { static const char* _client_status_names[] { diff --git a/src/network/network_server.h b/src/network/network_server.h index 19e2dbe594..b0caabdb1d 100644 --- a/src/network/network_server.h +++ b/src/network/network_server.h @@ -100,6 +100,9 @@ public: uint desync_frame_seed = 0; uint desync_frame_state_checksum = 0; + uint rcon_auth_failures = 0; + uint settings_auth_failures = 0; + ServerNetworkGameSocketHandler(SOCKET s); ~ServerNetworkGameSocketHandler(); @@ -131,6 +134,8 @@ public: NetworkRecvStatus SendConfigUpdate(); NetworkRecvStatus SendSettingsAccessUpdate(bool ok); + NetworkRecvStatus HandleAuthFailure(uint &failure_count); + std::string GetDebugInfo() const override; static void Send(); diff --git a/src/settings_type.h b/src/settings_type.h index 7080412518..40bac7f2e2 100644 --- a/src/settings_type.h +++ b/src/settings_type.h @@ -383,6 +383,7 @@ struct NetworkSettings { std::string network_id; ///< network ID for servers std::string company_password_storage_token; ///< company password storage token std::string company_password_storage_secret; ///< company password storage secret + uint8 max_auth_failures; ///< maximum auth failures before client is kicked bool autoclean_companies; ///< automatically remove companies that are not in use uint8 autoclean_unprotected; ///< remove passwordless companies after this many months uint8 autoclean_protected; ///< remove the password from passworded companies after this many months diff --git a/src/table/settings/network_settings.ini b/src/table/settings/network_settings.ini index fd72fed6fa..75bb53d43f 100644 --- a/src/table/settings/network_settings.ini +++ b/src/table/settings/network_settings.ini @@ -256,3 +256,11 @@ var = network.reload_cfg flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY def = false cat = SC_EXPERT + +[SDTC_VAR] +var = network.max_auth_failures +type = SLE_UINT8 +flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC | SF_NETWORK_ONLY +def = 10 +min = 0 +max = 255