Skip to content

Commit 82e35ed

Browse files
committed
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.
1 parent 181f7ba commit 82e35ed

25 files changed

+3350
-308
lines changed

build/android/jni/Android.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,13 +208,15 @@ LOCAL_SRC_FILES := \
208208
jni/src/version.cpp \
209209
jni/src/voxel.cpp \
210210
jni/src/voxelalgorithms.cpp \
211+
jni/src/util/auth.cpp \
211212
jni/src/util/base64.cpp \
212213
jni/src/util/directiontables.cpp \
213214
jni/src/util/numeric.cpp \
214215
jni/src/util/pointedthing.cpp \
215216
jni/src/util/serialize.cpp \
216217
jni/src/util/sha1.cpp \
217218
jni/src/util/string.cpp \
219+
jni/src/util/srp.cpp \
218220
jni/src/util/timetaker.cpp \
219221
jni/src/unittest/test.cpp \
220222
jni/src/unittest/test_collision.cpp \
@@ -243,6 +245,8 @@ LOCAL_SRC_FILES := \
243245
jni/src/client/clientlauncher.cpp \
244246
jni/src/client/tile.cpp
245247

248+
# intentionally kept out (we already build openssl itself): jni/src/util/sha256.c
249+
246250
# Network
247251
LOCAL_SRC_FILES += \
248252
jni/src/network/connection.cpp \

src/client.cpp

Lines changed: 122 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
2222
#include <sstream>
2323
#include <IFileSystem.h>
2424
#include "jthread/jmutexautolock.h"
25+
#include "util/auth.h"
2526
#include "util/directiontables.h"
2627
#include "util/pointedthing.h"
2728
#include "util/serialize.h"
2829
#include "util/string.h"
30+
#include "util/srp.h"
2931
#include "client.h"
3032
#include "network/clientopcodes.h"
3133
#include "filesys.h"
@@ -255,6 +257,8 @@ Client::Client(
255257
m_highlighted_pos(0,0,0),
256258
m_map_seed(0),
257259
m_password(password),
260+
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
261+
m_auth_data(NULL),
258262
m_access_denied(false),
259263
m_itemdef_received(false),
260264
m_nodedef_received(false),
@@ -404,10 +408,13 @@ void Client::step(float dtime)
404408
memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
405409
memset(pPassword, 0, PASSWORD_SIZE * sizeof(char));
406410

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

410415
sendLegacyInit(pName, pPassword);
416+
if (LATEST_PROTOCOL_VERSION >= 25)
417+
sendInit(myplayer->getName());
411418
}
412419

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

