230 changes: 136 additions & 94 deletions src/clientiface.h

Large diffs are not rendered by default.

38 changes: 33 additions & 5 deletions src/network/clientopcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 };
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
};
99 changes: 95 additions & 4 deletions src/network/clientpackethandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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 "
Expand All @@ -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);

Expand All @@ -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)
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
4 changes: 3 additions & 1 deletion src/network/networkpacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ class NetworkPacket
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);
Expand Down
120 changes: 90 additions & 30 deletions src/network/networkprotocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand All @@ -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,
/*
Expand Down Expand Up @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
40 changes: 34 additions & 6 deletions src/network/serveropcodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 };
Expand All @@ -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
Expand Down Expand Up @@ -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
};
472 changes: 348 additions & 124 deletions src/network/serverpackethandler.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/script/lua_api/l_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "tool.h"
#include "filesys.h"
#include "settings.h"
#include "util/auth.h"

// debug(...)
// Writes a line to dstream
Expand Down
38 changes: 38 additions & 0 deletions src/server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2537,6 +2537,13 @@ void Server::RespawnPlayer(u16 peer_id)
playersao->setPos(pos);
}
}
void Server::DenySudoAccess(u16 peer_id)
{
DSTACK(__FUNCTION_NAME);

NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id);
Send(&pkt);
}

void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
{
Expand All @@ -2558,6 +2565,37 @@ void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
m_con.DisconnectPeer(peer_id);
}

void Server::acceptAuth(u16 peer_id, bool forSudoMode)
{
DSTACK(__FUNCTION_NAME);

if (!forSudoMode) {
RemoteClient* client = getClient(peer_id, CS_Invalid);

NetworkPacket resp_pkt(TOCLIENT_AUTH_ACCEPT, 1 + 6 + 8 + 4, peer_id);

// Right now, the auth mechs don't change between login and sudo mode.
u32 sudo_auth_mechs = client->allowed_auth_mechs;
client->allowed_sudo_mechs = sudo_auth_mechs;

resp_pkt << v3f(0,0,0) << (u64) m_env->getServerMap().getSeed()
<< g_settings->getFloat("dedicated_server_step")
<< sudo_auth_mechs;

Send(&resp_pkt);
m_clients.event(peer_id, CSE_AuthAccept);
} else {
NetworkPacket resp_pkt(TOCLIENT_ACCEPT_SUDO_MODE, 1 + 6 + 8 + 4, peer_id);

// We only support SRP right now
u32 sudo_auth_mechs = AUTH_MECHANISM_FIRST_SRP;

resp_pkt << sudo_auth_mechs;
Send(&resp_pkt);
m_clients.event(peer_id, CSE_SudoSuccess);
}
}

void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
{
DSTACK(__FUNCTION_NAME);
Expand Down
6 changes: 5 additions & 1 deletion src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ class Server : public con::PeerHandler, public MapEventReceiver,
void handleCommand_Null(NetworkPacket* pkt) {};
void handleCommand_Deprecated(NetworkPacket* pkt);
void handleCommand_Init(NetworkPacket* pkt);
void handleCommand_Auth(NetworkPacket* pkt);
void handleCommand_Init_Legacy(NetworkPacket* pkt);
void handleCommand_Init2(NetworkPacket* pkt);
void handleCommand_RequestMedia(NetworkPacket* pkt);
Expand All @@ -213,6 +212,9 @@ class Server : public con::PeerHandler, public MapEventReceiver,
void handleCommand_RemovedSounds(NetworkPacket* pkt);
void handleCommand_NodeMetaFields(NetworkPacket* pkt);
void handleCommand_InventoryFields(NetworkPacket* pkt);
void handleCommand_FirstSrp(NetworkPacket* pkt);
void handleCommand_SrpBytesA(NetworkPacket* pkt);
void handleCommand_SrpBytesM(NetworkPacket* pkt);

void ProcessData(NetworkPacket *pkt);

Expand Down Expand Up @@ -360,7 +362,9 @@ class Server : public con::PeerHandler, public MapEventReceiver,
void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout);

void DenySudoAccess(u16 peer_id);
void DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason="");
void acceptAuth(u16 peer_id, bool forSudoMode);
void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
Expand Down
3 changes: 3 additions & 0 deletions src/util/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
set(UTIL_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
${CMAKE_CURRENT_SOURCE_DIR}/sha256.c
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
PARENT_SCOPE)

126 changes: 126 additions & 0 deletions src/util/auth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
Minetest
Copyright (C) 2015 est31 <MTest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <algorithm>
#include <string>
#include "auth.h"
#include "base64.h"
#include "sha1.h"
#include "srp.h"
#include "string.h"

// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
// their password. (Exception : if the password field is
// blank, we send a blank password - this is for backwards
// compatibility with password-less players).
std::string translatePassword(const std::string &name,
const std::string &password)
{
if (password.length() == 0)
return "";

std::string slt = name + password;
SHA1 sha1;
sha1.addBytes(slt.c_str(), slt.length());
unsigned char *digest = sha1.getDigest();
std::string pwd = base64_encode(digest, 20);
free(digest);
return pwd;
}

void getSRPVerifier(const std::string &name,
const std::string &password, char **salt, size_t *salt_len,
char **bytes_v, size_t *len_v)
{
std::string n_name = lowercase(name);
srp_create_salted_verification_key(SRP_SHA256, SRP_NG_2048,
n_name.c_str(), (const unsigned char *)password.c_str(),
password.size(), (unsigned char **)salt, salt_len,
(unsigned char **)bytes_v, len_v, NULL, NULL);
}

// Get a db-ready SRP verifier
// The salt param is only modifyable by this method so that you can free it
// if it was allocated. You shouldn't use it for other purposes, as you will
// need the contents of salt_len too.
inline static std::string getSRPVerifier(const std::string &name,
const std::string &password, char ** salt, size_t salt_len)
{
char * bytes_v = NULL;
size_t len_v;
getSRPVerifier(name, password, salt, &salt_len,
&bytes_v, &len_v);
std::string ret_val = encodeSRPVerifier(std::string(bytes_v, len_v),
std::string(*salt, salt_len));
free(bytes_v);
return ret_val;
}

// Get a db-ready SRP verifier
std::string getSRPVerifier(const std::string &name,
const std::string &password)
{
char * salt = NULL;
std::string ret_val = getSRPVerifier(name,
password, &salt, 0);
free(salt);
return ret_val;
}

// Get a db-ready SRP verifier
std::string getSRPVerifier(const std::string &name,
const std::string &password, const std::string &salt)
{
// The implementation won't change the salt if its set,
// therefore we can cast.
char *salt_cstr = (char *)salt.c_str();
return getSRPVerifier(name, password,
&salt_cstr, salt.size());
}

// Make a SRP verifier db-ready
std::string encodeSRPVerifier(const std::string &verifier,
const std::string &salt)
{
std::ostringstream ret_str;
ret_str << "#1#"
<< base64_encode((unsigned char*) salt.c_str(), salt.size()) << "#"
<< base64_encode((unsigned char*) verifier.c_str(), verifier.size());
return ret_str.str();
}

bool decodeSRPVerifier(const std::string &enc_pwd,
std::string *salt, std::string *bytes_v)
{
std::vector<std::string> pwd_components = str_split(enc_pwd, '#');

if ((pwd_components.size() != 4)
|| (pwd_components[1] != "1") // 1 means srp
|| !base64_is_valid(pwd_components[2])
|| !base64_is_valid(pwd_components[3]))
return false;

std::string salt_str = base64_decode(pwd_components[2]);
std::string bytes_v_str = base64_decode(pwd_components[3]);
*salt = salt_str;
*bytes_v = bytes_v_str;
return true;

}
37 changes: 37 additions & 0 deletions src/util/auth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Minetest
Copyright (C) 2015 est31 <MTest31@outlook.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef AUTH_H
#define AUTH_H

std::string translatePassword(const std::string &name,
const std::string &password);
void getSRPVerifier(const std::string &name,
const std::string &password, char **salt, size_t *salt_len,
char **bytes_v, size_t *len_v);
std::string getSRPVerifier(const std::string &name,
const std::string &password);
std::string getSRPVerifier(const std::string &name,
const std::string &password, const std::string &salt);
std::string encodeSRPVerifier(const std::string &verifier,
const std::string &salt);
bool decodeSRPVerifier(const std::string &enc_pwd,
std::string *salt, std::string *bytes_v);

#endif
428 changes: 428 additions & 0 deletions src/util/md32_common.h

Large diffs are not rendered by default.

154 changes: 154 additions & 0 deletions src/util/sha2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/* crypto/sha/sha.h */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
* This package is an SSL implementation written
* by Eric Young (eay@cryptsoft.com).
* The implementation was written so as to conform with Netscapes SSL.
*
* This library is free for commercial and non-commercial use as long as
* the following conditions are aheared to. The following conditions
* apply to all code found in this distribution, be it the RC4, RSA,
* lhash, DES, etc., code; not just the SSL code. The SSL documentation
* included with this distribution is covered by the same copyright terms
* except that the holder is Tim Hudson (tjh@cryptsoft.com).
*
* Copyright remains Eric Young's, and as such any Copyright notices in
* the code are not to be removed.
* If this package is used in a product, Eric Young should be given attribution
* as the author of the parts of the library used.
* This can be in the form of a textual message at program startup or
* in documentation (online or textual) provided with the package.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* "This product includes cryptographic software written by
* Eric Young (eay@cryptsoft.com)"
* The word 'cryptographic' can be left out if the rouines from the library
* being used are not cryptographic related :-).
* 4. If you include any Windows specific code (or a derivative thereof) from
* the apps directory (application code) you must include an acknowledgement:
* "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
*
* THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* The licence and distribution terms for any publically available version or
* derivative of this code cannot be changed. i.e. this code cannot simply be
* copied and put under another distribution licence
* [including the GNU Public Licence.]
*/

#ifndef HEADER_SHA_H
# define HEADER_SHA_H

# include <stddef.h>

#ifdef __cplusplus
extern "C" {
#endif

# if defined(OPENSSL_NO_SHA) || (defined(OPENSSL_NO_SHA0) && defined(OPENSSL_NO_SHA1))
# error SHA is disabled.
# endif

# if defined(OPENSSL_FIPS)
# define FIPS_SHA_SIZE_T size_t
# endif

/*
Compat stuff from OpenSSL land
*/

/* crypto.h */

# define fips_md_init(alg) fips_md_init_ctx(alg, alg)

# define fips_md_init_ctx(alg, cx) \
int alg##_Init(cx##_CTX *c)
# define fips_cipher_abort(alg) while(0)

/*-
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* ! SHA_LONG has to be at least 32 bits wide. If it's wider, then !
* ! SHA_LONG_LOG2 has to be defined along. !
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/

# if defined(__LP32__)
# define SHA_LONG unsigned long
# elif defined(__ILP64__)
# define SHA_LONG unsigned long
# define SHA_LONG_LOG2 3
# else
# define SHA_LONG unsigned int
# endif

# define SHA_LBLOCK 16
# define SHA_CBLOCK (SHA_LBLOCK*4)/* SHA treats input data as a
* contiguous array of 32 bit wide
* big-endian values. */
# define SHA_LAST_BLOCK (SHA_CBLOCK-8)
# define SHA_DIGEST_LENGTH 20

typedef struct SHAstate_st {
SHA_LONG h0, h1, h2, h3, h4;
SHA_LONG Nl, Nh;
SHA_LONG data[SHA_LBLOCK];
unsigned int num;
} SHA_CTX;

# define SHA256_CBLOCK (SHA_LBLOCK*4)/* SHA-256 treats input data as a
* contiguous array of 32 bit wide
* big-endian values. */
# define SHA224_DIGEST_LENGTH 28
# define SHA256_DIGEST_LENGTH 32

typedef struct SHA256state_st {
SHA_LONG h[8];
SHA_LONG Nl, Nh;
SHA_LONG data[SHA_LBLOCK];
unsigned int num, md_len;
} SHA256_CTX;

# ifndef OPENSSL_NO_SHA256
# ifdef OPENSSL_FIPS
int private_SHA224_Init(SHA256_CTX *c);
int private_SHA256_Init(SHA256_CTX *c);
# endif
int SHA224_Init(SHA256_CTX *c);
int SHA224_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA224_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md);
int SHA256_Init(SHA256_CTX *c);
int SHA256_Update(SHA256_CTX *c, const void *data, size_t len);
int SHA256_Final(unsigned char *md, SHA256_CTX *c);
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md);
void SHA256_Transform(SHA256_CTX *c, const unsigned char *data);
# endif

