mirror of
https://github.com/OpenTTD/OpenTTD
synced 2025-12-18 19:02:20 +01:00
Feature: admin support for password authentication without sending password
Using either password authenticated key exchange (PAKE) or authorized keys
This commit is contained in:
@@ -38,6 +38,9 @@ uint8_t _network_admins_connected = 0;
|
||||
NetworkAdminSocketPool _networkadminsocket_pool("NetworkAdminSocket");
|
||||
INSTANTIATE_POOL_METHODS(NetworkAdminSocket)
|
||||
|
||||
static NetworkAuthenticationDefaultPasswordProvider _admin_password_provider(_settings_client.network.admin_password); ///< Provides the password validation for the game's password.
|
||||
static NetworkAuthenticationDefaultAuthorizedKeyHandler _admin_authorized_key_handler(_settings_client.network.admin_authorized_keys); ///< Provides the authorized key handling for the game authentication.
|
||||
|
||||
/** The timeout for authorisation of the client. */
|
||||
static const std::chrono::seconds ADMIN_AUTHORISATION_TIMEOUT(10);
|
||||
|
||||
@@ -90,7 +93,7 @@ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
|
||||
*/
|
||||
/* static */ bool ServerNetworkAdminSocketHandler::AllowConnection()
|
||||
{
|
||||
bool accept = !_settings_client.network.admin_password.empty() && _network_admins_connected < MAX_ADMINS;
|
||||
bool accept = _settings_client.network.AdminAuthenticationConfigured() && _network_admins_connected < MAX_ADMINS;
|
||||
/* We can't go over the MAX_ADMINS limit here. However, if we accept
|
||||
* the connection, there has to be space in the pool. */
|
||||
static_assert(NetworkAdminSocketPool::MAX_SIZE == MAX_ADMINS);
|
||||
@@ -134,6 +137,9 @@ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
|
||||
*/
|
||||
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode error)
|
||||
{
|
||||
/* Whatever the error might be, authentication (keys) must be released as soon as possible. */
|
||||
this->authentication_handler = nullptr;
|
||||
|
||||
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_ERROR);
|
||||
|
||||
p->Send_uint8(error);
|
||||
@@ -791,6 +797,71 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_EXTERNAL_CHAT(P
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Secure authentication send and receive methods.
|
||||
*/
|
||||
|
||||
NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_JOIN_SECURE(Packet &p)
|
||||
{
|
||||
if (this->status != ADMIN_STATUS_INACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
|
||||
|
||||
this->admin_name = p.Recv_string(NETWORK_CLIENT_NAME_LENGTH);
|
||||
this->admin_version = p.Recv_string(NETWORK_REVISION_LENGTH);
|
||||
NetworkAuthenticationMethodMask method_mask = p.Recv_uint16();
|
||||
|
||||
/* Always exclude key exchange only, as that provides no credential checking. */
|
||||
ClrBit(method_mask, NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY);
|
||||
|
||||
if (this->admin_name.empty() || this->admin_version.empty()) {
|
||||
/* No name or version supplied. */
|
||||
return this->SendError(NETWORK_ERROR_ILLEGAL_PACKET);
|
||||
}
|
||||
|
||||
auto handler = NetworkAuthenticationServerHandler::Create(&_admin_password_provider, &_admin_authorized_key_handler, method_mask);
|
||||
if (!handler->CanBeUsed()) return this->SendError(NETWORK_ERROR_NO_AUTHENTICATION_METHOD_AVAILABLE);
|
||||
|
||||
this->authentication_handler = std::move(handler);
|
||||
Debug(net, 3, "[admin] '{}' ({}) has connected", this->admin_name, this->admin_version);
|
||||
|
||||
return this->SendAuthRequest();
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendAuthRequest()
|
||||
{
|
||||
this->status = ADMIN_STATUS_AUTHENTICATE;
|
||||
|
||||
Debug(net, 6, "[admin] '{}' ({}) authenticating using {}", this->admin_name, this->admin_version, this->authentication_handler->GetName());
|
||||
|
||||
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_AUTH_REQUEST);
|
||||
this->authentication_handler->SendRequest(*p);
|
||||
|
||||
this->SendPacket(std::move(p));
|
||||
|
||||
return NETWORK_RECV_STATUS_OKAY;
|
||||
}
|
||||
|
||||
NetworkRecvStatus ServerNetworkAdminSocketHandler::Receive_ADMIN_AUTH_RESPONSE(Packet &p)
|
||||
{
|
||||
if (this->status != ADMIN_STATUS_AUTHENTICATE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
|
||||
|
||||
switch (this->authentication_handler->ReceiveResponse(p)) {
|
||||
case NetworkAuthenticationServerHandler::AUTHENTICATED:
|
||||
Debug(net, 3, "[admin] '{}' ({}) authenticated", this->admin_name, this->admin_version);
|
||||
|
||||
this->authentication_handler = nullptr;
|
||||
return this->SendProtocol();
|
||||
|
||||
case NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD:
|
||||
Debug(net, 6, "[admin] '{}' ({}) authentication failed, trying next method", this->admin_name, this->admin_version);
|
||||
return this->SendAuthRequest();
|
||||
|
||||
case NetworkAuthenticationServerHandler::NOT_AUTHENTICATED:
|
||||
default:
|
||||
Debug(net, 3, "[admin] '{}' ({}) authentication failed", this->admin_name, this->admin_version);
|
||||
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Useful wrapper functions
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user