953+
void Client::deleteAuthData()
954+
{
955+
if (!m_auth_data)
956+
return;
957+
958+
switch (m_chosen_auth_mech) {
959+
case AUTH_MECHANISM_FIRST_SRP:
960+
break;
961+
case AUTH_MECHANISM_SRP:
962+
case AUTH_MECHANISM_LEGACY_PASSWORD:
963+
srp_user_delete((SRPUser *) m_auth_data);
964+
m_auth_data = NULL;
965+
break;
966+
case AUTH_MECHANISM_NONE:
967+
break;
968+
}
969+
}
970+
971+
972+
AuthMechanism Client::choseAuthMech(const u32 mechs)
973+
{
974+
if (mechs & AUTH_MECHANISM_SRP)
975+
return AUTH_MECHANISM_SRP;
976+
977+
if (mechs & AUTH_MECHANISM_FIRST_SRP)
978+
return AUTH_MECHANISM_FIRST_SRP;
979+
980+
if (mechs & AUTH_MECHANISM_LEGACY_PASSWORD)
981+
return AUTH_MECHANISM_LEGACY_PASSWORD;
982+
983+
return AUTH_MECHANISM_NONE;
984+
}
985+
946986
void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
947987
{
948988
NetworkPacket pkt(TOSERVER_INIT_LEGACY,
@@ -956,6 +996,70 @@ void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
956996
Send(&pkt);
957997
}
958998

999+
void Client::sendInit(const std::string &playerName)
1000+
{
1001+
NetworkPacket pkt(TOSERVER_INIT, 1 + 2 + 2 + (1 + playerName.size()));
1002+
1003+
// TODO (later) actually send supported compression modes
1004+
pkt << (u8) SER_FMT_VER_HIGHEST_READ << (u8) 42;
1005+
pkt << (u16) CLIENT_PROTOCOL_VERSION_MIN << (u16) CLIENT_PROTOCOL_VERSION_MAX;
1006+
pkt << playerName;
1007+
1008+
Send(&pkt);
1009+
}
1010+
1011+
void Client::startAuth(AuthMechanism chosen_auth_mechanism)
1012+
{
1013+
m_chosen_auth_mech = chosen_auth_mechanism;
1014+
1015+
switch (chosen_auth_mechanism) {
1016+
case AUTH_MECHANISM_FIRST_SRP: {
1017+
// send srp verifier to server
1018+
NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
1019+
char *salt, *bytes_v;
1020+
std::size_t len_salt, len_v;
1021+
salt = NULL;
1022+
getSRPVerifier(getPlayerName(), m_password,
1023+
&salt, &len_salt, &bytes_v, &len_v);
1024+
resp_pkt
1025+
<< std::string((char*)salt, len_salt)
1026+
<< std::string((char*)bytes_v, len_v)
1027+
<< (u8)((m_password == "") ? 1 : 0);
1028+
free(salt);
1029+
free(bytes_v);
1030+
Send(&resp_pkt);
1031+
break;
1032+
}
1033+
case AUTH_MECHANISM_SRP:
1034+
case AUTH_MECHANISM_LEGACY_PASSWORD: {
1035+
u8 based_on = 1;
1036+
1037+
if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
1038+
m_password = translatePassword(getPlayerName(), m_password);
1039+
based_on = 0;
1040+
}
1041+
1042+
std::string playername_u = lowercase(getPlayerName());
1043+
m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
1044+
getPlayerName().c_str(), playername_u.c_str(),
1045+
(const unsigned char *) m_password.c_str(),
1046+
m_password.length(), NULL, NULL);
1047+
char *bytes_A = 0;
1048+
size_t len_A = 0;
1049+
srp_user_start_authentication((struct SRPUser *) m_auth_data,
1050+
NULL, NULL, 0, (unsigned char **) &bytes_A, &len_A);
1051+
1052+
NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
1053+
resp_pkt << std::string(bytes_A, len_A) << based_on;
1054+
free(bytes_A);
1055+
Send(&resp_pkt);
1056+
break;
1057+
}
1058+
case AUTH_MECHANISM_NONE:
1059+
break; // not handled in this method
1060+
}
1061+
}
1062+
9591063
void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
9601064
{
9611065
NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
@@ -1066,24 +1170,30 @@ void Client::sendChangePassword(const std::string &oldpassword,
10661170
const std::string &newpassword)
10671171
{
10681172
Player *player = m_env.getLocalPlayer();
1069-
if(player == NULL)
1173+
if (player == NULL)
10701174
return;
10711175

10721176
std::string playername = player->getName();
1073-
std::string oldpwd = translatePassword(playername, oldpassword);
1074-
std::string newpwd = translatePassword(playername, newpassword);
1177+
if (m_proto_ver >= 25) {
1178+
// get into sudo mode and then send new password to server
1179+
m_password = oldpassword;
1180+
m_new_password = newpassword;
1181+
startAuth(choseAuthMech(m_sudo_auth_methods));
1182+
} else {
1183+
std::string oldpwd = translatePassword(playername, oldpassword);
1184+
std::string newpwd = translatePassword(playername, newpassword);
10751185

1076-
NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
1186+
NetworkPacket pkt(TOSERVER_PASSWORD_LEGACY, 2 * PASSWORD_SIZE);
10771187

1078-
for(u8 i = 0; i < PASSWORD_SIZE; i++) {
1079-
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
1080-
}
1188+
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
1189+
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
1190+
}
10811191

1082-
for(u8 i = 0; i < PASSWORD_SIZE; i++) {
1083-
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
1192+
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
1193+
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
1194+
}
1195+
Send(&pkt);
10841196
}
1085-
1086-
Send(&pkt);
10871197
}
10881198

10891199

src/client.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
351351
void handleCommand_Deprecated(NetworkPacket* pkt);
352352
void handleCommand_Hello(NetworkPacket* pkt);
353353
void handleCommand_AuthAccept(NetworkPacket* pkt);
354+
void handleCommand_AcceptSudoMode(NetworkPacket* pkt);
355+
void handleCommand_DenySudoMode(NetworkPacket* pkt);
354356
void handleCommand_InitLegacy(NetworkPacket* pkt);
355357
void handleCommand_AccessDenied(NetworkPacket* pkt);
356358
void handleCommand_RemoveNode(NetworkPacket* pkt);
@@ -391,6 +393,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
391393
void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
392394
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
393395
void handleCommand_EyeOffset(NetworkPacket* pkt);
396+
void handleCommand_SrpBytesSandB(NetworkPacket* pkt);
394397

395398
void ProcessData(NetworkPacket *pkt);
396399

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

548+
void deleteAuthData();
549+
// helper method shared with clientpackethandler
550+
static AuthMechanism choseAuthMech(const u32 mechs);
551+
545552
void sendLegacyInit(const char* playerName, const char* playerPassword);
553+
void sendInit(const std::string &playerName);
554+
void startAuth(AuthMechanism chosen_auth_mechanism);
546555
void sendDeletedBlocks(std::vector<v3s16> &blocks);
547556
void sendGotBlocks(v3s16 block);
548557
void sendRemovedSounds(std::vector<s32> &soundList);
549558

