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:
parent
f1bd2a5496
commit
e81197134b
7 changed files with 84 additions and 11 deletions
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 };
|
||||
};
|
||||
|
|
|
@ -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 ();
|
||||
}
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue