summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/clientopcodes.cpp38
-rw-r--r--src/network/clientpackethandler.cpp99
-rw-r--r--src/network/networkpacket.h4
-rw-r--r--src/network/networkprotocol.h120
-rw-r--r--src/network/serveropcodes.cpp40
-rw-r--r--src/network/serverpackethandler.cpp472
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);
+}