559+
// Helper function
560+
inline std::string getPlayerName()
561+
{ return m_env.getLocalPlayer()->getName(); }
562+
550563
float m_packetcounter_timer;
551564
float m_connection_reinit_timer;
552565
float m_avg_rtt_timer;
@@ -569,6 +582,8 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
569582
IrrlichtDevice *m_device;
570583
// Server serialization version
571584
u8 m_server_ser_ver;
585+
// Used version of the protocol with server
586+
u8 m_proto_ver;
572587
u16 m_playeritem;
573588
bool m_inventory_updated;
574589
Inventory *m_inventory_from_server;
@@ -584,9 +599,23 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
584599
//s32 m_daynight_i;
585600
//u32 m_daynight_ratio;
586601
std::queue<std::wstring> m_chat_queue;
602+
603+
// The authentication methods we can use to enter sudo mode (=change password)
604+
u32 m_sudo_auth_methods;
605+
587606
// The seed returned by the server in TOCLIENT_INIT is stored here
588607
u64 m_map_seed;
608+
609+
// Auth data
610+
std::string m_playername;
589611
std::string m_password;
612+
// If set, this will be sent (and cleared) upon a TOCLIENT_ACCEPT_SUDO_MODE
613+
std::string m_new_password;
614+
// Usable by auth mechanisms.
615+
AuthMechanism m_chosen_auth_mech;
616+
void * m_auth_data;
617+
618+
590619
bool m_access_denied;
591620
std::string m_access_denied_reason;
592621
std::queue<ClientEvent> m_client_event_queue;

src/client/clientlauncher.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
392392
else
393393
playername = menudata.name;
394394

395-
password = translatePassword(playername, menudata.password);
395+
password = menudata.password;
396396

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

src/clientiface.cpp

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
3131
#include "emerge.h"
3232
#include "serverobject.h" // TODO this is used for cleanup of only
3333
#include "log.h"
34+
#include "util/srp.h"
3435

3536
const char *ClientInterface::statenames[] = {
3637
"Invalid",
@@ -427,10 +428,12 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
427428
//intentionally do nothing
428429
break;
429430
case CS_Created:
430-
switch(event)
431-
{
432-
case CSE_Init:
433-
m_state = CS_InitSent;
431+
switch (event) {
432+
case CSE_Hello:
433+
m_state = CS_HelloSent;
434+
break;
435+
case CSE_InitLegacy:
436+
m_state = CS_AwaitingInit2;
434437
break;
435438
case CSE_Disconnect:
436439
m_state = CS_Disconnecting;
@@ -447,7 +450,32 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
447450
case CS_Denied:
448451
/* don't do anything if in denied state */
449452
break;
450-
case CS_InitSent:
453+
case CS_HelloSent:
454+
switch(event)
455+
{
456+
case CSE_AuthAccept:
457+
m_state = CS_AwaitingInit2;
458+
if ((chosen_mech == AUTH_MECHANISM_SRP)
459+
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
460+
srp_verifier_delete((SRPVerifier *) auth_data);
461+
chosen_mech = AUTH_MECHANISM_NONE;
462+
break;
463+
case CSE_Disconnect:
464+
m_state = CS_Disconnecting;
465+
break;
466+
case CSE_SetDenied:
467+
m_state = CS_Denied;
468+
if ((chosen_mech == AUTH_MECHANISM_SRP)
469+
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
470+
srp_verifier_delete((SRPVerifier *) auth_data);
471+
chosen_mech = AUTH_MECHANISM_NONE;
472+
break;
473+
default:
474+
myerror << "HelloSent: Invalid client state transition! " << event;
475+
throw ClientStateError(myerror.str());
476+
}
477+
break;
478+
case CS_AwaitingInit2:
451479
switch(event)
452480
{
453481
case CSE_GotInit2:
@@ -514,13 +542,38 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
514542
case CSE_Disconnect:
515543
m_state = CS_Disconnecting;
516544
break;
545+
case CSE_SudoSuccess:
546+
m_state = CS_SudoMode;
547+
if ((chosen_mech == AUTH_MECHANISM_SRP)
548+
|| (chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD))
549+
srp_verifier_delete((SRPVerifier *) auth_data);
550+
chosen_mech = AUTH_MECHANISM_NONE;
551+
break;
517552
/* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */
518553
default:
519554
myerror << "Active: Invalid client state transition! " << event;
520555
throw ClientStateError(myerror.str());
521556
break;
522557
}
523558
break;
559+
case CS_SudoMode:
560+
switch(event)
561+
{
562+
case CSE_SetDenied:
563+
m_state = CS_Denied;
564+
break;
565+
case CSE_Disconnect:
566+
m_state = CS_Disconnecting;
567+
break;
568+
case CSE_SudoLeave:
569+
m_state = CS_Active;
570+
break;
571+
default:
572+
myerror << "Active: Invalid client state transition! " << event;
573+
throw ClientStateError(myerror.str());
574+
break;
575+
}
576+
break;
524577
case CS_Disconnecting:
525578
/* we are already disconnecting */
526579
break;

0 commit comments

Comments
 (0)