summaryrefslogtreecommitdiff
path: root/src/network
diff options
context:
space:
mode:
authorest31 <MTest31@outlook.com>2015-04-12 04:49:13 +0200
committerest31 <MTest31@outlook.com>2015-05-11 18:40:27 +0200
commit82e35edff52d88dcd64a9bfc9d2c4c93f1341b78 (patch)
treebf55839e296c6d1cb9b7f81b8f5cf69e0492b4c0 /src/network
parent181f7baa453c58d4070de7196fd74663110946a8 (diff)
downloadminetest-82e35edff52d88dcd64a9bfc9d2c4c93f1341b78.tar.gz
minetest-82e35edff52d88dcd64a9bfc9d2c4c93f1341b78.tar.bz2
minetest-82e35edff52d88dcd64a9bfc9d2c4c93f1341b78.zip
Make early protocol auth mechanism generic, and add SRP
Adds everything needed for SRP (and everything works too), but still deactivated, as protocol v25 init packets aren't final yet. Can be activated by changing the LATEST_PROTOCOL_VERSION header to 25 inside networkprotocol.h.
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);
+}