Unsafe encoding in IPC (#1851)

Co-authored-by: Sergey Kroshnin <sergiysw@gmail.com>
Co-authored-by: Devin Alexander Torres <d@devinus.io>
This commit is contained in:
cryptocode 2019-04-14 23:47:37 +02:00 committed by GitHub
commit e81197134b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 84 additions and 11 deletions

View file

@ -58,7 +58,9 @@ namespace ipc
* Request is preamble followed by 32-bit BE payload length and payload bytes.
* Response is 32-bit BE payload length followed by payload bytes.
*/
json_legacy = 1
json_legacy = 0x1,
/** Request/response is same as json_legacy and exposes unsafe RPC's */
json_unsafe = 0x2
};
/** IPC transport interface */

View file

@ -84,8 +84,8 @@ public:
});
}
/** Handler for payload_encoding::json_legacy */
void rpc_handle_query ()
/** Handler for payload_encoding::json_legacy and payload_encoding::json_unsafe */
void rpc_handle_query (bool allow_unsafe)
{
session_timer.restart ();
auto request_id_l (std::to_string (server.id_dispenser.fetch_add (1)));
@ -129,7 +129,8 @@ public:
// Note that if the rpc action is async, the shared_ptr<rpc_handler> lifetime will be extended by the action handler
auto handler (std::make_shared<nano::rpc_handler> (node, server.rpc, body, request_id_l, response_handler_l));
handler->process_request ();
// For unsafe actions to be allowed, the unsafe encoding must be used AND the transport config must allow it
handler->process_request (allow_unsafe && config_transport.allow_unsafe);
}
/** Async request reader */
@ -147,15 +148,16 @@ public:
this_l->node.logger.always_log ("IPC: Invalid preamble");
}
}
else if (this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast<uint8_t> (nano::ipc::payload_encoding::json_legacy))
else if (this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast<uint8_t> (nano::ipc::payload_encoding::json_legacy) || this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast<uint8_t> (nano::ipc::payload_encoding::json_unsafe))
{
auto allow_unsafe (this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast<uint8_t> (nano::ipc::payload_encoding::json_unsafe));
// Length of payload
this_l->async_read_exactly (&this_l->buffer_size, sizeof (this_l->buffer_size), [this_l]() {
this_l->async_read_exactly (&this_l->buffer_size, sizeof (this_l->buffer_size), [this_l, allow_unsafe]() {
boost::endian::big_to_native_inplace (this_l->buffer_size);
this_l->buffer.resize (this_l->buffer_size);
// Payload (ptree compliant JSON string)
this_l->async_read_exactly (this_l->buffer.data (), this_l->buffer_size, [this_l]() {
this_l->rpc_handle_query ();
this_l->async_read_exactly (this_l->buffer.data (), this_l->buffer_size, [this_l, allow_unsafe]() {
this_l->rpc_handle_query (allow_unsafe);
});
});
}

View file

@ -20,6 +20,7 @@ nano::error nano::ipc::ipc_config::serialize_json (nano::jsonconfig & json) cons
domain_l.put ("io_threads", transport_domain.io_threads);
}
domain_l.put ("enable", transport_domain.enabled);
domain_l.put ("allow_unsafe", transport_domain.allow_unsafe);
domain_l.put ("path", transport_domain.path);
domain_l.put ("io_timeout", transport_domain.io_timeout);
json.put_child ("local", domain_l);
@ -32,6 +33,7 @@ nano::error nano::ipc::ipc_config::deserialize_json (nano::jsonconfig & json)
if (tcp_l)
{
tcp_l->get_optional<long> ("io_threads", transport_tcp.io_threads, -1);
tcp_l->get_optional<bool> ("allow_unsafe", transport_tcp.allow_unsafe);
tcp_l->get<bool> ("enable", transport_tcp.enabled);
tcp_l->get<uint16_t> ("port", transport_tcp.port);
tcp_l->get<size_t> ("io_timeout", transport_tcp.io_timeout);
@ -41,6 +43,7 @@ nano::error nano::ipc::ipc_config::deserialize_json (nano::jsonconfig & json)
if (domain_l)
{
domain_l->get_optional<long> ("io_threads", transport_domain.io_threads, -1);
domain_l->get_optional<bool> ("allow_unsafe", transport_domain.allow_unsafe);
domain_l->get<bool> ("enable", transport_domain.enabled);
domain_l->get<std::string> ("path", transport_domain.path);
domain_l->get<size_t> ("io_timeout", transport_domain.io_timeout);

View file

@ -16,6 +16,7 @@ namespace ipc
public:
virtual ~ipc_config_transport () = default;
bool enabled{ false };
bool allow_unsafe{ false };
size_t io_timeout{ 15 };
long io_threads{ -1 };
};

View file

@ -4169,6 +4169,27 @@ void nano::rpc_handler::wallet_republish ()
response_errors ();
}
void nano::rpc_handler::wallet_seed ()
{
rpc_control_impl ();
auto wallet (wallet_impl ());
if (!ec)
{
auto transaction (node.wallets.tx_begin_read ());
if (wallet->store.valid_password (transaction))
{
nano::raw_key seed;
wallet->store.seed (seed, transaction);
response_l.put ("seed", seed.data.to_string ());
}
else
{
ec = nano::error_common::wallet_locked;
}
}
response_errors ();
}
void nano::rpc_handler::wallet_work_get ()
{
rpc_control_impl ();
@ -4400,7 +4421,7 @@ std::string filter_request (boost::property_tree::ptree tree_a)
}
}
void nano::rpc_handler::process_request ()
void nano::rpc_handler::process_request (bool unsafe_a)
{
try
{
@ -4441,7 +4462,18 @@ void nano::rpc_handler::process_request ()
else
{
// Try the rest of the options
if (action == "chain")
if (action == "wallet_seed")
{
if (unsafe_a || rpc.node.network_params.network.is_test_network ())
{
wallet_seed ();
}
else
{
error_response (response, "Unsafe RPC not allowed");
}
}
else if (action == "chain")
{
chain ();
}

View file

@ -16,7 +16,12 @@ class rpc_handler : public std::enable_shared_from_this<nano::rpc_handler>
{
public:
rpc_handler (nano::node &, nano::rpc &, std::string const &, std::string const &, std::function<void(boost::property_tree::ptree const &)> const &);
void process_request ();
/**
* Process http request
* @param unsafe If true, the caller requests an unsafe action. This will be granted only if the config allows it.
*/
void process_request (bool unsafe = false);
void account_balance ();
void account_block_count ();
void account_count ();
@ -117,6 +122,7 @@ public:
void wallet_representative ();
void wallet_representative_set ();
void wallet_republish ();
void wallet_seed ();
void wallet_work_get ();
void work_generate ();
void work_cancel ();

View file

@ -2605,6 +2605,33 @@ TEST (rpc, representatives)
ASSERT_EQ (nano::genesis_account, representatives[0]);
}
// wallet_seed is only available over IPC's unsafe encoding, and when running on test network
TEST (rpc, wallet_seed)
{
nano::system system (24000, 1);
nano::raw_key seed;
{
auto transaction (system.nodes[0]->wallets.tx_begin ());
system.wallet (0)->store.seed (seed, transaction);
}
nano::rpc rpc (system.io_ctx, *system.nodes[0], nano::rpc_config (true));
rpc.start ();
boost::property_tree::ptree request;
request.put ("action", "wallet_seed");
request.put ("wallet", system.nodes[0]->wallets.items.begin ()->first.to_string ());
test_response response (request, rpc, system.io_ctx);
system.deadline_set (5s);
while (response.status == 0)
{
ASSERT_NO_ERROR (system.poll ());
}
ASSERT_EQ (200, response.status);
{
std::string seed_text (response.json.get<std::string> ("seed"));
ASSERT_EQ (seed.data.to_string (), seed_text);
}
}
TEST (rpc, wallet_change_seed)
{
nano::system system0 (24000, 1);