Merge branch 'master' into jgrpp

# Conflicts:
#	src/console_cmds.cpp
#	src/network/core/config.h
#	src/network/core/packet.h
#	src/network/core/tcp.cpp
#	src/network/core/tcp_game.cpp
#	src/network/core/tcp_game.h
#	src/network/network_client.cpp
#	src/network/network_client.h
#	src/network/network_gui.h
#	src/network/network_server.cpp
#	src/network/network_server.h
#	src/table/settings/network_secrets_settings.ini
This commit is contained in:
Jonathan G Rennison
2024-05-27 15:42:21 +01:00
36 changed files with 2005 additions and 262 deletions

View File

@@ -935,6 +935,7 @@ DEF_CONSOLE_CMD(ConRcon)
if (argc == 0) {
IConsolePrint(CC_HELP, "Remote control the server from another client. Usage: 'rcon <password> <command>'.");
IConsolePrint(CC_HELP, "Remember to enclose the command in quotes, otherwise only the first parameter is sent.");
IConsolePrint(CC_HELP, "When your client's public key is in the 'authorized keys' for 'rcon', '*' may be used instead of the password.");
return true;
}
@@ -2139,6 +2140,100 @@ DEF_CONSOLE_CMD(ConCompanyPassword)
return true;
}
/** All the known authorized keys with their name. */
static std::vector<std::pair<std::string_view, std::vector<std::string> *>> _console_cmd_authorized_keys{
{ "rcon", &_settings_client.network.rcon_authorized_keys },
{ "server", &_settings_client.network.server_authorized_keys },
};
/**
* Simple helper to find the location of the given authorized key in the authorized keys.
* @param authorized_keys The keys to look through.
* @param authorized_key The key to look for.
* @return The iterator to the location of the authorized key, or \c authorized_keys.end().
*/
static auto FindKey(std::vector<std::string> *authorized_keys, std::string_view authorized_key)
{
return std::find_if(authorized_keys->begin(), authorized_keys->end(), [authorized_key](auto &value) { return StrEqualsIgnoreCase(value, authorized_key); });
}
DEF_CONSOLE_CMD(ConNetworkAuthorizedKey)
{
if (argc <= 2) {
IConsolePrint(CC_HELP, "List and update authorized keys. Usage: 'authorized_key list [type]|add [type] [key]|remove [type] [key]'.");
IConsolePrint(CC_HELP, " list: list all the authorized keys of the given type.");
IConsolePrint(CC_HELP, " add: add the given key to the authorized keys of the given type.");
IConsolePrint(CC_HELP, " remove: remove the given key from the authorized keys of the given type; use 'all' to remove all authorized keys.");
IConsolePrint(CC_HELP, "Instead of a key, use 'client:<id>' to add/remove the key of that given client.");
std::string buffer;
for (auto [name, _] : _console_cmd_authorized_keys) fmt::format_to(std::back_inserter(buffer), ", {}", name);
IConsolePrint(CC_HELP, "The supported types are: all{}.", buffer);
return true;
}
bool valid_type = false; ///< Whether a valid type was given.
for (auto [name, authorized_keys] : _console_cmd_authorized_keys) {
if (!StrEqualsIgnoreCase(argv[2], name) && !StrEqualsIgnoreCase(argv[2], "all")) continue;
valid_type = true;
if (StrEqualsIgnoreCase(argv[1], "list")) {
IConsolePrint(CC_WHITE, "The authorized keys for {} are:", name);
for (auto &authorized_key : *authorized_keys) IConsolePrint(CC_INFO, " {}", authorized_key);
continue;
}
if (argc <= 3) {
IConsolePrint(CC_ERROR, "You must enter the key.");
return false;
}
std::string authorized_key = argv[3];
if (StrStartsWithIgnoreCase(authorized_key, "client:")) {
std::string id_string(authorized_key.substr(7));
authorized_key = NetworkGetPublicKeyOfClient(static_cast<ClientID>(std::stoi(id_string)));
if (authorized_key.empty()) {
IConsolePrint(CC_ERROR, "You must enter a valid client id; see 'clients'.");
return false;
}
}
auto iter = FindKey(authorized_keys, authorized_key);
if (StrEqualsIgnoreCase(argv[1], "add")) {
if (iter == authorized_keys->end()) {
authorized_keys->push_back(authorized_key);
IConsolePrint(CC_INFO, "Added {} to {}.", authorized_key, name);
} else {
IConsolePrint(CC_WARNING, "Not added {} to {} as it already exists.", authorized_key, name);
}
continue;
}
if (StrEqualsIgnoreCase(argv[1], "remove")) {
if (iter != authorized_keys->end()) {
authorized_keys->erase(iter);
IConsolePrint(CC_INFO, "Removed {} from {}.", authorized_key, name);
} else {
IConsolePrint(CC_WARNING, "Not removed {} from {} as it does not exist.", authorized_key, name);
}
continue;
}
IConsolePrint(CC_WARNING, "No valid action was given.");
return false;
}
if (!valid_type) {
IConsolePrint(CC_WARNING, "No valid type was given.");
return false;
}
return true;
}
DEF_CONSOLE_CMD(ConCompanyPasswordHash)
{
if (argc == 0) {
@@ -4052,6 +4147,9 @@ void IConsoleStdLibRegister()
IConsole::CmdRegister("unpause", ConUnpauseGame, ConHookServerOrNoNetwork);
IConsole::CmdRegister("step", ConStepGame, ConHookNoNetwork);
IConsole::CmdRegister("authorized_key", ConNetworkAuthorizedKey, ConHookServerOnly);
IConsole::AliasRegister("ak", "authorized_key %+");
IConsole::CmdRegister("company_pw", ConCompanyPassword, ConHookNeedNetwork);
IConsole::AliasRegister("company_password", "company_pw %+");
IConsole::CmdRegister("company_pw_hash", ConCompanyPasswordHash, ConHookServerOnly);

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

@@ -16,6 +16,9 @@ add_files(
network_content_gui.h
network_coordinator.cpp
network_coordinator.h
network_crypto.cpp
network_crypto.h
network_crypto_internal.h
network_func.h
network_gamelist.cpp
network_gamelist.h

View File

@@ -93,6 +93,12 @@ static const uint NETWORK_GRF_NAME_LENGTH = 80; ///< Maxim
*/
static const uint NETWORK_MAX_GRF_COUNT = 255;
/**
* The maximum length of the hexadecimal encoded secret keys, in bytes including '\0'.
* This is related to \c X25519_KEY_SIZE in the network crypto internals.
*/
static const uint NETWORK_SECRET_KEY_LENGTH = 32 * 2 + 1;
/**
* Maximum version supported in PACKET_SERVER_GAME_INFO_EXTENDED
*/

View File

@@ -13,6 +13,7 @@
#define NETWORK_CORE_CORE_H
#include "../../newgrf_config.h"
#include "../network_crypto.h"
#include "config.h"
bool NetworkCoreInitialize();
@@ -43,6 +44,11 @@ class NetworkSocketHandler {
private:
bool has_quit; ///< Whether the current client has quit/send a bad packet
protected:
friend struct Packet;
std::unique_ptr<class NetworkEncryptionHandler> receive_encryption_handler; ///< The handler for decrypting received packets.
std::unique_ptr<class NetworkEncryptionHandler> send_encryption_handler; ///< The handler for encrypting sent packets.
public:
/** Create a new unbound socket */
NetworkSocketHandler() { this->has_quit = false; }

View File

@@ -30,7 +30,7 @@
* loose some the data of the packet, so there you pass the maximum
* size for the packet you expect from the network.
*/
Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) : pos(0), limit(limit)
Packet::Packet(Packet::ReadTag tag, NetworkSocketHandler *cs, size_t limit, size_t initial_read_size) : pos(0), limit(limit)
{
assert(cs != nullptr);
@@ -46,18 +46,24 @@ Packet::Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size)
* the limit as it might break things if the other side is not expecting
* much larger packets than what they support.
*/
Packet::Packet(PacketType type, size_t limit) : pos(0), limit(limit), cs(nullptr)
Packet::Packet(NetworkSocketHandler *cs, PacketType type, size_t limit) : pos(0), limit(limit), cs(cs)
{
this->ResetState(type);
}
void Packet::ResetState(PacketType type)
{
this->cs = nullptr;
this->buffer.clear();
/* Allocate space for the the size so we can write that in just before sending the packet. */
this->Send_uint16(0);
size_t size = EncodedLengthOfPacketSize();
if (cs != nullptr && cs->send_encryption_handler != nullptr) {
/* Allocate some space for the message authentication code of the encryption. */
size += cs->send_encryption_handler->MACSize();
}
assert(this->CanWriteToPacket(size));
this->buffer.resize(size, 0);
this->Send_uint8(type);
}
@@ -66,11 +72,19 @@ void Packet::ResetState(PacketType type)
*/
void Packet::PrepareToSend()
{
assert(this->cs == nullptr);
/* Prevent this to be called twice and for packets that have been received. */
assert(this->buffer[0] == 0 && this->buffer[1] == 0);
this->buffer[0] = GB(this->Size(), 0, 8);
this->buffer[1] = GB(this->Size(), 8, 8);
if (cs != nullptr && cs->send_encryption_handler != nullptr) {
size_t offset = EncodedLengthOfPacketSize();
size_t mac_size = cs->send_encryption_handler->MACSize();
size_t message_offset = offset + mac_size;
cs->send_encryption_handler->Encrypt(std::span(&this->buffer[offset], mac_size), std::span(&this->buffer[message_offset], this->buffer.size() - message_offset));
}
this->pos = 0; // We start reading from here
this->buffer.shrink_to_fit();
}
@@ -130,7 +144,7 @@ bool Packet::CanReadFromPacket(size_t bytes_to_read, bool close_connection)
*/
bool Packet::HasPacketSizeData() const
{
return this->pos >= sizeof(PacketSize);
return this->pos >= EncodedLengthOfPacketSize();
}
/**
@@ -163,20 +177,30 @@ bool Packet::ParsePacketSize()
/* If the size of the packet is less than the bytes required for the size and type of
* the packet, or more than the allowed limit, then something is wrong with the packet.
* In those cases the packet can generally be regarded as containing garbage data. */
if (size < sizeof(PacketSize) + sizeof(PacketType) || size > this->limit) return false;
if (size < EncodedLengthOfPacketSize() + EncodedLengthOfPacketType() || size > this->limit) return false;
this->buffer.resize(size);
this->pos = sizeof(PacketSize);
this->pos = static_cast<PacketSize>(EncodedLengthOfPacketSize());
return true;
}
/**
* Prepares the packet so it can be read
* @return True when the packet was valid, otherwise false.
*/
void Packet::PrepareToRead()
bool Packet::PrepareToRead()
{
/* Put the position on the right place */
this->pos = sizeof(PacketSize);
this->pos = static_cast<PacketSize>(EncodedLengthOfPacketSize());
if (cs == nullptr || cs->receive_encryption_handler == nullptr) return true;
size_t mac_size = cs->receive_encryption_handler->MACSize();
if (this->buffer.size() <= pos + mac_size) return false;
bool valid = cs->receive_encryption_handler->Decrypt(std::span(&this->buffer[pos], mac_size), std::span(&this->buffer[pos + mac_size], this->buffer.size() - pos - mac_size));
this->pos += static_cast<PacketSize>(mac_size);
return valid;
}
/**
@@ -185,8 +209,10 @@ void Packet::PrepareToRead()
*/
PacketType Packet::GetPacketType() const
{
assert(this->Size() >= sizeof(PacketSize) + sizeof(PacketType));
return static_cast<PacketType>(buffer[sizeof(PacketSize)]);
assert(this->Size() >= EncodedLengthOfPacketSize() + EncodedLengthOfPacketType());
size_t offset = EncodedLengthOfPacketSize();
if (cs != nullptr && cs->send_encryption_handler != nullptr) offset += cs->send_encryption_handler->MACSize();
return static_cast<PacketType>(buffer[offset]);
}
/**
@@ -198,6 +224,22 @@ size_t Packet::RemainingBytesToTransfer() const
return this->Size() - this->pos;
}
/**
* Extract at most the length of the span bytes from the packet into the span.
* @param span The span to write the bytes to.
* @return The number of bytes that were actually read.
*/
size_t Packet::Recv_bytes(std::span<uint8_t> span)
{
auto tranfer_to_span = [](std::span<uint8_t> destination, const char *source, size_t amount) {
size_t to_copy = std::min(amount, destination.size());
std::copy(source, source + to_copy, destination.data());
return to_copy;
};
return this->TransferOut(tranfer_to_span, span);
}
bool SubPacketDeserialiser::CanDeserialiseBytes(size_t bytes_to_read, bool raise_error)
{
if (this->pos + bytes_to_read > this->size) {

View File

@@ -45,6 +45,9 @@ typedef uint8_t PacketType; ///< Identifier for the packet
* (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))
*/
struct Packet : public BufferSerialisationHelper<Packet>, public BufferDeserialisationHelper<Packet> {
static constexpr size_t EncodedLengthOfPacketSize() { return sizeof(PacketSize); }
static constexpr size_t EncodedLengthOfPacketType() { return sizeof(PacketType); }
private:
/** The current read/write position in the packet */
PacketSize pos;
@@ -57,8 +60,9 @@ private:
NetworkSocketHandler *cs;
public:
Packet(NetworkSocketHandler *cs, size_t limit, size_t initial_read_size = sizeof(PacketSize));
Packet(PacketType type, size_t limit = COMPAT_MTU);
struct ReadTag{};
Packet(ReadTag tag, NetworkSocketHandler *cs, size_t limit, size_t initial_read_size = EncodedLengthOfPacketSize());
Packet(NetworkSocketHandler *cs, PacketType type, size_t limit = COMPAT_MTU);
void ResetState(PacketType type);
@@ -82,7 +86,7 @@ public:
bool HasPacketSizeData() const;
bool ParsePacketSize();
size_t Size() const;
void PrepareToRead();
[[nodiscard]] bool PrepareToRead();
PacketType GetPacketType() const;
bool CanReadFromPacket(size_t bytes_to_read, bool close_connection = false);
@@ -189,6 +193,21 @@ public:
return bytes;
}
/**
* Send as many of the bytes as possible in the packet. This can mean
* that it is possible that not all bytes are sent. To cope with this
* the function returns the span of bytes that were not sent.
* @param span The span describing the range of bytes to send.
* @return The span of bytes that were not written.
*/
std::span<const uint8_t> Send_bytes(const std::span<const uint8_t> span)
{
size_t amount = this->Send_binary_until_full(span.data(), span.data() + span.size());
return span.subspan(amount);
}
size_t Recv_bytes(std::span<uint8_t> span);
NetworkSocketHandler *GetParentSocket() { return this->cs; }
};

View File

@@ -174,7 +174,7 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
if (!this->IsConnected()) return nullptr;
if (this->packet_recv == nullptr) {
this->packet_recv = std::make_unique<Packet>(this, TCP_MTU);
this->packet_recv = std::make_unique<Packet>(Packet::ReadTag{}, this, TCP_MTU);
}
Packet &p = *this->packet_recv.get();
@@ -230,10 +230,11 @@ std::unique_ptr<Packet> NetworkTCPSocketHandler::ReceivePacket()
}
}
p.PrepareToRead();
/* Prepare for receiving a new packet */
if (!p.PrepareToRead()) {
DEBUG(net, 0, "Invalid packet received (too small / decryption error)");
this->CloseConnection();
return nullptr;
}
return std::move(this->packet_recv);
}

View File

@@ -33,10 +33,12 @@ static const char* _packet_game_type_names[] {
"SERVER_GAME_INFO",
"CLIENT_GAME_INFO",
"SERVER_GAME_INFO_EXTENDED",
"SERVER_AUTH_REQUEST",
"CLIENT_AUTH_RESPONSE",
"SERVER_AUTH_COMPLETED",
"CLIENT_IDENTIFY",
"SERVER_CHECK_NEWGRFS",
"CLIENT_NEWGRFS_CHECKED",
"SERVER_NEED_GAME_PASSWORD",
"CLIENT_GAME_PASSWORD",
"SERVER_NEED_COMPANY_PASSWORD",
"CLIENT_COMPANY_PASSWORD",
"CLIENT_SETTINGS_PASSWORD",
@@ -153,9 +155,11 @@ NetworkRecvStatus NetworkGameSocketHandler::HandlePacket(Packet &p)
case PACKET_SERVER_GAME_INFO: return this->Receive_SERVER_GAME_INFO(p);
case PACKET_SERVER_GAME_INFO_EXTENDED: return this->Receive_SERVER_GAME_INFO_EXTENDED(p);
case PACKET_SERVER_CLIENT_INFO: return this->Receive_SERVER_CLIENT_INFO(p);
case PACKET_SERVER_NEED_GAME_PASSWORD: return this->Receive_SERVER_NEED_GAME_PASSWORD(p);
case PACKET_CLIENT_IDENTIFY: return this->Receive_CLIENT_IDENTIFY(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_ENABLE_ENCRYPTION: return this->Receive_SERVER_ENABLE_ENCRYPTION(p);
case PACKET_CLIENT_COMPANY_PASSWORD: return this->Receive_CLIENT_COMPANY_PASSWORD(p);
case PACKET_CLIENT_SETTINGS_PASSWORD: return this->Receive_CLIENT_SETTINGS_PASSWORD(p);
case PACKET_SERVER_SETTINGS_ACCESS: return this->Receive_SERVER_SETTINGS_ACCESS(p);
@@ -233,57 +237,59 @@ NetworkRecvStatus NetworkGameSocketHandler::ReceiveInvalidPacket(PacketGameType
return NETWORK_RECV_STATUS_MALFORMED_PACKET;
}
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_FULL); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO_EXTENDED(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO_EXTENDED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CLIENT_INFO(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CLIENT_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEED_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SETTINGS_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SETTINGS_ACCESS); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WAIT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_WAIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_BEGIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_SIZE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_SIZE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DATA(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DATA); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DONE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MAP_OK); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_JOIN(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_JOIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FRAME(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_FRAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SYNC(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SYNC); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ACK(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ACK); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMMAND); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMMAND); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHAT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_EXTERNAL_CHAT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_EXTERNAL_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_LOG(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_DESYNC_LOG(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_MSG(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_SYNC_DATA(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_SYNC_DATA); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEWGAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_RCON(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_RCON); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_RCON(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_RCON); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHECK_NEWGRFS); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_NEWGRFS_CHECKED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MOVE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_MOVE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MOVE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_UPDATE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(Packet &p) { return this->ReceiveInvalidPacket(PACKET_SERVER_CONFIG_UPDATE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FULL(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_FULL); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_BANNED(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_BANNED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_JOIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GAME_INFO(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_GAME_INFO_EXTENDED(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_GAME_INFO_EXTENDED); }
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_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_AUTH_RESPONSE(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_AUTH_RESPONSE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ENABLE_ENCRYPTION(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_ENABLE_ENCRYPTION); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMPANY_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMPANY_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SETTINGS_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SETTINGS_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SETTINGS_ACCESS(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_SETTINGS_ACCESS); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WELCOME(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_WELCOME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_GETMAP(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_GETMAP); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_WAIT(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_WAIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_BEGIN(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_BEGIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_SIZE(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_SIZE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DATA(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DATA); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MAP_DONE(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_MAP_DONE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MAP_OK(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MAP_OK); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_JOIN(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_JOIN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_FRAME(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_FRAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SYNC(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_SYNC); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ACK(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ACK); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_COMMAND(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_COMMAND); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMMAND(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMMAND); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_CHAT(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHAT(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_EXTERNAL_CHAT(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_EXTERNAL_CHAT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_PASSWORD(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_PASSWORD); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_SET_NAME(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_SET_NAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_QUIT(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_ERROR(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_ERROR); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_LOG(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_DESYNC_LOG(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_MSG(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_DESYNC_LOG); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_DESYNC_SYNC_DATA(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_DESYNC_SYNC_DATA); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_QUIT(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_ERROR_QUIT(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_ERROR_QUIT); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_SHUTDOWN(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_SHUTDOWN); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_NEWGAME(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_NEWGAME); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_RCON(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_RCON); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_RCON(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_RCON); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_CHECK_NEWGRFS); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_NEWGRFS_CHECKED); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_MOVE(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_MOVE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_CLIENT_MOVE(Packet &) { return this->ReceiveInvalidPacket(PACKET_CLIENT_MOVE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_COMPANY_UPDATE(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_COMPANY_UPDATE); }
NetworkRecvStatus NetworkGameSocketHandler::Receive_SERVER_CONFIG_UPDATE(Packet &) { return this->ReceiveInvalidPacket(PACKET_SERVER_CONFIG_UPDATE); }
std::string NetworkGameSocketHandler::GetDebugInfo() const { return ""; }

View File

@@ -60,13 +60,19 @@ enum PacketGameType : uint8_t {
PACKET_SERVER_GAME_INFO_EXTENDED, ///< Information about the server (extended). Note that the server should not use this ID directly.
/* 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_ENABLE_ENCRYPTION, ///< The server tells that authentication has completed and requests to enable encryption with the keys of the last \c PACKET_CLIENT_AUTH_RESPONSE.
/* 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 join step, the first 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.
PACKET_CLIENT_SETTINGS_PASSWORD, ///< Client sends the (hashed) settings password.
@@ -220,10 +226,21 @@ protected:
virtual NetworkRecvStatus Receive_SERVER_CLIENT_INFO(Packet &p);
/**
* Indication to the client that the server needs a game password.
* The client tells the server about the identity of the client:
* string Name of the client (max NETWORK_NAME_LENGTH).
* uint8_t ID of the company to play as (1..MAX_COMPANIES).
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_NEED_GAME_PASSWORD(Packet &p);
virtual NetworkRecvStatus Receive_CLIENT_IDENTIFY(Packet &p);
/**
* 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_AUTH_REQUEST(Packet &p);
/**
* Indication to the client that the server needs a company password:
@@ -234,12 +251,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 is complete and encryption has to be used from here on forward.
* @param p The packet that was just received.
*/
virtual NetworkRecvStatus Receive_SERVER_ENABLE_ENCRYPTION(Packet &p);
/**
* Send a password to the server to authorize

View File

@@ -35,7 +35,7 @@ public:
/* Check if the client is banned. */
for (const auto &entry : _network_ban_list) {
if (address.IsInNetmask(entry.c_str())) {
Packet p(Tban_packet);
Packet p(nullptr, Tban_packet);
p.PrepareToSend();
DEBUG(net, 2, "[%s] Banned ip tried to join (%s), refused", Tsocket::GetName(), entry.c_str());
@@ -52,7 +52,7 @@ public:
if (!Tsocket::AllowConnection()) {
/* No more clients allowed?
* Send to the client that we are full! */
Packet p(Tfull_packet);
Packet p(nullptr, Tfull_packet);
p.PrepareToSend();
if (p.TransferOut<int>(send, s, 0) < 0) {

View File

@@ -88,7 +88,7 @@ void NetworkUDPSocketHandler::SendPacket(Packet &p, NetworkAddress &recv, bool a
const size_t packet_size = p.Size();
const uint8_t frag_count = (uint8_t)((packet_size + PAYLOAD_MTU - 1) / PAYLOAD_MTU);
Packet frag(PACKET_UDP_EX_MULTI);
Packet frag(this, PACKET_UDP_EX_MULTI);
uint8_t current_frag = 0;
size_t offset = 0;
while (offset < packet_size) {
@@ -147,7 +147,7 @@ void NetworkUDPSocketHandler::ReceivePackets()
memset(&client_addr, 0, sizeof(client_addr));
/* The limit is UDP_MTU, but also allocate that much as we need to read the whole packet in one go. */
Packet p(this, UDP_MTU, UDP_MTU);
Packet p(Packet::ReadTag{}, this, UDP_MTU, UDP_MTU);
socklen_t client_len = sizeof(client_addr);
/* Try to receive anything */
@@ -169,7 +169,10 @@ void NetworkUDPSocketHandler::ReceivePackets()
DEBUG(net, 1, "received a packet with mismatching size from %s, (%u, %u)", NetworkAddressDumper().GetAddressAsString(&address), (uint)nbytes, (uint)p.Size());
continue;
}
p.PrepareToRead();
if (!p.PrepareToRead()) {
DEBUG(net, 1, "Invalid packet received (too small / decryption error)");
continue;
}
/* Handle the packet */
this->HandleUDPPacket(p, address);
@@ -236,13 +239,13 @@ void NetworkUDPSocketHandler::Receive_EX_MULTI(Packet &p, NetworkAddress &client
DEBUG(net, 6, "[udp] merged multi-part packet from %s: " OTTD_PRINTFHEX64 ", %u bytes",
NetworkAddressDumper().GetAddressAsString(client_addr), token, total_payload);
Packet merged(this, TCP_MTU, 0);
Packet merged(Packet::ReadTag{}, this, TCP_MTU, 0);
merged.ReserveBuffer(total_payload);
for (auto &frag : fs.fragments) {
merged.Send_binary((const uint8_t *)frag.data(), frag.size());
}
merged.ParsePacketSize();
merged.PrepareToRead();
if (!merged.PrepareToRead()) return;
/* If the size does not match the packet must be corrupted.
* Otherwise it will be marked as corrupted later on. */

View File

@@ -386,6 +386,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

@@ -135,7 +135,7 @@ ServerNetworkAdminSocketHandler::~ServerNetworkAdminSocketHandler()
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode error)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_ERROR);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_ERROR);
p->Send_uint8(error);
this->SendPacket(std::move(p));
@@ -150,7 +150,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendError(NetworkErrorCode er
/** Send the protocol version to the admin. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendProtocol()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_PROTOCOL);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_PROTOCOL);
/* announce the protocol version */
p->Send_uint8(NETWORK_GAME_ADMIN_VERSION);
@@ -170,7 +170,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendProtocol()
/** Send a welcome message to the admin. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_WELCOME);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_WELCOME);
p->Send_string(_settings_client.network.server_name);
p->Send_string(_openttd_revision);
@@ -191,7 +191,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendWelcome()
/** Tell the admin we started a new game. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendNewGame()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_NEWGAME);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_NEWGAME);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -199,7 +199,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendNewGame()
/** Tell the admin we're shutting down. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendShutdown()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_SHUTDOWN);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_SHUTDOWN);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -207,7 +207,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendShutdown()
/** Tell the admin the date. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_DATE);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_DATE);
p->Send_uint32(CalTime::CurDate().base());
this->SendPacket(std::move(p));
@@ -221,7 +221,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendDate()
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientJoin(ClientID client_id)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CLIENT_JOIN);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CLIENT_JOIN);
p->Send_uint32(client_id);
this->SendPacket(std::move(p));
@@ -239,7 +239,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkC
/* Only send data when we're a proper client, not just someone trying to query the server. */
if (ci == nullptr) return NETWORK_RECV_STATUS_OKAY;
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CLIENT_INFO);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CLIENT_INFO);
p->Send_uint32(ci->client_id);
p->Send_string(cs == nullptr ? "" : const_cast<NetworkAddress &>(cs->client_address).GetHostname());
@@ -260,7 +260,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientInfo(const NetworkC
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientUpdate(const NetworkClientInfo *ci)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CLIENT_UPDATE);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CLIENT_UPDATE);
p->Send_uint32(ci->client_id);
p->Send_string(ci->client_name);
@@ -277,7 +277,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientUpdate(const Networ
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientQuit(ClientID client_id)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CLIENT_QUIT);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CLIENT_QUIT);
p->Send_uint32(client_id);
this->SendPacket(std::move(p));
@@ -292,7 +292,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientQuit(ClientID clien
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientError(ClientID client_id, NetworkErrorCode error)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CLIENT_ERROR);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CLIENT_ERROR);
p->Send_uint32(client_id);
p->Send_uint8 (error);
@@ -307,7 +307,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendClientError(ClientID clie
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyNew(CompanyID company_id)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_NEW);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_NEW);
p->Send_uint8(company_id);
this->SendPacket(std::move(p));
@@ -321,7 +321,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyNew(CompanyID comp
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company *c)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_INFO);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_INFO);
p->Send_uint8 (c->index);
SetDParam(0, c->index);
@@ -346,7 +346,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyInfo(const Company
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Company *c)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_UPDATE);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_UPDATE);
p->Send_uint8 (c->index);
SetDParam(0, c->index);
@@ -369,7 +369,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyUpdate(const Compa
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyRemove(CompanyID company_id, AdminCompanyRemoveReason acrr)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_REMOVE);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_REMOVE);
p->Send_uint8(company_id);
p->Send_uint8(acrr);
@@ -386,7 +386,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyEconomy()
/* Get the income. */
Money income = -std::reduce(std::begin(company->yearly_expenses[0]), std::end(company->yearly_expenses[0]));
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_ECONOMY);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_ECONOMY);
p->Send_uint8(company->index);
@@ -419,7 +419,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats()
/* Go through all the companies. */
for (const Company *company : Company::Iterate()) {
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_COMPANY_STATS);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_COMPANY_STATS);
/* Send the information. */
p->Send_uint8(company->index);
@@ -448,7 +448,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCompanyStats()
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action, DestType desttype, ClientID client_id, const std::string &msg, NetworkTextMessageData data)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CHAT);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CHAT);
p->Send_uint8 (action);
p->Send_uint8 (desttype);
@@ -466,7 +466,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendChat(NetworkAction action
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRconEnd(const std::string_view command)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_RCON_END);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_RCON_END);
p->Send_string(command);
this->SendPacket(std::move(p));
@@ -481,7 +481,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRconEnd(const std::string
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendRcon(uint16_t colour, const std::string_view result)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_RCON);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_RCON);
p->Send_uint16(colour);
p->Send_string(result);
@@ -540,7 +540,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const std::string
* smaller than COMPAT_MTU. */
if (origin.size() + string.size() + 2 + 3 >= COMPAT_MTU) return NETWORK_RECV_STATUS_OKAY;
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CONSOLE);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CONSOLE);
p->Send_string(origin);
p->Send_string(string);
@@ -555,7 +555,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendConsole(const std::string
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const std::string_view json)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_GAMESCRIPT);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_GAMESCRIPT);
p->Send_string(json);
this->SendPacket(std::move(p));
@@ -566,7 +566,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendGameScript(const std::str
/** Send ping-reply (pong) to admin **/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendPong(uint32_t d1)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_PONG);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_PONG);
p->Send_uint32(d1);
this->SendPacket(std::move(p));
@@ -577,7 +577,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendPong(uint32_t d1)
/** Send the names of the commands. */
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CMD_NAMES);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CMD_NAMES);
for (uint i = 0; i < CMD_END; i++) {
const char *cmdname = GetCommandName(i);
@@ -589,7 +589,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
p->Send_bool(false);
this->SendPacket(std::move(p));
p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CMD_NAMES);
p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CMD_NAMES);
}
p->Send_bool(true);
@@ -611,7 +611,7 @@ NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdNames()
*/
NetworkRecvStatus ServerNetworkAdminSocketHandler::SendCmdLogging(ClientID client_id, const CommandPacket &cp)
{
auto p = std::make_unique<Packet>(ADMIN_PACKET_SERVER_CMD_LOGGING);
auto p = std::make_unique<Packet>(this, ADMIN_PACKET_SERVER_CMD_LOGGING);
p->Send_uint32(client_id);
p->Send_uint8 (cp.company);

View File

@@ -458,7 +458,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendKeyPasswordPacket(PacketTy
static_assert(std::tuple_size<decltype(ss.shared_data)>::value == 64);
crypto_aead_lock(message.data(), mac.data(), ss.shared_data.data(), nonce.data(), keys.x25519_pub_key.data(), keys.x25519_pub_key.size(), message.data(), message.size());
auto p = std::make_unique<Packet>(packet_type, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, packet_type, TCP_MTU);
static_assert(std::tuple_size<decltype(keys.x25519_pub_key)>::value == 32);
p->Send_binary(keys.x25519_pub_key);
p->Send_binary(nonce);
@@ -482,9 +482,17 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin()
_network_join_status = NETWORK_JOIN_STATUS_AUTHORIZING;
SetWindowDirty(WC_NETWORK_STATUS_WINDOW, WN_NETWORK_STATUS_WINDOW_JOIN);
auto p = std::make_unique<Packet>(PACKET_CLIENT_JOIN);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_JOIN);
p->Send_string(_openttd_revision);
p->Send_uint32(_openttd_newgrf_version);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::SendIdentify()
{
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_IDENTIFY, TCP_MTU);
p->Send_string(_settings_client.network.client_name); // Client name
p->Send_uint8 (_network_join.company); // PlayAs
p->Send_uint8 (0); // Used to be language
@@ -495,7 +503,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendJoin()
/** Tell the server we got all the NewGRFs. */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendNewGRFsOk()
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_NEWGRFS_CHECKED);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_NEWGRFS_CHECKED, TCP_MTU);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -504,10 +512,13 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendNewGRFsOk()
* Set the game password as requested.
* @param password The game password.
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const std::string &password)
NetworkRecvStatus ClientNetworkGameSocketHandler::SendAuthResponse()
{
NetworkSharedSecrets ss;
return my_client->SendKeyPasswordPacket(PACKET_CLIENT_GAME_PASSWORD, ss, password, nullptr);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_AUTH_RESPONSE, TCP_MTU);
my_client->authentication_handler->SendResponse(*p);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
/**
@@ -516,7 +527,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendGamePassword(const std::st
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const std::string &password)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_COMPANY_PASSWORD, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_COMPANY_PASSWORD, TCP_MTU);
p->Send_string(GenerateCompanyPasswordHash(password, _company_password_server_id, _company_password_game_seed));
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
@@ -529,7 +540,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCompanyPassword(const std:
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSettingsPassword(const std::string &password)
{
if (password.empty()) {
auto p = std::make_unique<Packet>(PACKET_CLIENT_SETTINGS_PASSWORD, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_SETTINGS_PASSWORD, TCP_MTU);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
} else {
@@ -543,7 +554,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendGetMap()
{
my_client->status = STATUS_MAP_WAIT;
auto p = std::make_unique<Packet>(PACKET_CLIENT_GETMAP, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_GETMAP, TCP_MTU);
#if defined(WITH_ZSTD)
p->Send_bool(true);
#else
@@ -558,7 +569,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendMapOk()
{
my_client->status = STATUS_ACTIVE;
auto p = std::make_unique<Packet>(PACKET_CLIENT_MAP_OK, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_MAP_OK, TCP_MTU);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -566,7 +577,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendMapOk()
/** Send an acknowledgement from the server's ticks. */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendAck()
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_ACK, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_ACK, TCP_MTU);
p->Send_uint32(_frame_counter);
p->Send_uint8 (my_client->token);
@@ -580,7 +591,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendAck()
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendCommand(const CommandPacket &cp)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_COMMAND, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_COMMAND, TCP_MTU);
my_client->NetworkGameSocketHandler::SendCommand(*p, cp);
my_client->SendPacket(std::move(p));
@@ -591,7 +602,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendCommand(const CommandPacke
NetworkRecvStatus ClientNetworkGameSocketHandler::SendChat(NetworkAction action, DestType type, int dest, const std::string &msg, NetworkTextMessageData data)
{
if (!my_client) return NETWORK_RECV_STATUS_CLIENT_QUIT;
auto p = std::make_unique<Packet>(PACKET_CLIENT_CHAT, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_CHAT, TCP_MTU);
p->Send_uint8 (action);
p->Send_uint8 (type);
@@ -606,7 +617,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendChat(NetworkAction action,
/** Send an error-packet over the network */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendError(NetworkErrorCode errorno, NetworkRecvStatus recvstatus)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_ERROR, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_ERROR, TCP_MTU);
p->Send_uint8(errorno);
p->Send_uint8(recvstatus);
@@ -620,7 +631,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendError(NetworkErrorCode err
NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncLog(const std::string &log)
{
for (size_t offset = 0; offset < log.size();) {
auto p = std::make_unique<Packet>(PACKET_CLIENT_DESYNC_LOG, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_DESYNC_LOG, TCP_MTU);
size_t size = std::min<size_t>(log.size() - offset, TCP_MTU - 2 - p->Size());
p->Send_uint16((uint16_t)size);
p->Send_binary((const uint8_t *)(log.data() + offset), size);
@@ -634,7 +645,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncLog(const std::strin
/** Send an error-packet over the network */
NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncMessage(const char *msg)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_DESYNC_MSG, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_DESYNC_MSG, TCP_MTU);
p->Send_uint32(EconTime::CurDate().base());
p->Send_uint16(EconTime::CurDateFract());
p->Send_uint8(TickSkipCounter());
@@ -658,7 +669,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncSyncData()
return NETWORK_RECV_STATUS_OKAY;
}
auto p = std::make_unique<Packet>(PACKET_CLIENT_DESYNC_SYNC_DATA, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_DESYNC_SYNC_DATA, TCP_MTU);
p->Send_uint32((uint32_t)_network_sync_record_counts.size());
uint32_t offset = 0;
for (uint32_t count : _network_sync_record_counts) {
@@ -681,7 +692,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendDesyncSyncData()
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetPassword(const std::string &password)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_SET_PASSWORD, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_SET_PASSWORD, TCP_MTU);
p->Send_string(GenerateCompanyPasswordHash(password, _company_password_server_id, _company_password_game_seed));
my_client->SendPacket(std::move(p));
@@ -694,7 +705,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetPassword(const std::str
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetName(const std::string &name)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_SET_NAME, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_SET_NAME, TCP_MTU);
p->Send_string(name);
my_client->SendPacket(std::move(p));
@@ -706,7 +717,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendSetName(const std::string
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendQuit()
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_QUIT, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_QUIT, TCP_MTU);
my_client->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
@@ -729,7 +740,7 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::SendRCon(const std::string &pa
*/
NetworkRecvStatus ClientNetworkGameSocketHandler::SendMove(CompanyID company, const std::string &password)
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_MOVE, TCP_MTU);
auto p = std::make_unique<Packet>(my_client, PACKET_CLIENT_MOVE, TCP_MTU);
p->Send_uint8(company);
p->Send_string(GenerateCompanyPasswordHash(password, _company_password_server_id, _company_password_game_seed));
my_client->SendPacket(std::move(p));
@@ -852,6 +863,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);
@@ -875,7 +887,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_ENCRYPTED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
uint grf_count = p.Recv_uint32();
if (grf_count > MAX_NON_STATIC_GRF_COUNT) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
@@ -907,28 +919,63 @@ NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_CHECK_NEWGRFS(P
return ret;
}
NetworkRecvStatus ClientNetworkGameSocketHandler::Receive_SERVER_NEED_GAME_PASSWORD(Packet &p)
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;
this->status = STATUS_AUTH_GAME;
static_assert(_server_x25519_pub_key.size() == 32);
p.Recv_binary(_server_x25519_pub_key);
_password_server_id = p.Recv_string(NETWORK_SERVER_ID_LENGTH);
if (this->HasClientQuit()) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
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_ENABLE_ENCRYPTION(Packet &)
{
if (this->status != STATUS_AUTH_GAME || this->authentication_handler == nullptr) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
this->receive_encryption_handler = this->authentication_handler->CreateServerToClientEncryptionHandler();
this->send_encryption_handler = this->authentication_handler->CreateClientToServerEncryptionHandler();
this->authentication_handler = nullptr;
this->status = STATUS_ENCRYPTED;
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_ENCRYPTED || this->status >= STATUS_AUTH_COMPANY) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
this->status = STATUS_AUTH_COMPANY;
_company_password_game_seed = p.Recv_uint32();
@@ -939,14 +986,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_ENCRYPTED || this->status >= STATUS_AUTHORIZED) return NETWORK_RECV_STATUS_MALFORMED_PACKET;
this->status = STATUS_AUTHORIZED;
_network_own_client_id = (ClientID)p.Recv_uint32();
@@ -1438,8 +1485,9 @@ const char *ClientNetworkGameSocketHandler::GetServerStatusName(ServerStatus sta
static const char* _server_status_names[] {
"INACTIVE",
"JOIN",
"NEWGRFS_CHECK",
"AUTH_GAME",
"ENCRYPTED",
"NEWGRFS_CHECK",
"AUTH_COMPANY",
"AUTHORIZED",
"MAP_WAIT",

View File

@@ -15,6 +15,7 @@
/** Class for handling the client side of the game connection. */
class ClientNetworkGameSocketHandler : 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.
@@ -24,8 +25,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_ENCRYPTED, ///< The game authentication has completed and from here on the connection to the server is encrypted.
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.
@@ -54,7 +56,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_ENABLE_ENCRYPTION(Packet &p) override;
NetworkRecvStatus Receive_SERVER_NEED_COMPANY_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_SERVER_SETTINGS_ACCESS(Packet &p) override;
NetworkRecvStatus Receive_SERVER_WELCOME(Packet &p) override;
@@ -83,6 +86,7 @@ protected:
static NetworkRecvStatus SendNewGRFsOk();
static NetworkRecvStatus SendGetMap();
static NetworkRecvStatus SendMapOk();
static NetworkRecvStatus SendIdentify();
void CheckConnection();
NetworkRecvStatus SendKeyPasswordPacket(PacketType packet_type, NetworkSharedSecrets &ss, const std::string &password, const std::string *payload);
@@ -111,7 +115,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 SendSettingsPassword(const std::string &password);

View File

@@ -204,7 +204,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentType type)
this->Connect();
auto p = std::make_unique<Packet>(PACKET_CONTENT_CLIENT_INFO_LIST);
auto p = std::make_unique<Packet>(this, PACKET_CONTENT_CLIENT_INFO_LIST);
p->Send_uint8 ((uint8_t)type);
p->Send_uint32(0xffffffff);
p->Send_uint8 (2);
@@ -239,7 +239,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(uint count, const Con
* The rest of the packet can be used for the IDs. */
uint p_count = std::min<uint>(count, (TCP_MTU - sizeof(PacketSize) - sizeof(uint8_t) - sizeof(uint16_t)) / sizeof(uint32_t));
auto p = std::make_unique<Packet>(PACKET_CONTENT_CLIENT_INFO_ID, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_CONTENT_CLIENT_INFO_ID, TCP_MTU);
p->Send_uint16(p_count);
for (uint i = 0; i < p_count; i++) {
@@ -269,7 +269,7 @@ void ClientNetworkContentSocketHandler::RequestContentList(ContentVector *cv, bo
uint offset = 0;
while (cv->size() > offset) {
auto p = std::make_unique<Packet>(send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID, TCP_MTU);
auto p = std::make_unique<Packet>(this, send_md5sum ? PACKET_CONTENT_CLIENT_INFO_EXTID_MD5 : PACKET_CONTENT_CLIENT_INFO_EXTID, TCP_MTU);
const uint to_send = std::min<uint>(static_cast<uint>(cv->size() - offset), max_per_packet);
p->Send_uint8(static_cast<uint8_t>(to_send));
@@ -371,7 +371,7 @@ void ClientNetworkContentSocketHandler::DownloadSelectedContentFallback(const Co
* The rest of the packet can be used for the IDs. */
uint p_count = std::min<uint>(count, (TCP_MTU - sizeof(PacketSize) - sizeof(uint8_t) - sizeof(uint16_t)) / sizeof(uint32_t));
auto p = std::make_unique<Packet>(PACKET_CONTENT_CLIENT_CONTENT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_CONTENT_CLIENT_CONTENT, TCP_MTU);
p->Send_uint16(p_count);
for (uint i = 0; i < p_count; i++) {

View File

@@ -458,7 +458,7 @@ void ClientNetworkCoordinatorSocketHandler::Register()
this->Connect();
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_SERVER_REGISTER);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_REGISTER);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_uint8(_settings_client.network.server_game_type);
p->Send_uint16(_settings_client.network.server_port);
@@ -480,7 +480,7 @@ void ClientNetworkCoordinatorSocketHandler::SendServerUpdate()
{
DEBUG(net, 6, "Sending server update to Game Coordinator");
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERVER_UPDATE, TCP_MTU);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo(), this->next_update.time_since_epoch() != std::chrono::nanoseconds::zero());
@@ -498,7 +498,7 @@ void ClientNetworkCoordinatorSocketHandler::GetListing()
_network_game_list_version++;
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_CLIENT_LISTING);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_LISTING);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_uint8(NETWORK_GAME_INFO_VERSION);
p->Send_string(_openttd_revision);
@@ -530,7 +530,7 @@ void ClientNetworkCoordinatorSocketHandler::ConnectToServer(const std::string &i
this->Connect();
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_CLIENT_CONNECT);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECT);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(invite_code);
@@ -547,7 +547,7 @@ void ClientNetworkCoordinatorSocketHandler::ConnectFailure(const std::string &to
/* Connecter will destroy itself. */
this->game_connecter = nullptr;
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_CONNECT_FAILED);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(token);
p->Send_uint8(tracking_number);
@@ -578,7 +578,7 @@ void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &to
} else {
/* The client informs the Game Coordinator about the success. The server
* doesn't have to, as it is implied by the client telling. */
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_CLIENT_CONNECTED);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_CLIENT_CONNECTED);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(token);
this->SendPacket(std::move(p));
@@ -606,7 +606,7 @@ void ClientNetworkCoordinatorSocketHandler::ConnectSuccess(const std::string &to
*/
void ClientNetworkCoordinatorSocketHandler::StunResult(const std::string &token, uint8_t family, bool result)
{
auto p = std::make_unique<Packet>(PACKET_COORDINATOR_SERCLI_STUN_RESULT);
auto p = std::make_unique<Packet>(this, PACKET_COORDINATOR_SERCLI_STUN_RESULT);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(token);
p->Send_uint8(family);

View File

@@ -0,0 +1,475 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file network_crypto.cpp Implementation of the network specific cryptography helpers. */
#include "../stdafx.h"
#include "network_crypto_internal.h"
#include "core/packet.h"
#include "../3rdparty/monocypher/monocypher.h"
#include "../core/random_func.hpp"
#include "../debug.h"
#include "../debug_fmt.h"
#include "../string_func.h"
#include "../safeguards.h"
/**
* Call \c crypto_wipe for all the data in the given span.
* @param span The span to cryptographically wipe.
*/
static void crypto_wipe(std::span<uint8_t> span)
{
crypto_wipe(span.data(), span.size());
}
/** Ensure the derived keys do not get leaked when we're done with it. */
X25519DerivedKeys::~X25519DerivedKeys()
{
crypto_wipe(keys);
}
/**
* Get the key to encrypt or decrypt a message sent from the client to the server.
* @return The raw bytes of the key.
*/
std::span<const uint8_t> X25519DerivedKeys::ClientToServer() const
{
return std::span(this->keys.data(), X25519_KEY_SIZE);
}
/**
* Get the key to encrypt or decrypt a message sent from the server to the client.
* @return The raw bytes of the key.
*/
std::span<const uint8_t> X25519DerivedKeys::ServerToClient() const
{
return std::span(this->keys.data() + X25519_KEY_SIZE, X25519_KEY_SIZE);
}
/**
* Perform the actual key exchange.
* @param peer_public_key The public key chosen by the other participant of the key exchange.
* @param side Whether we are the client or server; used to hash the public key of us and the peer in the right order.
* @param our_secret_key The secret key of us.
* @param our_public_key The public key of us.
* @param extra_payload Extra payload to put into the hash function to create the derived keys.
* @return True when the key exchange has succeeded, false when an illegal public key was given.
*/
bool X25519DerivedKeys::Exchange(const X25519PublicKey &peer_public_key, X25519KeyExchangeSide side,
const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload)
{
X25519Key shared_secret;
crypto_x25519(shared_secret.data(), our_secret_key.data(), peer_public_key.data());
if (std::all_of(shared_secret.begin(), shared_secret.end(), [](auto v) { return v == 0; })) {
/* A shared secret of all zeros means that the peer tried to force the shared secret to a known constant. */
return false;
}
crypto_blake2b_ctx ctx;
crypto_blake2b_init(&ctx, this->keys.size());
crypto_blake2b_update(&ctx, shared_secret.data(), shared_secret.size());
switch (side) {
case X25519KeyExchangeSide::SERVER:
crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
break;
case X25519KeyExchangeSide::CLIENT:
crypto_blake2b_update(&ctx, peer_public_key.data(), peer_public_key.size());
crypto_blake2b_update(&ctx, our_public_key.data(), our_public_key.size());
break;
default:
NOT_REACHED();
}
crypto_blake2b_update(&ctx, reinterpret_cast<const uint8_t *>(extra_payload.data()), extra_payload.size());
crypto_blake2b_final(&ctx, this->keys.data());
return true;
}
/**
* Encryption handler implementation for monocypther encryption after a X25519 key exchange.
*/
class X25519EncryptionHandler : public NetworkEncryptionHandler {
private:
crypto_aead_ctx context; ///< The actual encryption context.
public:
/**
* Create the encryption handler.
* @param key The key used for the encryption.
* @param nonce The nonce used for the encryption.
*/
X25519EncryptionHandler(const std::span<const uint8_t> key, const X25519Nonce &nonce)
{
assert(key.size() == X25519_KEY_SIZE);
crypto_aead_init_x(&this->context, key.data(), nonce.data());
}
/** Ensure the encryption context is wiped! */
~X25519EncryptionHandler()
{
crypto_wipe(&this->context, sizeof(this->context));
}
size_t MACSize() const override
{
return X25519_MAC_SIZE;
}
bool Decrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
{
return crypto_aead_read(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size()) == 0;
}
void Encrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) override
{
crypto_aead_write(&this->context, message.data(), mac.data(), nullptr, 0, message.data(), message.size());
}
};
/** Ensure the key does not get leaked when we're done with it. */
X25519Key::~X25519Key()
{
crypto_wipe(*this);
}
/**
* Create a new secret key that's filled with random bytes.
* @return The randomly filled key.
*/
/* static */ X25519SecretKey X25519SecretKey::CreateRandom()
{
X25519SecretKey secret_key;
RandomBytesWithFallback(secret_key);
return secret_key;
}
/**
* Create the public key associated with this secret key.
* @return The public key.
*/
X25519PublicKey X25519SecretKey::CreatePublicKey() const
{
X25519PublicKey public_key;
crypto_x25519_public_key(public_key.data(), this->data());
return public_key;
}
/**
* Create a new nonce that's filled with random bytes.
* @return The randomly filled nonce.
*/
/* static */ X25519Nonce X25519Nonce::CreateRandom()
{
X25519Nonce nonce;
RandomBytesWithFallback(nonce);
return nonce;
}
/** Ensure the nonce does not get leaked when we're done with it. */
X25519Nonce::~X25519Nonce()
{
crypto_wipe(*this);
}
/**
* Create the handler, and generate the public keys accordingly.
* @param secret_key The secret key to use for this handler. Defaults to secure random data.
*/
X25519AuthenticationHandler::X25519AuthenticationHandler(const X25519SecretKey &secret_key) :
our_secret_key(secret_key), our_public_key(secret_key.CreatePublicKey()), nonce(X25519Nonce::CreateRandom())
{
}
/* virtual */ void X25519AuthenticationHandler::SendRequest(Packet &p)
{
p.Send_bytes(this->our_public_key);
p.Send_bytes(this->nonce);
}
/**
* Read the key exchange data from a \c Packet that came from the server,
* @param p The packet that has been received.
* @return True when the data seems correct.
*/
bool X25519AuthenticationHandler::ReceiveRequest(Packet &p)
{
if (p.RemainingBytesToTransfer() != X25519_KEY_SIZE + X25519_NONCE_SIZE) {
Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
return false;
}
p.Recv_bytes(this->peer_public_key);
p.Recv_bytes(this->nonce);
return true;
}
/**
* Perform the key exchange, and when that is correct fill the \c Packet with the appropriate data.
* @param p The packet that has to be sent.
* @param derived_key_extra_payload The extra payload to pass to the key exchange.
* @return Whether the key exchange was successful or not.
*/
bool X25519AuthenticationHandler::SendResponse(Packet &p, std::string_view derived_key_extra_payload)
{
if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::CLIENT,
this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
return false;
}
X25519KeyExchangeMessage message;
RandomBytesWithFallback(message);
X25519Mac mac;
crypto_aead_lock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), nonce.data(),
this->our_public_key.data(), this->our_public_key.size(), message.data(), message.size());
p.Send_bytes(this->our_public_key);
p.Send_bytes(mac);
p.Send_bytes(message);
return true;
}
/**
* Get the public key the peer provided for the key exchange.
* @return The hexadecimal string representation of the peer's public key.
*/
std::string X25519AuthenticationHandler::GetPeerPublicKey() const
{
return FormatArrayAsHex(this->peer_public_key);
}
std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateClientToServerEncryptionHandler() const
{
return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ClientToServer(), this->nonce);
}
std::unique_ptr<NetworkEncryptionHandler> X25519AuthenticationHandler::CreateServerToClientEncryptionHandler() const
{
return std::make_unique<X25519EncryptionHandler>(this->derived_keys.ServerToClient(), this->nonce);
}
/**
* Read the key exchange data from a \c Packet that came from the client, and check whether the client passes the key
* exchange successfully.
* @param p The packet that has been received.
* @param derived_key_extra_payload The extra payload to pass to the key exchange.
* @return Whether the authentication was successful or not.
*/
NetworkAuthenticationServerHandler::ResponseResult X25519AuthenticationHandler::ReceiveResponse(Packet &p, std::string_view derived_key_extra_payload)
{
if (p.RemainingBytesToTransfer() != X25519_KEY_SIZE + X25519_MAC_SIZE + X25519_KEY_EXCHANGE_MESSAGE_SIZE) {
Debug(net, 1, "[crypto] Received auth response of illegal size; authentication aborted.");
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
}
X25519KeyExchangeMessage message{};
X25519Mac mac;
p.Recv_bytes(this->peer_public_key);
p.Recv_bytes(mac);
p.Recv_bytes(message);
if (!this->derived_keys.Exchange(this->peer_public_key, X25519KeyExchangeSide::SERVER,
this->our_secret_key, this->our_public_key, derived_key_extra_payload)) {
Debug(net, 0, "[crypto] Peer sent an illegal public key; authentication aborted.");
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
}
if (crypto_aead_unlock(message.data(), mac.data(), this->derived_keys.ClientToServer().data(), nonce.data(),
this->peer_public_key.data(), this->peer_public_key.size(), message.data(), message.size()) != 0) {
/*
* The ciphertext and the message authentication code do not match with the encryption key.
* This is most likely an invalid password, or possibly a bug in the client.
*/
return NetworkAuthenticationServerHandler::NOT_AUTHENTICATED;
}
return NetworkAuthenticationServerHandler::AUTHENTICATED;
}
/* virtual */ NetworkAuthenticationClientHandler::RequestResult X25519PAKEClientHandler::ReceiveRequest(struct Packet &p)
{
bool success = this->X25519AuthenticationHandler::ReceiveRequest(p);
if (!success) return NetworkAuthenticationClientHandler::INVALID;
this->handler->AskUserForPassword(this->handler);
return NetworkAuthenticationClientHandler::AWAIT_USER_INPUT;
}
/**
* Get the secret key from the given string. If that is not a valid secret key, reset it with a random one.
* Furthermore update the public key so it is always in sync with the private key.
* @param secret_key The secret key to read/validate/fix.
* @param public_key The public key to update.
* @return The valid secret key.
*/
/* static */ X25519SecretKey X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key)
{
X25519SecretKey key{};
if (!ConvertHexToBytes(secret_key, key)) {
if (secret_key.empty()) {
Debug(net, 3, "[crypto] Creating a new random key");
} else {
Debug(net, 0, "[crypto] Found invalid secret key, creating a new random key");
}
key = X25519SecretKey::CreateRandom();
secret_key = FormatArrayAsHex(key);
}
public_key = FormatArrayAsHex(key.CreatePublicKey());
return key;
}
/* virtual */ NetworkAuthenticationServerHandler::ResponseResult X25519AuthorizedKeyServerHandler::ReceiveResponse(Packet &p)
{
ResponseResult result = this->X25519AuthenticationHandler::ReceiveResponse(p, {});
if (result != AUTHENTICATED) return result;
std::string peer_public_key = this->GetPeerPublicKey();
return this->authorized_key_handler->IsAllowed(peer_public_key) ? AUTHENTICATED : NOT_AUTHENTICATED;
}
/* virtual */ NetworkAuthenticationClientHandler::RequestResult CombinedAuthenticationClientHandler::ReceiveRequest(struct Packet &p)
{
NetworkAuthenticationMethod method = static_cast<NetworkAuthenticationMethod>(p.Recv_uint8());
auto is_of_method = [method](Handler &handler) { return handler->GetAuthenticationMethod() == method; };
auto it = std::find_if(handlers.begin(), handlers.end(), is_of_method);
if (it == handlers.end()) return INVALID;
this->current_handler = it->get();
Debug(net, 9, "Received {} authentication request", this->GetName());
return this->current_handler->ReceiveRequest(p);
}
/* virtual */ bool CombinedAuthenticationClientHandler::SendResponse(struct Packet &p)
{
Debug(net, 9, "Sending {} authentication response", this->GetName());
return this->current_handler->SendResponse(p);
}
/* virtual */ std::string_view CombinedAuthenticationClientHandler::GetName() const
{
return this->current_handler != nullptr ? this->current_handler->GetName() : "Unknown";
}
/* virtual */ NetworkAuthenticationMethod CombinedAuthenticationClientHandler::GetAuthenticationMethod() const
{
return this->current_handler != nullptr ? this->current_handler->GetAuthenticationMethod() : NETWORK_AUTH_METHOD_END;
}
/**
* Add the given sub-handler to this handler, if the handler can be used (e.g. there are authorized keys or there is a password).
* @param handler The handler to add.
*/
void CombinedAuthenticationServerHandler::Add(CombinedAuthenticationServerHandler::Handler &&handler)
{
/* Is the handler configured correctly, e.g. does it have a password? */
if (!handler->CanBeUsed()) return;
this->handlers.push_back(std::move(handler));
}
/* virtual */ void CombinedAuthenticationServerHandler::SendRequest(struct Packet &p)
{
Debug(net, 9, "Sending {} authentication request", this->GetName());
p.Send_uint8(this->handlers.back()->GetAuthenticationMethod());
this->handlers.back()->SendRequest(p);
}
/* virtual */ NetworkAuthenticationServerHandler::ResponseResult CombinedAuthenticationServerHandler::ReceiveResponse(struct Packet &p)
{
Debug(net, 9, "Receiving {} authentication response", this->GetName());
ResponseResult result = this->handlers.back()->ReceiveResponse(p);
if (result != NOT_AUTHENTICATED) return result;
this->handlers.pop_back();
return this->CanBeUsed() ? RETRY_NEXT_METHOD : NOT_AUTHENTICATED;
}
/* virtual */ std::string_view CombinedAuthenticationServerHandler::GetName() const
{
return this->CanBeUsed() ? this->handlers.back()->GetName() : "Unknown";
}
/* virtual */ NetworkAuthenticationMethod CombinedAuthenticationServerHandler::GetAuthenticationMethod() const
{
return this->CanBeUsed() ? this->handlers.back()->GetAuthenticationMethod() : NETWORK_AUTH_METHOD_END;
}
/* virtual */ bool CombinedAuthenticationServerHandler::CanBeUsed() const
{
return !this->handlers.empty();
}
/* virtual */ void NetworkAuthenticationPasswordRequestHandler::Reply(const std::string &password)
{
this->password = password;
this->SendResponse();
}
/* virtual */ bool NetworkAuthenticationDefaultAuthorizedKeyHandler::IsAllowed(std::string_view peer_public_key) const
{
for (const auto &allowed : *this->authorized_keys) {
if (StrEqualsIgnoreCase(allowed, peer_public_key)) return true;
}
return false;
}
/**
* Create a NetworkAuthenticationClientHandler.
* @param password_handler The handler for when a request for password needs to be passed on to the user.
* @param secret_key The location where the secret key is stored; can be overwritten when invalid.
* @param public_key The location where the public key is stored; can be overwritten when invalid.
*/
/* static */ std::unique_ptr<NetworkAuthenticationClientHandler> NetworkAuthenticationClientHandler::Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key)
{
auto secret = X25519AuthorizedKeyClientHandler::GetValidSecretKeyAndUpdatePublicKey(secret_key, public_key);
auto handler = std::make_unique<CombinedAuthenticationClientHandler>();
handler->Add(std::make_unique<X25519KeyExchangeOnlyClientHandler>(secret));
handler->Add(std::make_unique<X25519PAKEClientHandler>(secret, std::move(password_handler)));
handler->Add(std::make_unique<X25519AuthorizedKeyClientHandler>(secret));
return handler;
}
/**
* Create a NetworkAuthenticationServerHandler.
* @param password_provider Callback to provide the password handling. Must remain valid until the authentication has succeeded or failed. Can be \c nullptr to skip password checks.
* @param authorized_key_handler Callback to provide the authorized key handling. Must remain valid until the authentication has succeeded or failed. Can be \c nullptr to skip authorized key checks.
* @param client_supported_method_mask Bitmask of the methods that are supported by the client. Defaults to support of all methods.
*/
std::unique_ptr<NetworkAuthenticationServerHandler> NetworkAuthenticationServerHandler::Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask)
{
auto secret = X25519SecretKey::CreateRandom();
auto handler = std::make_unique<CombinedAuthenticationServerHandler>();
if (password_provider != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_PAKE)) {
handler->Add(std::make_unique<X25519PAKEServerHandler>(secret, password_provider));
}
if (authorized_key_handler != nullptr && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY)) {
handler->Add(std::make_unique<X25519AuthorizedKeyServerHandler>(secret, authorized_key_handler));
}
if (!handler->CanBeUsed() && HasBit(client_supported_method_mask, NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY)) {
/* Fall back to the plain handler when neither password, nor authorized keys are configured. */
handler->Add(std::make_unique<X25519KeyExchangeOnlyServerHandler>(secret));
}
return handler;
}

View File

@@ -0,0 +1,287 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file network_crypto.h Crypto specific bits of the network handling.
*
* This provides a set of functionality to perform authentication combined with a key exchange,
* to create a shared secret as well as encryption using those shared secrets.
*
* For the authentication/key exchange, the server determines the available methods and creates
* the appropriate \c NetworkAuthenticationServerHandler. This will be used to create a request
* for the client, which instantiates a \c NetworkAuthenticationClientHandler to handle that
* request.
* At the moment there are three types of request: key exchange only, password-authenticated key
* exchange (PAKE) and authorized keys. When the request is for a password, the user is asked
* for the password via an essentially asynchronous callback from the client handler. For the
* other requests no input from the user is needed, and these are immediately ready to generate
* the response for the server.
*
* The server will validate the response resulting in either the user being authenticated or not.
* When the user failed authentication, there might be a possibility to retry. For example when
* the server has configured authorized keys and passwords; when the client fails with the
* authorized keys, it will retry with the password.
*
* Once the key exchange/authentication has been done, the server can signal the client to
* upgrade the network connection to use encryption using the shared secret of the key exchange.
*/
#ifndef NETWORK_CRYPTO_H
#define NETWORK_CRYPTO_H
/**
* Base class for handling the encryption (or decryption) of a network connection.
*/
class NetworkEncryptionHandler {
public:
virtual ~NetworkEncryptionHandler() {}
/**
* Get the size of the MAC (Message Authentication Code) used by the underlying encryption protocol.
* @return The size, in bytes, of the MACs.
*/
virtual size_t MACSize() const = 0;
/**
* Decrypt the given message in-place, validating against the given MAC.
* @param mac The message authentication code (MAC).
* @param message The location of the message to decrypt.
* @return Whether decryption and authentication/validation of the message succeeded.
*/
virtual bool Decrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) = 0;
/**
* Encrypt the given message in-place, and write the associated MAC.
* @param mac The location to write the message authentication code (MAC) to.
* @param message The location of the message to encrypt.
*/
virtual void Encrypt(std::span<std::uint8_t> mac, std::span<std::uint8_t> message) = 0;
};
/**
* Callback interface for requests for passwords in the context of network authentication.
*/
class NetworkAuthenticationPasswordRequest {
public:
virtual ~NetworkAuthenticationPasswordRequest() {}
/**
* Reply to the request with the given password.
*/
virtual void Reply(const std::string &password) = 0;
};
/**
* Callback interface for client implementations to provide the handling of the password requests.
*/
class NetworkAuthenticationPasswordRequestHandler : public NetworkAuthenticationPasswordRequest {
protected:
friend class X25519PAKEClientHandler;
std::string password; ///< The entered password.
public:
virtual void Reply(const std::string &password) override;
/**
* Callback to trigger sending the response for the password request.
*/
virtual void SendResponse() = 0;
/**
* Callback to trigger asking the user for the password.
* @param request The request to the user, to which it can reply with the password.
*/
virtual void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) = 0;
};
/**
* Callback interface for server implementations to provide the current password.
*/
class NetworkAuthenticationPasswordProvider {
public:
virtual ~NetworkAuthenticationPasswordProvider() {}
/**
* Callback to return the password where to validate against.
* @return \c std::string_view of the current password; an empty view means no password check will be performed.
*/
virtual std::string_view GetPassword() const = 0;
};
/**
* Default implementation of the password provider.
*/
class NetworkAuthenticationDefaultPasswordProvider : public NetworkAuthenticationPasswordProvider {
private:
const std::string *password; ///< The password to check against.
public:
/**
* Create the provider with the pointer to the password that is to be used. A pointer, so this can handle
* situations where the password gets changed over time.
* @param password The reference to the configured password.
*/
NetworkAuthenticationDefaultPasswordProvider(const std::string &password) : password(&password) {}
std::string_view GetPassword() const override { return *this->password; };
};
/**
* Callback interface for server implementations to provide the authorized key validation.
*/
class NetworkAuthenticationAuthorizedKeyHandler {
public:
virtual ~NetworkAuthenticationAuthorizedKeyHandler() {}
/**
* Check whether the key handler can be used, i.e. whether there are authorized keys to check against.
* @return \c true when it can be used, otherwise \c false.
*/
virtual bool CanBeUsed() const = 0;
/**
* Check whether the given public key of the peer is allowed in.
* @param peer_public_key The public key of the peer to check against.
* @return \c true when the key is allowed, otherwise \c false.
*/
virtual bool IsAllowed(std::string_view peer_public_key) const = 0;
};
/**
* Default implementation for the authorized key handler.
*/
class NetworkAuthenticationDefaultAuthorizedKeyHandler : public NetworkAuthenticationAuthorizedKeyHandler {
private:
const std::vector<std::string> *authorized_keys; ///< The authorized keys to check against.
public:
/**
* Create the handler that uses the given authorized keys to check against.
* @param authorized_keys The reference to the authorized keys to check against.
*/
NetworkAuthenticationDefaultAuthorizedKeyHandler(const std::vector<std::string> &authorized_keys) : authorized_keys(&authorized_keys) {}
bool CanBeUsed() const override { return !this->authorized_keys->empty(); }
bool IsAllowed(std::string_view peer_public_key) const override;
};
/** The authentication method that can be used. */
enum NetworkAuthenticationMethod : uint8_t {
NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY, ///< No actual authentication is taking place, just perform a x25519 key exchange.
NETWORK_AUTH_METHOD_X25519_PAKE, ///< Authentication using x25519 password-authenticated key agreement.
NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY, ///< Authentication using x22519 key exchange and authorized keys.
NETWORK_AUTH_METHOD_END, ///< Must ALWAYS be on the end of this list!! (period)
};
/** The mask of authentication methods that can be used. */
using NetworkAuthenticationMethodMask = uint16_t;
/**
* Base class for cryptographic authentication handlers.
*/
class NetworkAuthenticationHandler {
public:
virtual ~NetworkAuthenticationHandler() {}
/**
* Get the name of the handler for debug messages.
* @return The name of the handler.
*/
virtual std::string_view GetName() const = 0;
/**
* Get the method this handler is providing functionality for.
* @return The \c NetworkAuthenticationMethod.
*/
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const = 0;
/**
* Create a \a NetworkEncryptionHandler to encrypt or decrypt messages from the client to the server.
* @return The handler for the client to server encryption.
*/
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const = 0;
/**
* Create a \a NetworkEncryptionHandler to encrypt or decrypt messages from the server to the client.
* @return The handler for the server to client encryption.
*/
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const = 0;
};
/**
* Base class for client side cryptographic authentication handlers.
*/
class NetworkAuthenticationClientHandler : public NetworkAuthenticationHandler {
public:
/** The processing result of receiving a request. */
enum RequestResult {
AWAIT_USER_INPUT, ///< We have requested some user input, but must wait on that.
READY_FOR_RESPONSE, ///< We do not have to wait for user input, and can immediately respond to the server.
INVALID, ///< We have received an invalid request.
};
/**
* Read a request from the server.
* @param p The packet to read the request from.
* @return True when valid, otherwise false.
*/
virtual RequestResult ReceiveRequest(struct Packet &p) = 0;
/**
* Create the response to send to the server.
* @param p The packet to write the response from.
* @return True when a valid packet was made, otherwise false.
*/
virtual bool SendResponse(struct Packet &p) = 0;
static std::unique_ptr<NetworkAuthenticationClientHandler> Create(std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> password_handler, std::string &secret_key, std::string &public_key);
};
/**
* Base class for server side cryptographic authentication handlers.
*/
class NetworkAuthenticationServerHandler : public NetworkAuthenticationHandler {
public:
/** The processing result of receiving a response. */
enum ResponseResult {
AUTHENTICATED, ///< The client was authenticated successfully.
NOT_AUTHENTICATED, ///< All authentications for this handler have been exhausted.
RETRY_NEXT_METHOD, ///< The client failed to authenticate, but there is another method to try.
};
/**
* Create the request to send to the client.
* @param p The packet to write the request to.
*/
virtual void SendRequest(struct Packet &p) = 0;
/**
* Read the response from the client.
* @param p The packet to read the response from.
* @return The \c ResponseResult describing the result.
*/
virtual ResponseResult ReceiveResponse(struct Packet &p) = 0;
/**
* Checks whether this handler can be used with the current configuration.
* For example when there is no password, the handler cannot be used.
* @return True when this handler can be used.
*/
virtual bool CanBeUsed() const = 0;
/**
* Get the public key the peer provided during the authentication.
* @return The hexadecimal string representation of the peer's public key.
*/
virtual std::string GetPeerPublicKey() const = 0;
static std::unique_ptr<NetworkAuthenticationServerHandler> Create(const NetworkAuthenticationPasswordProvider *password_provider, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler, NetworkAuthenticationMethodMask client_supported_method_mask = ~static_cast<NetworkAuthenticationMethodMask>(0));
};
#endif /* NETWORK_CRYPTO_H */

