forked from Mirrorlandia_minetest/minetest
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.
This commit is contained in:
parent
181f7baa45
commit
82e35edff5
@ -208,6 +208,7 @@ LOCAL_SRC_FILES := \
|
|||||||
jni/src/version.cpp \
|
jni/src/version.cpp \
|
||||||
jni/src/voxel.cpp \
|
jni/src/voxel.cpp \
|
||||||
jni/src/voxelalgorithms.cpp \
|
jni/src/voxelalgorithms.cpp \
|
||||||
|
jni/src/util/auth.cpp \
|
||||||
jni/src/util/base64.cpp \
|
jni/src/util/base64.cpp \
|
||||||
jni/src/util/directiontables.cpp \
|
jni/src/util/directiontables.cpp \
|
||||||
jni/src/util/numeric.cpp \
|
jni/src/util/numeric.cpp \
|
||||||
@ -215,6 +216,7 @@ LOCAL_SRC_FILES := \
|
|||||||
jni/src/util/serialize.cpp \
|
jni/src/util/serialize.cpp \
|
||||||
jni/src/util/sha1.cpp \
|
jni/src/util/sha1.cpp \
|
||||||
jni/src/util/string.cpp \
|
jni/src/util/string.cpp \
|
||||||
|
jni/src/util/srp.cpp \
|
||||||
jni/src/util/timetaker.cpp \
|
jni/src/util/timetaker.cpp \
|
||||||
jni/src/unittest/test.cpp \
|
jni/src/unittest/test.cpp \
|
||||||
jni/src/unittest/test_collision.cpp \
|
jni/src/unittest/test_collision.cpp \
|
||||||
@ -243,6 +245,8 @@ LOCAL_SRC_FILES := \
|
|||||||
jni/src/client/clientlauncher.cpp \
|
jni/src/client/clientlauncher.cpp \
|
||||||
jni/src/client/tile.cpp
|
jni/src/client/tile.cpp
|
||||||
|
|
||||||
|
# intentionally kept out (we already build openssl itself): jni/src/util/sha256.c
|
||||||
|
|
||||||
# Network
|
# Network
|
||||||
LOCAL_SRC_FILES += \
|
LOCAL_SRC_FILES += \
|
||||||
jni/src/network/connection.cpp \
|
jni/src/network/connection.cpp \
|
||||||
|
136
src/client.cpp
136
src/client.cpp
@ -22,10 +22,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <IFileSystem.h>
|
#include <IFileSystem.h>
|
||||||
#include "jthread/jmutexautolock.h"
|
#include "jthread/jmutexautolock.h"
|
||||||
|
#include "util/auth.h"
|
||||||
#include "util/directiontables.h"
|
#include "util/directiontables.h"
|
||||||
#include "util/pointedthing.h"
|
#include "util/pointedthing.h"
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
#include "util/string.h"
|
#include "util/string.h"
|
||||||
|
#include "util/srp.h"
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "network/clientopcodes.h"
|
#include "network/clientopcodes.h"
|
||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
@ -255,6 +257,8 @@ Client::Client(
|
|||||||
m_highlighted_pos(0,0,0),
|
m_highlighted_pos(0,0,0),
|
||||||
m_map_seed(0),
|
m_map_seed(0),
|
||||||
m_password(password),
|
m_password(password),
|
||||||
|
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
|
||||||
|
m_auth_data(NULL),
|
||||||
m_access_denied(false),
|
m_access_denied(false),
|
||||||
m_itemdef_received(false),
|
m_itemdef_received(false),
|
||||||
m_nodedef_received(false),
|
m_nodedef_received(false),
|
||||||
@ -404,10 +408,13 @@ void Client::step(float dtime)
|
|||||||
memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
|
memset(pName, 0, PLAYERNAME_SIZE * sizeof(char));
|
||||||
memset(pPassword, 0, PASSWORD_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(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);
|
sendLegacyInit(pName, pPassword);
|
||||||
|
if (LATEST_PROTOCOL_VERSION >= 25)
|
||||||
|
sendInit(myplayer->getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not connected, return
|
// Not connected, return
|
||||||
@ -943,6 +950,39 @@ void Client::interact(u8 action, const PointedThing& pointed)
|
|||||||
Send(&pkt);
|
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)
|
void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
|
||||||
{
|
{
|
||||||
NetworkPacket pkt(TOSERVER_INIT_LEGACY,
|
NetworkPacket pkt(TOSERVER_INIT_LEGACY,
|
||||||
@ -956,6 +996,70 @@ void Client::sendLegacyInit(const char* playerName, const char* playerPassword)
|
|||||||
Send(&pkt);
|
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)
|
void Client::sendDeletedBlocks(std::vector<v3s16> &blocks)
|
||||||
{
|
{
|
||||||
NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
|
NetworkPacket pkt(TOSERVER_DELETEDBLOCKS, 1 + sizeof(v3s16) * blocks.size());
|
||||||
@ -1066,24 +1170,30 @@ void Client::sendChangePassword(const std::string &oldpassword,
|
|||||||
const std::string &newpassword)
|
const std::string &newpassword)
|
||||||
{
|
{
|
||||||
Player *player = m_env.getLocalPlayer();
|
Player *player = m_env.getLocalPlayer();
|
||||||
if(player == NULL)
|
if (player == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string playername = player->getName();
|
std::string playername = player->getName();
|
||||||
std::string oldpwd = translatePassword(playername, oldpassword);
|
if (m_proto_ver >= 25) {
|
||||||
std::string newpwd = translatePassword(playername, newpassword);
|
// 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++) {
|
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
|
||||||
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
|
pkt << (u8) (i < oldpwd.length() ? oldpwd[i] : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u8 i = 0; i < PASSWORD_SIZE; i++) {
|
||||||
|
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
|
||||||
|
}
|
||||||
|
Send(&pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(u8 i = 0; i < PASSWORD_SIZE; i++) {
|
|
||||||
pkt << (u8) (i < newpwd.length() ? newpwd[i] : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Send(&pkt);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
29
src/client.h
29
src/client.h
@ -351,6 +351,8 @@ public:
|
|||||||
void handleCommand_Deprecated(NetworkPacket* pkt);
|
void handleCommand_Deprecated(NetworkPacket* pkt);
|
||||||
void handleCommand_Hello(NetworkPacket* pkt);
|
void handleCommand_Hello(NetworkPacket* pkt);
|
||||||
void handleCommand_AuthAccept(NetworkPacket* pkt);
|
void handleCommand_AuthAccept(NetworkPacket* pkt);
|
||||||
|
void handleCommand_AcceptSudoMode(NetworkPacket* pkt);
|
||||||
|
void handleCommand_DenySudoMode(NetworkPacket* pkt);
|
||||||
void handleCommand_InitLegacy(NetworkPacket* pkt);
|
void handleCommand_InitLegacy(NetworkPacket* pkt);
|
||||||
void handleCommand_AccessDenied(NetworkPacket* pkt);
|
void handleCommand_AccessDenied(NetworkPacket* pkt);
|
||||||
void handleCommand_RemoveNode(NetworkPacket* pkt);
|
void handleCommand_RemoveNode(NetworkPacket* pkt);
|
||||||
@ -391,6 +393,7 @@ public:
|
|||||||
void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
|
void handleCommand_OverrideDayNightRatio(NetworkPacket* pkt);
|
||||||
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
|
void handleCommand_LocalPlayerAnimations(NetworkPacket* pkt);
|
||||||
void handleCommand_EyeOffset(NetworkPacket* pkt);
|
void handleCommand_EyeOffset(NetworkPacket* pkt);
|
||||||
|
void handleCommand_SrpBytesSandB(NetworkPacket* pkt);
|
||||||
|
|
||||||
void ProcessData(NetworkPacket *pkt);
|
void ProcessData(NetworkPacket *pkt);
|
||||||
|
|
||||||
@ -542,11 +545,21 @@ private:
|
|||||||
// Send the item number 'item' as player item to the server
|
// Send the item number 'item' as player item to the server
|
||||||
void sendPlayerItem(u16 item);
|
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 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 sendDeletedBlocks(std::vector<v3s16> &blocks);
|
||||||
void sendGotBlocks(v3s16 block);
|
void sendGotBlocks(v3s16 block);
|
||||||
void sendRemovedSounds(std::vector<s32> &soundList);
|
void sendRemovedSounds(std::vector<s32> &soundList);
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
inline std::string getPlayerName()
|
||||||
|
{ return m_env.getLocalPlayer()->getName(); }
|
||||||
|
|
||||||
float m_packetcounter_timer;
|
float m_packetcounter_timer;
|
||||||
float m_connection_reinit_timer;
|
float m_connection_reinit_timer;
|
||||||
float m_avg_rtt_timer;
|
float m_avg_rtt_timer;
|
||||||
@ -569,6 +582,8 @@ private:
|
|||||||
IrrlichtDevice *m_device;
|
IrrlichtDevice *m_device;
|
||||||
// Server serialization version
|
// Server serialization version
|
||||||
u8 m_server_ser_ver;
|
u8 m_server_ser_ver;
|
||||||
|
// Used version of the protocol with server
|
||||||
|
u8 m_proto_ver;
|
||||||
u16 m_playeritem;
|
u16 m_playeritem;
|
||||||
bool m_inventory_updated;
|
bool m_inventory_updated;
|
||||||
Inventory *m_inventory_from_server;
|
Inventory *m_inventory_from_server;
|
||||||
@ -584,9 +599,23 @@ private:
|
|||||||
//s32 m_daynight_i;
|
//s32 m_daynight_i;
|
||||||
//u32 m_daynight_ratio;
|
//u32 m_daynight_ratio;
|
||||||
std::queue<std::wstring> m_chat_queue;
|
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
|
// The seed returned by the server in TOCLIENT_INIT is stored here
|
||||||
u64 m_map_seed;
|
u64 m_map_seed;
|
||||||
|
|
||||||
|
// Auth data
|
||||||
|
std::string m_playername;
|
||||||
std::string m_password;
|
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;
|
bool m_access_denied;
|
||||||
std::string m_access_denied_reason;
|
std::string m_access_denied_reason;
|
||||||
std::queue<ClientEvent> m_client_event_queue;
|
std::queue<ClientEvent> m_client_event_queue;
|
||||||
|
@ -392,7 +392,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
|||||||
else
|
else
|
||||||
playername = menudata.name;
|
playername = menudata.name;
|
||||||
|
|
||||||
password = translatePassword(playername, menudata.password);
|
password = menudata.password;
|
||||||
|
|
||||||
g_settings->set("name", playername);
|
g_settings->set("name", playername);
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "emerge.h"
|
#include "emerge.h"
|
||||||
#include "serverobject.h" // TODO this is used for cleanup of only
|
#include "serverobject.h" // TODO this is used for cleanup of only
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
#include "util/srp.h"
|
||||||
|
|
||||||
const char *ClientInterface::statenames[] = {
|
const char *ClientInterface::statenames[] = {
|
||||||
"Invalid",
|
"Invalid",
|
||||||
@ -427,10 +428,12 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
|
|||||||
//intentionally do nothing
|
//intentionally do nothing
|
||||||
break;
|
break;
|
||||||
case CS_Created:
|
case CS_Created:
|
||||||
switch(event)
|
switch (event) {
|
||||||
{
|
case CSE_Hello:
|
||||||
case CSE_Init:
|
m_state = CS_HelloSent;
|
||||||
m_state = CS_InitSent;
|
break;
|
||||||
|
case CSE_InitLegacy:
|
||||||
|
m_state = CS_AwaitingInit2;
|
||||||
break;
|
break;
|
||||||
case CSE_Disconnect:
|
case CSE_Disconnect:
|
||||||
m_state = CS_Disconnecting;
|
m_state = CS_Disconnecting;
|
||||||
@ -447,7 +450,32 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
|
|||||||
case CS_Denied:
|
case CS_Denied:
|
||||||
/* don't do anything if in denied state */
|
/* don't do anything if in denied state */
|
||||||
break;
|
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)
|
switch(event)
|
||||||
{
|
{
|
||||||
case CSE_GotInit2:
|
case CSE_GotInit2:
|
||||||
@ -514,6 +542,13 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
|
|||||||
case CSE_Disconnect:
|
case CSE_Disconnect:
|
||||||
m_state = CS_Disconnecting;
|
m_state = CS_Disconnecting;
|
||||||
break;
|
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 */
|
/* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */
|
||||||
default:
|
default:
|
||||||
myerror << "Active: Invalid client state transition! " << event;
|
myerror << "Active: Invalid client state transition! " << event;
|
||||||
@ -521,6 +556,24 @@ void RemoteClient::notifyEvent(ClientStateEvent event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
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:
|
case CS_Disconnecting:
|
||||||
/* we are already disconnecting */
|
/* we are already disconnecting */
|
||||||
break;
|
break;
|
||||||
|
@ -47,96 +47,119 @@ class EmergeManager;
|
|||||||
| Created |
|
| Created |
|
||||||
| |
|
| |
|
||||||
\-----------------/
|
\-----------------/
|
||||||
|
|
| depending of the incoming packet
|
||||||
|
|
+---------------------------------------
|
||||||
+-----------------------------+ invalid playername, password
|
v |
|
||||||
|IN: | or denied by mod
|
+-----------------------------+ +-----------------------------+
|
||||||
| TOSERVER_INIT |------------------------------
|
|IN: | |IN: |
|
||||||
+-----------------------------+ |
|
| TOSERVER_INIT_LEGACY |----- | TOSERVER_INIT | invalid playername,
|
||||||
| |
|
+-----------------------------+ | +-----------------------------+ password (for _LEGACY),
|
||||||
| Auth ok |
|
| | | or denied by mod
|
||||||
| |
|
| Auth ok -------------------+---------------------------------
|
||||||
+-----------------------------+ |
|
| | |
|
||||||
|OUT: | |
|
+-----------------------------+ +-----------------------------+ |
|
||||||
| TOCLIENT_INIT | |
|
|OUT: | |OUT: | |
|
||||||
+-----------------------------+ |
|
| TOCLIENT_INIT_LEGACY | | TOCLIENT_HELLO | |
|
||||||
| |
|
+-----------------------------+ +-----------------------------+ |
|
||||||
v |
|
| | |
|
||||||
/-----------------\ |
|
| | |
|
||||||
| | |
|
v v |
|
||||||
| InitSent | |
|
/-----------------\ /-----------------\ |
|
||||||
| | |
|
| | | | |
|
||||||
\-----------------/ +------------------
|
| AwaitingInit2 |<--------- | HelloSent | |
|
||||||
| | |
|
| | | | | |
|
||||||
+-----------------------------+ +-----------------------------+ |
|
\-----------------/ | \-----------------/ |
|
||||||
|IN: | |OUT: | |
|
| | | |
|
||||||
| TOSERVER_INIT2 | | TOCLIENT_ACCESS_DENIED | |
|
+-----------------------------+ | *-----------------------------* Auth fails |
|
||||||
+-----------------------------+ +-----------------------------+ |
|
|IN: | | |Authentication, depending on |-----------------+
|
||||||
| | |
|
| TOSERVER_INIT2 | | | packet sent by client | |
|
||||||
v v |
|
+-----------------------------+ | *-----------------------------* |
|
||||||
/-----------------\ /-----------------\ |
|
| | | |
|
||||||
| | | | |
|
| | | Authentication |
|
||||||
| InitDone | | Denied | |
|
v | | successful |
|
||||||
| | | | |
|
/-----------------\ | v |
|
||||||
\-----------------/ \-----------------/ |
|
| | | +-----------------------------+ |
|
||||||
| |
|
| InitDone | | |OUT: | |
|
||||||
+-----------------------------+ |
|
| | | | TOCLIENT_AUTH_ACCEPT | |
|
||||||
|OUT: | |
|
\-----------------/ | +-----------------------------+ |
|
||||||
| TOCLIENT_MOVEMENT | |
|
| | | |
|
||||||
| TOCLIENT_ITEMDEF | |
|
+-----------------------------+ --------------------- |
|
||||||
| TOCLIENT_NODEDEF | |
|
|OUT: | |
|
||||||
| TOCLIENT_ANNOUNCE_MEDIA | |
|
| TOCLIENT_MOVEMENT | |
|
||||||
| TOCLIENT_DETACHED_INVENTORY | |
|
| TOCLIENT_ITEMDEF | |
|
||||||
| TOCLIENT_TIME_OF_DAY | |
|
| TOCLIENT_NODEDEF | |
|
||||||
+-----------------------------+ |
|
| TOCLIENT_ANNOUNCE_MEDIA | |
|
||||||
| |
|
| TOCLIENT_DETACHED_INVENTORY | |
|
||||||
| |
|
| TOCLIENT_TIME_OF_DAY | |
|
||||||
| ----------------------------------- |
|
+-----------------------------+ |
|
||||||
v | | |
|
| |
|
||||||
/-----------------\ v |
|
| |
|
||||||
| | +-----------------------------+ |
|
| ----------------------------- |
|
||||||
| DefinitionsSent | |IN: | |
|
v | | |
|
||||||
| | | TOSERVER_REQUEST_MEDIA | |
|
/-----------------\ v |
|
||||||
\-----------------/ | TOSERVER_RECEIVED_MEDIA | |
|
| | +-----------------------------+ |
|
||||||
| +-----------------------------+ |
|
| DefinitionsSent | |IN: | |
|
||||||
| ^ | |
|
| | | TOSERVER_REQUEST_MEDIA | |
|
||||||
| ----------------------------------- |
|
\-----------------/ | TOSERVER_RECEIVED_MEDIA | |
|
||||||
| |
|
| +-----------------------------+ |
|
||||||
+-----------------------------+ |
|
| ^ | |
|
||||||
|IN: | |
|
| ----------------------------- |
|
||||||
| TOSERVER_CLIENT_READY | |
|
| |
|
||||||
+-----------------------------+ |
|
+-----------------------------+ --------------------------------+
|
||||||
| async |
|
|IN: | | |
|
||||||
v mod action |
|
| TOSERVER_CLIENT_READY | v |
|
||||||
+-----------------------------+ (ban,kick) |
|
+-----------------------------+ +-------------------------------+ |
|
||||||
|OUT: | |
|
| |OUT: | |
|
||||||
| TOCLIENT_MOVE_PLAYER | |
|
v | TOCLIENT_ACCESS_DENIED_LEGAGY | |
|
||||||
| TOCLIENT_PRIVILEGES | |
|
+-----------------------------+ +-------------------------------+ |
|
||||||
| TOCLIENT_INVENTORY_FORMSPEC | |
|
|OUT: | | |
|
||||||
| UpdateCrafting | |
|
| TOCLIENT_MOVE_PLAYER | v |
|
||||||
| TOCLIENT_INVENTORY | |
|
| TOCLIENT_PRIVILEGES | /-----------------\ |
|
||||||
| TOCLIENT_HP (opt) | |
|
| TOCLIENT_INVENTORY_FORMSPEC | | | |
|
||||||
| TOCLIENT_BREATH | |
|
| UpdateCrafting | | Denied | |
|
||||||
| TOCLIENT_DEATHSCREEN | |
|
| TOCLIENT_INVENTORY | | | |
|
||||||
+-----------------------------+ |
|
| TOCLIENT_HP (opt) | \-----------------/ |
|
||||||
| |
|
| TOCLIENT_BREATH | |
|
||||||
v |
|
| TOCLIENT_DEATHSCREEN | |
|
||||||
/-----------------\ |
|
+-----------------------------+ |
|
||||||
| |------------------------------------------------------
|
| |
|
||||||
| Active |
|
v |
|
||||||
| |----------------------------------
|
/-----------------\ async mod action (ban, kick) |
|
||||||
\-----------------/ timeout |
|
| |---------------------------------------------------------------
|
||||||
| +-----------------------------+
|
---->| Active |
|
||||||
| |OUT: |
|
| | |----------------------------------------------
|
||||||
| | TOCLIENT_DISCONNECT |
|
| \-----------------/ timeout |
|
||||||
| +-----------------------------+
|
| | | +-----------------------------+
|
||||||
| |
|
| | | |OUT: |
|
||||||
| v
|
| | | | TOCLIENT_DISCONNECT |
|
||||||
+-----------------------------+ /-----------------\
|
| | | +-----------------------------+
|
||||||
|IN: | | |
|
| | | |
|
||||||
| TOSERVER_DISCONNECT |------------------->| Disconnecting |
|
| | | v
|
||||||
+-----------------------------+ | |
|
| | +-----------------------------+ /-----------------\
|
||||||
\-----------------/
|
| | |IN: | | |
|
||||||
|
| | | TOSERVER_DISCONNECT |------------------->| Disconnecting |
|
||||||
|
| | +-----------------------------+ | |
|
||||||
|
| | \-----------------/
|
||||||
|
| | any auth packet which was
|
||||||
|
| | allowed in TOCLIENT_AUTH_ACCEPT
|
||||||
|
| v
|
||||||
|
| *-----------------------------* Auth +-------------------------------+
|
||||||
|
| |Authentication, depending on | succeeds |OUT: |
|
||||||
|
| | packet sent by client |---------->| TOCLIENT_ACCEPT_SUDO_MODE |
|
||||||
|
| *-----------------------------* +-------------------------------+
|
||||||
|
| | |
|
||||||
|
| | Auth fails /-----------------\
|
||||||
|
| v | |
|
||||||
|
| +-------------------------------+ | SudoMode |
|
||||||
|
| |OUT: | | |
|
||||||
|
| | TOCLIENT_DENY_SUDO_MODE | \-----------------/
|
||||||
|
| +-------------------------------+ |
|
||||||
|
| | v
|
||||||
|
| | +-----------------------------+
|
||||||
|
| | sets password accordingly |IN: |
|
||||||
|
-------------------+-------------------------------| TOSERVER_FIRST_SRP |
|
||||||
|
+-----------------------------+
|
||||||
|
|
||||||
*/
|
*/
|
||||||
namespace con {
|
namespace con {
|
||||||
class Connection;
|
class Connection;
|
||||||
@ -150,19 +173,25 @@ enum ClientState
|
|||||||
CS_Disconnecting,
|
CS_Disconnecting,
|
||||||
CS_Denied,
|
CS_Denied,
|
||||||
CS_Created,
|
CS_Created,
|
||||||
CS_InitSent,
|
CS_AwaitingInit2,
|
||||||
|
CS_HelloSent,
|
||||||
CS_InitDone,
|
CS_InitDone,
|
||||||
CS_DefinitionsSent,
|
CS_DefinitionsSent,
|
||||||
CS_Active
|
CS_Active,
|
||||||
|
CS_SudoMode
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ClientStateEvent
|
enum ClientStateEvent
|
||||||
{
|
{
|
||||||
CSE_Init,
|
CSE_Hello,
|
||||||
|
CSE_AuthAccept,
|
||||||
|
CSE_InitLegacy,
|
||||||
CSE_GotInit2,
|
CSE_GotInit2,
|
||||||
CSE_SetDenied,
|
CSE_SetDenied,
|
||||||
CSE_SetDefinitionsSent,
|
CSE_SetDefinitionsSent,
|
||||||
CSE_SetClientReady,
|
CSE_SetClientReady,
|
||||||
|
CSE_SudoSuccess,
|
||||||
|
CSE_SudoLeave,
|
||||||
CSE_Disconnect
|
CSE_Disconnect
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -201,10 +230,24 @@ public:
|
|||||||
//
|
//
|
||||||
u16 net_proto_version;
|
u16 net_proto_version;
|
||||||
|
|
||||||
|
/* Authentication information */
|
||||||
|
std::string enc_pwd;
|
||||||
|
AuthMechanism chosen_mech;
|
||||||
|
void * auth_data;
|
||||||
|
u32 allowed_auth_mechs;
|
||||||
|
u32 allowed_sudo_mechs;
|
||||||
|
|
||||||
|
bool isSudoMechAllowed(AuthMechanism mech)
|
||||||
|
{ return allowed_sudo_mechs & mech; }
|
||||||
|
bool isMechAllowed(AuthMechanism mech)
|
||||||
|
{ return allowed_auth_mechs & mech; }
|
||||||
|
|
||||||
RemoteClient():
|
RemoteClient():
|
||||||
peer_id(PEER_ID_INEXISTENT),
|
peer_id(PEER_ID_INEXISTENT),
|
||||||
serialization_version(SER_FMT_VER_INVALID),
|
serialization_version(SER_FMT_VER_INVALID),
|
||||||
net_proto_version(0),
|
net_proto_version(0),
|
||||||
|
chosen_mech(AUTH_MECHANISM_NONE),
|
||||||
|
auth_data(NULL),
|
||||||
m_time_from_building(9999),
|
m_time_from_building(9999),
|
||||||
m_pending_serialization_version(SER_FMT_VER_INVALID),
|
m_pending_serialization_version(SER_FMT_VER_INVALID),
|
||||||
m_state(CS_Created),
|
m_state(CS_Created),
|
||||||
@ -303,7 +346,6 @@ public:
|
|||||||
/* get uptime */
|
/* get uptime */
|
||||||
u32 uptime();
|
u32 uptime();
|
||||||
|
|
||||||
|
|
||||||
/* set version information */
|
/* set version information */
|
||||||
void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) {
|
void setVersionInfo(u8 major, u8 minor, u8 patch, std::string full) {
|
||||||
m_version_major = major;
|
m_version_major = major;
|
||||||
|
@ -28,8 +28,8 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] =
|
|||||||
null_command_handler, // 0x01
|
null_command_handler, // 0x01
|
||||||
{ "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
|
{ "TOCLIENT_HELLO", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_Hello }, // 0x02
|
||||||
{ "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
|
{ "TOCLIENT_AUTH_ACCEPT", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AuthAccept }, // 0x03
|
||||||
null_command_handler, // 0x04
|
{ "TOCLIENT_ACCEPT_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_AcceptSudoMode}, // 0x04
|
||||||
null_command_handler, // 0x05
|
{ "TOCLIENT_DENY_SUDO_MODE", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DenySudoMode}, // 0x05
|
||||||
null_command_handler, // 0x06
|
null_command_handler, // 0x06
|
||||||
null_command_handler, // 0x07
|
null_command_handler, // 0x07
|
||||||
null_command_handler, // 0x08
|
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_LOCAL_PLAYER_ANIMATIONS", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_LocalPlayerAnimations }, // 0x51
|
||||||
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
|
{ "TOCLIENT_EYE_OFFSET", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_EyeOffset }, // 0x52
|
||||||
{ "TOCLIENT_DELETE_PARTICLESPAWNER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeleteParticleSpawner }, // 0x53
|
{ "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 };
|
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, // 0x00
|
||||||
null_command_factory, // 0x01
|
null_command_factory, // 0x01
|
||||||
null_command_factory, // 0x02
|
{ "TOSERVER_INIT", 1, false }, // 0x02
|
||||||
null_command_factory, // 0x03
|
null_command_factory, // 0x03
|
||||||
null_command_factory, // 0x04
|
null_command_factory, // 0x04
|
||||||
null_command_factory, // 0x05
|
null_command_factory, // 0x05
|
||||||
@ -129,7 +142,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
|
|||||||
null_command_factory, // 0x0c
|
null_command_factory, // 0x0c
|
||||||
null_command_factory, // 0x0d
|
null_command_factory, // 0x0d
|
||||||
null_command_factory, // 0x0e
|
null_command_factory, // 0x0e
|
||||||
{ "TOSERVER_INIT", 1, false }, // 0x0F
|
null_command_factory, // 0x0F
|
||||||
{ "TOSERVER_INIT_LEGACY", 1, false }, // 0x10
|
{ "TOSERVER_INIT_LEGACY", 1, false }, // 0x10
|
||||||
{ "TOSERVER_INIT2", 1, true }, // 0x11
|
{ "TOSERVER_INIT2", 1, true }, // 0x11
|
||||||
null_command_factory, // 0x12
|
null_command_factory, // 0x12
|
||||||
@ -175,11 +188,26 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] =
|
|||||||
{ "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a
|
{ "TOSERVER_REMOVED_SOUNDS", 1, true }, // 0x3a
|
||||||
{ "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b
|
{ "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b
|
||||||
{ "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c
|
{ "TOSERVER_INVENTORY_FIELDS", 0, true }, // 0x3c
|
||||||
{ "TOSERVER_PASSWORD", 0, true }, // 0x3d
|
null_command_factory, // 0x3d
|
||||||
null_command_factory, // 0x3e
|
null_command_factory, // 0x3e
|
||||||
null_command_factory, // 0x3f
|
null_command_factory, // 0x3f
|
||||||
{ "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
|
{ "TOSERVER_REQUEST_MEDIA", 1, true }, // 0x40
|
||||||
{ "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41
|
{ "TOSERVER_RECEIVED_MEDIA", 1, true }, // 0x41
|
||||||
{ "TOSERVER_BREATH", 0, true }, // 0x42
|
{ "TOSERVER_BREATH", 0, true }, // 0x42
|
||||||
{ "TOSERVER_CLIENT_READY", 0, true }, // 0x43
|
{ "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
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "strfnd.h"
|
#include "strfnd.h"
|
||||||
#include "network/clientopcodes.h"
|
#include "network/clientopcodes.h"
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
|
#include "util/srp.h"
|
||||||
|
|
||||||
void Client::handleCommand_Deprecated(NetworkPacket* pkt)
|
void Client::handleCommand_Deprecated(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
@ -44,10 +45,16 @@ void Client::handleCommand_Hello(NetworkPacket* pkt)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
u8 deployed;
|
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 "
|
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)) {
|
if (!ser_ver_supported(deployed)) {
|
||||||
infostream << "Client: TOCLIENT_HELLO: Server sent "
|
infostream << "Client: TOCLIENT_HELLO: Server sent "
|
||||||
@ -56,14 +63,43 @@ void Client::handleCommand_Hello(NetworkPacket* pkt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_server_ser_ver = deployed;
|
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)
|
void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
|
m_chosen_auth_mech = AUTH_MECHANISM_NONE;
|
||||||
|
deleteAuthData();
|
||||||
|
|
||||||
v3f playerpos;
|
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);
|
playerpos -= v3f(0, BS / 2, 0);
|
||||||
|
|
||||||
@ -82,7 +118,28 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
|
|||||||
|
|
||||||
m_state = LC_Init;
|
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)
|
void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
if (pkt->getSize() < 1)
|
if (pkt->getSize() < 1)
|
||||||
@ -101,6 +158,7 @@ void Client::handleCommand_InitLegacy(NetworkPacket* pkt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_server_ser_ver = deployed;
|
m_server_ser_ver = deployed;
|
||||||
|
m_proto_ver = deployed;
|
||||||
|
|
||||||
// Get player position
|
// Get player position
|
||||||
v3s16 playerpos_s16(0, BS * 2 + BS * 20, 0);
|
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;
|
*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);
|
||||||
|
}
|
||||||
|
@ -41,8 +41,10 @@ public:
|
|||||||
u16 getPeerId() { return m_peer_id; }
|
u16 getPeerId() { return m_peer_id; }
|
||||||
u16 getCommand() { return m_command; }
|
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);
|
char* getString(u32 from_offset);
|
||||||
|
// major difference to putCString(): doesn't write len into the buffer
|
||||||
void putRawString(const char* src, u32 len);
|
void putRawString(const char* src, u32 len);
|
||||||
|
|
||||||
NetworkPacket& operator>>(std::string& dst);
|
NetworkPacket& operator>>(std::string& dst);
|
||||||
|
@ -120,11 +120,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
permit translation
|
permit translation
|
||||||
Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and
|
Add TOCLIENT_DELETE_PARTICLESPAWNER (0x53), fixing the u16 read and
|
||||||
reading u32
|
reading u32
|
||||||
Add TOSERVER_INIT new opcode (0x02) for client presentation to server
|
Add new opcode TOSERVER_INIT for client presentation to server
|
||||||
Add TOSERVER_AUTH new opcode (0x03) for client authentication
|
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
|
Add TOCLIENT_HELLO for presenting server to client after client
|
||||||
presentation
|
presentation
|
||||||
Add TOCLIENT_AUTH_ACCEPT to accept connexion from client
|
Add TOCLIENT_AUTH_ACCEPT to accept connection from client
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define LATEST_PROTOCOL_VERSION 24
|
#define LATEST_PROTOCOL_VERSION 24
|
||||||
@ -151,14 +155,31 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
enum ToClientCommand
|
enum ToClientCommand
|
||||||
{
|
{
|
||||||
TOCLIENT_HELLO = 0x02,
|
TOCLIENT_HELLO = 0x02,
|
||||||
TOCLIENT_AUTH_ACCEPT = 0x03,
|
|
||||||
TOCLIENT_ACCESS_DENIED = 0x0A,
|
|
||||||
/*
|
/*
|
||||||
u16 command
|
Sent after TOSERVER_INIT.
|
||||||
u16 reason_length
|
|
||||||
wstring reason
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
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,
|
TOCLIENT_INIT_LEGACY = 0x10,
|
||||||
/*
|
/*
|
||||||
Server's reply to TOSERVER_INIT.
|
Server's reply to TOSERVER_INIT.
|
||||||
@ -173,7 +194,11 @@ enum ToClientCommand
|
|||||||
NOTE: The position in here is deprecated; position is
|
NOTE: The position in here is deprecated; position is
|
||||||
explicitly sent afterwards
|
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_BLOCKDATA = 0x20, //TODO: Multiple blocks
|
||||||
TOCLIENT_ADDNODE = 0x21,
|
TOCLIENT_ADDNODE = 0x21,
|
||||||
/*
|
/*
|
||||||
@ -589,7 +614,16 @@ enum ToClientCommand
|
|||||||
u32 id
|
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
|
enum ToServerCommand
|
||||||
@ -598,18 +632,11 @@ enum ToServerCommand
|
|||||||
/*
|
/*
|
||||||
Sent first after connected.
|
Sent first after connected.
|
||||||
|
|
||||||
[0] u16 TOSERVER_INIT
|
|
||||||
[2] u8 SER_FMT_VER_HIGHEST_READ
|
[2] u8 SER_FMT_VER_HIGHEST_READ
|
||||||
[3] u8 compression_modes
|
[3] u8 compression_modes
|
||||||
*/
|
[4] u16 minimum supported network protocol version
|
||||||
|
[6] u16 maximum supported network protocol version
|
||||||
TOSERVER_AUTH = 0x03,
|
[8] std::string player name
|
||||||
/*
|
|
||||||
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)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
TOSERVER_INIT_LEGACY = 0x10,
|
TOSERVER_INIT_LEGACY = 0x10,
|
||||||
@ -817,15 +844,6 @@ enum ToServerCommand
|
|||||||
u8[len] field value
|
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,
|
TOSERVER_REQUEST_MEDIA = 0x40,
|
||||||
/*
|
/*
|
||||||
u16 command
|
u16 command
|
||||||
@ -857,7 +875,49 @@ enum ToServerCommand
|
|||||||
u8[len] full_version_string
|
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 {
|
enum AccessDeniedCode {
|
||||||
|
@ -26,8 +26,8 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] =
|
|||||||
{
|
{
|
||||||
null_command_handler, // 0x00 (never use this)
|
null_command_handler, // 0x00 (never use this)
|
||||||
null_command_handler, // 0x01
|
null_command_handler, // 0x01
|
||||||
{ "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
|
{ "TOSERVER_INIT", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Init }, // 0x02
|
||||||
{ "TOSERVER_AUTH", TOSERVER_STATE_NOT_CONNECTED, &Server::handleCommand_Auth }, // 0x03
|
null_command_handler, // 0x03
|
||||||
null_command_handler, // 0x04
|
null_command_handler, // 0x04
|
||||||
null_command_handler, // 0x05
|
null_command_handler, // 0x05
|
||||||
null_command_handler, // 0x06
|
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_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a
|
||||||
{ "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
|
{ "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b
|
||||||
{ "TOSERVER_INVENTORY_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_InventoryFields }, // 0x3c
|
{ "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, // 0x3e
|
||||||
null_command_handler, // 0x3f
|
null_command_handler, // 0x3f
|
||||||
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
|
{ "TOSERVER_REQUEST_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_RequestMedia }, // 0x40
|
||||||
{ "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
|
{ "TOSERVER_RECEIVED_MEDIA", TOSERVER_STATE_STARTUP, &Server::handleCommand_ReceivedMedia }, // 0x41
|
||||||
{ "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42
|
{ "TOSERVER_BREATH", TOSERVER_STATE_INGAME, &Server::handleCommand_Breath }, // 0x42
|
||||||
{ "TOSERVER_CLIENT_READY", TOSERVER_STATE_STARTUP, &Server::handleCommand_ClientReady }, // 0x43
|
{ "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 };
|
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, // 0x00
|
||||||
null_command_factory, // 0x01
|
null_command_factory, // 0x01
|
||||||
{ "TOCLIENT_HELLO", 0, true }, // 0x02
|
null_command_factory, // 0x02
|
||||||
{ "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03
|
{ "TOCLIENT_AUTH_ACCEPT", 0, true }, // 0x03
|
||||||
null_command_factory, // 0x04
|
{ "TOCLIENT_ACCEPT_SUDO_MODE", 0, true }, // 0x04
|
||||||
null_command_factory, // 0x05
|
{ "TOCLIENT_DENY_SUDO_MODE", 0, true }, // 0x05
|
||||||
null_command_factory, // 0x06
|
null_command_factory, // 0x06
|
||||||
null_command_factory, // 0x07
|
null_command_factory, // 0x07
|
||||||
null_command_factory, // 0x08
|
null_command_factory, // 0x08
|
||||||
@ -182,4 +197,17 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] =
|
|||||||
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
|
{ "TOCLIENT_LOCAL_PLAYER_ANIMATIONS", 0, true }, // 0x51
|
||||||
{ "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
|
{ "TOCLIENT_EYE_OFFSET", 0, true }, // 0x52
|
||||||
{ "TOCLIENT_DELETE_PARTICLESPAWNER", 0, true }, // 0x53
|
{ "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
|
||||||
};
|
};
|
||||||
|
@ -32,9 +32,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "network/networkprotocol.h"
|
#include "network/networkprotocol.h"
|
||||||
#include "network/serveropcodes.h"
|
#include "network/serveropcodes.h"
|
||||||
|
#include "util/auth.h"
|
||||||
#include "util/base64.h"
|
#include "util/base64.h"
|
||||||
#include "util/pointedthing.h"
|
#include "util/pointedthing.h"
|
||||||
#include "util/serialize.h"
|
#include "util/serialize.h"
|
||||||
|
#include "util/srp.h"
|
||||||
|
|
||||||
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
|
void Server::handleCommand_Deprecated(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
@ -92,13 +94,14 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
|
|||||||
u8 compression_modes;
|
u8 compression_modes;
|
||||||
u16 min_net_proto_version = 0;
|
u16 min_net_proto_version = 0;
|
||||||
u16 max_net_proto_version;
|
u16 max_net_proto_version;
|
||||||
|
std::string playerName;
|
||||||
|
|
||||||
*pkt >> client_max >> compression_modes >> min_net_proto_version
|
*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;
|
u8 our_max = SER_FMT_VER_HIGHEST_READ;
|
||||||
// Use the highest version supported by both
|
// 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 it's lower than the lowest supported, give up.
|
||||||
if (deployed < SER_FMT_VER_LOWEST)
|
if (deployed < SER_FMT_VER_LOWEST)
|
||||||
deployed = SER_FMT_VER_INVALID;
|
deployed = SER_FMT_VER_INVALID;
|
||||||
@ -137,7 +140,7 @@ void Server::handleCommand_Init(NetworkPacket* pkt)
|
|||||||
|
|
||||||
client->net_proto_version = net_proto_version;
|
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 ||
|
if (net_proto_version < 25 ||
|
||||||
net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
|
net_proto_version < SERVER_PROTOCOL_VERSION_MIN ||
|
||||||
net_proto_version > SERVER_PROTOCOL_VERSION_MAX) {
|
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
|
/*
|
||||||
|
Validate player name
|
||||||
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;
|
|
||||||
|
|
||||||
const char* playername = playerName.c_str();
|
const char* playername = playerName.c_str();
|
||||||
|
|
||||||
if (playerName.size() > PLAYERNAME_SIZE) {
|
if (playerName.size() > PLAYERNAME_SIZE) {
|
||||||
@ -202,6 +178,11 @@ void Server::handleCommand_Auth(NetworkPacket* pkt)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_clients.setPlayerName(pkt->getPeerId(), playername);
|
||||||
|
//TODO (later) case insensitivity
|
||||||
|
|
||||||
|
std::string legacyPlayerNameCasing = playerName;
|
||||||
|
|
||||||
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
|
if (!isSingleplayer() && strcasecmp(playername, "singleplayer") == 0) {
|
||||||
actionstream << "Server: Player with the name \"singleplayer\" "
|
actionstream << "Server: Player with the name \"singleplayer\" "
|
||||||
<< "tried to connect from " << addr_s << std::endl;
|
<< "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 "
|
infostream << "Server: New connection: \"" << playerName << "\" from "
|
||||||
<< addr_s << " (peer_id=" << pkt->getPeerId() << ")" << std::endl;
|
<< 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.
|
// Enforce user limit.
|
||||||
// Don't enforce for users that have some admin right
|
// Don't enforce for users that have some admin right
|
||||||
if (m_clients.getClientIDs(CS_Created).size() >= g_settings->getU16("max_users") &&
|
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, "privs") &&
|
||||||
!checkPriv(playername, "password") &&
|
!checkPriv(playername, "password") &&
|
||||||
playername != g_settings->get("name")) {
|
playername != g_settings->get("name")) {
|
||||||
actionstream << "Server: " << playername << " tried to join, but there"
|
actionstream << "Server: " << playername << " tried to join from "
|
||||||
<< " are already max_users="
|
<< addr_s << ", but there" << " are already max_users="
|
||||||
<< g_settings->getU16("max_users") << " players." << std::endl;
|
<< g_settings->getU16("max_users") << " players." << std::endl;
|
||||||
DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
|
DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_TOO_MANY_USERS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string checkpwd; // Password hash to check against
|
/*
|
||||||
bool has_auth = m_script->getAuth(playername, &checkpwd, NULL);
|
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;
|
||||||
|
|
||||||
// If no authentication info exists for user, create it
|
client->chosen_mech = AUTH_MECHANISM_NONE;
|
||||||
if (!has_auth) {
|
|
||||||
if (!isSingleplayer() &&
|
if (has_auth) {
|
||||||
g_settings->getBool("disallow_empty_password") &&
|
std::vector<std::string> pwd_components = str_split(encpwd, '#');
|
||||||
playerPassword.empty()) {
|
if (pwd_components.size() == 4) {
|
||||||
actionstream << "Server: " << playerName
|
if (pwd_components[1] == "1") { // 1 means srp
|
||||||
<< " supplied empty password" << std::endl;
|
auth_mechs |= AUTH_MECHANISM_SRP;
|
||||||
DenyAccess(pkt->getPeerId(), SERVER_ACCESSDENIED_EMPTY_PASSWORD);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
std::string raw_default_password =
|
} else {
|
||||||
g_settings->get("default_password");
|
std::string default_password = g_settings->get("default_password");
|
||||||
std::string initial_password =
|
if (default_password.length() == 0) {
|
||||||
translatePassword(playername, raw_default_password);
|
auth_mechs |= AUTH_MECHANISM_FIRST_SRP;
|
||||||
|
} else {
|
||||||
// If default_password is empty, allow any initial password
|
// Take care of default passwords.
|
||||||
if (raw_default_password.length() == 0)
|
client->enc_pwd = getSRPVerifier(playerName, default_password);
|
||||||
initial_password = playerPassword.c_str();
|
auth_mechs |= AUTH_MECHANISM_SRP;
|
||||||
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
NetworkPacket resp_pkt(TOCLIENT_HELLO, 1 + 4
|
||||||
<< g_settings->getFloat("dedicated_server_step");
|
+ legacyPlayerNameCasing.size(), pkt->getPeerId());
|
||||||
|
|
||||||
|
resp_pkt << deployed << auth_mechs << legacyPlayerNameCasing;
|
||||||
|
|
||||||
Send(&resp_pkt);
|
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)
|
void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
|
||||||
@ -354,7 +319,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
|
|||||||
return;
|
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;
|
<< pkt->getPeerId() << ")" << std::endl;
|
||||||
|
|
||||||
// Do not allow multiple players in simple singleplayer mode.
|
// 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;
|
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: "
|
verbosestream << "Server: " << addr_s << ": Protocol version: min: "
|
||||||
<< min_net_proto_version << ", max: " << max_net_proto_version
|
<< min_net_proto_version << ", max: " << max_net_proto_version
|
||||||
<< ", chosen: " << net_proto_version << std::endl;
|
<< ", chosen: " << net_proto_version << std::endl;
|
||||||
@ -624,7 +593,7 @@ void Server::handleCommand_Init_Legacy(NetworkPacket* pkt)
|
|||||||
<< g_settings->getFloat("dedicated_server_step");
|
<< g_settings->getFloat("dedicated_server_step");
|
||||||
|
|
||||||
Send(&resp_pkt);
|
Send(&resp_pkt);
|
||||||
m_clients.event(pkt->getPeerId(), CSE_Init);
|
m_clients.event(pkt->getPeerId(), CSE_InitLegacy);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Server::handleCommand_Init2(NetworkPacket* pkt)
|
void Server::handleCommand_Init2(NetworkPacket* pkt)
|
||||||
@ -1223,33 +1192,33 @@ void Server::handleCommand_Breath(NetworkPacket* pkt)
|
|||||||
|
|
||||||
void Server::handleCommand_Password(NetworkPacket* pkt)
|
void Server::handleCommand_Password(NetworkPacket* pkt)
|
||||||
{
|
{
|
||||||
if ((pkt->getCommand() == TOSERVER_PASSWORD && pkt->getSize() < 4) ||
|
if (pkt->getSize() != PASSWORD_SIZE * 2)
|
||||||
pkt->getSize() != PASSWORD_SIZE * 2)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string oldpwd;
|
std::string oldpwd;
|
||||||
std::string newpwd;
|
std::string newpwd;
|
||||||
|
|
||||||
if (pkt->getCommand() == TOSERVER_PASSWORD) {
|
// Deny for clients using the new protocol
|
||||||
*pkt >> oldpwd >> newpwd;
|
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++) {
|
for (u16 i = 0; i < PASSWORD_SIZE - 1; i++) {
|
||||||
char c = pkt->getChar(PASSWORD_SIZE + i);
|
char c = pkt->getChar(i);
|
||||||
if (c == 0)
|
if (c == 0)
|
||||||
break;
|
break;
|
||||||
newpwd += c;
|
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());
|
Player *player = m_env->getPlayer(pkt->getPeerId());
|
||||||
@ -1848,3 +1817,258 @@ void Server::handleCommand_InventoryFields(NetworkPacket* pkt)
|
|||||||
|
|
||||||
m_script->on_playerReceiveFields(playersao, formname, fields);
|
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);
|
||||||
|
}
|
||||||
|
@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "tool.h"
|
#include "tool.h"
|
||||||
#include "filesys.h"
|
#include "filesys.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "util/auth.h"
|
||||||
|
|
||||||
// debug(...)
|
// debug(...)
|
||||||
// Writes a line to dstream
|
// Writes a line to dstream
|
||||||
|
@ -2537,6 +2537,13 @@ void Server::RespawnPlayer(u16 peer_id)
|
|||||||
playersao->setPos(pos);
|
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)
|
void Server::DenyAccess(u16 peer_id, AccessDeniedCode reason, const std::string &custom_reason)
|
||||||
{
|
{
|
||||||
@ -2558,6 +2565,37 @@ void Server::DenyAccess_Legacy(u16 peer_id, const std::wstring &reason)
|
|||||||
m_con.DisconnectPeer(peer_id);
|
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)
|
void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason)
|
||||||
{
|
{
|
||||||
DSTACK(__FUNCTION_NAME);
|
DSTACK(__FUNCTION_NAME);
|
||||||
|
@ -193,7 +193,6 @@ public:
|
|||||||
void handleCommand_Null(NetworkPacket* pkt) {};
|
void handleCommand_Null(NetworkPacket* pkt) {};
|
||||||
void handleCommand_Deprecated(NetworkPacket* pkt);
|
void handleCommand_Deprecated(NetworkPacket* pkt);
|
||||||
void handleCommand_Init(NetworkPacket* pkt);
|
void handleCommand_Init(NetworkPacket* pkt);
|
||||||
void handleCommand_Auth(NetworkPacket* pkt);
|
|
||||||
void handleCommand_Init_Legacy(NetworkPacket* pkt);
|
void handleCommand_Init_Legacy(NetworkPacket* pkt);
|
||||||
void handleCommand_Init2(NetworkPacket* pkt);
|
void handleCommand_Init2(NetworkPacket* pkt);
|
||||||
void handleCommand_RequestMedia(NetworkPacket* pkt);
|
void handleCommand_RequestMedia(NetworkPacket* pkt);
|
||||||
@ -213,6 +212,9 @@ public:
|
|||||||
void handleCommand_RemovedSounds(NetworkPacket* pkt);
|
void handleCommand_RemovedSounds(NetworkPacket* pkt);
|
||||||
void handleCommand_NodeMetaFields(NetworkPacket* pkt);
|
void handleCommand_NodeMetaFields(NetworkPacket* pkt);
|
||||||
void handleCommand_InventoryFields(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);
|
void ProcessData(NetworkPacket *pkt);
|
||||||
|
|
||||||
@ -360,7 +362,9 @@ public:
|
|||||||
void peerAdded(con::Peer *peer);
|
void peerAdded(con::Peer *peer);
|
||||||
void deletingPeer(con::Peer *peer, bool timeout);
|
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 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);
|
void DenyAccess_Legacy(u16 peer_id, const std::wstring &reason);
|
||||||
bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
|
bool getClientConInfo(u16 peer_id, con::rtt_stat_type type,float* retval);
|
||||||
bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
|
bool getClientInfo(u16 peer_id,ClientState* state, u32* uptime,
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
set(UTIL_SRCS
|
set(UTIL_SRCS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/sha256.c
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/string.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp
|
||||||
PARENT_SCOPE)
|
PARENT_SCOPE)
|
||||||
|
|
||||||
|
126
src/util/auth.cpp
Normal file
126
src/util/auth.cpp
Normal file
@ -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
src/util/auth.h
Normal file
37
src/util/auth.h
Normal file
@ -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
src/util/md32_common.h
Normal file
428
src/util/md32_common.h
Normal file
@ -0,0 +1,428 @@
|
|||||||
|
/* md32_common.h file used by sha256 implementation */
|
||||||
|
/* ====================================================================
|
||||||
|
* Copyright (c) 1999-2007 The OpenSSL Project. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 above 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 acknowledgment:
|
||||||
|
* "This product includes software developed by the OpenSSL Project
|
||||||
|
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
|
||||||
|
*
|
||||||
|
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
||||||
|
* endorse or promote products derived from this software without
|
||||||
|
* prior written permission. For written permission, please contact
|
||||||
|
* licensing@OpenSSL.org.
|
||||||
|
*
|
||||||
|
* 5. Products derived from this software may not be called "OpenSSL"
|
||||||
|
* nor may "OpenSSL" appear in their names without prior written
|
||||||
|
* permission of the OpenSSL Project.
|
||||||
|
*
|
||||||
|
* 6. Redistributions of any form whatsoever must retain the following
|
||||||
|
* acknowledgment:
|
||||||
|
* "This product includes software developed by the OpenSSL Project
|
||||||
|
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
||||||
|
* EXPRESSED 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 OpenSSL PROJECT OR
|
||||||
|
* ITS 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.
|
||||||
|
* ====================================================================
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* This is a generic 32 bit "collector" for message digest algorithms.
|
||||||
|
* Whenever needed it collects input character stream into chunks of
|
||||||
|
* 32 bit values and invokes a block function that performs actual hash
|
||||||
|
* calculations.
|
||||||
|
*
|
||||||
|
* Porting guide.
|
||||||
|
*
|
||||||
|
* Obligatory macros:
|
||||||
|
*
|
||||||
|
* DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
|
||||||
|
* this macro defines byte order of input stream.
|
||||||
|
* HASH_CBLOCK
|
||||||
|
* size of a unit chunk HASH_BLOCK operates on.
|
||||||
|
* HASH_LONG
|
||||||
|
* has to be at lest 32 bit wide, if it's wider, then
|
||||||
|
* HASH_LONG_LOG2 *has to* be defined along
|
||||||
|
* HASH_CTX
|
||||||
|
* context structure that at least contains following
|
||||||
|
* members:
|
||||||
|
* typedef struct {
|
||||||
|
* ...
|
||||||
|
* HASH_LONG Nl,Nh;
|
||||||
|
* either {
|
||||||
|
* HASH_LONG data[HASH_LBLOCK];
|
||||||
|
* unsigned char data[HASH_CBLOCK];
|
||||||
|
* };
|
||||||
|
* unsigned int num;
|
||||||
|
* ...
|
||||||
|
* } HASH_CTX;
|
||||||
|
* data[] vector is expected to be zeroed upon first call to
|
||||||
|
* HASH_UPDATE.
|
||||||
|
* HASH_UPDATE
|
||||||
|
* name of "Update" function, implemented here.
|
||||||
|
* HASH_TRANSFORM
|
||||||
|
* name of "Transform" function, implemented here.
|
||||||
|
* HASH_FINAL
|
||||||
|
* name of "Final" function, implemented here.
|
||||||
|
* HASH_BLOCK_DATA_ORDER
|
||||||
|
* name of "block" function capable of treating *unaligned* input
|
||||||
|
* message in original (data) byte order, implemented externally.
|
||||||
|
* HASH_MAKE_STRING
|
||||||
|
* macro convering context variables to an ASCII hash string.
|
||||||
|
*
|
||||||
|
* MD5 example:
|
||||||
|
*
|
||||||
|
* #define DATA_ORDER_IS_LITTLE_ENDIAN
|
||||||
|
*
|
||||||
|
* #define HASH_LONG MD5_LONG
|
||||||
|
* #define HASH_LONG_LOG2 MD5_LONG_LOG2
|
||||||
|
* #define HASH_CTX MD5_CTX
|
||||||
|
* #define HASH_CBLOCK MD5_CBLOCK
|
||||||
|
* #define HASH_UPDATE MD5_Update
|
||||||
|
* #define HASH_TRANSFORM MD5_Transform
|
||||||
|
* #define HASH_FINAL MD5_Final
|
||||||
|
* #define HASH_BLOCK_DATA_ORDER md5_block_data_order
|
||||||
|
*
|
||||||
|
* <appro@fy.chalmers.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
|
||||||
|
# error "DATA_ORDER must be defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HASH_CBLOCK
|
||||||
|
# error "HASH_CBLOCK must be defined!"
|
||||||
|
#endif
|
||||||
|
#ifndef HASH_LONG
|
||||||
|
# error "HASH_LONG must be defined!"
|
||||||
|
#endif
|
||||||
|
#ifndef HASH_CTX
|
||||||
|
# error "HASH_CTX must be defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HASH_UPDATE
|
||||||
|
# error "HASH_UPDATE must be defined!"
|
||||||
|
#endif
|
||||||
|
#ifndef HASH_TRANSFORM
|
||||||
|
# error "HASH_TRANSFORM must be defined!"
|
||||||
|
#endif
|
||||||
|
#ifndef HASH_FINAL
|
||||||
|
# error "HASH_FINAL must be defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HASH_BLOCK_DATA_ORDER
|
||||||
|
# error "HASH_BLOCK_DATA_ORDER must be defined!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Engage compiler specific rotate intrinsic function if available.
|
||||||
|
*/
|
||||||
|
#undef ROTATE
|
||||||
|
#ifndef PEDANTIC
|
||||||
|
# if defined(_MSC_VER)
|
||||||
|
# define ROTATE(a,n) _lrotl(a,n)
|
||||||
|
# elif defined(__ICC)
|
||||||
|
# define ROTATE(a,n) _rotl(a,n)
|
||||||
|
# elif defined(__MWERKS__)
|
||||||
|
# if defined(__POWERPC__)
|
||||||
|
# define ROTATE(a,n) __rlwinm(a,n,0,31)
|
||||||
|
# elif defined(__MC68K__)
|
||||||
|
/* Motorola specific tweak. <appro@fy.chalmers.se> */
|
||||||
|
# define ROTATE(a,n) ( n<24 ? __rol(a,n) : __ror(a,32-n) )
|
||||||
|
# else
|
||||||
|
# define ROTATE(a,n) __rol(a,n)
|
||||||
|
# endif
|
||||||
|
# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
|
||||||
|
/*
|
||||||
|
* Some GNU C inline assembler templates. Note that these are
|
||||||
|
* rotates by *constant* number of bits! But that's exactly
|
||||||
|
* what we need here...
|
||||||
|
* <appro@fy.chalmers.se>
|
||||||
|
*/
|
||||||
|
# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
|
||||||
|
# define ROTATE(a,n) ({ register unsigned int ret; \
|
||||||
|
asm ( \
|
||||||
|
"roll %1,%0" \
|
||||||
|
: "=r"(ret) \
|
||||||
|
: "I"(n), "0"((unsigned int)(a)) \
|
||||||
|
: "cc"); \
|
||||||
|
ret; \
|
||||||
|
})
|
||||||
|
# elif defined(_ARCH_PPC) || defined(_ARCH_PPC64) || \
|
||||||
|
defined(__powerpc) || defined(__ppc__) || defined(__powerpc64__)
|
||||||
|
# define ROTATE(a,n) ({ register unsigned int ret; \
|
||||||
|
asm ( \
|
||||||
|
"rlwinm %0,%1,%2,0,31" \
|
||||||
|
: "=r"(ret) \
|
||||||
|
: "r"(a), "I"(n)); \
|
||||||
|
ret; \
|
||||||
|
})
|
||||||
|
# elif defined(__s390x__)
|
||||||
|
# define ROTATE(a,n) ({ register unsigned int ret; \
|
||||||
|
asm ("rll %0,%1,%2" \
|
||||||
|
: "=r"(ret) \
|
||||||
|
: "r"(a), "I"(n)); \
|
||||||
|
ret; \
|
||||||
|
})
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
#endif /* PEDANTIC */
|
||||||
|
|
||||||
|
#ifndef ROTATE
|
||||||
|
# define ROTATE(a,n) (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
|
||||||
|
|
||||||
|
# ifndef PEDANTIC
|
||||||
|
# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
|
||||||
|
# if ((defined(__i386) || defined(__i386__)) && !defined(I386_ONLY)) || \
|
||||||
|
(defined(__x86_64) || defined(__x86_64__))
|
||||||
|
# if !defined(B_ENDIAN)
|
||||||
|
/*
|
||||||
|
* This gives ~30-40% performance improvement in SHA-256 compiled
|
||||||
|
* with gcc [on P4]. Well, first macro to be frank. We can pull
|
||||||
|
* this trick on x86* platforms only, because these CPUs can fetch
|
||||||
|
* unaligned data without raising an exception.
|
||||||
|
*/
|
||||||
|
# define HOST_c2l(c,l) ({ unsigned int r=*((const unsigned int *)(c)); \
|
||||||
|
asm ("bswapl %0":"=r"(r):"0"(r)); \
|
||||||
|
(c)+=4; (l)=r; })
|
||||||
|
# define HOST_l2c(l,c) ({ unsigned int r=(l); \
|
||||||
|
asm ("bswapl %0":"=r"(r):"0"(r)); \
|
||||||
|
*((unsigned int *)(c))=r; (c)+=4; r; })
|
||||||
|
# endif
|
||||||
|
# elif defined(__aarch64__)
|
||||||
|
# if defined(__BYTE_ORDER__)
|
||||||
|
# if defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__
|
||||||
|
# define HOST_c2l(c,l) ({ unsigned int r; \
|
||||||
|
asm ("rev %w0,%w1" \
|
||||||
|
:"=r"(r) \
|
||||||
|
:"r"(*((const unsigned int *)(c))));\
|
||||||
|
(c)+=4; (l)=r; })
|
||||||
|
# define HOST_l2c(l,c) ({ unsigned int r; \
|
||||||
|
asm ("rev %w0,%w1" \
|
||||||
|
:"=r"(r) \
|
||||||
|
:"r"((unsigned int)(l)));\
|
||||||
|
*((unsigned int *)(c))=r; (c)+=4; r; })
|
||||||
|
# elif defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__==__ORDER_BIG_ENDIAN__
|
||||||
|
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
|
||||||
|
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# if defined(__s390__) || defined(__s390x__)
|
||||||
|
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, (l))
|
||||||
|
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, (l))
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef HOST_c2l
|
||||||
|
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++)))<<24), \
|
||||||
|
l|=(((unsigned long)(*((c)++)))<<16), \
|
||||||
|
l|=(((unsigned long)(*((c)++)))<< 8), \
|
||||||
|
l|=(((unsigned long)(*((c)++))) ) )
|
||||||
|
# endif
|
||||||
|
# ifndef HOST_l2c
|
||||||
|
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l)>>24)&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l) )&0xff), \
|
||||||
|
l)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
|
||||||
|
|
||||||
|
# ifndef PEDANTIC
|
||||||
|
# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
|
||||||
|
# if defined(__s390x__)
|
||||||
|
# define HOST_c2l(c,l) ({ asm ("lrv %0,%1" \
|
||||||
|
:"=d"(l) :"m"(*(const unsigned int *)(c)));\
|
||||||
|
(c)+=4; (l); })
|
||||||
|
# define HOST_l2c(l,c) ({ asm ("strv %1,%0" \
|
||||||
|
:"=m"(*(unsigned int *)(c)) :"d"(l));\
|
||||||
|
(c)+=4; (l); })
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
|
||||||
|
# ifndef B_ENDIAN
|
||||||
|
/* See comment in DATA_ORDER_IS_BIG_ENDIAN section. */
|
||||||
|
# define HOST_c2l(c,l) ((l)=*((const unsigned int *)(c)), (c)+=4, l)
|
||||||
|
# define HOST_l2c(l,c) (*((unsigned int *)(c))=(l), (c)+=4, l)
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef HOST_c2l
|
||||||
|
# define HOST_c2l(c,l) (l =(((unsigned long)(*((c)++))) ), \
|
||||||
|
l|=(((unsigned long)(*((c)++)))<< 8), \
|
||||||
|
l|=(((unsigned long)(*((c)++)))<<16), \
|
||||||
|
l|=(((unsigned long)(*((c)++)))<<24) )
|
||||||
|
# endif
|
||||||
|
# ifndef HOST_l2c
|
||||||
|
# define HOST_l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l)>> 8)&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l)>>16)&0xff), \
|
||||||
|
*((c)++)=(unsigned char)(((l)>>24)&0xff), \
|
||||||
|
l)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Time for some action:-)
|
||||||
|
*/
|
||||||
|
|
||||||
|
int HASH_UPDATE(HASH_CTX *c, const void *data_, size_t len)
|
||||||
|
{
|
||||||
|
const unsigned char *data = data_;
|
||||||
|
unsigned char *p;
|
||||||
|
HASH_LONG l;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
l = (c->Nl + (((HASH_LONG) len) << 3)) & 0xffffffffUL;
|
||||||
|
/*
|
||||||
|
* 95-05-24 eay Fixed a bug with the overflow handling, thanks to Wei Dai
|
||||||
|
* <weidai@eskimo.com> for pointing it out.
|
||||||
|
*/
|
||||||
|
if (l < c->Nl) /* overflow */
|
||||||
|
c->Nh++;
|
||||||
|
c->Nh += (HASH_LONG) (len >> 29); /* might cause compiler warning on
|
||||||
|
* 16-bit */
|
||||||
|
c->Nl = l;
|
||||||
|
|
||||||
|
n = c->num;
|
||||||
|
if (n != 0) {
|
||||||
|
p = (unsigned char *)c->data;
|
||||||
|
|
||||||
|
if (len >= HASH_CBLOCK || len + n >= HASH_CBLOCK) {
|
||||||
|
memcpy(p + n, data, HASH_CBLOCK - n);
|
||||||
|
HASH_BLOCK_DATA_ORDER(c, p, 1);
|
||||||
|
n = HASH_CBLOCK - n;
|
||||||
|
data += n;
|
||||||
|
len -= n;
|
||||||
|
c->num = 0;
|
||||||
|
memset(p, 0, HASH_CBLOCK); /* keep it zeroed */
|
||||||
|
} else {
|
||||||
|
memcpy(p + n, data, len);
|
||||||
|
c->num += (unsigned int)len;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
n = len / HASH_CBLOCK;
|
||||||
|
if (n > 0) {
|
||||||
|
HASH_BLOCK_DATA_ORDER(c, data, n);
|
||||||
|
n *= HASH_CBLOCK;
|
||||||
|
data += n;
|
||||||
|
len -= n;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len != 0) {
|
||||||
|
p = (unsigned char *)c->data;
|
||||||
|
c->num = (unsigned int)len;
|
||||||
|
memcpy(p, data, len);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HASH_TRANSFORM(HASH_CTX *c, const unsigned char *data)
|
||||||
|
{
|
||||||
|
HASH_BLOCK_DATA_ORDER(c, data, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HASH_FINAL(unsigned char *md, HASH_CTX *c)
|
||||||
|
{
|
||||||
|
unsigned char *p = (unsigned char *)c->data;
|
||||||
|
size_t n = c->num;
|
||||||
|
|
||||||
|
p[n] = 0x80; /* there is always room for one */
|
||||||
|
n++;
|
||||||
|
|
||||||
|
if (n > (HASH_CBLOCK - 8)) {
|
||||||
|
memset(p + n, 0, HASH_CBLOCK - n);
|
||||||
|
n = 0;
|
||||||
|
HASH_BLOCK_DATA_ORDER(c, p, 1);
|
||||||
|
}
|
||||||
|
memset(p + n, 0, HASH_CBLOCK - 8 - n);
|
||||||
|
|
||||||
|
p += HASH_CBLOCK - 8;
|
||||||
|
#if defined(DATA_ORDER_IS_BIG_ENDIAN)
|
||||||
|
(void)HOST_l2c(c->Nh, p);
|
||||||
|
(void)HOST_l2c(c->Nl, p);
|
||||||
|
#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
|
||||||
|
(void)HOST_l2c(c->Nl, p);
|
||||||
|
(void)HOST_l2c(c->Nh, p);
|
||||||
|
#endif
|
||||||
|
p -= HASH_CBLOCK;
|
||||||
|
HASH_BLOCK_DATA_ORDER(c, p, 1);
|
||||||
|
c->num = 0;
|
||||||
|
memset(p, 0, HASH_CBLOCK);
|
||||||
|
|
||||||
|
#ifndef HASH_MAKE_STRING
|
||||||
|
# error "HASH_MAKE_STRING must be defined!"
|
||||||
|
#else
|
||||||
|
HASH_MAKE_STRING(c, md);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef MD32_REG_T
|
||||||
|
# if defined(__alpha) || defined(__sparcv9) || defined(__mips)
|
||||||
|
# define MD32_REG_T long
|
||||||
|
/*
|
||||||
|
* This comment was originaly written for MD5, which is why it
|
||||||
|
* discusses A-D. But it basically applies to all 32-bit digests,
|
||||||
|
* which is why it was moved to common header file.
|
||||||
|
*
|
||||||
|
* In case you wonder why A-D are declared as long and not
|
||||||
|
* as MD5_LONG. Doing so results in slight performance
|
||||||
|
* boost on LP64 architectures. The catch is we don't
|
||||||
|
* really care if 32 MSBs of a 64-bit register get polluted
|
||||||
|
* with eventual overflows as we *save* only 32 LSBs in
|
||||||
|
* *either* case. Now declaring 'em long excuses the compiler
|
||||||
|
* from keeping 32 MSBs zeroed resulting in 13% performance
|
||||||
|
* improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
|
||||||
|
* Well, to be honest it should say that this *prevents*
|
||||||
|
* performance degradation.
|
||||||
|
* <appro@fy.chalmers.se>
|
||||||
|
*/
|
||||||
|
# else
|
||||||
|
/*
|
||||||
|
* Above is not absolute and there are LP64 compilers that
|
||||||
|
* generate better code if MD32_REG_T is defined int. The above
|
||||||
|
* pre-processor condition reflects the circumstances under which
|
||||||
|
* the conclusion was made and is subject to further extension.
|
||||||
|
* <appro@fy.chalmers.se>
|
||||||
|
*/
|
||||||
|
# define MD32_REG_T int
|
||||||
|
# endif
|
||||||
|
#endif
|
154
src/util/sha2.h
Normal file
154
src/util/sha2.h
Normal file
@ -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
src/util/sha256.c
Normal file
404
src/util/sha256.c
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
/* crypto/sha/sha256.c */
|
||||||
|
/* ====================================================================
|
||||||
|
* Copyright (c) 2004 The OpenSSL Project. All rights reserved
|
||||||
|
* according to the OpenSSL license [found in ../../LICENSE].
|
||||||
|
* ====================================================================
|
||||||
|
*/
|
||||||
|
# include <stdlib.h>
|
||||||
|
# include <string.h>
|
||||||
|
|
||||||
|
# include <util/sha2.h>
|
||||||
|
|
||||||
|
# define OPENSSL_VERSION_TEXT "OpenSSL 1.0.2a 19 Mar 2015"
|
||||||
|
# define OPENSSL_VERSION_PTEXT " part of " OPENSSL_VERSION_TEXT
|
||||||
|
|
||||||
|
const char SHA256_version[] = "SHA-256" OPENSSL_VERSION_PTEXT;
|
||||||
|
|
||||||
|
/* mem_clr.c */
|
||||||
|
unsigned char cleanse_ctr = 0;
|
||||||
|
void OPENSSL_cleanse(void *ptr, size_t len)
|
||||||
|
{
|
||||||
|
unsigned char *p = ptr;
|
||||||
|
size_t loop = len, ctr = cleanse_ctr;
|
||||||
|
while (loop--) {
|
||||||
|
*(p++) = (unsigned char)ctr;
|
||||||
|
ctr += (17 + ((size_t)p & 0xF));
|
||||||
|
}
|
||||||
|
p = memchr(ptr, (unsigned char)ctr, len);
|
||||||
|
if (p)
|
||||||
|
ctr += (63 + (size_t)p);
|
||||||
|
cleanse_ctr = (unsigned char)ctr;
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
fips_md_init_ctx(SHA224, SHA256)
|
||||||
|
{
|
||||||
|
memset(c, 0, sizeof(*c));
|
||||||
|
c->h[0] = 0xc1059ed8UL;
|
||||||
|
c->h[1] = 0x367cd507UL;
|
||||||
|
c->h[2] = 0x3070dd17UL;
|
||||||
|
c->h[3] = 0xf70e5939UL;
|
||||||
|
c->h[4] = 0xffc00b31UL;
|
||||||
|
c->h[5] = 0x68581511UL;
|
||||||
|
c->h[6] = 0x64f98fa7UL;
|
||||||
|
c->h[7] = 0xbefa4fa4UL;
|
||||||
|
c->md_len = SHA224_DIGEST_LENGTH;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fips_md_init(SHA256)
|
||||||
|
{
|
||||||
|
memset(c, 0, sizeof(*c));
|
||||||
|
c->h[0] = 0x6a09e667UL;
|
||||||
|
c->h[1] = 0xbb67ae85UL;
|
||||||
|
c->h[2] = 0x3c6ef372UL;
|
||||||
|
c->h[3] = 0xa54ff53aUL;
|
||||||
|
c->h[4] = 0x510e527fUL;
|
||||||
|
c->h[5] = 0x9b05688cUL;
|
||||||
|
c->h[6] = 0x1f83d9abUL;
|
||||||
|
c->h[7] = 0x5be0cd19UL;
|
||||||
|
c->md_len = SHA256_DIGEST_LENGTH;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *SHA224(const unsigned char *d, size_t n, unsigned char *md)
|
||||||
|
{
|
||||||
|
SHA256_CTX c;
|
||||||
|
static unsigned char m[SHA224_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
if (md == NULL)
|
||||||
|
md = m;
|
||||||
|
SHA224_Init(&c);
|
||||||
|
SHA256_Update(&c, d, n);
|
||||||
|
SHA256_Final(md, &c);
|
||||||
|
OPENSSL_cleanse(&c, sizeof(c));
|
||||||
|
return (md);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *SHA256(const unsigned char *d, size_t n, unsigned char *md)
|
||||||
|
{
|
||||||
|
SHA256_CTX c;
|
||||||
|
static unsigned char m[SHA256_DIGEST_LENGTH];
|
||||||
|
|
||||||
|
if (md == NULL)
|
||||||
|
md = m;
|
||||||
|
SHA256_Init(&c);
|
||||||
|
SHA256_Update(&c, d, n);
|
||||||
|
SHA256_Final(md, &c);
|
||||||
|
OPENSSL_cleanse(&c, sizeof(c));
|
||||||
|
return (md);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SHA224_Update(SHA256_CTX *c, const void *data, size_t len)
|
||||||
|
{
|
||||||
|
return SHA256_Update(c, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int SHA224_Final(unsigned char *md, SHA256_CTX *c)
|
||||||
|
{
|
||||||
|
return SHA256_Final(md, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
# define DATA_ORDER_IS_BIG_ENDIAN
|
||||||
|
|
||||||
|
# define HASH_LONG SHA_LONG
|
||||||
|
# define HASH_CTX SHA256_CTX
|
||||||
|
# define HASH_CBLOCK SHA_CBLOCK
|
||||||
|
/*
|
||||||
|
* Note that FIPS180-2 discusses "Truncation of the Hash Function Output."
|
||||||
|
* default: case below covers for it. It's not clear however if it's
|
||||||
|
* permitted to truncate to amount of bytes not divisible by 4. I bet not,
|
||||||
|
* but if it is, then default: case shall be extended. For reference.
|
||||||
|
* Idea behind separate cases for pre-defined lenghts is to let the
|
||||||
|
* compiler decide if it's appropriate to unroll small loops.
|
||||||
|
*/
|
||||||
|
# define HASH_MAKE_STRING(c,s) do { \
|
||||||
|
unsigned long ll; \
|
||||||
|
unsigned int nn; \
|
||||||
|
switch ((c)->md_len) \
|
||||||
|
{ case SHA224_DIGEST_LENGTH: \
|
||||||
|
for (nn=0;nn<SHA224_DIGEST_LENGTH/4;nn++) \
|
||||||
|
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
|
||||||
|
break; \
|
||||||
|
case SHA256_DIGEST_LENGTH: \
|
||||||
|
for (nn=0;nn<SHA256_DIGEST_LENGTH/4;nn++) \
|
||||||
|
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
if ((c)->md_len > SHA256_DIGEST_LENGTH) \
|
||||||
|
return 0; \
|
||||||
|
for (nn=0;nn<(c)->md_len/4;nn++) \
|
||||||
|
{ ll=(c)->h[nn]; (void)HOST_l2c(ll,(s)); } \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
# define HASH_UPDATE SHA256_Update
|
||||||
|
# define HASH_TRANSFORM SHA256_Transform
|
||||||
|
# define HASH_FINAL SHA256_Final
|
||||||
|
# define HASH_BLOCK_DATA_ORDER sha256_block_data_order
|
||||||
|
# ifndef SHA256_ASM
|
||||||
|
static
|
||||||
|
# endif
|
||||||
|
void sha256_block_data_order(SHA256_CTX *ctx, const void *in, size_t num);
|
||||||
|
|
||||||
|
# include "md32_common.h"
|
||||||
|
|
||||||
|
# ifndef SHA256_ASM
|
||||||
|
static const SHA_LONG K256[64] = {
|
||||||
|
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
|
||||||
|
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
|
||||||
|
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
|
||||||
|
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
|
||||||
|
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
|
||||||
|
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
|
||||||
|
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
|
||||||
|
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
|
||||||
|
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
|
||||||
|
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
|
||||||
|
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
|
||||||
|
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
|
||||||
|
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
|
||||||
|
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
|
||||||
|
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
|
||||||
|
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIPS specification refers to right rotations, while our ROTATE macro
|
||||||
|
* is left one. This is why you might notice that rotation coefficients
|
||||||
|
* differ from those observed in FIPS document by 32-N...
|
||||||
|
*/
|
||||||
|
# define Sigma0(x) (ROTATE((x),30) ^ ROTATE((x),19) ^ ROTATE((x),10))
|
||||||
|
# define Sigma1(x) (ROTATE((x),26) ^ ROTATE((x),21) ^ ROTATE((x),7))
|
||||||
|
# define sigma0(x) (ROTATE((x),25) ^ ROTATE((x),14) ^ ((x)>>3))
|
||||||
|
# define sigma1(x) (ROTATE((x),15) ^ ROTATE((x),13) ^ ((x)>>10))
|
||||||
|
|
||||||
|
# define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
|
||||||
|
# define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
|
||||||
|
|
||||||
|
# ifdef OPENSSL_SMALL_FOOTPRINT
|
||||||
|
|
||||||
|
static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
|
||||||
|
size_t num)
|
||||||
|
{
|
||||||
|
unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1, T2;
|
||||||
|
SHA_LONG X[16], l;
|
||||||
|
int i;
|
||||||
|
const unsigned char *data = in;
|
||||||
|
|
||||||
|
while (num--) {
|
||||||
|
|
||||||
|
a = ctx->h[0];
|
||||||
|
b = ctx->h[1];
|
||||||
|
c = ctx->h[2];
|
||||||
|
d = ctx->h[3];
|
||||||
|
e = ctx->h[4];
|
||||||
|
f = ctx->h[5];
|
||||||
|
g = ctx->h[6];
|
||||||
|
h = ctx->h[7];
|
||||||
|
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[i] = l;
|
||||||
|
T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
|
||||||
|
T2 = Sigma0(a) + Maj(a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = d + T1;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = T1 + T2;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; i < 64; i++) {
|
||||||
|
s0 = X[(i + 1) & 0x0f];
|
||||||
|
s0 = sigma0(s0);
|
||||||
|
s1 = X[(i + 14) & 0x0f];
|
||||||
|
s1 = sigma1(s1);
|
||||||
|
|
||||||
|
T1 = X[i & 0xf] += s0 + s1 + X[(i + 9) & 0xf];
|
||||||
|
T1 += h + Sigma1(e) + Ch(e, f, g) + K256[i];
|
||||||
|
T2 = Sigma0(a) + Maj(a, b, c);
|
||||||
|
h = g;
|
||||||
|
g = f;
|
||||||
|
f = e;
|
||||||
|
e = d + T1;
|
||||||
|
d = c;
|
||||||
|
c = b;
|
||||||
|
b = a;
|
||||||
|
a = T1 + T2;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->h[0] += a;
|
||||||
|
ctx->h[1] += b;
|
||||||
|
ctx->h[2] += c;
|
||||||
|
ctx->h[3] += d;
|
||||||
|
ctx->h[4] += e;
|
||||||
|
ctx->h[5] += f;
|
||||||
|
ctx->h[6] += g;
|
||||||
|
ctx->h[7] += h;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# else
|
||||||
|
|
||||||
|
# define ROUND_00_15(i,a,b,c,d,e,f,g,h) do { \
|
||||||
|
T1 += h + Sigma1(e) + Ch(e,f,g) + K256[i]; \
|
||||||
|
h = Sigma0(a) + Maj(a,b,c); \
|
||||||
|
d += T1; h += T1; } while (0)
|
||||||
|
|
||||||
|
# define ROUND_16_63(i,a,b,c,d,e,f,g,h,X) do { \
|
||||||
|
s0 = X[(i+1)&0x0f]; s0 = sigma0(s0); \
|
||||||
|
s1 = X[(i+14)&0x0f]; s1 = sigma1(s1); \
|
||||||
|
T1 = X[(i)&0x0f] += s0 + s1 + X[(i+9)&0x0f]; \
|
||||||
|
ROUND_00_15(i,a,b,c,d,e,f,g,h); } while (0)
|
||||||
|
|
||||||
|
static void sha256_block_data_order(SHA256_CTX *ctx, const void *in,
|
||||||
|
size_t num)
|
||||||
|
{
|
||||||
|
unsigned MD32_REG_T a, b, c, d, e, f, g, h, s0, s1, T1;
|
||||||
|
SHA_LONG X[16];
|
||||||
|
int i;
|
||||||
|
const unsigned char *data = in;
|
||||||
|
const union {
|
||||||
|
long one;
|
||||||
|
char little;
|
||||||
|
} is_endian = {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
while (num--) {
|
||||||
|
|
||||||
|
a = ctx->h[0];
|
||||||
|
b = ctx->h[1];
|
||||||
|
c = ctx->h[2];
|
||||||
|
d = ctx->h[3];
|
||||||
|
e = ctx->h[4];
|
||||||
|
f = ctx->h[5];
|
||||||
|
g = ctx->h[6];
|
||||||
|
h = ctx->h[7];
|
||||||
|
|
||||||
|
if (!is_endian.little && sizeof(SHA_LONG) == 4
|
||||||
|
&& ((size_t)in % 4) == 0) {
|
||||||
|
const SHA_LONG *W = (const SHA_LONG *)data;
|
||||||
|
|
||||||
|
T1 = X[0] = W[0];
|
||||||
|
ROUND_00_15(0, a, b, c, d, e, f, g, h);
|
||||||
|
T1 = X[1] = W[1];
|
||||||
|
ROUND_00_15(1, h, a, b, c, d, e, f, g);
|
||||||
|
T1 = X[2] = W[2];
|
||||||
|
ROUND_00_15(2, g, h, a, b, c, d, e, f);
|
||||||
|
T1 = X[3] = W[3];
|
||||||
|
ROUND_00_15(3, f, g, h, a, b, c, d, e);
|
||||||
|
T1 = X[4] = W[4];
|
||||||
|
ROUND_00_15(4, e, f, g, h, a, b, c, d);
|
||||||
|
T1 = X[5] = W[5];
|
||||||
|
ROUND_00_15(5, d, e, f, g, h, a, b, c);
|
||||||
|
T1 = X[6] = W[6];
|
||||||
|
ROUND_00_15(6, c, d, e, f, g, h, a, b);
|
||||||
|
T1 = X[7] = W[7];
|
||||||
|
ROUND_00_15(7, b, c, d, e, f, g, h, a);
|
||||||
|
T1 = X[8] = W[8];
|
||||||
|
ROUND_00_15(8, a, b, c, d, e, f, g, h);
|
||||||
|
T1 = X[9] = W[9];
|
||||||
|
ROUND_00_15(9, h, a, b, c, d, e, f, g);
|
||||||
|
T1 = X[10] = W[10];
|
||||||
|
ROUND_00_15(10, g, h, a, b, c, d, e, f);
|
||||||
|
T1 = X[11] = W[11];
|
||||||
|
ROUND_00_15(11, f, g, h, a, b, c, d, e);
|
||||||
|
T1 = X[12] = W[12];
|
||||||
|
ROUND_00_15(12, e, f, g, h, a, b, c, d);
|
||||||
|
T1 = X[13] = W[13];
|
||||||
|
ROUND_00_15(13, d, e, f, g, h, a, b, c);
|
||||||
|
T1 = X[14] = W[14];
|
||||||
|
ROUND_00_15(14, c, d, e, f, g, h, a, b);
|
||||||
|
T1 = X[15] = W[15];
|
||||||
|
ROUND_00_15(15, b, c, d, e, f, g, h, a);
|
||||||
|
|
||||||
|
data += SHA256_CBLOCK;
|
||||||
|
} else {
|
||||||
|
SHA_LONG l;
|
||||||
|
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[0] = l;
|
||||||
|
ROUND_00_15(0, a, b, c, d, e, f, g, h);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[1] = l;
|
||||||
|
ROUND_00_15(1, h, a, b, c, d, e, f, g);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[2] = l;
|
||||||
|
ROUND_00_15(2, g, h, a, b, c, d, e, f);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[3] = l;
|
||||||
|
ROUND_00_15(3, f, g, h, a, b, c, d, e);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[4] = l;
|
||||||
|
ROUND_00_15(4, e, f, g, h, a, b, c, d);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[5] = l;
|
||||||
|
ROUND_00_15(5, d, e, f, g, h, a, b, c);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[6] = l;
|
||||||
|
ROUND_00_15(6, c, d, e, f, g, h, a, b);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[7] = l;
|
||||||
|
ROUND_00_15(7, b, c, d, e, f, g, h, a);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[8] = l;
|
||||||
|
ROUND_00_15(8, a, b, c, d, e, f, g, h);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[9] = l;
|
||||||
|
ROUND_00_15(9, h, a, b, c, d, e, f, g);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[10] = l;
|
||||||
|
ROUND_00_15(10, g, h, a, b, c, d, e, f);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[11] = l;
|
||||||
|
ROUND_00_15(11, f, g, h, a, b, c, d, e);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[12] = l;
|
||||||
|
ROUND_00_15(12, e, f, g, h, a, b, c, d);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[13] = l;
|
||||||
|
ROUND_00_15(13, d, e, f, g, h, a, b, c);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[14] = l;
|
||||||
|
ROUND_00_15(14, c, d, e, f, g, h, a, b);
|
||||||
|
HOST_c2l(data, l);
|
||||||
|
T1 = X[15] = l;
|
||||||
|
ROUND_00_15(15, b, c, d, e, f, g, h, a);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 16; i < 64; i += 8) {
|
||||||
|
ROUND_16_63(i + 0, a, b, c, d, e, f, g, h, X);
|
||||||
|
ROUND_16_63(i + 1, h, a, b, c, d, e, f, g, X);
|
||||||
|
ROUND_16_63(i + 2, g, h, a, b, c, d, e, f, X);
|
||||||
|
ROUND_16_63(i + 3, f, g, h, a, b, c, d, e, X);
|
||||||
|
ROUND_16_63(i + 4, e, f, g, h, a, b, c, d, X);
|
||||||
|
ROUND_16_63(i + 5, d, e, f, g, h, a, b, c, X);
|
||||||
|
ROUND_16_63(i + 6, c, d, e, f, g, h, a, b, X);
|
||||||
|
ROUND_16_63(i + 7, b, c, d, e, f, g, h, a, X);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->h[0] += a;
|
||||||
|
ctx->h[1] += b;
|
||||||
|
ctx->h[2] += c;
|
||||||
|
ctx->h[3] += d;
|
||||||
|
ctx->h[4] += e;
|
||||||
|
ctx->h[5] += f;
|
||||||
|
ctx->h[6] += g;
|
||||||
|
ctx->h[7] += h;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# endif
|
||||||
|
# endif /* SHA256_ASM */
|
1029
src/util/srp.cpp
Normal file
1029
src/util/srp.cpp
Normal file
File diff suppressed because it is too large
Load Diff
171
src/util/srp.h
Normal file
171
src/util/srp.h
Normal file
@ -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 */
|
@ -22,12 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "numeric.h"
|
#include "numeric.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
#include "sha1.h"
|
|
||||||
#include "base64.h"
|
|
||||||
#include "hex.h"
|
#include "hex.h"
|
||||||
#include "../porting.h"
|
#include "../porting.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -176,26 +173,6 @@ std::string wide_to_narrow(const std::wstring &wcs)
|
|||||||
|
|
||||||
#endif
|
#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)
|
std::string urlencode(std::string str)
|
||||||
{
|
{
|
||||||
// Encodes non-unreserved URI characters by a percent sign
|
// Encodes non-unreserved URI characters by a percent sign
|
||||||
|
@ -43,8 +43,7 @@ wchar_t *narrow_to_wide_c(const char *str);
|
|||||||
|
|
||||||
std::wstring narrow_to_wide(const std::string &mbs);
|
std::wstring narrow_to_wide(const std::string &mbs);
|
||||||
std::string wide_to_narrow(const std::wstring &wcs);
|
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 urlencode(std::string str);
|
||||||
std::string urldecode(std::string str);
|
std::string urldecode(std::string str);
|
||||||
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
|
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
|
||||||
|
Loading…
Reference in New Issue
Block a user