# define SHA384_DIGEST_LENGTH 48
# define SHA512_DIGEST_LENGTH 64

#ifdef __cplusplus
}
#endif

#endif
404 changes: 404 additions & 0 deletions src/util/sha256.c

Large diffs are not rendered by default.

1,029 changes: 1,029 additions & 0 deletions src/util/srp.cpp

Large diffs are not rendered by default.

171 changes: 171 additions & 0 deletions src/util/srp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Secure Remote Password 6a implementation
* https://github.com/est31/csrp-gmp
*
* The MIT License (MIT)
*
* Copyright (c) 2010, 2013 Tom Cocagne, 2015 est31 <MTest31@outlook.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/

/*
*
* Purpose: This is a direct implementation of the Secure Remote Password
* Protocol version 6a as described by
* http://srp.stanford.edu/design.html
*
* Author: tom.cocagne@gmail.com (Tom Cocagne)
*
* Dependencies: LibGMP
*
* Usage: Refer to test_srp.c for a demonstration
*
* Notes:
* This library allows multiple combinations of hashing algorithms and
* prime number constants. For authentication to succeed, the hash and
* prime number constants must match between
* srp_create_salted_verification_key(), srp_user_new(),
* and srp_verifier_new(). A recommended approach is to determine the
* desired level of security for an application and globally define the
* hash and prime number constants to the predetermined values.
*
* As one might suspect, more bits means more security. As one might also
* suspect, more bits also means more processing time. The test_srp.c
* program can be easily modified to profile various combinations of
* hash & prime number pairings.
*/