View File

@@ -0,0 +1,341 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file network_crypto_internal.h Internal bits to the crypto of the network handling. */
#ifndef NETWORK_CRYPTO_INTERNAL_H
#define NETWORK_CRYPTO_INTERNAL_H
#include "network_crypto.h"
/** The number of bytes the public and secret keys are in X25519. */
constexpr size_t X25519_KEY_SIZE = 32;
/** The number of bytes the nonces are in X25519. */
constexpr size_t X25519_NONCE_SIZE = 24;
/** The number of bytes the message authentication codes are in X25519. */
constexpr size_t X25519_MAC_SIZE = 16;
/** The number of bytes the (random) payload of the authentication message has. */
constexpr size_t X25519_KEY_EXCHANGE_MESSAGE_SIZE = 8;
/** Container for a X25519 key that is automatically crypto-wiped when destructed. */
struct X25519Key : std::array<uint8_t, X25519_KEY_SIZE> {
~X25519Key();
};
/** Container for a X25519 public key. */
struct X25519PublicKey : X25519Key {
};
/** Container for a X25519 secret key. */
struct X25519SecretKey : X25519Key {
static X25519SecretKey CreateRandom();
X25519PublicKey CreatePublicKey() const;
};
/** Container for a X25519 nonce that is automatically crypto-wiped when destructed. */
struct X25519Nonce : std::array<uint8_t, X25519_NONCE_SIZE> {
static X25519Nonce CreateRandom();
~X25519Nonce();
};
/** Container for a X25519 message authentication code. */
using X25519Mac = std::array<uint8_t, X25519_MAC_SIZE>;
/** Container for a X25519 key exchange message. */
using X25519KeyExchangeMessage = std::array<uint8_t, X25519_KEY_EXCHANGE_MESSAGE_SIZE>;
/** The side of the key exchange. */
enum class X25519KeyExchangeSide {
CLIENT, ///< We are the client.
SERVER, ///< We are the server.
};
/**
* Container for the keys that derived from the X25519 key exchange mechanism. This mechanism derives
* a key to encrypt both the client-to-server and a key to encrypt server-to-client communication.
*/
class X25519DerivedKeys {
private:
/** Single contiguous buffer to store the derived keys in, as they are generated as a single hash. */
std::array<uint8_t, X25519_KEY_SIZE + X25519_KEY_SIZE> keys;
public:
~X25519DerivedKeys();
std::span<const uint8_t> ClientToServer() const;
std::span<const uint8_t> ServerToClient() const;
bool Exchange(const X25519PublicKey &peer_public_key, X25519KeyExchangeSide side,
const X25519SecretKey &our_secret_key, const X25519PublicKey &our_public_key, std::string_view extra_payload);
};
/**
* Base for handlers using a X25519 key exchange to perform authentication.
*
* In general this works as follows:
* 1) the client and server have or generate a secret and public X25519 key.
* 2) the X25519 key exchange is performed at both the client and server, with their own secret key and their peer's public key.
* 3) a pair of derived keys is created by BLAKE2b-hashing the following into 64 bytes, in this particular order:
* - the shared secret from the key exchange;
* - the public key of the server;
* - the public key of the client;
* - optional extra payload, e.g. a password in the case of PAKE.
* The first of the pair of derived keys is usually used to encrypt client-to-server communication, and the second of the pair
* is usually used to encrypt server-to-client communication.
* 4) a XChaCha20-Poly1305 (authenticated) encryption is performed using:
* - the first of the pair of derived keys as encryption key;
* - a 24 byte nonce;
* - the public key of the client as additional authenticated data.
* - a 8 byte random number as content/message.
*
* The server initiates the request by sending its public key and a 24 byte nonce that is randomly generated. Normally the side
* that sends the encrypted data sends the nonce in their packet, which would be the client on our case. However, there are
* many implementations of clients due to the admin-protocol where this is used, and we cannot guarantee that they generate a
* good enough nonce. As such the server sends one instead. The server will create a new set of keys for each session.
*
* The client receives the request, performs the key exchange, generates the derived keys and then encrypts the message. This
* message must contain some content, so it has to be filled with 8 random bytes. Once the message has been encrypted, the
* client sends their public key, the encrypted message and the message authentication code (MAC) to the server in a response.
*
* The server receives the response, performs the key exchange, generates the derived keys, decrypts the message and validates the
* message authentication code, and finally the message. It is up to the sub class to perform the final authentication checks.
*/
class X25519AuthenticationHandler {
private:
X25519SecretKey our_secret_key; ///< The secret key used by us.
X25519PublicKey our_public_key; ///< The public key used by us.
X25519Nonce nonce; ///< The nonce to prevent replay attacks.
X25519DerivedKeys derived_keys; ///< Keys derived from the authentication process.
X25519PublicKey peer_public_key; ///< The public key used by our peer.
protected:
X25519AuthenticationHandler(const X25519SecretKey &secret_key);
void SendRequest(struct Packet &p);
bool ReceiveRequest(struct Packet &p);
bool SendResponse(struct Packet &p, std::string_view derived_key_extra_payload);
NetworkAuthenticationServerHandler::ResponseResult ReceiveResponse(struct Packet &p, std::string_view derived_key_extra_payload);
std::string GetPeerPublicKey() const;
std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const;
std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const;
};
/**
* Client side handler for using X25519 without actual authentication.
*
* This follows the method described in \c X25519AuthenticationHandler, without an extra payload.
*/
class X25519KeyExchangeOnlyClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
public:
/**
* Create the handler that that one does the key exchange.
* @param secret_key The secret key to initialize this handler with.
*/
X25519KeyExchangeOnlyClientHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
virtual RequestResult ReceiveRequest(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveRequest(p) ? READY_FOR_RESPONSE : INVALID; }
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, {}); }
virtual std::string_view GetName() const override { return "X25519-KeyExchangeOnly-client"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY; }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
};
/**
* Server side handler for using X25519 without actual authentication.
*
* This follows the method described in \c X25519AuthenticationHandler, without an extra payload.
*/
class X25519KeyExchangeOnlyServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
public:
/**
* Create the handler that that one does the key exchange.
* @param secret_key The secret key to initialize this handler with.
*/
X25519KeyExchangeOnlyServerHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
virtual ResponseResult ReceiveResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveResponse(p, {}); }
virtual std::string_view GetName() const override { return "X25519-KeyExchangeOnly-server"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_KEY_EXCHANGE_ONLY; }
virtual bool CanBeUsed() const override { return true; }
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
};
/**
* Client side handler for using X25519 with a password-authenticated key exchange.
*
* This follows the method described in \c X25519AuthenticationHandler, were the password is the extra payload.
*/
class X25519PAKEClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
private:
std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> handler;
public:
/**
* Create the handler with the given password handler.
* @param secret_key The secret key to initialize this handler with.
* @param handler The handler requesting the password from the user, if required.
*/
X25519PAKEClientHandler(const X25519SecretKey &secret_key, std::shared_ptr<NetworkAuthenticationPasswordRequestHandler> handler) : X25519AuthenticationHandler(secret_key), handler(handler) {}
virtual RequestResult ReceiveRequest(struct Packet &p) override;
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, this->handler->password); }
virtual std::string_view GetName() const override { return "X25519-PAKE-client"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_PAKE; }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
};
/**
* Server side handler for using X25519 with a password-authenticated key exchange.
*
* This follows the method described in \c X25519AuthenticationHandler, were the password is the extra payload.
*/
class X25519PAKEServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
private:
const NetworkAuthenticationPasswordProvider *password_provider; ///< The password to check against.
public:
/**
* Create the handler with the given password provider.
* @param secret_key The secret key to initialize this handler with.
* @param password_provider The provider for the passwords.
*/
X25519PAKEServerHandler(const X25519SecretKey &secret_key, const NetworkAuthenticationPasswordProvider *password_provider) : X25519AuthenticationHandler(secret_key), password_provider(password_provider) {}
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
virtual ResponseResult ReceiveResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveResponse(p, this->password_provider->GetPassword()); }
virtual std::string_view GetName() const override { return "X25519-PAKE-server"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_PAKE; }
virtual bool CanBeUsed() const override { return !this->password_provider->GetPassword().empty(); }
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
};
/**
* Handler for clients using a X25519 key exchange to perform authentication via a set of authorized (public) keys of clients.
*
* This follows the method described in \c X25519AuthenticationHandler. Once all these checks have succeeded, it will
* check whether the public key of the client is in the list of authorized keys to login.
*/
class X25519AuthorizedKeyClientHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationClientHandler {
public:
/**
* Create the handler that uses the given password to check against.
* @param secret_key The secret key to initialize this handler with.
*/
X25519AuthorizedKeyClientHandler(const X25519SecretKey &secret_key) : X25519AuthenticationHandler(secret_key) {}
virtual RequestResult ReceiveRequest(struct Packet &p) override { return this->X25519AuthenticationHandler::ReceiveRequest(p) ? READY_FOR_RESPONSE : INVALID; }
virtual bool SendResponse(struct Packet &p) override { return this->X25519AuthenticationHandler::SendResponse(p, {}); }
virtual std::string_view GetName() const override { return "X25519-AuthorizedKey-client"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY; }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
static X25519SecretKey GetValidSecretKeyAndUpdatePublicKey(std::string &secret_key, std::string &public_key);
};
/**
* Handler for servers using a X25519 key exchange to perform authentication via a set of authorized (public) keys of clients.
*
* This follows the method described in \c X25519AuthenticationHandler. Once all these checks have succeeded, it will
* check whether the public key of the client is in the list of authorized keys to login.
*/
class X25519AuthorizedKeyServerHandler : protected X25519AuthenticationHandler, public NetworkAuthenticationServerHandler {
private:
const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler; ///< The handler of the authorized keys.
public:
/**
* Create the handler that uses the given authorized keys to check against.
* @param secret_key The secret key to initialize this handler with.
* @param authorized_key_handler The handler of the authorized keys.
*/
X25519AuthorizedKeyServerHandler(const X25519SecretKey &secret_key, const NetworkAuthenticationAuthorizedKeyHandler *authorized_key_handler) : X25519AuthenticationHandler(secret_key), authorized_key_handler(authorized_key_handler) {}
virtual void SendRequest(struct Packet &p) override { this->X25519AuthenticationHandler::SendRequest(p); }
virtual ResponseResult ReceiveResponse(struct Packet &p) override;
virtual std::string_view GetName() const override { return "X25519-AuthorizedKey-server"; }
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override { return NETWORK_AUTH_METHOD_X25519_AUTHORIZED_KEY; }
virtual bool CanBeUsed() const override { return this->authorized_key_handler->CanBeUsed(); }
virtual std::string GetPeerPublicKey() const override { return this->X25519AuthenticationHandler::GetPeerPublicKey(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->X25519AuthenticationHandler::CreateServerToClientEncryptionHandler(); }
};
/**
* Handler for combining a number of authentication handlers, where the failure of one of the handlers will retry with
* another handler. For example when authorized keys fail, it can still fall back to a password.
*/
class CombinedAuthenticationClientHandler : public NetworkAuthenticationClientHandler {
public:
using Handler = std::unique_ptr<NetworkAuthenticationClientHandler>; ///< The type of the inner handlers.
private:
std::vector<Handler> handlers; ///< The handlers that we can authenticate with.
NetworkAuthenticationClientHandler *current_handler = nullptr; ///< The currently active handler.
public:
/**
* Add the given sub-handler to this handler.
* @param handler The handler to add.
*/
void Add(Handler &&handler) { this->handlers.push_back(std::move(handler)); }
virtual RequestResult ReceiveRequest(struct Packet &p) override;
virtual bool SendResponse(struct Packet &p) override;
virtual std::string_view GetName() const override;
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override;
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->current_handler->CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->current_handler->CreateServerToClientEncryptionHandler(); }
};
/**
* Handler for combining a number of authentication handlers, where the failure of one of the handlers will retry with
* another handler. For example when authorized keys fail, it can still fall back to a password.
*/
class CombinedAuthenticationServerHandler : public NetworkAuthenticationServerHandler {
public:
using Handler = std::unique_ptr<NetworkAuthenticationServerHandler>; ///< The type of the inner handlers.
private:
std::vector<Handler> handlers; ///< The handlers that we can (still) authenticate with.
public:
void Add(Handler &&handler);
virtual void SendRequest(struct Packet &p) override;
virtual ResponseResult ReceiveResponse(struct Packet &p) override;
virtual std::string_view GetName() const override;
virtual NetworkAuthenticationMethod GetAuthenticationMethod() const override;
virtual bool CanBeUsed() const override;
virtual std::string GetPeerPublicKey() const override { return this->handlers.back()->GetPeerPublicKey(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateClientToServerEncryptionHandler() const override { return this->handlers.back()->CreateClientToServerEncryptionHandler(); }
virtual std::unique_ptr<NetworkEncryptionHandler> CreateServerToClientEncryptionHandler() const override { return this->handlers.back()->CreateServerToClientEncryptionHandler(); }
};
#endif /* NETWORK_CRYPTO_INTERNAL_H */

View File

@@ -67,6 +67,7 @@ bool NetworkCompanyIsPassworded(CompanyID company_id);
uint NetworkMaxCompaniesAllowed();
bool NetworkMaxCompaniesReached();
void NetworkPrintClients();
std::string_view NetworkGetPublicKeyOfClient(ClientID client_id);
void NetworkHandlePauseChange(PauseMode prev_mode, PauseMode changed_mode);
/*** Commands ran by the server ***/

View File

@@ -2114,7 +2114,7 @@ uint32_t _network_join_bytes; ///< The number of bytes we already down
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)
{
@@ -2210,16 +2210,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);
}
};
@@ -2247,11 +2243,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 ShowNetworkGiveMoneyWindow(CompanyID company);
void ShowNetworkChatQueryWindow(DestType type, int dest);
void ShowJoinStatusWindow();

