From e81197134b2039945c85f816ea2ea06cdb6328ac Mon Sep 17 00:00:00 2001 From: cryptocode Date: Sun, 14 Apr 2019 23:47:37 +0200 Subject: [PATCH] Unsafe encoding in IPC (#1851) Co-authored-by: Sergey Kroshnin Co-authored-by: Devin Alexander Torres --- nano/lib/ipc.hpp | 4 +++- nano/node/ipc.cpp | 16 +++++++++------- nano/node/ipcconfig.cpp | 3 +++ nano/node/ipcconfig.hpp | 1 + nano/rpc/rpc_handler.cpp | 36 ++++++++++++++++++++++++++++++++++-- nano/rpc/rpc_handler.hpp | 8 +++++++- nano/rpc_test/rpc.cpp | 27 +++++++++++++++++++++++++++ 7 files changed, 84 insertions(+), 11 deletions(-) diff --git a/nano/lib/ipc.hpp b/nano/lib/ipc.hpp index fdeec354..a3c9f0a8 100644 --- a/nano/lib/ipc.hpp +++ b/nano/lib/ipc.hpp @@ -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 */ diff --git a/nano/node/ipc.cpp b/nano/node/ipc.cpp index 5bc09a84..a002afb1 100644 --- a/nano/node/ipc.cpp +++ b/nano/node/ipc.cpp @@ -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 lifetime will be extended by the action handler auto handler (std::make_shared (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 (nano::ipc::payload_encoding::json_legacy)) + else if (this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast (nano::ipc::payload_encoding::json_legacy) || this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast (nano::ipc::payload_encoding::json_unsafe)) { + auto allow_unsafe (this_l->buffer[nano::ipc::preamble_offset::encoding] == static_cast (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); }); }); } diff --git a/nano/node/ipcconfig.cpp b/nano/node/ipcconfig.cpp index 3f526ba0..7b5323c2 100644 --- a/nano/node/ipcconfig.cpp +++ b/nano/node/ipcconfig.cpp @@ -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 ("io_threads", transport_tcp.io_threads, -1); + tcp_l->get_optional ("allow_unsafe", transport_tcp.allow_unsafe); tcp_l->get ("enable", transport_tcp.enabled); tcp_l->get ("port", transport_tcp.port); tcp_l->get ("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 ("io_threads", transport_domain.io_threads, -1); + domain_l->get_optional ("allow_unsafe", transport_domain.allow_unsafe); domain_l->get ("enable", transport_domain.enabled); domain_l->get ("path", transport_domain.path); domain_l->get ("io_timeout", transport_domain.io_timeout); diff --git a/nano/node/ipcconfig.hpp b/nano/node/ipcconfig.hpp index 9302b3c2..6485a3fa 100644 --- a/nano/node/ipcconfig.hpp +++ b/nano/node/ipcconfig.hpp @@ -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 }; }; diff --git a/nano/rpc/rpc_handler.cpp b/nano/rpc/rpc_handler.cpp index cd8b343e..f84932fd 100644 --- a/nano/rpc/rpc_handler.cpp +++ b/nano/rpc/rpc_handler.cpp @@ -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 (); } diff --git a/nano/rpc/rpc_handler.hpp b/nano/rpc/rpc_handler.hpp index e350bfc9..2e252856 100644 --- a/nano/rpc/rpc_handler.hpp +++ b/nano/rpc/rpc_handler.hpp @@ -16,7 +16,12 @@ class rpc_handler : public std::enable_shared_from_this { public: rpc_handler (nano::node &, nano::rpc &, std::string const &, std::string const &, std::function 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 (); diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index e2182552..c095dfed 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -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 ("seed")); + ASSERT_EQ (seed.data.to_string (), seed_text); + } +} + TEST (rpc, wallet_change_seed) { nano::system system0 (24000, 1);