Skip to content

Commit

Permalink
Make early protocol auth mechanism generic, and add SRP
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
est31 committed May 11, 2015
1 parent 181f7ba commit 82e35ed
Show file tree
Hide file tree
Showing 25 changed files with 3,350 additions and 308 deletions.
4 changes: 4 additions & 0 deletions build/android/jni/Android.mk
Expand Up @@ -208,13 +208,15 @@ LOCAL_SRC_FILES := \
jni/src/version.cpp \
jni/src/voxel.cpp \
jni/src/voxelalgorithms.cpp \
jni/src/util/auth.cpp \
jni/src/util/base64.cpp \
jni/src/util/directiontables.cpp \
jni/src/util/numeric.cpp \
jni/src/util/pointedthing.cpp \
jni/src/util/serialize.cpp \
jni/src/util/sha1.cpp \
jni/src/util/string.cpp \
jni/src/util/srp.cpp \
jni/src/util/timetaker.cpp \
jni/src/unittest/test.cpp \
jni/src/unittest/test_collision.cpp \
Expand Down Expand Up @@ -243,6 +245,8 @@ LOCAL_SRC_FILES := \
jni/src/client/clientlauncher.cpp \
jni/src/client/tile.cpp

# intentionally kept out (we already build openssl itself): jni/src/util/sha256.c

# Network
LOCAL_SRC_FILES += \
jni/src/network/connection.cpp \
Expand Down
134 changes: 122 additions & 12 deletions src/client.cpp
Expand Up @@ -22,10 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <sstream>
#include <IFileSystem.h>
#include "jthread/jmutexautolock.h"
#include "util/auth.h"
#include "util/directiontables.h"
#include "util/pointedthing.h"
#include "util/serialize.h"
#include "util/string.h"
#include "util/srp.h"
#include "client.h"
#include "network/clientopcodes.h"
#include "filesys.h"
Expand Down Expand Up @@ -255,6 +257,8 @@ Client::Client(
m_highlighted_pos(0,0,0),
m_map_seed(0),
m_password(password),
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
m_auth_data(NULL),
m_access_denied(false),
m_itemdef_received(false),
m_nodedef_received(false),
Expand Down Expand Up @@ -404,10 +408,13 @@ void Client::step(float dtime)
memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));

std::string hashed_password = translatePassword(myplayer->getName(), m_password);
snprintf(pName, PLAYERNAME_SIZE, "%s", myplayer->getName());
snprintf(pPassword, PASSWORD_SIZE, "%s", m_password.c_str());
snprintf(pPassword, PASSWORD_SIZE, "%s", hashed_password.c_str());

sendLegacyInit(pName, pPassword);
if (LATEST_PROTOCOL_VERSION >= 25)
sendInit(myplayer->getName());
}

// Not connected, return
Expand Down Expand Up @@ -943,6 +950,39 @@ void Client::interact(u8 action, const PointedThing& pointed)
Send(&pkt);
}

void Client::deleteAuthData()
{
if (!m_auth_data)
return;

switch (m_chosen_auth_mech) {
case AUTH_MECHANISM_FIRST_SRP:
break;
case AUTH_MECHANISM_SRP:
case AUTH_MECHANISM_LEGACY_PASSWORD:
srp_user_delete((SRPUser *) m_auth_data);
m_auth_data = NULL;
break;
case AUTH_MECHANISM_NONE:
break;
}
}


AuthMechanism Client::choseAuthMech(const u32 mechs)
{
if (mechs & AUTH_MECHANISM_SRP)
return AUTH_MECHANISM_SRP;

if (mechs & AUTH_MECHANISM_FIRST_SRP)
return AUTH_MECHANISM_FIRST_SRP;

if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
return AUTH_MECHANISM_LEGACY_PASSWORD;

return AUTH_MECHANISM_NONE;
}

void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
{
NetworkPacket pkt(TOSERVER_INIT_LEGACY,
Expand All @@ -956,6 +996,70 @@ void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
Send(&pkt);
}

void Client::sendInit(const std::string &playerName)
{
NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));

// TODO (later) actually send supported compression modes
pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u8) 42;
pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
pkt << playerName;

Send(&pkt);
}

void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;

switch (chosen_auth_mechanism) {
case AUTH_MECHANISM_FIRST_SRP: {
// send srp verifier to server
NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
char *salt, *bytes_v;
std::size_t len_salt, len_v;
salt = NULL;
getSRPVerifier(getPlayerName(), m_password,
&salt, &len_salt, &bytes_v, &len_v);
resp_pkt
<< std::string((char*)salt, len_salt)
<< std::string((char*)bytes_v, len_v)
<< (u8)((m_password == "") ? 1 : 0);
free(salt);
free(bytes_v);
Send(&resp_pkt);
break;
}
case AUTH_MECHANISM_SRP:
case AUTH_MECHANISM_LEGACY_PASSWORD: {
u8 based_on = 1;

if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
m_password = translatePassword(getPlayerName(), m_password);
based_on = 0;
}

std::string playername_u = lowercase(getPlayerName());
m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
getPlayerName().c_str(), playername_u.c_str(),
(const unsigned char *) m_password.c_str(),
m_password.length(), NULL, NULL);
char *bytes_A = 0;
size_t len_A = 0;
srp_user_start_authentication((struct SRPUser *) m_auth_data,
NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);

NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
resp_pkt << std::string(bytes_A, len_A) << based_on;
free(bytes_A);
Send(&resp_pkt);
break;
}
case AUTH_MECHANISM_NONE:
break; // not handled in this method
}
}

void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
{
NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
Expand Down Expand Up @@ -1066,24 +1170,30 @@ void Client::sendChangePassword(const std::string &oldpassword,
const std::string &newpassword)
{
Player *player = m_env.getLocalPlayer();
if(player == NULL)
if (player == NULL)
return;

std::string playername = player->getName();
std::string oldpwd = translatePassword(playername, oldpassword);
std::string newpwd = translatePassword(playername, newpassword);
if (m_proto_ver >= 25) {
// get into sudo mode and then send new password to server
m_password = oldpassword;
m_new_password = newpassword;
startAuth(choseAuthMech(m_sudo_auth_methods));
} else {
std::string oldpwd = translatePassword(playername, oldpassword);
std::string newpwd = translatePassword(playername, newpassword);

NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);

for(u8 i = 0; i < PASSWORD_SIZE; i++) {
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
}
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
}

for(u8 i = 0; i < PASSWORD_SIZE; i++) {
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
}
Send(&pkt);
}

Send(&pkt);
}


Expand Down
29 changes: 29 additions & 0 deletions src/client.h
Expand Up @@ -351,6 +351,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void handleCommand_Deprecated(NetworkPacket* pkt);
void handleCommand_Hello(NetworkPacket* pkt);
void handleCommand_AuthAccept(NetworkPacket* pkt);
void handleCommand_AcceptSudoMode(NetworkPacket* pkt);
void handleCommand_DenySudoMode(NetworkPacket* pkt);
void handleCommand_InitLegacy(NetworkPacket* pkt);
void handleCommand_AccessDenied(NetworkPacket* pkt);
void handleCommand_RemoveNode(NetworkPacket* pkt);
Expand Down Expand Up @@ -391,6 +393,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
void handleCommand_EyeOffset(NetworkPacket* pkt);
void handleCommand_SrpBytesSandB(NetworkPacket* pkt);

void ProcessData(NetworkPacket *pkt);

Expand Down Expand Up @@ -542,11 +545,21 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
// Send the item number 'item' as player item to the server
void sendPlayerItem(u16 item);

void deleteAuthData();
// helper method shared with clientpackethandler
static AuthMechanism choseAuthMech(const u32 mechs);

void sendLegacyInit(const char* playerName, const char* playerPassword);
void sendInit(const std::string &playerName);
void startAuth(AuthMechanism chosen_auth_mechanism);
void sendDeletedBlocks(std::vector<v3s16> &blocks);
void sendGotBlocks(v3s16 block);
void sendRemovedSounds(std::vector<s32> &soundList);

// Helper function
inline std::string getPlayerName()
{ return m_env.getLocalPlayer()->getName(); }

