diff options
Diffstat (limited to 'src/network')
-rw-r--r-- | src/network/clientopcodes.cpp | 38 | ||||
-rw-r--r-- | src/network/clientpackethandler.cpp | 99 | ||||
-rw-r--r-- | src/network/networkpacket.h | 4 | ||||
-rw-r--r-- | src/network/networkprotocol.h | 120 | ||||
-rw-r--r-- | src/network/serveropcodes.cpp | 40 | ||||
-rw-r--r-- | src/network/serverpackethandler.cpp | 472 |
6 files changed, 603 insertions, 170 deletions
diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index 556e8d0c0..3364de8c5 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -28,8 +28,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = null_command_handler, // 0x01 { "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02 { "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03 - null_command_handler, // 0x04 - null_command_handler, // 0x05 + { "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04 + { "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05 null_command_handler, // 0x06 null_command_handler, // 0x07 null_command_handler, // 0x08 @@ -108,6 +108,19 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51 { "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52 { "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53 + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + null_command_handler, + { "TOCLIENT_SRP_BYTES_S_B", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_SrpBytesSandB }, // 0x60 }; const static ServerCommandFactory null_command_factory = { "TOSERVER_NULL", 0, false }; @@ -116,7 +129,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { null_command_factory, // 0x00 null_command_factory, // 0x01 - null_command_factory, // 0x02 + { "TOSERVER_INIT", 1, false }, // 0x02 null_command_factory, // 0x03 null_command_factory, // 0x04 null_command_factory, // 0x05 @@ -129,7 +142,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = null_command_factory, // 0x0c null_command_factory, // 0x0d null_command_factory, // 0x0e - { "TOSERVER_INIT", 1, false }, // 0x0F + null_command_factory, // 0x0F { "TOSERVER_INIT_LEGACY", 1, false }, // 0x10 { "TOSERVER_INIT2", 1, true }, // 0x11 null_command_factory, // 0x12 @@ -175,11 +188,26 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b { "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c - { "TOSERVER_PASSWORD", 0, true }, // 0x3d + null_command_factory, // 0x3d null_command_factory, // 0x3e null_command_factory, // 0x3f { "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41 { "TOSERVER_BREATH", 0, true }, // 0x42 { "TOSERVER_CLIENT_READY", 0, true }, // 0x43 + null_command_factory, // 0x44 + null_command_factory, // 0x45 + null_command_factory, // 0x46 + null_command_factory, // 0x47 + null_command_factory, // 0x48 + null_command_factory, // 0x49 + null_command_factory, // 0x4a + null_command_factory, // 0x4b + null_command_factory, // 0x4c + null_command_factory, // 0x4d + null_command_factory, // 0x4e + null_command_factory, // 0x4f + { "TOSERVER_FIRST_SRP", 1, true }, // 0x50 + { "TOSERVER_SRP_BYTES_A", 1, true }, // 0x51 + { "TOSERVER_SRP_BYTES_M", 1, true }, // 0x52 }; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index bddf8f6fd..2106e4368 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "strfnd.h" #include "network/clientopcodes.h" #include "util/serialize.h" +#include "util/srp.h" void Client::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -44,10 +45,16 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) return; u8 deployed; - *pkt >> deployed; + u32 auth_mechs; + std::string username_legacy; // for case insensitivity + *pkt >> deployed >> auth_mechs >> username_legacy; + + // Chose an auth method we support + AuthMechanism chosen_auth_mechanism = choseAuthMech(auth_mechs); infostream << "Client: TOCLIENT_HELLO received with " - "deployed=" << ((int)deployed & 0xff) << std::endl; + "deployed=" << ((int)deployed & 0xff) << ", auth_mechs=" + << auth_mechs << ", chosen=" << chosen_auth_mechanism << std::endl; if (!ser_ver_supported(deployed)) { infostream << "Client: TOCLIENT_HELLO: Server sent " @@ -56,14 +63,43 @@ void Client::handleCommand_Hello(NetworkPacket* pkt) } m_server_ser_ver = deployed; + m_proto_ver = deployed; + + //TODO verify that username_legacy matches sent username, only + // differs in casing (make both uppercase and compare) + // This is only neccessary though when we actually want to add casing support + + if (m_chosen_auth_mech != AUTH_MECHANISM_NONE) { + // we recieved a TOCLIENT_HELLO while auth was already going on + errorstream << "Client: TOCLIENT_HELLO while auth was already going on" + << "(chosen_mech=" << m_chosen_auth_mech << ")." << std::endl; + if ((m_chosen_auth_mech == AUTH_MECHANISM_SRP) + || (m_chosen_auth_mech == AUTH_MECHANISM_LEGACY_PASSWORD)) { + srp_user_delete((SRPUser *) m_auth_data); + m_auth_data = 0; + } + } + + // Authenticate using that method, or abort if there wasn't any method found + if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) { + startAuth(chosen_auth_mechanism); + } else { + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + m_access_denied = true; + m_access_denied_reason = "Unknown"; + m_con.Disconnect(); + } - // @ TODO auth to server } void Client::handleCommand_AuthAccept(NetworkPacket* pkt) { + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + deleteAuthData(); + v3f playerpos; - *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval; + *pkt >> playerpos >> m_map_seed >> m_recommended_send_interval + >> m_sudo_auth_methods; playerpos -= v3f(0, BS / 2, 0); @@ -82,7 +118,28 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt) m_state = LC_Init; } +void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt) +{ + m_chosen_auth_mech = AUTH_MECHANISM_NONE; + deleteAuthData(); + + m_password = m_new_password; + + verbosestream << "Client: Recieved TOCLIENT_ACCEPT_SUDO_MODE." << std::endl; + // send packet to actually set the password + startAuth(AUTH_MECHANISM_FIRST_SRP); + + // reset again + m_chosen_auth_mech = AUTH_MECHANISM_NONE; +} +void Client::handleCommand_DenySudoMode(NetworkPacket* pkt) +{ + m_chat_queue.push(L"Password change denied. Password NOT changed."); + // reset everything and be sad + deleteAuthData(); + m_chosen_auth_mech = AUTH_MECHANISM_NONE; +} void Client::handleCommand_InitLegacy(NetworkPacket* pkt) { if (pkt->getSize() < 1) @@ -101,6 +158,7 @@ void Client::handleCommand_InitLegacy(NetworkPacket* pkt) } m_server_ser_ver = deployed; + m_proto_ver = deployed; // Get player position v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0); @@ -1105,3 +1163,36 @@ void Client::handleCommand_EyeOffset(NetworkPacket* pkt) *pkt >> player->eye_offset_first >> player->eye_offset_third; } + +void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt) +{ + if ((m_chosen_auth_mech != AUTH_MECHANISM_LEGACY_PASSWORD) + && (m_chosen_auth_mech != AUTH_MECHANISM_SRP)) { + errorstream << "Client: Recieved SRP S_B login message," + << " but wasn't supposed to (chosen_mech=" + << m_chosen_auth_mech << ")." << std::endl; + return; + } + + char *bytes_M = 0; + size_t len_M = 0; + SRPUser *usr = (SRPUser *) m_auth_data; + std::string s; + std::string B; + *pkt >> s >> B; + + infostream << "Client: Recieved TOCLIENT_SRP_BYTES_S_B." << std::endl; + + srp_user_process_challenge(usr, (const unsigned char *) s.c_str(), s.size(), + (const unsigned char *) B.c_str(), B.size(), + (unsigned char **) &bytes_M, &len_M); + + if ( !bytes_M ) { + errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl; + return; + } + + NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_M, 0); + resp_pkt << std::string(bytes_M, len_M); + Send(&resp_pkt); +} diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index 0d2015e7f..0408b9cac 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -41,8 +41,10 @@ public: u16 getPeerId() { return m_peer_id; } u16 getCommand() { return m_command; } - // Data extractors + // Returns a c-string without copying. + // A better name for this would be getRawString() char* getString(u32 from_offset); + // major difference to putCString(): doesn't write len into the buffer void putRawString(const char* src, u32 len); NetworkPacket& operator>>(std::string& dst); diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index ba934957d..ba12a206e 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -120,11 +120,15 @@ with this program; if not, write to the Free Software Foundation, Inc., permit translation Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and reading u32 - Add TOSERVER_INIT new opcode (0x02) for client presentation to server - Add TOSERVER_AUTH new opcode (0x03) for client authentication + Add new opcode TOSERVER_INIT for client presentation to server + Add new opcodes TOSERVER_FIRST_SRP, TOSERVER_SRP_BYTES_A, + TOSERVER_SRP_BYTES_M, TOCLIENT_SRP_BYTES_S_B + for the three supported auth mechanisms around srp + Add new opcodes TOCLIENT_ACCEPT_SUDO_MODE and TOCLIENT_DENY_SUDO_MODE + for sudo mode handling (auth mech generic way of changing password). Add TOCLIENT_HELLO for presenting server to client after client presentation - Add TOCLIENT_AUTH_ACCEPT to accept connexion from client + Add TOCLIENT_AUTH_ACCEPT to accept connection from client */ #define LATEST_PROTOCOL_VERSION 24 @@ -151,14 +155,31 @@ with this program; if not, write to the Free Software Foundation, Inc., enum ToClientCommand { TOCLIENT_HELLO = 0x02, - TOCLIENT_AUTH_ACCEPT = 0x03, - TOCLIENT_ACCESS_DENIED = 0x0A, /* - u16 command - u16 reason_length - wstring reason + Sent after TOSERVER_INIT. + + u8 deployed version + u32 supported auth methods + std::string username that should be used for legacy hash (for proper casing) */ + TOCLIENT_AUTH_ACCEPT = 0x03, + /* + Message from server to accept auth. + v3s16 player's position + v3f(0,BS/2,0) floatToInt'd + u64 map seed + f1000 recommended send interval + u32 : supported auth methods for sudo mode + (where the user can change their password) + */ + TOCLIENT_ACCEPT_SUDO_MODE = 0x04, + /* + Sent to client to show it is in sudo mode now. + */ + TOCLIENT_DENY_SUDO_MODE = 0x05, + /* + Signals client that sudo mode auth failed. + */ TOCLIENT_INIT_LEGACY = 0x10, /* Server's reply to TOSERVER_INIT. @@ -173,7 +194,11 @@ enum ToClientCommand NOTE: The position in here is deprecated; position is explicitly sent afterwards */ - + TOCLIENT_ACCESS_DENIED = 0x0A, + /* + u8 reason + std::string custom reason (if reason == SERVER_ACCESSDENIED_CUSTOM_STRING) + */ TOCLIENT_BLOCKDATA = 0x20, //TODO: Multiple blocks TOCLIENT_ADDNODE = 0x21, /* @@ -589,7 +614,16 @@ enum ToClientCommand u32 id */ - TOCLIENT_NUM_MSG_TYPES = 0x54, + TOCLIENT_SRP_BYTES_S_B = 0x60, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP. + + u16 command + std::string bytes_s + std::string bytes_B + */ + + TOCLIENT_NUM_MSG_TYPES = 0x61, }; enum ToServerCommand @@ -598,18 +632,11 @@ enum ToServerCommand /* Sent first after connected. - [0] u16 TOSERVER_INIT [2] u8 SER_FMT_VER_HIGHEST_READ [3] u8 compression_modes - */ - - TOSERVER_AUTH = 0x03, - /* - Sent first after presentation (INIT). - [0] std::string player_name - [0+*] std::string password (new in some version) - [0+*+*] u16 minimum supported network protocol version (added sometime) - [0+*+*+2] u16 maximum supported network protocol version (added later than the previous one) + [4] u16 minimum supported network protocol version + [6] u16 maximum supported network protocol version + [8] std::string player name */ TOSERVER_INIT_LEGACY = 0x10, @@ -817,15 +844,6 @@ enum ToServerCommand u8[len] field value */ - TOSERVER_PASSWORD = 0x3d, - /* - Sent to change password. - - [0] u16 TOSERVER_PASSWORD - [2] std::string old password - [2+*] std::string new password - */ - TOSERVER_REQUEST_MEDIA = 0x40, /* u16 command @@ -857,7 +875,49 @@ enum ToServerCommand u8[len] full_version_string */ - TOSERVER_NUM_MSG_TYPES = 0x44, + TOSERVER_FIRST_SRP = 0x50, + /* + Belonging to AUTH_MECHANISM_FIRST_SRP. + + std::string srp salt + std::string srp verification key + u8 is_empty (=1 if password is empty, 0 otherwise) + */ + + TOSERVER_SRP_BYTES_A = 0x51, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP, + depending on current_login_based_on. + + std::string bytes_A + u8 current_login_based_on : on which version of the password's + hash this login is based on (0 legacy hash, + or 1 directly the password) + */ + + TOSERVER_SRP_BYTES_M = 0x52, + /* + Belonging to AUTH_MECHANISM_LEGACY_PASSWORD and AUTH_MECHANISM_SRP. + + std::string bytes_M + */ + + TOSERVER_NUM_MSG_TYPES = 0x53, +}; + +enum AuthMechanism +{ + // reserved + AUTH_MECHANISM_NONE = 0, + + // SRP based on the legacy hash + AUTH_MECHANISM_LEGACY_PASSWORD = 1 << 0, + + // SRP based on the srp verification key + AUTH_MECHANISM_SRP = 1 << 1, + + // Establishes a srp verification key, for first login and password changing + AUTH_MECHANISM_FIRST_SRP = 1 << 2, }; enum AccessDeniedCode { diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 9c1da9ad1..92d24fe40 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -26,8 +26,8 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = { null_command_handler, // 0x00 (never use this) null_command_handler, // 0x01 - { "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02 - { "TOSERVER_AUTH", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Auth }, // 0x03 + { "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02 + null_command_handler, // 0x03 null_command_handler, // 0x04 null_command_handler, // 0x05 null_command_handler, // 0x06 @@ -85,13 +85,28 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b { "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c - { "TOSERVER_PASSWORD", TOSERVER_STATE_INGAME, &Server::handleCommand_Password }, // 0x3d + null_command_handler, // 0x3d null_command_handler, // 0x3e null_command_handler, // 0x3f { "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40 { "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41 { "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42 { "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43 + null_command_handler, // 0x44 + null_command_handler, // 0x45 + null_command_handler, // 0x46 + null_command_handler, // 0x47 + null_command_handler, // 0x48 + null_command_handler, // 0x49 + null_command_handler, // 0x4a + null_command_handler, // 0x4b + null_command_handler, // 0x4c + null_command_handler, // 0x4d + null_command_handler, // 0x4e + null_command_handler, // 0x4f + { "TOSERVER_FIRST_SRP", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_FirstSrp }, // 0x50 + { "TOSERVER_SRP_BYTES_A", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesA }, // 0x51 + { "TOSERVER_SRP_BYTES_M", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_SrpBytesM }, // 0x52 }; const static ClientCommandFactory null_command_factory = { "TOCLIENT_NULL", 0, false }; @@ -100,10 +115,10 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { null_command_factory, // 0x00 null_command_factory, // 0x01 - { "TOCLIENT_HELLO", 0, true }, // 0x02 + null_command_factory, // 0x02 { "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03 - null_command_factory, // 0x04 - null_command_factory, // 0x05 + { "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04 + { "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05 null_command_factory, // 0x06 null_command_factory, // 0x07 null_command_factory, // 0x08 @@ -182,4 +197,17 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51 { "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52 { "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53 + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + null_command_factory, + { "TOSERVER_SRP_BYTES_S_B", 0, true }, // 0x60 }; diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 6b1d5bc02..a6f5ffca1 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -32,9 +32,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "network/networkprotocol.h" #include "network/serveropcodes.h" +#include "util/auth.h" #include "util/base64.h" #include "util/pointedthing.h" #include "util/serialize.h" +#include "util/srp.h" void Server::handleCommand_Deprecated(NetworkPacket* pkt) { @@ -92,13 +94,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt) u8 compression_modes; u16 min_net_proto_version = 0; u16 max_net_proto_version; + std::string playerName; *pkt >> client_max >> compression_modes >> min_net_proto_version - >> max_net_proto_version; + >> max_net_proto_version >> playerName; u8 our_max = SER_FMT_VER_HIGHEST_READ; // Use the highest version supported by both - int deployed = std::min(client_max, our_max); + u8 deployed = std::min(client_max, our_max); // If it's lower than the lowest supported, give up. if (deployed < SER_FMT_VER_LOWEST) deployed = SER_FMT_VER_INVALID; @@ -137,7 +140,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt) client->net_proto_version = net_proto_version; - // On this handler protocol version 25 is required + // On this handler at least protocol version 25 is required if (net_proto_version < 25 || net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) { @@ -156,36 +159,9 @@ void Server::handleCommand_Init(NetworkPacket* pkt) } } - // @TODO: check if we support same modes, but not required now - - client->setSupportedCompressionModes(compression_modes); - - m_clients.event(pkt->getPeerId(), CSE_Init); -} - -void Server::handleCommand_Auth(NetworkPacket* pkt) -{ - std::string addr_s; - try { - Address address = getPeerAddress(pkt->getPeerId()); - addr_s = address.serializeString(); - } - catch (con::PeerNotFoundException &e) { - /* - * no peer for this packet found - * most common reason is peer timeout, e.g. peer didn't - * respond for some time, your server was overloaded or - * things like that. - */ - infostream << "Server::ProcessData(): Canceling: peer " - << pkt->getPeerId() << " not found" << std::endl; - return; - } - - std::string playerName, playerPassword; - - *pkt >> playerName >> playerPassword; - + /* + Validate player name + */ const char* playername = playerName.c_str(); if (playerName.size() > PLAYERNAME_SIZE) { @@ -202,6 +178,11 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) return; } + m_clients.setPlayerName(pkt->getPeerId(), playername); + //TODO (later) case insensitivity + + std::string legacyPlayerNameCasing = playerName; + if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) { actionstream << "Server: Player with the name \"singleplayer\" " << "tried to connect from " << addr_s << std::endl; @@ -222,23 +203,9 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) } } - if (playerPassword.size() > PASSWORD_SIZE) { - actionstream << "Server: Player with an too long password " - << "tried to connect from " << addr_s << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - infostream << "Server: New connection: \"" << playerName << "\" from " << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; - if(!base64_is_valid(playerPassword)){ - actionstream << "Server: " << playerName - << " supplied invalid password hash" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - // Enforce user limit. // Don't enforce for users that have some admin right if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") && @@ -247,76 +214,74 @@ void Server::handleCommand_Auth(NetworkPacket* pkt) !checkPriv(playername, "privs") && !checkPriv(playername, "password") && playername != g_settings->get("name")) { - actionstream << "Server: " << playername << " tried to join, but there" - << " are already max_users=" + actionstream << "Server: " << playername << " tried to join from " + << addr_s << ", but there" << " are already max_users=" << g_settings->getU16("max_users") << " players." << std::endl; DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS); return; } - std::string checkpwd; // Password hash to check against - bool has_auth = m_script->getAuth(playername, &checkpwd, NULL); - - // If no authentication info exists for user, create it - if (!has_auth) { - if (!isSingleplayer() && - g_settings->getBool("disallow_empty_password") && - playerPassword.empty()) { - actionstream << "Server: " << playerName - << " supplied empty password" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD); + /* + Compose auth methods for answer + */ + std::string encpwd; // encrypted Password field for the user + bool has_auth = m_script->getAuth(playername, &encpwd, NULL); + u32 auth_mechs = 0; + + client->chosen_mech = AUTH_MECHANISM_NONE; + + if (has_auth) { + std::vector<std::string> pwd_components = str_split(encpwd, '#'); + if (pwd_components.size() == 4) { + if (pwd_components[1] == "1") { // 1 means srp + auth_mechs |= AUTH_MECHANISM_SRP; + client->enc_pwd = encpwd; + } else { + actionstream << "User " << playername + << " tried to log in, but password field" + << " was invalid (unknown mechcode)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + return; + } + } else if (base64_is_valid(encpwd)) { + auth_mechs |= AUTH_MECHANISM_LEGACY_PASSWORD; + client->enc_pwd = encpwd; + } else { + actionstream << "User " << playername + << " tried to log in, but password field" + << " was invalid (invalid base64)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); return; } - std::string raw_default_password = - g_settings->get("default_password"); - std::string initial_password = - translatePassword(playername, raw_default_password); - - // If default_password is empty, allow any initial password - if (raw_default_password.length() == 0) - initial_password = playerPassword.c_str(); - - m_script->createAuth(playername, initial_password); - } - - has_auth = m_script->getAuth(playername, &checkpwd, NULL); - - if(!has_auth) { - actionstream << "Server: " << playerName << " cannot be authenticated" - << " (auth handler does not work?)" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); - return; - } - - if(playerPassword.c_str() != checkpwd) { - actionstream << "Server: " << playerName << " supplied wrong password" - << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); - return; - } - - RemotePlayer *player = - static_cast<RemotePlayer*>(m_env->getPlayer(playername)); - - if (player && player->peer_id != 0) { - errorstream << "Server: " << playername << ": Failed to emerge player" - << " (player allocated to an another client)" << std::endl; - DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_ALREADY_CONNECTED); + } else { + std::string default_password = g_settings->get("default_password"); + if (default_password.length() == 0) { + auth_mechs |= AUTH_MECHANISM_FIRST_SRP; + } else { + // Take care of default passwords. + client->enc_pwd = getSRPVerifier(playerName, default_password); + auth_mechs |= AUTH_MECHANISM_SRP; + } } - m_clients.setPlayerName(pkt->getPeerId(), playername); - /* - Answer with a TOCLIENT_INIT + Answer with a TOCLIENT_HELLO */ - NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, pkt->getPeerId()); + verbosestream << "Sending TOCLIENT_HELLO with auth method field: " + << auth_mechs << std::endl; - resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed() - << g_settings->getFloat("dedicated_server_step"); + NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4 + + legacyPlayerNameCasing.size(), pkt->getPeerId()); + + resp_pkt << deployed << auth_mechs << legacyPlayerNameCasing; Send(&resp_pkt); - m_clients.event(pkt->getPeerId(), CSE_Init); + + client->allowed_auth_mechs = auth_mechs; + client->setSupportedCompressionModes(compression_modes); + + m_clients.event(pkt->getPeerId(), CSE_Hello); } void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) @@ -354,7 +319,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) return; } - verbosestream << "Server: Got TOSERVER_INIT from " << addr_s << " (peer_id=" + verbosestream << "Server: Got TOSERVER_INIT_LEGACY from " << addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl; // Do not allow multiple players in simple singleplayer mode. @@ -422,6 +387,10 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) net_proto_version = max_net_proto_version; } + // The client will send up to date init packet, ignore this one + if (net_proto_version >= 25) + return; + verbosestream << "Server: " << addr_s << ": Protocol version: min: " << min_net_proto_version << ", max: " << max_net_proto_version << ", chosen: " << net_proto_version << std::endl; @@ -624,7 +593,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt) << g_settings->getFloat("dedicated_server_step"); Send(&resp_pkt); - m_clients.event(pkt->getPeerId(), CSE_Init); + m_clients.event(pkt->getPeerId(), CSE_InitLegacy); } void Server::handleCommand_Init2(NetworkPacket* pkt) @@ -1223,33 +1192,33 @@ void Server::handleCommand_Breath(NetworkPacket* pkt) void Server::handleCommand_Password(NetworkPacket* pkt) { - if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) || - pkt->getSize() != PASSWORD_SIZE * 2) + if (pkt->getSize() != PASSWORD_SIZE * 2) return; std::string oldpwd; std::string newpwd; - if (pkt->getCommand() == TOSERVER_PASSWORD) { - *pkt >> oldpwd >> newpwd; + // Deny for clients using the new protocol + RemoteClient* client = getClient(pkt->getPeerId(), CS_Created); + if (client->net_proto_version >= 25) { + infostream << "Server::handleCommand_Password(): Denying change: " + << " Client protocol version for peer_id=" << pkt->getPeerId() + << " too new!" << std::endl; + return; } - // 13/03/15 - // Protocol v24 compat. Please remove in 1 year after - // client convergence to 0.4.13/0.5.x - else { - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(i); - if (c == 0) - break; - oldpwd += c; - } - for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { - char c = pkt->getChar(PASSWORD_SIZE + i); - if (c == 0) - break; - newpwd += c; - } + for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { + char c = pkt->getChar(i); + if (c == 0) + break; + oldpwd += c; + } + + for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) { + char c = pkt->getChar(PASSWORD_SIZE + i); + if (c == 0) + break; + newpwd += c; } Player *player = m_env->getPlayer(pkt->getPeerId()); @@ -1848,3 +1817,258 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt) m_script->on_playerReceiveFields(playersao, formname, fields); } + +void Server::handleCommand_FirstSrp(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + std::string playername = client->getName(); + + std::string salt; + std::string verification_key; + + std::string addr_s = getPeerAddress(pkt->getPeerId()).serializeString(); + u8 is_empty; + + *pkt >> salt >> verification_key >> is_empty; + + verbosestream << "Server: Got TOSERVER_FIRST_SRP from " << addr_s + << ", with is_empty= " << is_empty << std::endl; + + // Either this packet is sent because the user is new or to change the password + if (cstate == CS_HelloSent) { + if (!client->isMechAllowed(AUTH_MECHANISM_FIRST_SRP)) { + actionstream << "Server: Client from " << addr_s + << " tried to set password without being " + << "authenticated, or the username being new." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + + if (!isSingleplayer() && + g_settings->getBool("disallow_empty_password") && + is_empty == 1) { + actionstream << "Server: " << playername + << " supplied empty password from " << addr_s << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD); + return; + } + + std::string initial_ver_key; + std::string raw_default_password = g_settings->get("default_password"); + // If default_password is empty, allow any initial password + if (raw_default_password.length() == 0) { + initial_ver_key = encodeSRPVerifier(verification_key, salt); + } else { + initial_ver_key = getSRPVerifier(playername, raw_default_password); + } + + m_script->createAuth(playername, initial_ver_key); + + acceptAuth(pkt->getPeerId(), false); + } else { + if (cstate < CS_SudoMode) { + infostream << "Server::ProcessData(): Ignoring TOSERVER_FIRST_SRP from " + << addr_s << ": " << "Client has wrong state " << cstate << "." + << std::endl; + return; + } + m_clients.event(pkt->getPeerId(), CSE_SudoLeave); + std::string pw_db_field = encodeSRPVerifier(verification_key, salt); + bool success = m_script->setPassword(playername, pw_db_field); + if (success) { + actionstream << playername << " changes password" << std::endl; + SendChatMessage(pkt->getPeerId(), L"Password change successful."); + } else { + actionstream << playername << " tries to change password but " + << "it fails" << std::endl; + SendChatMessage(pkt->getPeerId(), L"Password change failed or unavailable."); + } + } +} + +void Server::handleCommand_SrpBytesA(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + bool wantSudo = (cstate == CS_Active); + + if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { + actionstream << "Server: got SRP _A packet in wrong state " + << cstate << " from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << ". Ignoring." << std::endl; + return; + } + + if (client->chosen_mech != AUTH_MECHANISM_NONE) { + actionstream << "Server: got SRP _A packet, while auth" + << "is already going on with mech " << client->chosen_mech + << " from " << getPeerAddress(pkt->getPeerId()).serializeString() + << " (wantSudo=" << wantSudo << "). Ignoring." << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + std::string bytes_A; + u8 based_on; + *pkt >> bytes_A >> based_on; + + infostream << "Server: TOSERVER_SRP_BYTES_A received with " + << "based_on=" << int(based_on) << " and len_A=" + << bytes_A.length() << "." << std::endl; + + AuthMechanism chosen = (based_on == 0) ? + AUTH_MECHANISM_LEGACY_PASSWORD : AUTH_MECHANISM_SRP; + + if (wantSudo) { + if (!client->isSudoMechAllowed(chosen)) { + actionstream << "Server: Player \"" << client->getName() + << "\" at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " tried to change password using unallowed mech " + << chosen << "." << std::endl; + DenySudoAccess(pkt->getPeerId()); + return; + } + } else { + if (!client->isMechAllowed(chosen)) { + actionstream << "Server: Client tried to authenticate from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << " using unallowed mech " << chosen << "." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + client->chosen_mech = chosen; + + std::string bytes_s; + std::string bytes_v; + + if (based_on == 0) { + char *p_bytes_s = 0; + size_t len_s = 0; + char *p_bytes_v = 0; + size_t len_v = 0; + getSRPVerifier(client->getName(), client->enc_pwd, + &p_bytes_s, &len_s, + &p_bytes_v, &len_v); + bytes_s = std::string(p_bytes_s, len_s); + bytes_v = std::string(p_bytes_v, len_v); + free(p_bytes_s); + free(p_bytes_v); + } else if (!decodeSRPVerifier(client->enc_pwd, &bytes_s, &bytes_v)) { + // Non-base64 errors should have been catched in the init handler + actionstream << "Server: User " << client->getName() + << " tried to log in, but srp verifier field" + << " was invalid (most likely invalid base64)." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_SERVER_FAIL); + return; + } + + char *bytes_B = 0; + size_t len_B = 0; + + client->auth_data = srp_verifier_new(SRP_SHA256, SRP_NG_2048, + client->getName().c_str(), + (const unsigned char *) bytes_s.c_str(), bytes_s.size(), + (const unsigned char *) bytes_v.c_str(), bytes_v.size(), + (const unsigned char *) bytes_A.c_str(), bytes_A.size(), + NULL, 0, + (unsigned char **) &bytes_B, &len_B, NULL, NULL); + + if (!bytes_B) { + actionstream << "Server: User " << client->getName() + << " tried to log in, SRP-6a safety check violated in _A handler." + << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + NetworkPacket resp_pkt(TOCLIENT_SRP_BYTES_S_B, 0, pkt->getPeerId()); + resp_pkt << bytes_s << std::string(bytes_B, len_B); + Send(&resp_pkt); +} + +void Server::handleCommand_SrpBytesM(NetworkPacket* pkt) +{ + RemoteClient* client = getClient(pkt->getPeerId(), CS_Invalid); + ClientState cstate = client->getState(); + + bool wantSudo = (cstate == CS_Active); + + verbosestream << "Server: Recieved TOCLIENT_SRP_BYTES_M." << std::endl; + + if (!((cstate == CS_HelloSent) || (cstate == CS_Active))) { + actionstream << "Server: got SRP _M packet in wrong state " + << cstate << " from " + << getPeerAddress(pkt->getPeerId()).serializeString() + << ". Ignoring." << std::endl; + return; + } + + if ((client->chosen_mech != AUTH_MECHANISM_SRP) + && (client->chosen_mech != AUTH_MECHANISM_LEGACY_PASSWORD)) { + actionstream << "Server: got SRP _M packet, while auth" + << "is going on with mech " << client->chosen_mech + << " from " << getPeerAddress(pkt->getPeerId()).serializeString() + << " (wantSudo=" << wantSudo << "). Denying." << std::endl; + if (wantSudo) { + DenySudoAccess(pkt->getPeerId()); + return; + } else { + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + } + + std::string bytes_M; + *pkt >> bytes_M; + + if (srp_verifier_get_session_key_length((SRPVerifier *) client->auth_data) + != bytes_M.size()) { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " sent bytes_M with invalid length " << bytes_M.size() << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_UNEXPECTED_DATA); + return; + } + + unsigned char *bytes_HAMK = 0; + + srp_verifier_verify_session((SRPVerifier *) client->auth_data, + (unsigned char *)bytes_M.c_str(), &bytes_HAMK); + + if (!bytes_HAMK) { + if (wantSudo) { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " tried to change their password, but supplied wrong" + << " (SRP) password for authentication." << std::endl; + DenySudoAccess(pkt->getPeerId()); + return; + } else { + actionstream << "Server: User " << client->getName() + << " at " << getPeerAddress(pkt->getPeerId()).serializeString() + << " supplied wrong (SRP) password from address " + << getPeerAddress(pkt->getPeerId()).serializeString() + << "." << std::endl; + DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_WRONG_PASSWORD); + return; + } + } + + acceptAuth(pkt->getPeerId(), wantSudo); +} |