#ifndef SRP_H
#define SRP_H


struct SRPVerifier;
struct SRPUser;

typedef enum
{
SRP_NG_1024,
SRP_NG_2048,
SRP_NG_4096,
SRP_NG_8192,
SRP_NG_CUSTOM
} SRP_NGType;

typedef enum
{
/*SRP_SHA1,*/
/*SRP_SHA224,*/
SRP_SHA256,
/*SRP_SHA384,
SRP_SHA512*/
} SRP_HashAlgorithm;

/* Out: bytes_v, len_v
*
* The caller is responsible for freeing the memory allocated for bytes_v
*
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type.
* If provided, they must contain ASCII text of the hexidecimal notation.
*
* If bytes_s == NULL, it is filled with random data. The caller is responsible for freeing.
*/
void srp_create_salted_verification_key( SRP_HashAlgorithm alg,
SRP_NGType ng_type, const char *username_for_verifier,
const unsigned char *password, size_t len_password,
unsigned char **bytes_s, size_t *len_s,
unsigned char **bytes_v, size_t *len_v,
const char * n_hex, const char *g_hex );

/* Out: bytes_B, len_B.
*
* On failure, bytes_B will be set to NULL and len_B will be set to 0
*
* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type
*
* If bytes_b == NULL, random data is used for b.
*/
struct SRPVerifier* srp_verifier_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username,
const unsigned char *bytes_s, size_t len_s,
const unsigned char *bytes_v, size_t len_v,
const unsigned char *bytes_A, size_t len_A,
const unsigned char *bytes_b, size_t len_b,
unsigned char** bytes_B, size_t *len_B,
const char* n_hex, const char* g_hex);


void srp_verifier_delete( struct SRPVerifier* ver );


int srp_verifier_is_authenticated( struct SRPVerifier* ver );


const char * srp_verifier_get_username( struct SRPVerifier* ver );

/* key_length may be null */
const unsigned char* srp_verifier_get_session_key( struct SRPVerifier* ver,
size_t *key_length );


size_t srp_verifier_get_session_key_length(struct SRPVerifier* ver);


/* user_M must be exactly srp_verifier_get_session_key_length() bytes in size */
void srp_verifier_verify_session( struct SRPVerifier* ver,
const unsigned char* user_M, unsigned char** bytes_HAMK );

/*******************************************************************************/

/* The n_hex and g_hex parameters should be 0 unless SRP_NG_CUSTOM is used for ng_type */
struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type,
const char *username, const char *username_for_verifier,
const unsigned char *bytes_password, size_t len_password,
const char *n_hex, const char *g_hex);

void srp_user_delete(struct SRPUser * usr);

int srp_user_is_authenticated(struct SRPUser * usr);


const char* srp_user_get_username(struct SRPUser * usr);

/* key_length may be null */
const unsigned char* srp_user_get_session_key(struct SRPUser* usr, size_t* key_length);

size_t srp_user_get_session_key_length(struct SRPUser* usr);

/* Output: username, bytes_A, len_A. If you don't want it get written, set username to NULL.
* If bytes_a == NULL, random data is used for a. */
void srp_user_start_authentication(struct SRPUser* usr, char** username,
const unsigned char* bytes_a, size_t len_a,
unsigned char** bytes_A, size_t* len_A);

/* Output: bytes_M, len_M (len_M may be null and will always be
* srp_user_get_session_key_length() bytes in size) */
void srp_user_process_challenge(struct SRPUser *usr,
const unsigned char *bytes_s, size_t len_s,
const unsigned char *bytes_B, size_t len_B,
unsigned char **bytes_M, size_t *len_M);

/* bytes_HAMK must be exactly srp_user_get_session_key_length() bytes in size */
void srp_user_verify_session(struct SRPUser* usr, const unsigned char* bytes_HAMK);

#endif /* Include Guard */
23 changes: 0 additions & 23 deletions src/util/string.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "numeric.h"
#include "log.h"

#include "sha1.h"
#include "base64.h"
#include "hex.h"
#include "../porting.h"

#include <algorithm>
#include <sstream>
#include <iomanip>
#include <map>
Expand Down Expand Up @@ -176,26 +173,6 @@ std::string wide_to_narrow(const std::wstring &wcs)

#endif

// Get an sha-1 hash of the player's name combined with
// the password entered. That's what the server uses as
// their password. (Exception : if the password field is
// blank, we send a blank password - this is for backwards
// compatibility with password-less players).
std::string translatePassword(const std::string &playername,
const std::string &password)
{
if (password.length() == 0)
return "";

std::string slt = playername + password;
SHA1 sha1;
sha1.addBytes(slt.c_str(), slt.length());
unsigned char *digest = sha1.getDigest();
std::string pwd = base64_encode(digest, 20);
free(digest);
return pwd;
}

std::string urlencode(std::string str)
{
// Encodes non-unreserved URI characters by a percent sign
Expand Down
3 changes: 1 addition & 2 deletions src/util/string.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ wchar_t *narrow_to_wide_c(const char *str);

std::wstring narrow_to_wide(const std::string &mbs);
std::string wide_to_narrow(const std::wstring &wcs);
std::string translatePassword(const std::string &playername,
const std::string &password);

std::string urlencode(std::string str);
std::string urldecode(std::string str);
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
Expand Down