float m_packetcounter_timer;
float m_connection_reinit_timer;
float m_avg_rtt_timer;
Expand All @@ -569,6 +582,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
IrrlichtDevice *m_device;
// Server serialization version
u8 m_server_ser_ver;
// Used version of the protocol with server
u8 m_proto_ver;
u16 m_playeritem;
bool m_inventory_updated;
Inventory *m_inventory_from_server;
Expand All @@ -584,9 +599,23 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
//s32 m_daynight_i;
//u32 m_daynight_ratio;
std::queue<std::wstring> m_chat_queue;

// The authentication methods we can use to enter sudo mode (=change password)
u32 m_sudo_auth_methods;

// The seed returned by the server in TOCLIENT_INIT is stored here
u64 m_map_seed;

// Auth data
std::string m_playername;
std::string m_password;
// If set, this will be sent (and cleared) upon a TOCLIENT_ACCEPT_SUDO_MODE
std::string m_new_password;
// Usable by auth mechanisms.
AuthMechanism m_chosen_auth_mech;
void * m_auth_data;


bool m_access_denied;
std::string m_access_denied_reason;
std::queue<ClientEvent> m_client_event_queue;
Expand Down
2 changes: 1 addition & 1 deletion src/client/clientlauncher.cpp
Expand Up @@ -392,7 +392,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
else
playername = menudata.name;

password = translatePassword(playername, menudata.password);
password = menudata.password;

g_settings->set("name", playername);

Expand Down
63 changes: 58 additions & 5 deletions src/clientiface.cpp
Expand Up @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "emerge.h"
#include "serverobject.h" // TODO this is used for cleanup of only
#include "log.h"
#include "util/srp.h"

const char *ClientInterface::statenames[] = {
"Invalid",
Expand Down Expand Up @@ -427,10 +428,12 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
//intentionally do nothing
break;
case CS_Created:
switch(event)
{
case CSE_Init:
m_state = CS_InitSent;
switch (event) {
case CSE_Hello:
m_state = CS_HelloSent;
break;
case CSE_InitLegacy:
m_state = CS_AwaitingInit2;
break;
case CSE_Disconnect:
m_state = CS_Disconnecting;
Expand All @@ -447,7 +450,32 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
case CS_Denied:
/* don't do anything if in denied state */
break;
case CS_InitSent:
case CS_HelloSent:
switch(event)
{
case CSE_AuthAccept:
m_state = CS_AwaitingInit2;
if ((chosen_mech == AUTH_MECHANISM_SRP)
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
srp_verifier_delete((SRPVerifier *) auth_data);
chosen_mech = AUTH_MECHANISM_NONE;
break;
case CSE_Disconnect:
m_state = CS_Disconnecting;
break;
case CSE_SetDenied:
m_state = CS_Denied;
if ((chosen_mech == AUTH_MECHANISM_SRP)
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
srp_verifier_delete((SRPVerifier *) auth_data);
chosen_mech = AUTH_MECHANISM_NONE;
break;
default:
myerror << "HelloSent: Invalid client state transition! " << event;
throw ClientStateError(myerror.str());
}
break;
case CS_AwaitingInit2:
switch(event)
{
case CSE_GotInit2:
Expand Down Expand Up @@ -514,13 +542,38 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
case CSE_Disconnect:
m_state = CS_Disconnecting;
break;
case CSE_SudoSuccess:
m_state = CS_SudoMode;
if ((chosen_mech == AUTH_MECHANISM_SRP)
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
srp_verifier_delete((SRPVerifier *) auth_data);
chosen_mech = AUTH_MECHANISM_NONE;
break;
/* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */
default:
myerror << "Active: Invalid client state transition! " << event;
throw ClientStateError(myerror.str());
break;
}
break;
case CS_SudoMode:
switch(event)
{
case CSE_SetDenied:
m_state = CS_Denied;
break;
case CSE_Disconnect:
m_state = CS_Disconnecting;
break;
case CSE_SudoLeave:
m_state = CS_Active;
break;
default:
myerror << "Active: Invalid client state transition! " << event;
throw ClientStateError(myerror.str());
break;
}
break;
case CS_Disconnecting:
/* we are already disconnecting */
break;
Expand Down

0 comments on commit 82e35ed

Please sign in to comment.