View File

@@ -82,7 +82,7 @@ void QueryNetworkGameSocketHandler::Send()
*/
NetworkRecvStatus QueryNetworkGameSocketHandler::SendGameInfo()
{
auto p = std::make_unique<Packet>(PACKET_CLIENT_GAME_INFO);
auto p = std::make_unique<Packet>(this, PACKET_CLIENT_GAME_INFO);
p->Send_uint32(FIND_SERVER_EXTENDED_TOKEN);
p->Send_uint8(PACKET_SERVER_GAME_INFO_EXTENDED); // reply type
p->Send_uint16(0); // flags

View File

@@ -58,6 +58,11 @@ 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.
static NetworkAuthenticationDefaultAuthorizedKeyHandler _rcon_authorized_key_handler(_settings_client.network.rcon_authorized_keys); ///< Provides the authorized key validation for rcon.
/** Writing a savegame directly to a number of packets. */
struct PacketWriter : SaveFilter {
ServerNetworkGameSocketHandler *cs; ///< Socket we are associated with.
@@ -146,7 +151,7 @@ struct PacketWriter : SaveFilter {
/* We want to abort the saving when the socket is closed. */
if (this->cs == nullptr) SlError(STR_NETWORK_ERROR_LOSTCONNECTION);
if (this->current == nullptr) this->current = std::make_unique<Packet>(PACKET_SERVER_MAP_DATA, TCP_MTU);
if (this->current == nullptr) this->current = std::make_unique<Packet>(this->cs, PACKET_SERVER_MAP_DATA, TCP_MTU);
uint8_t *bufe = buf + size;
while (buf != bufe) {
@@ -155,7 +160,7 @@ struct PacketWriter : SaveFilter {
if (!this->current->CanWriteToPacket(1)) {
this->packets.push_back(std::move(this->current));
if (buf != bufe) this->current = std::make_unique<Packet>(PACKET_SERVER_MAP_DATA, TCP_MTU);
if (buf != bufe) this->current = std::make_unique<Packet>(this->cs, PACKET_SERVER_MAP_DATA, TCP_MTU);
}
}
@@ -173,10 +178,10 @@ struct PacketWriter : SaveFilter {
if (this->current != nullptr) this->packets.push_back(std::move(this->current));
/* Add a packet stating that this is the end to the queue. */
this->packets.push_back(std::make_unique<Packet>(PACKET_SERVER_MAP_DONE));
this->packets.push_back(std::make_unique<Packet>(this->cs, PACKET_SERVER_MAP_DONE));
/* Fast-track the size to the client. */
this->map_size_packet.reset(new Packet(PACKET_SERVER_MAP_SIZE, TCP_MTU));
this->map_size_packet.reset(new Packet(this->cs, PACKET_SERVER_MAP_SIZE, TCP_MTU));
this->map_size_packet->Send_uint32((uint32_t)this->total_size);
}
};
@@ -390,7 +395,7 @@ static void NetworkHandleCommandQueue(NetworkClientSocket *cs);
NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientInfo *ci)
{
if (ci->client_id != INVALID_CLIENT_ID) {
auto p = std::make_unique<Packet>(PACKET_SERVER_CLIENT_INFO, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_CLIENT_INFO, TCP_MTU);
p->Send_uint32(ci->client_id);
p->Send_uint8 (ci->client_playas);
p->Send_string(ci->client_name);
@@ -403,7 +408,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendClientInfo(NetworkClientIn
/** Send the client information about the server. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_GAME_INFO, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_GAME_INFO, TCP_MTU);
SerializeNetworkGameInfo(*p, GetCurrentNetworkServerGameInfo());
this->SendPacket(std::move(p));
@@ -413,7 +418,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfo()
NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfoExtended(PacketGameType reply_type, uint16_t flags, uint16_t version)
{
auto p = std::make_unique<Packet>(reply_type, TCP_MTU);
auto p = std::make_unique<Packet>(this, reply_type, TCP_MTU);
SerializeNetworkGameInfoExtended(*p, GetCurrentNetworkServerGameInfo(), flags, version);
this->SendPacket(std::move(p));
@@ -428,7 +433,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendGameInfoExtended(PacketGam
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode error, const std::string &reason)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_ERROR, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_ERROR, TCP_MTU);
p->Send_uint8(error);
if (!reason.empty()) p->Send_string(reason);
@@ -473,7 +478,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendError(NetworkErrorCode err
NetworkRecvStatus ServerNetworkGameSocketHandler::SendDesyncLog(const std::string &log)
{
for (size_t offset = 0; offset < log.size();) {
auto p = std::make_unique<Packet>(PACKET_SERVER_DESYNC_LOG, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_DESYNC_LOG, TCP_MTU);
size_t size = std::min<size_t>(log.size() - offset, TCP_MTU - 2 - p->Size());
p->Send_uint16(static_cast<uint16_t>(size));
p->Send_binary((const uint8_t *)(log.data() + offset), size);
@@ -487,18 +492,17 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendDesyncLog(const std::strin
/** Send the check for the NewGRFs. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_CHECK_NEWGRFS, TCP_MTU);
/* Invalid packet when status is anything but STATUS_INACTIVE. */
if (this->status != STATUS_INACTIVE) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
/* Invalid packet when status is anything but STATUS_IDENTIFY. */
if (this->status != STATUS_IDENTIFY) return this->CloseConnection(NETWORK_RECV_STATUS_MALFORMED_PACKET);
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);
p->Send_uint32(GetGRFConfigListNonStaticCount(_grfconfig));
for (const GRFConfig *c = _grfconfig; c != nullptr; c = c->next) {
if (!HasBit(c->flags, GCF_STATIC)) SerializeGRFIdentifier(*p, c->ident);
@@ -509,27 +513,34 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGRFCheck()
}
/** Request the game password. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
NetworkRecvStatus ServerNetworkGameSocketHandler::SendAuthRequest()
{
/* 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);
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;
const NetworkGameKeys &keys = this->GetKeys();
if (this->authentication_handler == nullptr) {
this->authentication_handler = NetworkAuthenticationServerHandler::Create(&_password_provider, &_authorized_key_handler);
}
auto p = std::make_unique<Packet>(PACKET_SERVER_NEED_GAME_PASSWORD, TCP_MTU);
static_assert(std::tuple_size<decltype(keys.x25519_pub_key)>::value == 32);
p->Send_binary(keys.x25519_pub_key);
p->Send_string(_settings_client.network.network_id);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_AUTH_REQUEST, TCP_MTU);
this->authentication_handler->SendRequest(*p);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
/** Notify the client that the authentication has completed and tell that for the remainder of this socket encryption is enabled. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendEnableEncryption()
{
/* 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_ENABLE_ENCRYPTION, TCP_MTU);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -537,8 +548,8 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedGamePassword()
/** Request the company password. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
{
/* 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);
this->status = STATUS_AUTH_COMPANY;
@@ -550,7 +561,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendNeedCompanyPassword()
/* Reset 'lag' counters */
this->last_frame = this->last_frame_server = _frame_counter;
auto p = std::make_unique<Packet>(PACKET_SERVER_NEED_COMPANY_PASSWORD, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_NEED_COMPANY_PASSWORD, TCP_MTU);
p->Send_uint32(_settings_game.game_creation.generation_seed);
p->Send_string(_network_company_server_id);
this->SendPacket(std::move(p));
@@ -572,7 +583,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWelcome()
const NetworkGameKeys &keys = this->GetKeys();
auto p = std::make_unique<Packet>(PACKET_SERVER_WELCOME, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_WELCOME, TCP_MTU);
p->Send_uint32(this->client_id);
p->Send_uint32(_settings_game.game_creation.generation_seed);
static_assert(std::tuple_size<decltype(keys.x25519_pub_key)>::value == 32);
@@ -602,7 +613,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendWait()
if (new_cs->GetInfo()->join_date < this->GetInfo()->join_date || (new_cs->GetInfo()->join_date == this->GetInfo()->join_date && new_cs->client_id < this->client_id)) waiting++;
}
auto p = std::make_unique<Packet>(PACKET_SERVER_WAIT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_WAIT, TCP_MTU);
p->Send_uint8(waiting);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
@@ -648,7 +659,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
this->savegame = std::make_shared<PacketWriter>(this);
/* Now send the _frame_counter and how many packets are coming */
auto p = std::make_unique<Packet>(PACKET_SERVER_MAP_BEGIN, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_MAP_BEGIN, TCP_MTU);
p->Send_uint32(_frame_counter);
this->SendPacket(std::move(p));
@@ -687,7 +698,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMap()
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendJoin(ClientID client_id)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_JOIN, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_JOIN, TCP_MTU);
p->Send_uint32(client_id);
@@ -698,7 +709,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendJoin(ClientID client_id)
/** Tell the client that they may run to a particular frame. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendFrame()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_FRAME, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_FRAME, TCP_MTU);
p->Send_uint32(_frame_counter);
p->Send_uint32(_frame_counter_max);
#ifdef ENABLE_NETWORK_SYNC_EVERY_FRAME
@@ -719,7 +730,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendFrame()
/** Request the client to sync. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendSync()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_SYNC, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_SYNC, TCP_MTU);
p->Send_uint32(_frame_counter);
p->Send_uint32(_sync_seed_1);
@@ -734,7 +745,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendSync()
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCommand(const CommandPacket &cp)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_COMMAND, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_COMMAND, TCP_MTU);
this->NetworkGameSocketHandler::SendCommand(*p, cp);
p->Send_uint32(cp.frame);
@@ -756,7 +767,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendChat(NetworkAction action,
{
if (this->status < STATUS_PRE_ACTIVE) return NETWORK_RECV_STATUS_OKAY;
auto p = std::make_unique<Packet>(PACKET_SERVER_CHAT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_CHAT, TCP_MTU);
p->Send_uint8 (action);
p->Send_uint32(client_id);
@@ -779,7 +790,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendExternalChat(const std::st
{
if (this->status < STATUS_PRE_ACTIVE) return NETWORK_RECV_STATUS_OKAY;
auto p = std::make_unique<Packet>(PACKET_SERVER_EXTERNAL_CHAT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_EXTERNAL_CHAT, TCP_MTU);
p->Send_string(source);
p->Send_uint16(colour);
@@ -797,7 +808,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendExternalChat(const std::st
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendErrorQuit(ClientID client_id, NetworkErrorCode errorno)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_ERROR_QUIT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_ERROR_QUIT, TCP_MTU);
p->Send_uint32(client_id);
p->Send_uint8 (errorno);
@@ -812,7 +823,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendErrorQuit(ClientID client_
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendQuit(ClientID client_id)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_QUIT, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_QUIT, TCP_MTU);
p->Send_uint32(client_id);
@@ -823,7 +834,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendQuit(ClientID client_id)
/** Tell the client we're shutting down. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendShutdown()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_SHUTDOWN, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_SHUTDOWN, TCP_MTU);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -831,7 +842,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendShutdown()
/** Tell the client we're starting a new game. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendNewGame()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_NEWGAME, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_NEWGAME, TCP_MTU);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -857,10 +868,10 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConResult(uint16_t colour
std::array<uint8_t, 24> nonce;
RandomBytesWithFallback(nonce);
/* Encrypt in place, use first half of hash as key */
/* Encrypt in place */
crypto_aead_lock(message.data(), mac.data(), this->rcon_reply_key, nonce.data(), nullptr, 0, message.data(), message.size());
auto p = std::make_unique<Packet>(PACKET_SERVER_RCON, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_RCON, TCP_MTU);
static_assert(nonce.size() == 24);
static_assert(mac.size() == 16);
p->Send_binary(nonce);
@@ -876,7 +887,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConResult(uint16_t colour
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConDenied()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_RCON, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_RCON, TCP_MTU);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
}
@@ -888,7 +899,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendRConDenied()
*/
NetworkRecvStatus ServerNetworkGameSocketHandler::SendMove(ClientID client_id, CompanyID company_id)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_MOVE, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_MOVE, TCP_MTU);
p->Send_uint32(client_id);
p->Send_uint8(company_id);
@@ -899,7 +910,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendMove(ClientID client_id, C
/** Send an update about the company password states. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_COMPANY_UPDATE, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_COMPANY_UPDATE, TCP_MTU);
static_assert(sizeof(_network_company_passworded) <= sizeof(uint16_t));
p->Send_uint16(_network_company_passworded);
@@ -910,7 +921,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendCompanyUpdate()
/** Send an update about the max company/spectator counts. */
NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
{
auto p = std::make_unique<Packet>(PACKET_SERVER_CONFIG_UPDATE, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_CONFIG_UPDATE, TCP_MTU);
p->Send_uint8(_settings_client.network.max_companies);
p->Send_string(_settings_client.network.server_name);
@@ -920,7 +931,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::SendConfigUpdate()
NetworkRecvStatus ServerNetworkGameSocketHandler::SendSettingsAccessUpdate(bool ok)
{
auto p = std::make_unique<Packet>(PACKET_SERVER_SETTINGS_ACCESS, TCP_MTU);
auto p = std::make_unique<Packet>(this, PACKET_SERVER_SETTINGS_ACCESS, TCP_MTU);
p->Send_bool(ok);
this->SendPacket(std::move(p));
return NETWORK_RECV_STATUS_OKAY;
@@ -950,7 +961,7 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_NEWGRFS_CHECKED
return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
}
return this->SendNeedGamePassword();
return this->SendNeedCompanyPassword();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p)
@@ -974,6 +985,13 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p)
return this->SendError(NETWORK_ERROR_WRONG_REVISION);
}
return this->SendAuthRequest();
}
NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_IDENTIFY(Packet &p)
{
if (this->status != STATUS_IDENTIFY) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
std::string client_name = p.Recv_string(NETWORK_CLIENT_NAME_LENGTH);
CompanyID playas = (Owner)p.Recv_uint8();
@@ -1024,22 +1042,52 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_JOIN(Packet &p)
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);
}
/* Check game password. Allow joining if we cleared the password meanwhile */
if (!_settings_client.network.server_password.empty()) {
NetworkSharedSecrets ss;
if (!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.server_password, nullptr, 0)) {
/* Password is invalid */
return this->SendError(NETWORK_ERROR_WRONG_PASSWORD);
}
auto authentication_method = this->authentication_handler->GetAuthenticationMethod();
switch (this->authentication_handler->ReceiveResponse(p)) {
case NetworkAuthenticationServerHandler::AUTHENTICATED:
break;
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->SendEnableEncryption();
if (status != NETWORK_RECV_STATUS_OKAY) return status;
this->peer_public_key = this->authentication_handler->GetPeerPublicKey();
this->receive_encryption_handler = this->authentication_handler->CreateClientToServerEncryptionHandler();
this->send_encryption_handler = this->authentication_handler->CreateServerToClientEncryptionHandler();
this->authentication_handler = nullptr;
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)
@@ -1664,17 +1712,28 @@ NetworkRecvStatus ServerNetworkGameSocketHandler::Receive_CLIENT_RCON(Packet &p)
{
if (this->status != STATUS_ACTIVE) return this->SendError(NETWORK_ERROR_NOT_EXPECTED);
if (_settings_client.network.rcon_password.empty()) {
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
}
std::string command;
NetworkSharedSecrets ss;
if (!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.rcon_password, &command, NETWORK_RCONCOMMAND_LENGTH)) {
DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
bool done = false;
if (_rcon_authorized_key_handler.IsAllowed(this->peer_public_key)) {
/* We are allowed, try to handle using '*' password */
size_t saved_pos = p.GetDeserialisationPosition();
if (this->ParseKeyPasswordPacket(p, ss, "*", &command, NETWORK_RCONCOMMAND_LENGTH)) {
done = true;
} else {
p.GetDeserialisationPosition() = saved_pos;
}
}
if (!done) {
if (_settings_client.network.rcon_password.empty()) {
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
}
if (!this->ParseKeyPasswordPacket(p, ss, _settings_client.network.rcon_password, &command, NETWORK_RCONCOMMAND_LENGTH)) {
DEBUG(net, 0, "[rcon] wrong password from client-id %d", this->client_id);
NetworkServerSendRconDenied(this->client_id);
return this->HandleAuthFailure(this->rcon_auth_failures);
}
}
DEBUG(net, 3, "[rcon] Client-id %d executed: %s", this->client_id, command.c_str());
@@ -1729,8 +1788,9 @@ const char *ServerNetworkGameSocketHandler::GetClientStatusName(ClientStatus sta
{
static const char* _client_status_names[] {
"INACTIVE",
"NEWGRFS_CHECK",
"AUTH_GAME",
"IDENTIFY",
"NEWGRFS_CHECK",
"AUTH_COMPANY",
"AUTHORIZED",
"MAP_WAIT",
@@ -2023,6 +2083,7 @@ void NetworkServer_Tick(bool send_frame)
break;
case NetworkClientSocket::STATUS_INACTIVE:
case NetworkClientSocket::STATUS_IDENTIFY:
case NetworkClientSocket::STATUS_NEWGRFS_CHECK:
case NetworkClientSocket::STATUS_AUTHORIZED:
/* NewGRF check and authorized states should be handled almost instantly.
@@ -2198,8 +2259,9 @@ void NetworkServerShowStatusToConsole()
{
static const char * const stat_str[] = {
"inactive",
"checking NewGRFs",
"authorizing (server password)",
"identifing client",
"checking NewGRFs",
"authorizing (company password)",
"authorized",
"waiting",
@@ -2431,6 +2493,18 @@ void NetworkPrintClients()
}
}
/**
* Get the public key of the client with the given id.
* @param client_id The id of the client.
* @return View of the public key, which is empty when the client does not exist.
*/
std::string_view NetworkGetPublicKeyOfClient(ClientID client_id)
{
auto socket = NetworkClientSocket::GetByClientID(client_id);
return socket == nullptr ? "" : socket->GetPeerPublicKey();
}
/**
* Perform all the server specific administration of a new company.
* @param c The newly created company; can't be nullptr.

View File

@@ -27,9 +27,13 @@ class ServerNetworkGameSocketHandler : public NetworkClientSocketPool::PoolItem<
uint8_t *rcon_reply_key = nullptr;
protected:
std::unique_ptr<class NetworkAuthenticationServerHandler> authentication_handler; ///< The handler for the authentication.
std::string peer_public_key; ///< The public key of our client.
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_SETTINGS_PASSWORD(Packet &p) override;
NetworkRecvStatus Receive_CLIENT_GETMAP(Packet &p) override;
@@ -52,7 +56,8 @@ protected:
NetworkRecvStatus SendGameInfoExtended(PacketGameType reply_type, uint16_t flags, uint16_t version);
NetworkRecvStatus SendNewGRFCheck();
NetworkRecvStatus SendWelcome();
NetworkRecvStatus SendNeedGamePassword();
NetworkRecvStatus SendAuthRequest();
NetworkRecvStatus SendEnableEncryption();
NetworkRecvStatus SendNeedCompanyPassword();
bool ParseKeyPasswordPacket(Packet &p, NetworkSharedSecrets &ss, const std::string &password, std::string *payload, size_t length);
@@ -61,8 +66,9 @@ public:
/** Status of a client */
enum ClientStatus {
STATUS_INACTIVE, ///< The client is not connected nor active.
STATUS_NEWGRFS_CHECK, ///< The client is checking NewGRFs.
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_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.
@@ -150,6 +156,7 @@ public:
}
const char *GetClientIP();
std::string_view GetPeerPublicKey() const { return this->peer_public_key; }
static ServerNetworkGameSocketHandler *GetByClientID(ClientID client_id);
};

View File

@@ -86,7 +86,7 @@ std::unique_ptr<ClientNetworkStunSocketHandler> ClientNetworkStunSocketHandler::
stun_handler->Connect(token, family);
auto p = std::make_unique<Packet>(PACKET_STUN_SERCLI_STUN);
auto p = std::make_unique<Packet>(stun_handler.get(), PACKET_STUN_SERCLI_STUN);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(token);
p->Send_uint8(family);

View File

@@ -100,7 +100,7 @@ void ClientNetworkTurnSocketHandler::Connect()
{
auto turn_handler = std::make_unique<ClientNetworkTurnSocketHandler>(token, tracking_number, connection_string);
auto p = std::make_unique<Packet>(PACKET_TURN_SERCLI_CONNECT);
auto p = std::make_unique<Packet>(turn_handler.get(), PACKET_TURN_SERCLI_CONNECT);
p->Send_uint8(NETWORK_COORDINATOR_VERSION);
p->Send_string(ticket);

View File

@@ -146,6 +146,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,
};

View File

@@ -60,9 +60,9 @@ struct UDPSocket {
static UDPSocket _udp_client("Client"); ///< udp client socket
static UDPSocket _udp_server("Server"); ///< udp server socket
static Packet PrepareUdpClientFindServerPacket()
static Packet PrepareUdpClientFindServerPacket(NetworkUDPSocketHandler *socket)
{
Packet p(PACKET_UDP_CLIENT_FIND_SERVER);
Packet p(socket, PACKET_UDP_CLIENT_FIND_SERVER);
p.Send_uint32(FIND_SERVER_EXTENDED_TOKEN);
p.Send_uint16(0); // flags
p.Send_uint16(0); // version
@@ -92,7 +92,7 @@ void ServerNetworkUDPSocketHandler::Receive_CLIENT_FIND_SERVER(Packet &p, Networ
return;
}
Packet packet(PACKET_UDP_SERVER_RESPONSE);
Packet packet(this, PACKET_UDP_SERVER_RESPONSE);
this->SendPacket(packet, client_addr);
DEBUG(net, 7, "Queried from %s", client_addr.GetHostname());
@@ -103,7 +103,7 @@ void ServerNetworkUDPSocketHandler::Reply_CLIENT_FIND_SERVER_extended(Packet &p,
[[maybe_unused]] uint16_t flags = p.Recv_uint16();
uint16_t version = p.Recv_uint16();
Packet packet(PACKET_UDP_EX_SERVER_RESPONSE);
Packet packet(this, PACKET_UDP_EX_SERVER_RESPONSE);
this->SendPacket(packet, client_addr);
DEBUG(net, 7, "Queried (extended: %u) from %s", version, client_addr.GetHostname());
@@ -140,7 +140,7 @@ static void NetworkUDPBroadCast(NetworkUDPSocketHandler *socket)
for (NetworkAddress &addr : _broadcast_list) {
DEBUG(net, 5, "Broadcasting to %s", addr.GetHostname());
Packet p = PrepareUdpClientFindServerPacket();
Packet p = PrepareUdpClientFindServerPacket(socket);
socket->SendPacket(p, addr, true, true);
}
}

View File

@@ -197,6 +197,8 @@ private:
"newgrf",
"servers",
"server_bind_addresses",
"server_authorized_keys",
"rcon_authorized_keys",
};
public:
@@ -2778,6 +2780,8 @@ static void HandlePrivateSettingDescs(IniFile &private_ini, SettingDescProc *pro
proc_list(private_ini, "server_bind_addresses", _network_bind_list);
proc_list(private_ini, "servers", _network_host_list);
proc_list(private_ini, "bans", _network_ban_list);
proc_list(private_ini, "server_authorized_keys", _settings_client.network.server_authorized_keys);
proc_list(private_ini, "rcon_authorized_keys", _settings_client.network.rcon_authorized_keys);
}
}

View File

@@ -435,10 +435,14 @@ struct NetworkSettings {
std::string server_invite_code_secret; ///< Secret to proof we got this invite code from the Game Coordinator.
std::string server_name; ///< name of the server
std::string server_password; ///< password for joining this server
std::vector<std::string> server_authorized_keys; ///< Public keys of clients that are authorized to connect to the game.
std::string rcon_password; ///< password for rconsole (server side)
std::vector<std::string> rcon_authorized_keys; ///< Public keys of clients that are authorized to use the rconsole (server side).
std::string admin_password; ///< password for the admin network
std::string settings_password; ///< password for game settings (server side)
std::string client_name; ///< name of the player (as client)
std::string client_secret_key; ///< The secret key of the client for authorized key logins.
std::string client_public_key; ///< The public key of the client for authorized key logins.
std::string default_company_pass; ///< default password for new companies in encrypted form
std::string connect_to_ip; ///< default for the "Add server" query
std::string network_id; ///< network ID for servers

View File

@@ -75,6 +75,24 @@ def = nullptr
pre_cb = ReplaceAsteriskWithEmptyPassword
cat = SC_EXPERT
[SDTC_SSTR]
var = network.client_secret_key
type = SLE_STR
length = NETWORK_SECRET_KEY_LENGTH
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = nullptr
; Prevent the user from setting the secret key from the console using 'setting'
pre_cb = [](auto) { return false; }
[SDTC_SSTR]
var = network.client_public_key
type = SLE_STR
length = NETWORK_SECRET_KEY_LENGTH
flags = SF_NOT_IN_SAVE | SF_NO_NETWORK_SYNC
def = nullptr
; Prevent the user from setting the public key from the console using 'setting'
pre_cb = [](auto) { return false; }
[SDTC_SSTR]
var = network.default_company_pass
type = SLE_STR

View File

@@ -10,6 +10,7 @@ add_test_files(
string_func.cpp
strings_func.cpp
test_main.cpp
test_network_crypto.cpp
test_script_admin.cpp
test_window_desc.cpp
)

View File

@@ -0,0 +1,271 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file test_network_crypto.cpp Tests for network related crypto functions. */
#include "../stdafx.h"
#include "../3rdparty/catch2/catch.hpp"
#include "../core/format.hpp"
#include "../network/network_crypto_internal.h"
#include "../network/core/packet.h"
#include "../string_func.h"
#include <set>
/* The length of the hexadecimal representation of a X25519 key must fit in the key length. */
static_assert(NETWORK_SECRET_KEY_LENGTH >= X25519_KEY_SIZE * 2 + 1);
class MockNetworkSocketHandler : public NetworkSocketHandler {
public:
MockNetworkSocketHandler(std::unique_ptr<NetworkEncryptionHandler> &&receive = {}, std::unique_ptr<NetworkEncryptionHandler> &&send = {})
{
this->receive_encryption_handler = std::move(receive);
this->send_encryption_handler = std::move(send);
}
};
static MockNetworkSocketHandler mock_socket_handler;
static std::tuple<Packet, bool> CreatePacketForReading(Packet &source, MockNetworkSocketHandler *socket_handler)
{
source.PrepareToSend();
Packet dest(Packet::ReadTag{}, socket_handler, COMPAT_MTU, source.Size());
auto transfer_in = [](Packet &source, char *dest_data, size_t length) {
auto transfer_out = [](char *dest_data, const char *source_data, size_t length) {
std::copy(source_data, source_data + length, dest_data);
return length;
};
return source.TransferOutWithLimit(transfer_out, length, dest_data);
};
dest.TransferIn(transfer_in, source);
bool valid = dest.PrepareToRead();
dest.Recv_uint8(); // Ignore the type
return { dest, valid };
}
class TestPasswordRequestHandler : public NetworkAuthenticationPasswordRequestHandler {
private:
std::string password;
public:
TestPasswordRequestHandler(std::string &password) : password(password) {}
void SendResponse() override {}
void AskUserForPassword(std::shared_ptr<NetworkAuthenticationPasswordRequest> request) override { request->Reply(this->password); }
};
static void TestAuthentication(NetworkAuthenticationServerHandler &server, NetworkAuthenticationClientHandler &client,
NetworkAuthenticationServerHandler::ResponseResult expected_response_result,
NetworkAuthenticationClientHandler::RequestResult expected_request_result)
{
Packet request(&mock_socket_handler, PacketType{});
server.SendRequest(request);
bool valid;
std::tie(request, valid) = CreatePacketForReading(request, &mock_socket_handler);
CHECK(valid);
CHECK(client.ReceiveRequest(request) == expected_request_result);
Packet response(&mock_socket_handler, PacketType{});
client.SendResponse(response);
std::tie(response, valid) = CreatePacketForReading(response, &mock_socket_handler);
CHECK(valid);
CHECK(server.ReceiveResponse(response) == expected_response_result);
}
TEST_CASE("Authentication_KeyExchangeOnly")
{
X25519KeyExchangeOnlyServerHandler server(X25519SecretKey::CreateRandom());
X25519KeyExchangeOnlyClientHandler client(X25519SecretKey::CreateRandom());
TestAuthentication(server, client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
}
static void TestAuthenticationPAKE(std::string server_password, std::string client_password,
NetworkAuthenticationServerHandler::ResponseResult expected_response_result)
{
NetworkAuthenticationDefaultPasswordProvider server_password_provider(server_password);
X25519PAKEServerHandler server(X25519SecretKey::CreateRandom(), &server_password_provider);
X25519PAKEClientHandler client(X25519SecretKey::CreateRandom(), std::make_shared<TestPasswordRequestHandler>(client_password));
TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
}
TEST_CASE("Authentication_PAKE")
{
SECTION("Correct password") {
TestAuthenticationPAKE("sikrit", "sikrit", NetworkAuthenticationServerHandler::AUTHENTICATED);
}
SECTION("Empty password") {
TestAuthenticationPAKE("", "", NetworkAuthenticationServerHandler::AUTHENTICATED);
}
SECTION("Wrong password") {
TestAuthenticationPAKE("sikrit", "secret", NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
}
}
static void TestAuthenticationAuthorizedKey(const X25519SecretKey &client_secret_key, const X25519PublicKey &server_expected_public_key,
NetworkAuthenticationServerHandler::ResponseResult expected_response_result)
{
std::vector<std::string> authorized_keys;
authorized_keys.emplace_back(FormatArrayAsHex(server_expected_public_key));
NetworkAuthenticationDefaultAuthorizedKeyHandler authorized_key_handler(authorized_keys);
X25519AuthorizedKeyServerHandler server(X25519SecretKey::CreateRandom(), &authorized_key_handler);
X25519AuthorizedKeyClientHandler client(client_secret_key);
TestAuthentication(server, client, expected_response_result, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
}
TEST_CASE("Authentication_AuthorizedKey")
{
auto client_secret_key = X25519SecretKey::CreateRandom();
auto valid_client_public_key = client_secret_key.CreatePublicKey();
auto invalid_client_public_key = X25519SecretKey::CreateRandom().CreatePublicKey();
SECTION("Correct public key") {
TestAuthenticationAuthorizedKey(client_secret_key, valid_client_public_key, NetworkAuthenticationServerHandler::AUTHENTICATED);
}
SECTION("Incorrect public key") {
TestAuthenticationAuthorizedKey(client_secret_key, invalid_client_public_key, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED);
}
}
TEST_CASE("Authentication_Combined")
{
auto client_secret_key = X25519SecretKey::CreateRandom();
std::string client_secret_key_str = FormatArrayAsHex(client_secret_key);
auto client_public_key = client_secret_key.CreatePublicKey();
std::string client_public_key_str = FormatArrayAsHex(client_public_key);
std::vector<std::string> valid_authorized_keys;
valid_authorized_keys.emplace_back(client_public_key_str);
NetworkAuthenticationDefaultAuthorizedKeyHandler valid_authorized_key_handler(valid_authorized_keys);
std::vector<std::string> invalid_authorized_keys;
invalid_authorized_keys.emplace_back("not-a-valid-authorized-key");
NetworkAuthenticationDefaultAuthorizedKeyHandler invalid_authorized_key_handler(invalid_authorized_keys);
std::vector<std::string> no_authorized_keys;
NetworkAuthenticationDefaultAuthorizedKeyHandler no_authorized_key_handler(no_authorized_keys);
std::string no_password = "";
NetworkAuthenticationDefaultPasswordProvider no_password_provider(no_password);
std::string valid_password = "sikrit";
NetworkAuthenticationDefaultPasswordProvider valid_password_provider(valid_password);
std::string invalid_password = "secret";
NetworkAuthenticationDefaultPasswordProvider invalid_password_provider(invalid_password);
auto client = NetworkAuthenticationClientHandler::Create(std::make_shared<TestPasswordRequestHandler>(valid_password), client_secret_key_str, client_public_key_str);
SECTION("Invalid authorized keys, invalid password") {
auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &invalid_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
}
SECTION("Invalid authorized keys, valid password") {
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &invalid_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::RETRY_NEXT_METHOD, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
}
SECTION("Valid authorized keys, valid password") {
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &valid_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
}
SECTION("No authorized keys, invalid password") {
auto server = NetworkAuthenticationServerHandler::Create(&invalid_password_provider, &no_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::NOT_AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
}
SECTION("No authorized keys, valid password") {
auto server = NetworkAuthenticationServerHandler::Create(&valid_password_provider, &no_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::AWAIT_USER_INPUT);
}
SECTION("No authorized keys, no password") {
auto server = NetworkAuthenticationServerHandler::Create(&no_password_provider, &no_authorized_key_handler);
TestAuthentication(*server, *client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
}
}
static void CheckEncryption(MockNetworkSocketHandler *sending_socket_handler, MockNetworkSocketHandler *receiving_socket_handler)
{
PacketType sent_packet_type{ 1 };
uint64_t sent_value = 0x1234567890ABCDEF;
std::set<PacketType> encrypted_packet_types;
for (int i = 0; i < 10; i++) {
Packet request(sending_socket_handler, sent_packet_type);
request.Send_uint64(sent_value);
auto [response, valid] = CreatePacketForReading(request, receiving_socket_handler);
CHECK(valid);
CHECK(response.Recv_uint64() == sent_value);
encrypted_packet_types.insert(request.GetPacketType());
}
/*
* Check whether it looks like encryption has happened. This is done by checking the value
* of the packet type after encryption. If after a few iterations more than one encrypted
* value has been seen, then we know that some type of encryption/scrambling is happening.
*
* Technically this check could fail erroneously when 16 subsequent encryptions yield the
* same encrypted packet type. However, with encryption that byte should have random value
* value, so the chance of this happening are tiny given enough iterations.
* Roughly in the order of 2**((iterations - 1) * 8), which with 10 iterations is in the
* one-in-sextillion (10**21) order of magnitude.
*/
CHECK(encrypted_packet_types.size() != 1);
}
TEST_CASE("Encryption handling")
{
X25519KeyExchangeOnlyServerHandler server(X25519SecretKey::CreateRandom());
X25519KeyExchangeOnlyClientHandler client(X25519SecretKey::CreateRandom());
TestAuthentication(server, client, NetworkAuthenticationServerHandler::AUTHENTICATED, NetworkAuthenticationClientHandler::READY_FOR_RESPONSE);
MockNetworkSocketHandler server_socket_handler(server.CreateClientToServerEncryptionHandler(), server.CreateServerToClientEncryptionHandler());
MockNetworkSocketHandler client_socket_handler(client.CreateServerToClientEncryptionHandler(), client.CreateClientToServerEncryptionHandler());
SECTION("Encyption happening client -> server") {
CheckEncryption(&client_socket_handler, &server_socket_handler);
}
SECTION("Encyption happening server -> client") {
CheckEncryption(&server_socket_handler, &client_socket_handler);
}
SECTION("Unencrypted packet sent causes invalid read packet") {
Packet request(&mock_socket_handler, PacketType{});
request.Send_uint64(0);
auto [response, valid] = CreatePacketForReading(request, &client_socket_handler);
CHECK(!valid);
}
}