mirror of https://github.com/minetest/minetest.git
Change the way how password is stored in C++ engine.
This commit is contained in:
parent
cc1bfc6d03
commit
83b02e3a1a
|
@ -38,6 +38,7 @@ set(client_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/activeobjectmgr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/camera.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/client.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientauth.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientenvironment.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientlauncher.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/clientmap.cpp
|
||||
|
|
|
@ -97,7 +97,7 @@ void PacketCounter::print(std::ostream &o) const
|
|||
*/
|
||||
|
||||
Client::Client(
|
||||
const char *playername,
|
||||
const std::string &playername,
|
||||
const std::string &password,
|
||||
MapDrawControl &control,
|
||||
IWritableTextureSource *tsrc,
|
||||
|
@ -126,7 +126,7 @@ Client::Client(
|
|||
m_allow_login_or_register(allow_login_or_register),
|
||||
m_server_ser_ver(SER_FMT_VER_INVALID),
|
||||
m_last_chat_message_sent(time(NULL)),
|
||||
m_password(password),
|
||||
m_auth(playername, password),
|
||||
m_chosen_auth_mech(AUTH_MECHANISM_NONE),
|
||||
m_media_downloader(new ClientMediaDownloader()),
|
||||
m_state(LC_Created),
|
||||
|
@ -134,7 +134,7 @@ Client::Client(
|
|||
m_modchannel_mgr(new ModChannelMgr())
|
||||
{
|
||||
// Add local player
|
||||
m_env.setLocalPlayer(new LocalPlayer(this, playername));
|
||||
m_env.setLocalPlayer(new LocalPlayer(this, playername.c_str()));
|
||||
|
||||
// Make the mod storage database and begin the save for later
|
||||
m_mod_storage_database =
|
||||
|
@ -1113,20 +1113,7 @@ void Client::interact(InteractAction action, const PointedThing& pointed)
|
|||
|
||||
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;
|
||||
}
|
||||
m_auth.clear();
|
||||
m_chosen_auth_mech = AUTH_MECHANISM_NONE;
|
||||
}
|
||||
|
||||
|
@ -1168,36 +1155,34 @@ void Client::startAuth(AuthMechanism chosen_auth_mechanism)
|
|||
switch (chosen_auth_mechanism) {
|
||||
case AUTH_MECHANISM_FIRST_SRP: {
|
||||
// send srp verifier to server
|
||||
std::string verifier;
|
||||
std::string salt;
|
||||
generate_srp_verifier_and_salt(playername, m_password,
|
||||
&verifier, &salt);
|
||||
const std::string &verifier = m_auth.getSrpVerifier();
|
||||
const std::string &salt = m_auth.getSrpSalt();
|
||||
|
||||
NetworkPacket resp_pkt(TOSERVER_FIRST_SRP, 0);
|
||||
resp_pkt << salt << verifier << (u8)((m_password.empty()) ? 1 : 0);
|
||||
resp_pkt << salt << verifier << (u8)((m_auth.getIsEmpty()) ? 1 : 0);
|
||||
|
||||
Send(&resp_pkt);
|
||||
break;
|
||||
}
|
||||
case AUTH_MECHANISM_SRP:
|
||||
case AUTH_MECHANISM_LEGACY_PASSWORD: {
|
||||
u8 based_on = 1;
|
||||
u8 based_on;
|
||||
void * auth_data;
|
||||
|
||||
if (chosen_auth_mechanism == AUTH_MECHANISM_LEGACY_PASSWORD) {
|
||||
m_password = translate_password(playername, m_password);
|
||||
based_on = 0;
|
||||
auth_data = m_auth.getLegacyAuthData();
|
||||
}
|
||||
else {
|
||||
based_on = 1;
|
||||
auth_data = m_auth.getSrpAuthData();
|
||||
}
|
||||
|
||||
std::string playername_u = lowercase(playername);
|
||||
m_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
|
||||
playername.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_Result res = srp_user_start_authentication(
|
||||
(struct SRPUser *) m_auth_data, NULL, NULL, 0,
|
||||
(unsigned char **) &bytes_A, &len_A);
|
||||
static_cast<struct SRPUser *>(auth_data), NULL, NULL, 0,
|
||||
reinterpret_cast<unsigned char **>(&bytes_A), &len_A);
|
||||
FATAL_ERROR_IF(res != SRP_OK, "Creating local SRP user failed.");
|
||||
|
||||
NetworkPacket resp_pkt(TOSERVER_SRP_BYTES_A, 0);
|
||||
|
@ -1357,8 +1342,9 @@ void Client::sendChangePassword(const std::string &oldpassword,
|
|||
return;
|
||||
|
||||
// get into sudo mode and then send new password to server
|
||||
m_password = oldpassword;
|
||||
m_new_password = newpassword;
|
||||
std::string playername = m_env.getLocalPlayer()->getName();
|
||||
m_auth.applyPassword(playername, oldpassword);
|
||||
m_new_auth.applyPassword(playername, newpassword);
|
||||
startAuth(choseAuthMech(m_sudo_auth_methods));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "network/peerhandler.h"
|
||||
#include "gameparams.h"
|
||||
#include "clientdynamicinfo.h"
|
||||
#include "clientauth.h"
|
||||
#include "util/numeric.h"
|
||||
|
||||
#ifdef SERVER
|
||||
|
@ -123,7 +124,7 @@ public:
|
|||
*/
|
||||
|
||||
Client(
|
||||
const char *playername,
|
||||
const std::string &playername,
|
||||
const std::string &password,
|
||||
MapDrawControl &control,
|
||||
IWritableTextureSource *tsrc,
|
||||
|
@ -533,12 +534,11 @@ private:
|
|||
|
||||
// Auth data
|
||||
std::string m_playername;
|
||||
std::string m_password;
|
||||
ClientAuth m_auth;
|
||||
// If set, this will be sent (and cleared) upon a TOCLIENT_ACCEPT_SUDO_MODE
|
||||
std::string m_new_password;
|
||||
ClientAuth m_new_auth;
|
||||
// Usable by auth mechanisms.
|
||||
AuthMechanism m_chosen_auth_mech;
|
||||
void *m_auth_data = nullptr;
|
||||
|
||||
bool m_access_denied = false;
|
||||
bool m_access_denied_reconnect = false;
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2024 SFENCE, <sfence.software@gmail.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 "clientauth.h"
|
||||
#include "util/auth.h"
|
||||
#include "util/srp.h"
|
||||
|
||||
ClientAuth::ClientAuth() :
|
||||
m_is_empty(true),
|
||||
m_srp_verifier(""),
|
||||
m_srp_salt("")
|
||||
{
|
||||
}
|
||||
|
||||
ClientAuth::ClientAuth(const std::string &player_name, const std::string &password)
|
||||
{
|
||||
applyPassword(player_name, password);
|
||||
}
|
||||
|
||||
ClientAuth::~ClientAuth()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
ClientAuth &ClientAuth::operator=(ClientAuth &&other)
|
||||
{
|
||||
clear();
|
||||
|
||||
m_is_empty = other.m_is_empty;
|
||||
|
||||
m_srp_verifier = other.m_srp_verifier;
|
||||
m_srp_salt = other.m_srp_salt;
|
||||
|
||||
m_legacy_auth_data = other.m_legacy_auth_data;
|
||||
m_srp_auth_data = other.m_srp_auth_data;
|
||||
|
||||
other.m_legacy_auth_data = nullptr;
|
||||
other.m_srp_auth_data = nullptr;
|
||||
|
||||
other.clear();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ClientAuth::applyPassword(const std::string &player_name, const std::string &password)
|
||||
{
|
||||
clear();
|
||||
// AUTH_MECHANISM_FIRST_SRP
|
||||
generate_srp_verifier_and_salt(player_name, password, &m_srp_verifier, &m_srp_salt);
|
||||
m_is_empty = password.empty();
|
||||
|
||||
std::string player_name_u = lowercase(player_name);
|
||||
// AUTH_MECHANISM_SRP
|
||||
m_srp_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
|
||||
player_name.c_str(), player_name_u.c_str(),
|
||||
reinterpret_cast<const unsigned char *>(password.c_str()),
|
||||
password.length(), NULL, NULL);
|
||||
// AUTH_MECHANISM_LEGACY_PASSWORD
|
||||
std::string translated = translate_password(player_name, password);
|
||||
m_legacy_auth_data = srp_user_new(SRP_SHA256, SRP_NG_2048,
|
||||
player_name.c_str(), player_name_u.c_str(),
|
||||
reinterpret_cast<const unsigned char *>(translated.c_str()),
|
||||
translated.length(), NULL, NULL);
|
||||
}
|
||||
|
||||
void * ClientAuth::getAuthData(AuthMechanism chosen_auth_mech) const
|
||||
{
|
||||
switch (chosen_auth_mech) {
|
||||
case AUTH_MECHANISM_LEGACY_PASSWORD:
|
||||
return m_legacy_auth_data;
|
||||
case AUTH_MECHANISM_SRP:
|
||||
return m_srp_auth_data;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ClientAuth::clear()
|
||||
{
|
||||
if (m_legacy_auth_data != nullptr) {
|
||||
srp_user_delete(m_legacy_auth_data);
|
||||
m_legacy_auth_data = nullptr;
|
||||
}
|
||||
if (m_srp_auth_data != nullptr) {
|
||||
srp_user_delete(m_srp_auth_data);
|
||||
m_srp_auth_data = nullptr;
|
||||
}
|
||||
m_srp_verifier.clear();
|
||||
m_srp_salt.clear();
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2024 SFENCE, <sfence.software@gmail.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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "network/networkprotocol.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
struct SRPUser;
|
||||
|
||||
class ClientAuth
|
||||
{
|
||||
public:
|
||||
ClientAuth();
|
||||
ClientAuth(const std::string &player_name, const std::string &password);
|
||||
|
||||
~ClientAuth();
|
||||
DISABLE_CLASS_COPY(ClientAuth);
|
||||
|
||||
ClientAuth(ClientAuth &&other) { *this = std::move(other); }
|
||||
ClientAuth &operator=(ClientAuth &&other);
|
||||
|
||||
void applyPassword(const std::string &player_name, const std::string &password);
|
||||
|
||||
bool getIsEmpty() const { return m_is_empty; }
|
||||
const std::string &getSrpVerifier() const { return m_srp_verifier; }
|
||||
const std::string &getSrpSalt() const { return m_srp_salt; }
|
||||
void * getLegacyAuthData() const { return m_legacy_auth_data; }
|
||||
void * getSrpAuthData() const { return m_srp_auth_data; }
|
||||
void * getAuthData(AuthMechanism chosen_auth_mech) const;
|
||||
|
||||
void clear();
|
||||
private:
|
||||
bool m_is_empty;
|
||||
|
||||
std::string m_srp_verifier;
|
||||
std::string m_srp_salt;
|
||||
|
||||
SRPUser *m_legacy_auth_data = nullptr;
|
||||
SRPUser *m_srp_auth_data = nullptr;
|
||||
};
|
|
@ -449,6 +449,9 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
|||
server_name = menudata.servername;
|
||||
server_description = menudata.serverdescription;
|
||||
|
||||
/* make sure that password will not stay somewhere in memory */
|
||||
clear_string(menudata.password);
|
||||
|
||||
start_data.local_server = !menudata.simple_singleplayer_mode &&
|
||||
start_data.address.empty();
|
||||
} else {
|
||||
|
|
|
@ -683,7 +683,7 @@ public:
|
|||
bool startup(bool *kill,
|
||||
InputHandler *input,
|
||||
RenderingEngine *rendering_engine,
|
||||
const GameStartData &game_params,
|
||||
GameStartData &game_params,
|
||||
std::string &error_message,
|
||||
bool *reconnect,
|
||||
ChatBackend *chat_backend);
|
||||
|
@ -1072,7 +1072,7 @@ Game::~Game()
|
|||
bool Game::startup(bool *kill,
|
||||
InputHandler *input,
|
||||
RenderingEngine *rendering_engine,
|
||||
const GameStartData &start_data,
|
||||
GameStartData &start_data,
|
||||
std::string &error_message,
|
||||
bool *reconnect,
|
||||
ChatBackend *chat_backend)
|
||||
|
@ -1114,8 +1114,11 @@ bool Game::startup(bool *kill,
|
|||
start_data.socket_port, start_data.game_spec))
|
||||
return false;
|
||||
|
||||
if (!createClient(start_data))
|
||||
if (!createClient(start_data)) {
|
||||
start_data.erasePassword();
|
||||
return false;
|
||||
}
|
||||
start_data.erasePassword();
|
||||
|
||||
m_rendering_engine->initialize(client, hud);
|
||||
|
||||
|
@ -1618,7 +1621,7 @@ bool Game::connectToServer(const GameStartData &start_data,
|
|||
|
||||
|
||||
try {
|
||||
client = new Client(start_data.name.c_str(),
|
||||
client = new Client(start_data.name,
|
||||
start_data.password,
|
||||
*draw_control, texture_src, shader_src,
|
||||
itemdef_manager, nodedef_manager, sound_manager.get(), eventmgr,
|
||||
|
@ -4526,7 +4529,7 @@ void Game::showPauseMenu()
|
|||
void the_game(bool *kill,
|
||||
InputHandler *input,
|
||||
RenderingEngine *rendering_engine,
|
||||
const GameStartData &start_data,
|
||||
GameStartData &start_data,
|
||||
std::string &error_message,
|
||||
ChatBackend &chat_backend,
|
||||
bool *reconnect_requested) // Used for local game
|
||||
|
|
|
@ -53,7 +53,7 @@ struct CameraOrientation {
|
|||
void the_game(bool *kill,
|
||||
InputHandler *input,
|
||||
RenderingEngine *rendering_engine,
|
||||
const GameStartData &start_data,
|
||||
GameStartData &start_data,
|
||||
std::string &error_message,
|
||||
ChatBackend &chat_backend,
|
||||
bool *reconnect_requested);
|
||||
|
|
|
@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#include "irrlichttypes.h"
|
||||
#include "content/subgames.h"
|
||||
#include "util/auth.h"
|
||||
|
||||
// Information provided from "main"
|
||||
struct GameParams
|
||||
|
@ -55,4 +56,9 @@ struct GameStartData : GameParams
|
|||
|
||||
// "world_path" must be kept in sync!
|
||||
WorldSpec world_spec;
|
||||
|
||||
void erasePassword() {
|
||||
/* make sure that password will not stay somewhere in memory */
|
||||
clear_string(password);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -97,8 +97,7 @@ void Client::handleCommand_Hello(NetworkPacket* pkt)
|
|||
<< "(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;
|
||||
m_auth.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,9 +165,7 @@ void Client::handleCommand_AuthAccept(NetworkPacket* pkt)
|
|||
|
||||
void Client::handleCommand_AcceptSudoMode(NetworkPacket* pkt)
|
||||
{
|
||||
deleteAuthData();
|
||||
|
||||
m_password = m_new_password;
|
||||
m_auth = std::move(m_new_auth);
|
||||
|
||||
verbosestream << "Client: Received TOCLIENT_ACCEPT_SUDO_MODE." << std::endl;
|
||||
|
||||
|
@ -195,6 +192,8 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt)
|
|||
m_access_denied = true;
|
||||
m_access_denied_reason = "Unknown";
|
||||
|
||||
deleteAuthData();
|
||||
|
||||
if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) {
|
||||
// Legacy code from 0.4.12 and older but is still used
|
||||
// in some places of the server code
|
||||
|
@ -1570,16 +1569,16 @@ void Client::handleCommand_SrpBytesSandB(NetworkPacket* pkt)
|
|||
|
||||
char *bytes_M = 0;
|
||||
size_t len_M = 0;
|
||||
SRPUser *usr = (SRPUser *) m_auth_data;
|
||||
SRPUser *usr = static_cast<SRPUser *>(m_auth.getAuthData(m_chosen_auth_mech));
|
||||
std::string s;
|
||||
std::string B;
|
||||
*pkt >> s >> B;
|
||||
|
||||
infostream << "Client: Received 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);
|
||||
srp_user_process_challenge(usr, reinterpret_cast<const unsigned char *>(s.c_str()), s.size(),
|
||||
reinterpret_cast<const unsigned char *>(B.c_str()), B.size(),
|
||||
reinterpret_cast<unsigned char **>(&bytes_M), &len_M);
|
||||
|
||||
if ( !bytes_M ) {
|
||||
errorstream << "Client: SRP-6a S_B safety check violation!" << std::endl;
|
||||
|
|
|
@ -17,6 +17,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
// enable include of memset_s function
|
||||
#define __STDC_WANT_LIB_EXT1__ 1
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "auth.h"
|
||||
|
@ -26,6 +29,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "util/string.h"
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#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
|
||||
|
@ -134,3 +141,21 @@ bool decode_srp_verifier_and_salt(const std::string &encoded,
|
|||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// Override every character before clearing
|
||||
void clear_string(std::string &text)
|
||||
{
|
||||
#ifdef __STDC_LIB_EXT1__
|
||||
memset_s((void *)text.data(), text.size(), '0', text.size());
|
||||
#elif _WIN32
|
||||
SecureZeroMemory((void *)text.data(), text.size());
|
||||
#else
|
||||
volatile char *ch = (char *)text.data();
|
||||
size_t n = text.size();
|
||||
for (;n>0;n--) {
|
||||
*ch = 0;
|
||||
ch++;
|
||||
}
|
||||
#endif
|
||||
text.clear();
|
||||
}
|
||||
|
|
|
@ -45,3 +45,6 @@ std::string encode_srp_verifier(const std::string &verifier,
|
|||
/// and salt components.
|
||||
bool decode_srp_verifier_and_salt(const std::string &encoded,
|
||||
std::string *verifier, std::string *salt);
|
||||
|
||||
/// Override every character before clearing
|
||||
void clear_string(std::string &text);
|
||||
|
|
Loading…
Reference in New Issue