Merge pull request #1464
427054c3
Enabled HTTP auth support for monero-wallet-rpc (Lee Clagett)
This commit is contained in:
commit
5fcf7c1c18
5 changed files with 205 additions and 17 deletions
|
@ -53,8 +53,8 @@ namespace tools
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* data_file_file = _fdopen(data_file_descriptor, "wb");
|
const std::unique_ptr<FILE, tools::close_file> data_file_file{_fdopen(data_file_descriptor, "wb")};
|
||||||
if (0 == data_file_file)
|
if (nullptr == data_file_file)
|
||||||
{
|
{
|
||||||
// Call CloseHandle is not necessary
|
// Call CloseHandle is not necessary
|
||||||
_close(data_file_descriptor);
|
_close(data_file_descriptor);
|
||||||
|
@ -62,11 +62,10 @@ namespace tools
|
||||||
}
|
}
|
||||||
|
|
||||||
// HACK: undocumented constructor, this code may not compile
|
// HACK: undocumented constructor, this code may not compile
|
||||||
std::ofstream data_file(data_file_file);
|
std::ofstream data_file(data_file_file.get());
|
||||||
if (data_file.fail())
|
if (data_file.fail())
|
||||||
{
|
{
|
||||||
// Call CloseHandle and _close are not necessary
|
// Call CloseHandle and _close are not necessary
|
||||||
fclose(data_file_file);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -85,7 +84,6 @@ namespace tools
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
// To make sure the file is fully stored on disk
|
// To make sure the file is fully stored on disk
|
||||||
::FlushFileBuffers(data_file_handle);
|
::FlushFileBuffers(data_file_handle);
|
||||||
fclose(data_file_file);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -50,6 +50,92 @@ namespace tools
|
||||||
{
|
{
|
||||||
std::function<void(int)> signal_handler::m_handler;
|
std::function<void(int)> signal_handler::m_handler;
|
||||||
|
|
||||||
|
std::unique_ptr<std::FILE, tools::close_file> create_private_file(const std::string& name)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
struct close_handle
|
||||||
|
{
|
||||||
|
void operator()(HANDLE handle) const noexcept
|
||||||
|
{
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<void, close_handle> process = nullptr;
|
||||||
|
{
|
||||||
|
HANDLE temp{};
|
||||||
|
const bool fail = OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, std::addressof(temp)) == 0;
|
||||||
|
process.reset(temp);
|
||||||
|
if (fail)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD sid_size = 0;
|
||||||
|
GetTokenInformation(process.get(), TokenOwner, nullptr, 0, std::addressof(sid_size));
|
||||||
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
std::unique_ptr<char[]> sid{new char[sid_size]};
|
||||||
|
if (!GetTokenInformation(process.get(), TokenOwner, sid.get(), sid_size, std::addressof(sid_size)))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const PSID psid = reinterpret_cast<const PTOKEN_OWNER>(sid.get())->Owner;
|
||||||
|
const DWORD daclSize =
|
||||||
|
sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - sizeof(DWORD);
|
||||||
|
|
||||||
|
const std::unique_ptr<char[]> dacl{new char[daclSize]};
|
||||||
|
if (!InitializeAcl(reinterpret_cast<PACL>(dacl.get()), daclSize, ACL_REVISION))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!AddAccessAllowedAce(reinterpret_cast<PACL>(dacl.get()), ACL_REVISION, (READ_CONTROL | FILE_GENERIC_READ | DELETE), psid))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
SECURITY_DESCRIPTOR descriptor{};
|
||||||
|
if (!InitializeSecurityDescriptor(std::addressof(descriptor), SECURITY_DESCRIPTOR_REVISION))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!SetSecurityDescriptorDacl(std::addressof(descriptor), true, reinterpret_cast<PACL>(dacl.get()), false))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES attributes{sizeof(SECURITY_ATTRIBUTES), std::addressof(descriptor), false};
|
||||||
|
std::unique_ptr<void, close_handle> file{
|
||||||
|
CreateFile(
|
||||||
|
name.c_str(),
|
||||||
|
GENERIC_WRITE, FILE_SHARE_READ,
|
||||||
|
std::addressof(attributes),
|
||||||
|
CREATE_NEW, FILE_ATTRIBUTE_TEMPORARY,
|
||||||
|
nullptr
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
const int fd = _open_osfhandle(reinterpret_cast<intptr_t>(file.get()), 0);
|
||||||
|
if (0 <= fd)
|
||||||
|
{
|
||||||
|
file.release();
|
||||||
|
std::FILE* real_file = _fdopen(fd, "w");
|
||||||
|
if (!real_file)
|
||||||
|
{
|
||||||
|
_close(fd);
|
||||||
|
}
|
||||||
|
return {real_file, tools::close_file{}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
const int fd = open(name.c_str(), (O_RDWR | O_EXCL | O_CREAT), S_IRUSR);
|
||||||
|
if (0 <= fd)
|
||||||
|
{
|
||||||
|
std::FILE* file = fdopen(fd, "w");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return {file, tools::close_file{}};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
std::string get_windows_version_display_string()
|
std::string get_windows_version_display_string()
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,13 +30,15 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <mutex>
|
#include <boost/thread/locks.hpp>
|
||||||
#include <system_error>
|
#include <boost/thread/mutex.hpp>
|
||||||
#include <boost/filesystem.hpp>
|
#include <csignal>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "crypto/crypto.h"
|
|
||||||
#include "crypto/hash.h"
|
#include "crypto/hash.h"
|
||||||
#include "misc_language.h"
|
|
||||||
#include "p2p/p2p_protocol_defs.h"
|
#include "p2p/p2p_protocol_defs.h"
|
||||||
|
|
||||||
/*! \brief Various Tools
|
/*! \brief Various Tools
|
||||||
|
@ -46,6 +48,21 @@
|
||||||
*/
|
*/
|
||||||
namespace tools
|
namespace tools
|
||||||
{
|
{
|
||||||
|
//! Functional class for closing C file handles.
|
||||||
|
struct close_file
|
||||||
|
{
|
||||||
|
void operator()(std::FILE* handle) const noexcept
|
||||||
|
{
|
||||||
|
if (handle)
|
||||||
|
{
|
||||||
|
std::fclose(handle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//! \return File only readable by owner. nullptr if `filename` exists.
|
||||||
|
std::unique_ptr<std::FILE, close_file> create_private_file(const std::string& filename);
|
||||||
|
|
||||||
/*! \brief Returns the default data directory.
|
/*! \brief Returns the default data directory.
|
||||||
*
|
*
|
||||||
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
* \details Windows < Vista: C:\\Documents and Settings\\Username\\Application Data\\CRYPTONOTE_NAME
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
//
|
//
|
||||||
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
||||||
#include <boost/asio/ip/address.hpp>
|
#include <boost/asio/ip/address.hpp>
|
||||||
|
#include <boost/filesystem/operations.hpp>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "include_base_utils.h"
|
#include "include_base_utils.h"
|
||||||
using namespace epee;
|
using namespace epee;
|
||||||
|
@ -36,10 +37,12 @@ using namespace epee;
|
||||||
#include "wallet/wallet_args.h"
|
#include "wallet/wallet_args.h"
|
||||||
#include "common/command_line.h"
|
#include "common/command_line.h"
|
||||||
#include "common/i18n.h"
|
#include "common/i18n.h"
|
||||||
|
#include "common/util.h"
|
||||||
#include "cryptonote_core/cryptonote_format_utils.h"
|
#include "cryptonote_core/cryptonote_format_utils.h"
|
||||||
#include "cryptonote_core/account.h"
|
#include "cryptonote_core/account.h"
|
||||||
#include "wallet_rpc_server_commands_defs.h"
|
#include "wallet_rpc_server_commands_defs.h"
|
||||||
#include "misc_language.h"
|
#include "misc_language.h"
|
||||||
|
#include "string_coding.h"
|
||||||
#include "string_tools.h"
|
#include "string_tools.h"
|
||||||
#include "crypto/hash.h"
|
#include "crypto/hash.h"
|
||||||
|
|
||||||
|
@ -47,9 +50,11 @@ namespace
|
||||||
{
|
{
|
||||||
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
|
const command_line::arg_descriptor<std::string, true> arg_rpc_bind_port = {"rpc-bind-port", "Sets bind port for server"};
|
||||||
const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"};
|
const command_line::arg_descriptor<std::string> arg_rpc_bind_ip = {"rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1"};
|
||||||
const command_line::arg_descriptor<std::string> arg_user_agent = {"user-agent", "Restrict RPC to clients using this user agent", ""};
|
const command_line::arg_descriptor<std::string> arg_rpc_login = {"rpc-login", "Specify username[:password] required for RPC connection"};
|
||||||
|
const command_line::arg_descriptor<bool> arg_disable_rpc_login = {"disable-rpc-login", "Disable HTTP authentication for RPC"};
|
||||||
const command_line::arg_descriptor<bool> arg_confirm_external_bind = {"confirm-external-bind", "Confirm rcp-bind-ip value is NOT a loopback (local) IP"};
|
const command_line::arg_descriptor<bool> arg_confirm_external_bind = {"confirm-external-bind", "Confirm rcp-bind-ip value is NOT a loopback (local) IP"};
|
||||||
|
|
||||||
|
constexpr const char default_rpc_username[] = "monero";
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace tools
|
namespace tools
|
||||||
|
@ -60,9 +65,19 @@ namespace tools
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w)
|
wallet_rpc_server::wallet_rpc_server(wallet2& w):m_wallet(w), rpc_login_filename(), m_stop(false)
|
||||||
{}
|
{}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
wallet_rpc_server::~wallet_rpc_server()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
boost::system::error_code ec{};
|
||||||
|
boost::filesystem::remove(rpc_login_filename, ec);
|
||||||
|
}
|
||||||
|
catch (...) {}
|
||||||
|
}
|
||||||
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
bool wallet_rpc_server::run()
|
bool wallet_rpc_server::run()
|
||||||
{
|
{
|
||||||
m_stop = false;
|
m_stop = false;
|
||||||
|
@ -110,11 +125,79 @@ namespace tools
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epee::net_utils::http::http_auth::login login{};
|
||||||
|
|
||||||
|
const bool disable_auth = command_line::get_arg(vm, arg_disable_rpc_login);
|
||||||
|
const std::string user_pass = command_line::get_arg(vm, arg_rpc_login);
|
||||||
|
const std::string bind_port = command_line::get_arg(vm, arg_rpc_bind_port);
|
||||||
|
|
||||||
|
if (disable_auth)
|
||||||
|
{
|
||||||
|
if (!user_pass.empty())
|
||||||
|
{
|
||||||
|
LOG_ERROR(tr("Cannot specify --") << arg_disable_rpc_login.name << tr(" and --") << arg_rpc_login.name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // auth enabled
|
||||||
|
{
|
||||||
|
if (user_pass.empty())
|
||||||
|
{
|
||||||
|
login.username = default_rpc_username;
|
||||||
|
|
||||||
|
std::array<std::uint8_t, 16> rand_128bit{{}};
|
||||||
|
crypto::rand(rand_128bit.size(), rand_128bit.data());
|
||||||
|
login.password = string_encoding::base64_encode(rand_128bit.data(), rand_128bit.size());
|
||||||
|
}
|
||||||
|
else // user password
|
||||||
|
{
|
||||||
|
const auto loc = user_pass.find(':');
|
||||||
|
login.username = user_pass.substr(0, loc);
|
||||||
|
if (loc != std::string::npos)
|
||||||
|
{
|
||||||
|
login.password = user_pass.substr(loc + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tools::password_container pwd(true);
|
||||||
|
pwd.read_password("RPC password");
|
||||||
|
login.password = pwd.password();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (login.username.empty() || login.password.empty())
|
||||||
|
{
|
||||||
|
LOG_ERROR(tr("Blank username or password not permitted for RPC authenticaion"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!login.username.empty());
|
||||||
|
assert(!login.password.empty());
|
||||||
|
|
||||||
|
std::string temp = "monero-wallet-rpc." + bind_port + ".login";
|
||||||
|
const auto cookie = tools::create_private_file(temp);
|
||||||
|
if (!cookie)
|
||||||
|
{
|
||||||
|
LOG_ERROR(tr("Failed to create file ") << temp << tr(". Check permissions or remove file"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
rpc_login_filename.swap(temp); // nothrow guarantee destructor cleanup
|
||||||
|
temp = rpc_login_filename;
|
||||||
|
std::fputs(login.username.c_str(), cookie.get());
|
||||||
|
std::fputc(':', cookie.get());
|
||||||
|
std::fputs(login.password.c_str(), cookie.get());
|
||||||
|
std::fflush(cookie.get());
|
||||||
|
if (std::ferror(cookie.get()))
|
||||||
|
{
|
||||||
|
LOG_ERROR(tr("Error writing to file ") << temp);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_PRINT_L0(tr("RPC username/password is stored in file ") << temp);
|
||||||
|
} // end auth enabled
|
||||||
|
|
||||||
m_net_server.set_threads_prefix("RPC");
|
m_net_server.set_threads_prefix("RPC");
|
||||||
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
|
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
|
||||||
command_line::get_arg(vm, arg_rpc_bind_port),
|
std::move(bind_port), std::move(bind_ip), std::string{}, boost::make_optional(!disable_auth, std::move(login))
|
||||||
std::move(bind_ip),
|
|
||||||
command_line::get_arg(vm, arg_user_agent)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//------------------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
@ -1132,7 +1215,8 @@ int main(int argc, char** argv) {
|
||||||
tools::wallet2::init_options(desc_params);
|
tools::wallet2::init_options(desc_params);
|
||||||
command_line::add_arg(desc_params, arg_rpc_bind_ip);
|
command_line::add_arg(desc_params, arg_rpc_bind_ip);
|
||||||
command_line::add_arg(desc_params, arg_rpc_bind_port);
|
command_line::add_arg(desc_params, arg_rpc_bind_port);
|
||||||
command_line::add_arg(desc_params, arg_user_agent);
|
command_line::add_arg(desc_params, arg_rpc_login);
|
||||||
|
command_line::add_arg(desc_params, arg_disable_rpc_login);
|
||||||
command_line::add_arg(desc_params, arg_confirm_external_bind);
|
command_line::add_arg(desc_params, arg_confirm_external_bind);
|
||||||
command_line::add_arg(desc_params, arg_wallet_file);
|
command_line::add_arg(desc_params, arg_wallet_file);
|
||||||
command_line::add_arg(desc_params, arg_from_json);
|
command_line::add_arg(desc_params, arg_from_json);
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include <boost/program_options/options_description.hpp>
|
#include <boost/program_options/options_description.hpp>
|
||||||
#include <boost/program_options/variables_map.hpp>
|
#include <boost/program_options/variables_map.hpp>
|
||||||
|
#include <string>
|
||||||
#include "net/http_server_impl_base.h"
|
#include "net/http_server_impl_base.h"
|
||||||
#include "wallet_rpc_server_commands_defs.h"
|
#include "wallet_rpc_server_commands_defs.h"
|
||||||
#include "wallet2.h"
|
#include "wallet2.h"
|
||||||
|
@ -48,6 +49,7 @@ namespace tools
|
||||||
static const char* tr(const char* str);
|
static const char* tr(const char* str);
|
||||||
|
|
||||||
wallet_rpc_server(wallet2& cr);
|
wallet_rpc_server(wallet2& cr);
|
||||||
|
~wallet_rpc_server();
|
||||||
|
|
||||||
bool init(const boost::program_options::variables_map& vm);
|
bool init(const boost::program_options::variables_map& vm);
|
||||||
bool run();
|
bool run();
|
||||||
|
@ -118,6 +120,7 @@ namespace tools
|
||||||
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
|
bool on_query_key(const wallet_rpc::COMMAND_RPC_QUERY_KEY::request& req, wallet_rpc::COMMAND_RPC_QUERY_KEY::response& res, epee::json_rpc::error& er);
|
||||||
|
|
||||||
wallet2& m_wallet;
|
wallet2& m_wallet;
|
||||||
|
std::string rpc_login_filename;
|
||||||
std::atomic<bool> m_stop;
|
std::atomic<bool> m_stop;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue