Feature: authenticate to the server without sending the password

Either using password-authentication key exchange or via authorized keys
This commit is contained in:
Rubidium 2024-03-14 21:08:21 +01:00 committed by rubidium42
parent dd532cbc77
commit 5706801ea7
11 changed files with 184 additions and 79 deletions

View File

@ -2574,6 +2574,7 @@ STR_NETWORK_ERROR_BAD_PLAYER_NAME :{WHITE}Your pla
STR_NETWORK_ERROR_BAD_SERVER_NAME :{WHITE}Your server name has not been set. The name can be set at the top of the Multiplayer window
STR_NETWORK_ERROR_WRONG_REVISION :{WHITE}The revision of this client does not match the server's revision
STR_NETWORK_ERROR_WRONG_PASSWORD :{WHITE}Wrong password
STR_NETWORK_ERROR_NOT_ON_ALLOW_LIST :{WHITE}You are not on the list of allowed clients
STR_NETWORK_ERROR_SERVER_FULL :{WHITE}The server is full
STR_NETWORK_ERROR_SERVER_BANNED :{WHITE}You are banned from this server
STR_NETWORK_ERROR_KICKED :{WHITE}You were kicked out of the game
@ -2589,7 +2590,7 @@ STR_NETWORK_ERROR_INVALID_CLIENT_NAME :{WHITE}Your pla
STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION_CAPTION :{WHITE}Possible connection loss
STR_NETWORK_ERROR_CLIENT_GUI_LOST_CONNECTION :{WHITE}The last {NUM} second{P "" s} no data has arrived from the server
###length 21
###length 22
STR_NETWORK_ERROR_CLIENT_GENERAL :general error
STR_NETWORK_ERROR_CLIENT_DESYNC :desync error
STR_NETWORK_ERROR_CLIENT_SAVEGAME :could not load map
@ -2601,6 +2602,7 @@ STR_NETWORK_ERROR_CLIENT_NOT_EXPECTED :received invali
STR_NETWORK_ERROR_CLIENT_WRONG_REVISION :wrong revision
STR_NETWORK_ERROR_CLIENT_NAME_IN_USE :name already in use
STR_NETWORK_ERROR_CLIENT_WRONG_PASSWORD :wrong password
STR_NETWORK_ERROR_CLIENT_NOT_ON_ALLOW_LIST :not on allow list
STR_NETWORK_ERROR_CLIENT_COMPANY_MISMATCH :wrong company in DoCommand
STR_NETWORK_ERROR_CLIENT_KICKED :kicked by server
STR_NETWORK_ERROR_CLIENT_CHEATER :was trying to use a cheat

View File

@ -82,9 +82,10 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet &p)
case PACKET_SERVER_GAME_INFO: return this->Receive_SERVER_GAME_INFO(p);
case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p);
case PACKET_CLIENT_IDENTIFY: return this->Receive_CLIENT_IDENTIFY(p);
case PACKET_SERVER_NEED_GAME_PASSWORD: return this->Receive_SERVER_NEED_GAME_PASSWORD(p);
case PACKET_SERVER_AUTH_REQUEST: return this->Receive_SERVER_AUTH_REQUEST(p);
case PACKET_SERVER_NEED_COMPANY_PASSWORD: return this->Receive_SERVER_NEED_COMPANY_PASSWORD(p);
case PACKET_CLIENT_GAME_PASSWORD: return this->Receive_CLIENT_GAME_PASSWORD(p);
case PACKET_CLIENT_AUTH_RESPONSE: return this->Receive_CLIENT_AUTH_RESPONSE(p);
case PACKET_SERVER_AUTH_COMPLETED: return this->Receive_SERVER_AUTH_COMPLETED(p);
case PACKET_CLIENT_COMPANY_PASSWORD: return this->Receive_CLIENT_COMPANY_PASSWORD(p);
case PACKET_SERVER_WELCOME: return this->Receive_SERVER_WELCOME(p);
case PACKET_CLIENT_GETMAP: return this->Receive_CLIENT_GETMAP(p);
@ -164,9 +165,10 @@ NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet &) {
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_IDENTIFY); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_AUTH_REQUEST(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_AUTH_REQUEST); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_AUTH_RESPONSE(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_AUTH_RESPONSE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_AUTH_COMPLETED(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_AUTH_COMPLETED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }

View File

@ -15,6 +15,7 @@
#include "os_abstraction.h"
#include "tcp.h"
#include "../network_type.h"
#include "../network_crypto.h"
#include "../../core/pool_type.hpp"
#include <chrono>
@ -56,16 +57,19 @@ enum PacketGameType : uint8_t {
* the map and other important data.
*/
/* After the initial join, the next step is identification. */
/* After the join step, the first perform game authentication and enabling encryption. */
PACKET_SERVER_AUTH_REQUEST, ///< The server requests the client to authenticate using a number of methods.
PACKET_CLIENT_AUTH_RESPONSE, ///< The client responds to the authentication request.
PACKET_SERVER_AUTH_COMPLETED, ///< The server indicates the authentication is completed.
/* After the authentication is done, the next step is identification. */
PACKET_CLIENT_IDENTIFY, ///< Client telling the server the client's name and requested company.
/* After the identify step, the next is checking NewGRFs. */
PACKET_SERVER_CHECK_NEWGRFS, ///< Server sends NewGRF IDs and MD5 checksums for the client to check.
PACKET_CLIENT_NEWGRFS_CHECKED, ///< Client acknowledges that it has all required NewGRFs.
/* Checking the game, and then company passwords. */
PACKET_SERVER_NEED_GAME_PASSWORD, ///< Server requests the (hashed) game password.
PACKET_CLIENT_GAME_PASSWORD, ///< Clients sends the (hashed) game password.
/* Checking the company passwords. */
PACKET_SERVER_NEED_COMPANY_PASSWORD, ///< Server requests the (hashed) company password.
PACKET_CLIENT_COMPANY_PASSWORD, ///< Client sends the (hashed) company password.
@ -214,10 +218,13 @@ protected:
virtual NetworkRecvStatus Receive_CLIENT_IDENTIFY(Packet &p);
/**
* Indication to the client that the server needs a game password.
* Indication to the client that it needs to authenticate:
* bool Whether to use the password in the key exchange.
* 32 * uint8_t Public key of the server.
* 24 * uint8_t Nonce for the key exchange.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet &p);
virtual NetworkRecvStatus Receive_SERVER_AUTH_REQUEST(Packet &p);
/**
* Indication to the client that the server needs a company password:
@ -228,12 +235,19 @@ protected:
virtual NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &p);
/**
* Send a password to the server to authorize:
* uint8_t Password type (see NetworkPasswordType).
* string The password.
* Send the response to the authentication request:
* 32 * uint8_t Public key of the client.
* 8 * uint8_t Random message that got encoded and signed.
* 16 * uint8_t Message authentication code.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet &p);
virtual NetworkRecvStatus Receive_CLIENT_AUTH_RESPONSE(Packet &p);
/**
* Indication to the client that authentication has completed.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_AUTH_COMPLETED(Packet &p);
/**
* Send a password to the server to authorize

View File

@ -321,6 +321,7 @@ StringID GetNetworkErrorMsg(NetworkErrorCode err)
STR_NETWORK_ERROR_CLIENT_TIMEOUT_MAP,
STR_NETWORK_ERROR_CLIENT_TIMEOUT_JOIN,
STR_NETWORK_ERROR_CLIENT_INVALID_CLIENT_NAME,
STR_NETWORK_ERROR_CLIENT_NOT_ON_ALLOW_LIST,
};
static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END);

View File

@ -349,7 +349,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin()
p->Send_uint32(_openttd_newgrf_version);
my_client->SendPacket(std::move(p));
return ClientNetworkGameSocketHandler::SendIdentify();
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::SendIdentify()
@ -377,13 +377,14 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendNewGRFsOk()
* Set the game password as requested.
* @param password The game password.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const std::string &password)
NetworkRecvStatus ClientNetworkGameSocketHandler::SendAuthResponse()
{
Debug(net, 9, "Client::SendGamePassword()");
Debug(net, 9, "Client::SendAuthResponse()");
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_GAME_PASSWORD);
p->Send_string(password);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_AUTH_RESPONSE);
my_client->authentication_handler->SendResponse(*p);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@ -680,6 +681,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet &p
STR_NETWORK_ERROR_TIMEOUT_MAP, // NETWORK_ERROR_TIMEOUT_MAP
STR_NETWORK_ERROR_TIMEOUT_JOIN, // NETWORK_ERROR_TIMEOUT_JOIN
STR_NETWORK_ERROR_INVALID_CLIENT_NAME, // NETWORK_ERROR_INVALID_CLIENT_NAME
STR_NETWORK_ERROR_NOT_ON_ALLOW_LIST, // NETWORK_ERROR_NOT_ON_ALLOW_LIST
};
static_assert(lengthof(network_error_strings) == NETWORK_ERROR_END);
@ -705,7 +707,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_ERROR(Packet &p
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(Packet &p)
{
if (this->status != STATUS_JOIN) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status != STATUS_AUTHENTICATED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
uint grf_count = p.Recv_uint8();
NetworkRecvStatus ret = NETWORK_RECV_STATUS_OKAY;
@ -736,26 +738,67 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P
return ret;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet &)
class ClientGamePasswordRequestHandler : public NetworkAuthenticationPasswordRequestHandler {
virtual void SendResponse() override { MyClient::SendAuthResponse(); }
virtual void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) override
{
if (!_network_join.server_password.empty()) {
request->Reply(_network_join.server_password);
} else {
ShowNetworkNeedPassword(NETWORK_GAME_PASSWORD, request);
}
}
};
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_AUTH_REQUEST(Packet &p)
{
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status != STATUS_JOIN && this->status != STATUS_AUTH_GAME) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
Debug(net, 9, "Client::status = AUTH_GAME");
this->status = STATUS_AUTH_GAME;
Debug(net, 9, "Client::Receive_SERVER_NEED_GAME_PASSWORD()");
Debug(net, 9, "Client::Receive_SERVER_AUTH_REQUEST()");
if (!_network_join.server_password.empty()) {
return SendGamePassword(_network_join.server_password);
if (this->authentication_handler == nullptr) {
this->authentication_handler = NetworkAuthenticationClientHandler::Create(std::make_shared<ClientGamePasswordRequestHandler>(),
_settings_client.network.client_secret_key, _settings_client.network.client_public_key);
}
switch (this->authentication_handler->ReceiveRequest(p)) {
case NetworkAuthenticationClientHandler::READY_FOR_RESPONSE:
return SendAuthResponse();
ShowNetworkNeedPassword(NETWORK_GAME_PASSWORD);
case NetworkAuthenticationClientHandler::AWAIT_USER_INPUT:
return NETWORK_RECV_STATUS_OKAY;
return NETWORK_RECV_STATUS_OKAY;
case NetworkAuthenticationClientHandler::INVALID:
default:
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
}
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_AUTH_COMPLETED(Packet &)
{
if (this->status != STATUS_AUTH_GAME || this->authentication_handler == nullptr) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
Debug(net, 9, "Client::Receive_SERVER_AUTH_COMPLETED()");
this->authentication_handler = nullptr;
Debug(net, 9, "Client::status = AUTHENTICATED");
this->status = STATUS_AUTHENTICATED;
return this->SendIdentify();
}
class CompanyPasswordRequest : public NetworkAuthenticationPasswordRequest {
virtual void Reply(const std::string &password) override
{
MyClient::SendCompanyPassword(password);
}
};
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &p)
{
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTH_COMPANY) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status < STATUS_AUTHENTICATED || this->status >= STATUS_AUTH_COMPANY) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
Debug(net, 9, "Client::status = AUTH_COMPANY");
this->status = STATUS_AUTH_COMPANY;
@ -769,14 +812,14 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PA
return SendCompanyPassword(_network_join.company_password);
}
ShowNetworkNeedPassword(NETWORK_COMPANY_PASSWORD);
ShowNetworkNeedPassword(NETWORK_COMPANY_PASSWORD, std::make_shared<CompanyPasswordRequest>());
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet &p)
{
if (this->status < STATUS_JOIN || this->status >= STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
if (this->status < STATUS_AUTHENTICATED || this->status >= STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
Debug(net, 9, "Client::status = AUTHORIZED");
this->status = STATUS_AUTHORIZED;

View File

@ -15,6 +15,7 @@
/** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : public ZeroedMemoryAllocator, public NetworkGameSocketHandler {
private:
std::unique_ptr<class NetworkAuthenticationClientHandler> authentication_handler; ///< The handler for the authentication.
std::string connection_string; ///< Address we are connected to.
std::shared_ptr<struct PacketReader> savegame; ///< Packet reader for reading the savegame.
uint8_t token; ///< The token we need to send back to the server to prove we're the right client.
@ -23,8 +24,9 @@ private:
enum ServerStatus {
STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_JOIN, ///< We are trying to join a server.
STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs.
STATUS_AUTH_GAME, ///< Last action was requesting game (server) password.
STATUS_AUTHENTICATED, ///< The game authentication has completed.
STATUS_NEWGRFS_CHECK, ///< Last action was checking NewGRFs.
STATUS_AUTH_COMPANY, ///< Last action was requesting company password.
STATUS_AUTHORIZED, ///< The client is authorized at the server.
STATUS_MAP_WAIT, ///< The client is waiting as someone else is downloading the map.
@ -44,7 +46,8 @@ protected:
NetworkRecvStatus Receive_SERVER_BANNED(Packet &p) override;
NetworkRecvStatus Receive_SERVER_ERROR(Packet &p) override;
NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet &p) override;
NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_SERVER_AUTH_REQUEST(Packet &p) override;
NetworkRecvStatus Receive_SERVER_AUTH_COMPLETED(Packet &p) override;
NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_SERVER_WELCOME(Packet &p) override;
NetworkRecvStatus Receive_SERVER_WAIT(Packet &p) override;
@ -86,7 +89,7 @@ public:
static NetworkRecvStatus SendQuit();
static NetworkRecvStatus SendAck();
static NetworkRecvStatus SendGamePassword(const std::string &password);
static NetworkRecvStatus SendAuthResponse();
static NetworkRecvStatus SendCompanyPassword(const std::string &password);
static NetworkRecvStatus SendChat(NetworkAction action, DestType type, int dest, const std::string &msg, int64_t data);

View File

@ -2103,7 +2103,7 @@ uint32_t _network_join_bytes; ///< The number of bytes we already do
uint32_t _network_join_bytes_total; ///< The total number of bytes to download.
struct NetworkJoinStatusWindow : Window {
NetworkPasswordType password_type;
std::shared_ptr<NetworkAuthenticationPasswordRequest> request;
NetworkJoinStatusWindow(WindowDesc *desc) : Window(desc)
{
@ -2199,16 +2199,12 @@ struct NetworkJoinStatusWindow : Window {
void OnQueryTextFinished(char *str) override
{
if (StrEmpty(str)) {
if (StrEmpty(str) || this->request == nullptr) {
NetworkDisconnect();
return;
}
switch (this->password_type) {
case NETWORK_GAME_PASSWORD: MyClient::SendGamePassword (str); break;
case NETWORK_COMPANY_PASSWORD: MyClient::SendCompanyPassword(str); break;
default: NOT_REACHED();
}
this->request->Reply(str);
}
};
@ -2236,11 +2232,11 @@ void ShowJoinStatusWindow()
new NetworkJoinStatusWindow(&_network_join_status_window_desc);
}
void ShowNetworkNeedPassword(NetworkPasswordType npt)
void ShowNetworkNeedPassword(NetworkPasswordType npt, std::shared_ptr<NetworkAuthenticationPasswordRequest> request)
{
NetworkJoinStatusWindow *w = (NetworkJoinStatusWindow *)FindWindowById(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
if (w == nullptr) return;
w->password_type = npt;
w->request = request;
StringID caption;
switch (npt) {

View File

@ -17,7 +17,7 @@
#include "network_type.h"
#include "network_gamelist.h"
void ShowNetworkNeedPassword(NetworkPasswordType npt);
void ShowNetworkNeedPassword(NetworkPasswordType npt, std::shared_ptr<class NetworkAuthenticationPasswordRequest> request);
void ShowNetworkChatQueryWindow(DestType type, int dest);
void ShowJoinStatusWindow();
void ShowNetworkGameWindow();

View File

@ -58,6 +58,10 @@ INSTANTIATE_POOL_METHODS(NetworkClientSocket)
/** Instantiate the listen sockets. */
template SocketList TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED>::sockets;
static NetworkAuthenticationDefaultPasswordProvider _password_provider(_settings_client.network.server_password); ///< Provides the password validation for the game's password.
static NetworkAuthenticationDefaultAuthorizedKeyHandler _authorized_key_handler(_settings_client.network.server_authorized_keys); ///< Provides the authorized key handling for the game authentication.
/** Writing a savegame directly to a number of packets. */
struct PacketWriter : SaveFilter {
ServerNetworkGameSocketHandler *cs; ///< Socket we are associated with.
@ -407,8 +411,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
this->status = STATUS_NEWGRFS_CHECK;
if (_grfconfig == nullptr) {
/* There are no NewGRFs, continue with the game password. */
return this->SendNeedGamePassword();
/* There are no NewGRFs, continue with the company password. */
return this->SendNeedCompanyPassword();
}
auto p = std::make_unique<Packet>(this, PACKET_SERVER_CHECK_NEWGRFS, TCP_MTU);
@ -429,25 +433,39 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
}
/** Request the game password. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
NetworkRecvStatus ServerNetworkGameSocketHandler::SendAuthRequest()
{
Debug(net, 9, "client[{}] SendNeedGamePassword()", this->client_id);
Debug(net, 9, "client[{}] SendAuthRequest()", this->client_id);
/* Invalid packet when status is anything but STATUS_NEWGRFS_CHECK. */
if (this->status != STATUS_NEWGRFS_CHECK) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
/* Invalid packet when status is anything but STATUS_INACTIVE or STATUS_AUTH_GAME. */
if (this->status != STATUS_INACTIVE && status != STATUS_AUTH_GAME) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
Debug(net, 9, "client[{}] status = AUTH_GAME", this->client_id);
this->status = STATUS_AUTH_GAME;
if (_settings_client.network.server_password.empty()) {
/* Do not actually need a game password, continue with the company password. */
return this->SendNeedCompanyPassword();
}
/* Reset 'lag' counters */
this->last_frame = this->last_frame_server = _frame_counter;
auto p = std::make_unique<Packet>(this, PACKET_SERVER_NEED_GAME_PASSWORD);
if (this->authentication_handler == nullptr) {
this->authentication_handler = NetworkAuthenticationServerHandler::Create(&_password_provider, &_authorized_key_handler);
}
auto p = std::make_unique<Packet>(this, PACKET_SERVER_AUTH_REQUEST);
this->authentication_handler->SendRequest(*p);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
/** Notify the client that the authentication has completed. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendAuthCompleted()
{
Debug(net, 9, "client[{}] SendAuthCompleted()", this->client_id);
/* Invalid packet when status is anything but STATUS_AUTH_GAME. */
if (this->status != STATUS_AUTH_GAME) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_AUTH_COMPLETED);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@ -457,8 +475,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
{
Debug(net, 9, "client[{}] SendNeedCompanyPassword()", this->client_id);
/* Invalid packet when status is anything but STATUS_AUTH_GAME. */
if (this->status != STATUS_AUTH_GAME) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
/* Invalid packet when status is anything but STATUS_NEWGRFS_CHECK. */
if (this->status != STATUS_NEWGRFS_CHECK) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
Debug(net, 9, "client[{}] status = AUTH_COMPANY", this->client_id);
this->status = STATUS_AUTH_COMPANY;
@ -865,7 +883,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED
Debug(net, 9, "client[{}] Receive_CLIENT_NEWGRFS_CHECKED()", this->client_id);
return this->SendNeedGamePassword();
return this->SendNeedCompanyPassword();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p)
@ -891,13 +909,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p)
return this->SendError(NETWORK_ERROR_WRONG_REVISION);
}
Debug(net, 9, "client[{}] status = IDENTIFY", this->client_id);
this->status = STATUS_IDENTIFY;
/* Reset 'lag' counters */
this->last_frame = this->last_frame_server = _frame_counter;
return NETWORK_RECV_STATUS_OKAY;
return this->SendAuthRequest();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet &p)
@ -953,24 +965,52 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet
return this->SendNewGRFCheck();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet &p)
static NetworkErrorCode GetErrorForAuthenticationMethod(NetworkAuthenticationMethod method)
{
switch (method) {
case NETWORK_AUTH_METHOD_X25519_PAKE:
return NETWORK_ERROR_WRONG_PASSWORD;
case NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY:
return NETWORK_ERROR_NOT_ON_ALLOW_LIST;
default:
NOT_REACHED();
}
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_AUTH_RESPONSE(Packet &p)
{
if (this->status != STATUS_AUTH_GAME) {
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
Debug(net, 9, "client[{}] Receive_CLIENT_GAME_PASSWORD()", this->client_id);
Debug(net, 9, "client[{}] Receive_CLIENT_AUTH_RESPONSE()", this->client_id);
std::string password = p.Recv_string(NETWORK_PASSWORD_LENGTH);
auto authentication_method = this->authentication_handler->GetAuthenticationMethod();
switch (this->authentication_handler->ReceiveResponse(p)) {
case NetworkAuthenticationServerHandler::AUTHENTICATED:
break;
/* Check game password. Allow joining if we cleared the password meanwhile */
if (!_settings_client.network.server_password.empty() &&
_settings_client.network.server_password.compare(password) != 0) {
/* Password is invalid */
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
case NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD:
return this->SendAuthRequest();
case NetworkAuthenticationServerHandler::NOT_AUTHENTICATED:
default:
return this->SendError(GetErrorForAuthenticationMethod(authentication_method));
}
return this->SendNeedCompanyPassword();
NetworkRecvStatus status = this->SendAuthCompleted();
if (status != NETWORK_RECV_STATUS_OKAY) return status;
this->authentication_handler = nullptr;
Debug(net, 9, "client[{}] status = IDENTIFY", this->client_id);
this->status = STATUS_IDENTIFY;
/* Reset 'lag' counters */
this->last_frame = this->last_frame_server = _frame_counter;
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &p)
@ -1978,9 +2018,9 @@ void NetworkServerShowStatusToConsole()
{
static const char * const stat_str[] = {
"inactive",
"authorizing (server password)",
"identifing client",
"checking NewGRFs",
"authorizing (server password)",
"authorizing (company password)",
"authorized",
"waiting",

View File

@ -23,10 +23,12 @@ extern NetworkClientSocketPool _networkclientsocket_pool;
/** Class for handling the server side of the game connection. */
class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<&_networkclientsocket_pool>, public NetworkGameSocketHandler, public TCPListenHandler<ServerNetworkGameSocketHandler, PACKET_SERVER_FULL, PACKET_SERVER_BANNED> {
protected:
std::unique_ptr<class NetworkAuthenticationServerHandler> authentication_handler; ///< The handler for the authentication.
NetworkRecvStatus Receive_CLIENT_JOIN(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_IDENTIFY(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_GAME_INFO(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_GAME_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_AUTH_RESPONSE(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_COMPANY_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_GETMAP(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_MAP_OK(Packet &p) override;
@ -44,16 +46,17 @@ protected:
NetworkRecvStatus SendGameInfo();
NetworkRecvStatus SendNewGRFCheck();
NetworkRecvStatus SendWelcome();
NetworkRecvStatus SendNeedGamePassword();
NetworkRecvStatus SendAuthRequest();
NetworkRecvStatus SendAuthCompleted();
NetworkRecvStatus SendNeedCompanyPassword();
public:
/** Status of a client */
enum ClientStatus {
STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_AUTH_GAME, ///< The client is authorizing with game (server) password.
STATUS_IDENTIFY, ///< The client is identifying itself.
STATUS_NEWGRFS_CHECK, ///< The client is checking NewGRFs.
STATUS_AUTH_GAME, ///< The client is authorizing with game (server) password.
STATUS_AUTH_COMPANY, ///< The client is authorizing with company password.
STATUS_AUTHORIZED, ///< The client is authorized.
STATUS_MAP_WAIT, ///< The client is waiting as someone else is downloading the map.

View File

@ -145,6 +145,7 @@ enum NetworkErrorCode {
NETWORK_ERROR_TIMEOUT_MAP,
NETWORK_ERROR_TIMEOUT_JOIN,
NETWORK_ERROR_INVALID_CLIENT_NAME,
NETWORK_ERROR_NOT_ON_ALLOW_LIST,
NETWORK_ERROR_END,
};