Don't store the user's password in plaintext in the prefs file
The game now supports @ or = in usernames for the purpose of saving them to a file. Though I don't think these are allowed currently, it's probably better to support it from the start rather than having things break if they later become allowed.
This commit is contained in:
parent
640e28f731
commit
9365538b3b
29 changed files with 401 additions and 212 deletions
|
@ -738,6 +738,7 @@
|
|||
<ClCompile Include="..\..\src\config_cache.cpp" />
|
||||
<ClCompile Include="..\..\src\controller_base.cpp" />
|
||||
<ClCompile Include="..\..\src\countdown_clock.cpp" />
|
||||
<ClCompile Include="..\..\src\preferences\credentials.cpp" />
|
||||
<ClCompile Include="..\..\src\cursor.cpp" />
|
||||
<ClCompile Include="..\..\src\desktop\clipboard.cpp">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Desktop\</ObjectFileName>
|
||||
|
@ -3439,6 +3440,7 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\preferences\credentials.hpp" />
|
||||
<ClInclude Include="..\..\src\about.hpp" />
|
||||
<ClInclude Include="..\..\src\actions\advancement.hpp" />
|
||||
<ClInclude Include="..\..\src\actions\attack.hpp" />
|
||||
|
|
|
@ -1536,6 +1536,9 @@
|
|||
<ClCompile Include="..\..\src\preferences\lobby.cpp">
|
||||
<Filter>Preferences</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\src\preferences\credentials.cpp">
|
||||
<Filter>Preferences</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\addon\client.hpp">
|
||||
|
@ -2983,6 +2986,9 @@
|
|||
<ClInclude Include="..\..\src\preferences\lobby.hpp">
|
||||
<Filter>Preferences</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\preferences\credentials.hpp">
|
||||
<Filter>Preferences</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="..\..\src\tests\test_sdl_utils.hpp">
|
||||
|
|
|
@ -28,6 +28,7 @@ hotkey/hotkey_item.cpp
|
|||
hotkey/hotkey_manager.cpp
|
||||
image.cpp
|
||||
image_modifications.cpp
|
||||
preferences/credentials.cpp
|
||||
preferences/general.cpp
|
||||
joystick.cpp
|
||||
key.cpp
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "log.hpp"
|
||||
#include "map_command_handler.hpp"
|
||||
#include "chat_command_handler.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "preferences/general.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "mp_ui_alerts.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "color.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
|
||||
#include <SDL_timer.h>
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@ std::string get_dir(const std::string &dir);
|
|||
|
||||
// The location of various important files:
|
||||
std::string get_prefs_file();
|
||||
std::string get_credentials_file();
|
||||
std::string get_default_prefs_file();
|
||||
std::string get_save_index_file();
|
||||
std::string get_saves_dir();
|
||||
|
|
|
@ -34,6 +34,11 @@ std::string get_prefs_file()
|
|||
return get_user_config_dir() + "/preferences";
|
||||
}
|
||||
|
||||
std::string get_credentials_file()
|
||||
{
|
||||
return get_user_config_dir() + "/credentials";
|
||||
}
|
||||
|
||||
std::string get_default_prefs_file()
|
||||
{
|
||||
#ifdef HAS_RELATIVE_DEFPREF
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mp_game_settings.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "tod_manager.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#include "ai/configuration.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "formula/string_utils.hpp"
|
||||
#include "game_initialization/mp_game_utils.hpp"
|
||||
#include "game_initialization/playcampaign.hpp"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "game_initialization/create_engine.hpp"
|
||||
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "formula/string_utils.hpp"
|
||||
#include "game_config_manager.hpp"
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "game_initialization/lobby_data.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "gui/dialogs/campaign_difficulty.hpp"
|
||||
#include "filesystem.hpp"
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "addon/manager.hpp" // for installed_addons
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "formula/string_utils.hpp"
|
||||
#include "game_config_manager.hpp"
|
||||
#include "game_initialization/mp_game_utils.hpp"
|
||||
|
@ -191,7 +192,7 @@ static std::unique_ptr<wesnothd_connection> open_connection(CVideo& video, const
|
|||
if(!*error) break;
|
||||
|
||||
do {
|
||||
std::string password = preferences::password();
|
||||
std::string password = preferences::password(host, login);
|
||||
|
||||
bool fall_through = (*error)["force_confirmation"].to_bool() ?
|
||||
(gui2::show_message(video, _("Confirm"), (*error)["message"], gui2::dialogs::message::ok_cancel_buttons) == gui2::window::CANCEL) :
|
||||
|
@ -289,7 +290,7 @@ static std::unique_ptr<wesnothd_connection> open_connection(CVideo& video, const
|
|||
error_message = (*error)["message"].str();
|
||||
}
|
||||
|
||||
gui2::dialogs::mp_login dlg(error_message, !((*error)["password_request"].empty()));
|
||||
gui2::dialogs::mp_login dlg(host, error_message, !((*error)["password_request"].empty()));
|
||||
dlg.show(video);
|
||||
|
||||
switch(dlg.get_retval()) {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "game_launcher.hpp"
|
||||
#include "game_errors.hpp"
|
||||
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "commandline_options.hpp" // for commandline_options
|
||||
#include "config.hpp" // for config, etc
|
||||
#include "config_assign.hpp"
|
||||
|
@ -239,14 +240,14 @@ game_launcher::game_launcher(const commandline_options& cmdline_opts, const char
|
|||
else
|
||||
multiplayer_server_ = "";
|
||||
}
|
||||
}
|
||||
if (cmdline_opts_.username) {
|
||||
preferences::disable_preferences_save();
|
||||
preferences::set_login(*cmdline_opts_.username);
|
||||
}
|
||||
if (cmdline_opts_.password) {
|
||||
preferences::disable_preferences_save();
|
||||
preferences::set_password(*cmdline_opts_.password);
|
||||
if (cmdline_opts_.username) {
|
||||
preferences::disable_preferences_save();
|
||||
preferences::set_login(*cmdline_opts_.username);
|
||||
if (cmdline_opts_.password) {
|
||||
preferences::disable_preferences_save();
|
||||
preferences::set_password(*cmdline_opts.server, *cmdline_opts.username, *cmdline_opts_.password);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (cmdline_opts_.test)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "formula/string_utils.hpp"
|
||||
#include "game_board.hpp"
|
||||
#include "game_display.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "gui/auxiliary/find_widget.hpp"
|
||||
#include "gui/dialogs/helper.hpp"
|
||||
#include "gui/widgets/label.hpp"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include "game_config.hpp"
|
||||
#include "game_config_manager.hpp"
|
||||
#include "game_initialization/mp_game_utils.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "gui/auxiliary/find_widget.hpp"
|
||||
#include "gui/dialogs/helper.hpp"
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
#include "gui/dialogs/multiplayer/mp_login.hpp"
|
||||
|
||||
#include "preferences/game.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "gui/auxiliary/find_widget.hpp"
|
||||
#include "gui/auxiliary/field.hpp"
|
||||
#include "gui/widgets/button.hpp"
|
||||
#include "gui/widgets/password_box.hpp"
|
||||
#include "gui/widgets/settings.hpp"
|
||||
|
@ -64,24 +65,32 @@ namespace dialogs
|
|||
|
||||
REGISTER_DIALOG(mp_login)
|
||||
|
||||
mp_login::mp_login(const std::string& label, const bool focus_password)
|
||||
mp_login::mp_login(const std::string& host, const std::string& label, const bool focus_password)
|
||||
: host_(host), focus_password_(focus_password)
|
||||
{
|
||||
register_label("login_label", false, label);
|
||||
register_text("user_name", true,
|
||||
username_ = register_text("user_name", true,
|
||||
&preferences::login,
|
||||
&preferences::set_login,
|
||||
!focus_password);
|
||||
|
||||
register_text("password", true,
|
||||
&preferences::password,
|
||||
nullptr /* The password box returns '*' as value. */,
|
||||
focus_password);
|
||||
|
||||
register_bool("remember_password", false,
|
||||
&preferences::remember_password,
|
||||
&preferences::set_remember_password);
|
||||
}
|
||||
|
||||
void mp_login::load_password(window& win) const
|
||||
{
|
||||
text_box& pwd = find_widget<text_box>(&win, "password", false);
|
||||
pwd.set_value(preferences::password(host_, username_->get_widget_value(win)));
|
||||
}
|
||||
|
||||
void mp_login::save_password(window& win) const
|
||||
{
|
||||
password_box& pwd = find_widget<password_box>(&win, "password", false);
|
||||
preferences::set_password(host_, username_->get_widget_value(win), pwd.get_real_value());
|
||||
}
|
||||
|
||||
void mp_login::pre_show(window& win)
|
||||
{
|
||||
if(button* btn = find_widget<button>(&win, "password_reminder", false, false)) {
|
||||
|
@ -93,12 +102,20 @@ void mp_login::pre_show(window& win)
|
|||
|
||||
btn->set_retval(2);
|
||||
}
|
||||
|
||||
text_box& login = find_widget<text_box>(&win, "user_name", false);
|
||||
login.connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(std::bind(&mp_login::load_password, this, std::ref(win)));
|
||||
|
||||
load_password(win);
|
||||
|
||||
if(focus_password_) {
|
||||
win.keyboard_capture(find_widget<text_box>(&win, "password", false, true));
|
||||
}
|
||||
}
|
||||
|
||||
void mp_login::post_show(window& win)
|
||||
{
|
||||
void mp_login::post_show(window& win) {
|
||||
if(get_retval() == window::OK) {
|
||||
preferences::set_password(find_widget<password_box>(&win, "password", false).get_real_value());
|
||||
save_password(win);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,13 +18,15 @@
|
|||
|
||||
namespace gui2
|
||||
{
|
||||
class field_text;
|
||||
|
||||
namespace dialogs
|
||||
{
|
||||
|
||||
class mp_login : public modal_dialog
|
||||
{
|
||||
public:
|
||||
mp_login(const std::string& label, const bool focus_password);
|
||||
mp_login(const std::string& host, const std::string& label, const bool focus_password);
|
||||
|
||||
private:
|
||||
/** Inherited from modal_dialog, implemented by REGISTER_DIALOG. */
|
||||
|
@ -35,6 +37,13 @@ private:
|
|||
|
||||
/** Inherited from modal_dialog. */
|
||||
virtual void post_show(window& window) override;
|
||||
|
||||
void load_password(window& win) const;
|
||||
void save_password(window& win) const;
|
||||
|
||||
const std::string host_;
|
||||
field_text* username_;
|
||||
bool focus_password_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include "gui/dialogs/multiplayer/mp_method_selection.hpp"
|
||||
|
||||
#include "preferences/game.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "gui/auxiliary/find_widget.hpp"
|
||||
#ifdef GUI2_EXPERIMENTAL_LISTBOX
|
||||
#include "gui/widgets/list.hpp"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include "gui/dialogs/preferences_dialog.hpp"
|
||||
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "formatter.hpp"
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "gettext.hpp"
|
||||
#include "wesnothd_connection.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "preferences/game.hpp"
|
||||
#include "preferences/lobby.hpp"
|
||||
#include "log.hpp"
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "chat_command_handler.hpp"
|
||||
#include "color.hpp"
|
||||
#include "config_assign.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "font/standard_colors.hpp"
|
||||
#include "formatter.hpp"
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "actions/vision.hpp"
|
||||
#include "ai/manager.hpp"
|
||||
#include "ai/testing.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
#include "display_chat_manager.hpp"
|
||||
#include "formula/string_utils.hpp"
|
||||
#include "game_events/manager.hpp"
|
||||
|
|
287
src/preferences/credentials.cpp
Normal file
287
src/preferences/credentials.cpp
Normal file
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "credentials.hpp"
|
||||
|
||||
#include "preferences/general.hpp"
|
||||
#include "serialization/unicode.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
static lg::log_domain log_config("config");
|
||||
#define ERR_CFG LOG_STREAM(err , log_config)
|
||||
|
||||
struct login_info
|
||||
{
|
||||
std::string username, server, key;
|
||||
login_info(const std::string& username, const std::string& server, const std::string& key)
|
||||
: username(username), server(server), key(key)
|
||||
{}
|
||||
};
|
||||
|
||||
static std::vector<login_info> credentials;
|
||||
|
||||
// Separate password entries with formfeed
|
||||
static const char CREDENTIAL_SEPARATOR = '\f';
|
||||
|
||||
static std::string encrypt(const std::string& text, const std::string& key);
|
||||
static std::string decrypt(const std::string& text, const std::string& key);
|
||||
static std::string build_key(const std::string& server, const std::string& login);
|
||||
static std::string escape(const std::string& text);
|
||||
static std::string unescape(const std::string& text);
|
||||
|
||||
static std::string get_system_username()
|
||||
{
|
||||
std::string res;
|
||||
#ifdef _WIN32
|
||||
wchar_t buffer[300];
|
||||
DWORD size = 300;
|
||||
if(GetUserNameW(buffer, &size)) {
|
||||
//size includes a terminating null character.
|
||||
assert(size > 0);
|
||||
res = unicode_cast<utf8::string>(boost::iterator_range<wchar_t*>(buffer, buffer + size - 1));
|
||||
}
|
||||
#else
|
||||
if(char* const login = getenv("USER")) {
|
||||
res = login;
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
static void clear_credentials()
|
||||
{
|
||||
// Zero them before clearing.
|
||||
// Probably overly paranoid, but doesn't hurt?
|
||||
for(auto& cred : credentials) {
|
||||
std::fill(cred.username.begin(), cred.username.end(), '\0');
|
||||
std::fill(cred.server.begin(), cred.server.end(), '\0');
|
||||
std::fill(cred.key.begin(), cred.key.end(), '\0');
|
||||
}
|
||||
credentials.clear();
|
||||
}
|
||||
|
||||
static const std::string EMPTY_LOGIN = "@@";
|
||||
|
||||
namespace preferences
|
||||
{
|
||||
std::string login()
|
||||
{
|
||||
std::string name = preferences::get("login", EMPTY_LOGIN);
|
||||
if(name == EMPTY_LOGIN) {
|
||||
name = get_system_username();
|
||||
} else if(name.size() > 2 && name[0] == '@' && name[name.size() - 1] == '@') {
|
||||
name = name.substr(1, name.size() - 2);
|
||||
} else {
|
||||
ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)" << std::endl;
|
||||
}
|
||||
if(name.empty()) {
|
||||
return "player";
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void set_login(const std::string& login)
|
||||
{
|
||||
preferences::set("login", '@' + login + '@');
|
||||
}
|
||||
|
||||
bool remember_password()
|
||||
{
|
||||
return preferences::get("remember_password", false);
|
||||
}
|
||||
|
||||
void set_remember_password(bool remember)
|
||||
{
|
||||
preferences::set("remember_password", remember);
|
||||
|
||||
if(remember) {
|
||||
load_credentials();
|
||||
} else {
|
||||
clear_credentials();
|
||||
}
|
||||
}
|
||||
|
||||
std::string password(const std::string& server, const std::string& login)
|
||||
{
|
||||
if(!remember_password()) {
|
||||
if(!credentials.empty() && credentials[0].username == login && credentials[0].server == server) {
|
||||
return decrypt(credentials[0].key, build_key(server, login));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
auto cred = std::find_if(credentials.begin(), credentials.end(), [&](const login_info& cred) {
|
||||
return cred.server == server && cred.username == login;
|
||||
});
|
||||
if(cred == credentials.end()) {
|
||||
return "";
|
||||
}
|
||||
return decrypt(unescape(cred->key), build_key(server, login));
|
||||
}
|
||||
|
||||
void set_password(const std::string& server, const std::string& login, const std::string& key)
|
||||
{
|
||||
if(!remember_password()) {
|
||||
clear_credentials();
|
||||
credentials.emplace_back(login, server, encrypt(key, build_key(server, login)));
|
||||
return;
|
||||
}
|
||||
auto cred = std::find_if(credentials.begin(), credentials.end(), [&](const login_info& cred) {
|
||||
return cred.server == server && cred.username == login;
|
||||
});
|
||||
if(cred == credentials.end()) {
|
||||
// This is equivalent to emplace_back, but also returns the iterator to the new element
|
||||
cred = credentials.emplace(credentials.end(), login, server, "");
|
||||
}
|
||||
cred->key = escape(encrypt(key, build_key(server, login)));
|
||||
}
|
||||
|
||||
void load_credentials()
|
||||
{
|
||||
if(!remember_password()) {
|
||||
return;
|
||||
}
|
||||
clear_credentials();
|
||||
std::string cred_file = filesystem::get_credentials_file();
|
||||
if(!filesystem::file_exists(cred_file)) {
|
||||
return;
|
||||
}
|
||||
filesystem::scoped_istream stream = filesystem::istream_file(cred_file, false);
|
||||
// Credentials file is a binary blob, so use streambuf iterator
|
||||
std::string data((std::istreambuf_iterator<char>(*stream)), (std::istreambuf_iterator<char>()));
|
||||
data = decrypt(data, build_key("global", get_system_username()));
|
||||
if(data.empty() || data[0] != CREDENTIAL_SEPARATOR) {
|
||||
ERR_CFG << "Invalid data in credentials file\n";
|
||||
std::fill(data.begin(), data.end(), '\0');
|
||||
return;
|
||||
}
|
||||
for(const std::string elem : utils::split(data, CREDENTIAL_SEPARATOR, utils::REMOVE_EMPTY)) {
|
||||
size_t at = elem.find_last_of('@');
|
||||
size_t eq = elem.find_first_of('=', at + 1);
|
||||
if(at != std::string::npos && eq != std::string::npos) {
|
||||
credentials.emplace_back(elem.substr(0, at), elem.substr(at + 1, eq - at - 1), elem.substr(eq + 1));
|
||||
}
|
||||
}
|
||||
std::fill(data.begin(), data.end(), '\0');
|
||||
}
|
||||
|
||||
void save_credentials()
|
||||
{
|
||||
if(!remember_password()) {
|
||||
filesystem::delete_file(filesystem::get_credentials_file());
|
||||
return;
|
||||
}
|
||||
std::ostringstream credentials_data;
|
||||
for(const auto& cred : credentials) {
|
||||
credentials_data.put(CREDENTIAL_SEPARATOR);
|
||||
std::copy(cred.username.begin(), cred.username.end(), std::ostreambuf_iterator<char>(credentials_data));
|
||||
credentials_data.put('@');
|
||||
std::copy(cred.server.begin(), cred.server.end(), std::ostreambuf_iterator<char>(credentials_data));
|
||||
credentials_data.put('=');
|
||||
std::copy(cred.key.begin(), cred.key.end(), std::ostreambuf_iterator<char>(credentials_data));
|
||||
}
|
||||
credentials_data.put(CREDENTIAL_SEPARATOR);
|
||||
try {
|
||||
filesystem::scoped_ostream credentials_file = filesystem::ostream_file(filesystem::get_credentials_file());
|
||||
std::string encrypted = encrypt(credentials_data.str(), build_key("global", get_system_username()));
|
||||
credentials_file->write(encrypted.c_str(), encrypted.size());
|
||||
} catch(filesystem::io_exception&) {
|
||||
ERR_CFG << "error writing to credentials file '" << filesystem::get_credentials_file() << "'" << std::endl;
|
||||
}
|
||||
size_t n = credentials_data.tellp();
|
||||
credentials_data.seekp(0, std::ios::beg);
|
||||
std::fill_n(std::ostreambuf_iterator<char>(credentials_data), n, '\0');
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Key-stretching (bcrypt was recommended)
|
||||
std::string build_key(const std::string& server, const std::string& login)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << login << get_system_username() << server;
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// FIXME: XOR encryption is a really terrible choice - swap it out for something better!
|
||||
// TODO: Maybe use cryptopp or something for AES encryption?
|
||||
static std::string xor_crypt(std::string text, const std::string& key)
|
||||
{
|
||||
const size_t m = key.size();
|
||||
for(size_t i = 0; i < text.size(); i++) {
|
||||
text[i] ^= key[i % m];
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::string encrypt(const std::string& text, const std::string& key)
|
||||
{
|
||||
return xor_crypt(text, key);
|
||||
}
|
||||
|
||||
std::string decrypt(const std::string& text, const std::string& key)
|
||||
{
|
||||
return xor_crypt(text, key);
|
||||
}
|
||||
|
||||
std::string unescape(const std::string& text)
|
||||
{
|
||||
std::string unescaped;
|
||||
unescaped.reserve(text.size());
|
||||
bool escaping = false;
|
||||
for(char c : text) {
|
||||
if(escaping) {
|
||||
if(c == '\xa') {
|
||||
unescaped.push_back('\xc');
|
||||
} else if(c == '.') {
|
||||
unescaped.push_back('@');
|
||||
} else {
|
||||
unescaped.push_back(c);
|
||||
}
|
||||
escaping = false;
|
||||
} else if(c == '\x1') {
|
||||
escaping = true;
|
||||
} else {
|
||||
unescaped.push_back(c);
|
||||
}
|
||||
}
|
||||
assert(!escaping);
|
||||
return unescaped;
|
||||
}
|
||||
|
||||
std::string escape(const std::string& text)
|
||||
{
|
||||
std::string escaped;
|
||||
escaped.reserve(text.size());
|
||||
for(char c : text) {
|
||||
if(c == '\x1') {
|
||||
escaped += "\x1\x1";
|
||||
} else if(c == '\xc') {
|
||||
escaped += "\x1\xa";
|
||||
} else if(c == '@') {
|
||||
escaped += "\x1.";
|
||||
} else {
|
||||
escaped.push_back(c);
|
||||
}
|
||||
}
|
||||
return escaped;
|
||||
}
|
30
src/preferences/credentials.hpp
Normal file
30
src/preferences/credentials.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright (C) 2017 by the Battle for Wesnoth Project http://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 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.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace preferences {
|
||||
std::string login();
|
||||
void set_login(const std::string& login);
|
||||
|
||||
std::string password(const std::string& server, const std::string& login);
|
||||
void set_password(const std::string& server, const std::string& login, const std::string& key);
|
||||
|
||||
bool remember_password();
|
||||
void set_remember_password(bool remember);
|
||||
|
||||
void load_credentials();
|
||||
void save_credentials();
|
||||
}
|
|
@ -28,20 +28,6 @@
|
|||
#include "wml_exception.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#ifdef _WIN32
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#ifdef INADDR_ANY
|
||||
#undef INADDR_ANY
|
||||
#endif
|
||||
#ifdef INADDR_BROADCAST
|
||||
#undef INADDR_BROADCAST
|
||||
#endif
|
||||
#ifdef INADDR_NONE
|
||||
#undef INADDR_NONE
|
||||
#endif
|
||||
#include <windows.h> //GetUserName
|
||||
#endif
|
||||
|
||||
|
||||
static lg::log_domain log_config("config");
|
||||
#define ERR_CFG LOG_STREAM(err , log_config)
|
||||
|
@ -72,28 +58,6 @@ bool options_initialized = false;
|
|||
|
||||
bool authenticated = false;
|
||||
|
||||
const char WRAP_CHAR = '@';
|
||||
const std::string EMPTY_WRAPPED_STRING = "@@";
|
||||
|
||||
std::string wrap_credentials_field_value(const std::string& value)
|
||||
{
|
||||
return WRAP_CHAR + value + WRAP_CHAR;
|
||||
}
|
||||
|
||||
std::string parse_wrapped_credentials_field(const std::string& raw)
|
||||
{
|
||||
if(raw.empty() || raw == EMPTY_WRAPPED_STRING) {
|
||||
// empty (wrapped or not)
|
||||
return raw;
|
||||
} else if(raw.length() < 2 || raw[0] != WRAP_CHAR || raw[raw.length() - 1] != WRAP_CHAR ) {
|
||||
// malformed/not wrapped (shouldn't happen)
|
||||
ERR_CFG << "malformed user credentials (did you manually edit the preferences file?)" << std::endl;
|
||||
return raw;
|
||||
}
|
||||
|
||||
return raw.substr(1, raw.length() - 2);
|
||||
}
|
||||
|
||||
void initialize_modifications(bool mp = true)
|
||||
{
|
||||
if (mp) {
|
||||
|
@ -453,117 +417,6 @@ void set_campaign_server(const std::string& host)
|
|||
preferences::set("campaign_server", host);
|
||||
}
|
||||
|
||||
bool wrap_password()
|
||||
{
|
||||
const bool have_old_password_format =
|
||||
(!preferences::have_setting("password_is_wrapped")) && preferences::have_setting("password");
|
||||
return have_old_password_format ? false : preferences::get("password_is_wrapped", true);
|
||||
}
|
||||
|
||||
void set_wrap_password(bool wrap)
|
||||
{
|
||||
preferences::set("password_is_wrapped", wrap);
|
||||
}
|
||||
|
||||
bool wrap_login()
|
||||
{
|
||||
const bool have_old_login_format =
|
||||
(!preferences::have_setting("login_is_wrapped")) && preferences::have_setting("login");
|
||||
return have_old_login_format ? false : preferences::get("login_is_wrapped", true);
|
||||
}
|
||||
|
||||
void set_wrap_login(bool wrap)
|
||||
{
|
||||
preferences::set("login_is_wrapped", wrap);
|
||||
}
|
||||
|
||||
static std::string get_system_username()
|
||||
{
|
||||
std::string res;
|
||||
#ifdef _WIN32
|
||||
wchar_t buffer[300];
|
||||
DWORD size = 300;
|
||||
if(GetUserNameW(buffer,&size)) {
|
||||
//size includes a terminating null character.
|
||||
assert(size > 0);
|
||||
res = unicode_cast<utf8::string>(boost::iterator_range<wchar_t*>(buffer, buffer + size - 1));
|
||||
}
|
||||
#else
|
||||
if(char* const login = getenv("USER")) {
|
||||
res = login;
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string login()
|
||||
{
|
||||
const std::string res = preferences::get("login");
|
||||
if(res.empty() || res == EMPTY_WRAPPED_STRING) {
|
||||
const std::string& login = get_system_username();
|
||||
if(!login.empty()) {
|
||||
return login;
|
||||
}
|
||||
|
||||
if(res.empty()) {
|
||||
return _("player");
|
||||
}
|
||||
}
|
||||
|
||||
if(!wrap_login()) {
|
||||
return res;
|
||||
} else {
|
||||
return parse_wrapped_credentials_field(res);
|
||||
}
|
||||
}
|
||||
|
||||
void set_login(const std::string& username)
|
||||
{
|
||||
set_wrap_login(true);
|
||||
preferences::set("login", wrap_credentials_field_value(username));
|
||||
}
|
||||
|
||||
namespace prv {
|
||||
std::string password;
|
||||
}
|
||||
|
||||
std::string password()
|
||||
{
|
||||
if(remember_password()) {
|
||||
const std::string saved_pass = preferences::get("password");
|
||||
if(!wrap_password()) {
|
||||
return saved_pass;
|
||||
} else {
|
||||
return parse_wrapped_credentials_field(saved_pass);
|
||||
}
|
||||
} else {
|
||||
return prv::password;
|
||||
}
|
||||
}
|
||||
|
||||
void set_password(const std::string& password)
|
||||
{
|
||||
prv::password = password;
|
||||
if(remember_password()) {
|
||||
set_wrap_password(true);
|
||||
preferences::set("password", wrap_credentials_field_value(password));
|
||||
}
|
||||
}
|
||||
|
||||
bool remember_password()
|
||||
{
|
||||
return preferences::get("remember_password", false);
|
||||
}
|
||||
|
||||
void set_remember_password(bool remember)
|
||||
{
|
||||
preferences::set("remember_password", remember);
|
||||
|
||||
if(!remember) {
|
||||
preferences::set("password", "");
|
||||
}
|
||||
}
|
||||
|
||||
bool turn_dialog()
|
||||
{
|
||||
return preferences::get("turn_dialog", false);
|
||||
|
|
|
@ -83,45 +83,6 @@ class acquaintance;
|
|||
std::string campaign_server();
|
||||
void set_campaign_server(const std::string& host);
|
||||
|
||||
/**
|
||||
* Returns whether the MP username is stored wrapped in markers.
|
||||
*
|
||||
* New usernames are stored in a specific format to force string interpretation
|
||||
* (due to bug #16571).
|
||||
*/
|
||||
bool wrap_login();
|
||||
void set_wrap_login(bool wrap);
|
||||
|
||||
std::string login();
|
||||
void set_login(const std::string& username);
|
||||
|
||||
// If password remembering is turned off use
|
||||
// prv::password instead. This way we will not
|
||||
// have to worry about whether to remember the
|
||||
// password or not anywhere else in the code.
|
||||
//
|
||||
// It is put into a separate namespace to make clear
|
||||
// it is "private" and not supposed to be edit outside
|
||||
// of the preferences functions.
|
||||
namespace prv {
|
||||
extern std::string password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the password is stored wrapped in markers.
|
||||
*
|
||||
* New passwords are stored in a specific format to force string interpretation
|
||||
* (due to bug #16571).
|
||||
*/
|
||||
bool wrap_password();
|
||||
void set_wrap_password(bool wrap);
|
||||
|
||||
std::string password();
|
||||
void set_password(const std::string& password);
|
||||
|
||||
bool remember_password();
|
||||
void set_remember_password(bool remember);
|
||||
|
||||
bool turn_dialog();
|
||||
void set_turn_dialog(bool ison);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "hotkey/hotkey_item.hpp"
|
||||
#include "lexical_cast.hpp"
|
||||
#include "log.hpp"
|
||||
#include "credentials.hpp"
|
||||
#include "preferences/general.hpp"
|
||||
#include "sound.hpp"
|
||||
#include "video.hpp" // non_interactive()
|
||||
|
@ -104,6 +105,7 @@ base_manager::base_manager()
|
|||
<< e.what()
|
||||
<< std::endl;
|
||||
}
|
||||
preferences::load_credentials();
|
||||
}
|
||||
|
||||
base_manager::~base_manager()
|
||||
|
@ -162,6 +164,8 @@ void write_preferences()
|
|||
ERR_FS << "error writing to preferences file '" << filesystem::get_prefs_file() << "'" << std::endl;
|
||||
}
|
||||
|
||||
preferences::save_credentials();
|
||||
|
||||
#ifndef _WIN32
|
||||
if(!prefs_file_existed) {
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "server/game.hpp"
|
||||
#include "server/player_network.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "preferences/credentials.hpp"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
|
|
@ -946,7 +946,7 @@ struct dialog_tester<mp_login>
|
|||
{
|
||||
mp_login* create()
|
||||
{
|
||||
return new mp_login("label", true);
|
||||
return new mp_login("wesnoth.org", "label", true);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue