diff --git a/contrib/epee/include/net/http_server_handlers_map2.h b/contrib/epee/include/net/http_server_handlers_map2.h index 05e0b691..5e73df90 100644 --- a/contrib/epee/include/net/http_server_handlers_map2.h +++ b/contrib/epee/include/net/http_server_handlers_map2.h @@ -507,6 +507,26 @@ namespace epee { return true;\ } +#define MAP_JON_RPC_CONDITIONAL(method_name, callback_f, command_type, predicate) \ + else if(predicate && auto_doc(current_zone_json_uri, method_name, true, docs) && callback_name == method_name) \ +{ \ + call_found = true; \ + PREPARE_OBJECTS_FROM_JSON(command_type) \ + if(!callback_f(req.params, resp.result, m_conn_context)) \ + { \ + epee::json_rpc::error_response fail_resp = AUTO_VAL_INIT(fail_resp); \ + fail_resp.jsonrpc = "2.0"; \ + fail_resp.method = req.method; \ + fail_resp.id = req.id; \ + fail_resp.error.code = -32603; \ + fail_resp.error.message = "Internal error"; \ + epee::serialization::store_t_to_json(static_cast(fail_resp), response_info.m_body); \ + return true; \ + } \ + FINALIZE_OBJECTS_TO_JSON(method_name) \ + return true;\ +} + #define MAP_JON_RPC_N(callback_f, command_type) MAP_JON_RPC(command_type::methodname(), callback_f, command_type) #define END_JSON_RPC_MAP() \ diff --git a/src/common/error_codes.h b/src/common/error_codes.h index 910e1e42..3a629d5e 100644 --- a/src/common/error_codes.h +++ b/src/common/error_codes.h @@ -46,3 +46,4 @@ #define API_RETURN_CODE_HTLC_ORIGIN_HASH_MISSMATCHED "HTLC_ORIGIN_HASH_MISSMATCHED" #define API_RETURN_CODE_WRAP "WRAP" #define API_RETURN_CODE_MISSING_ZC_INPUTS "MISSING_ZC_INPUTS" +#define API_RETURN_CODE_ARG_OUT_OF_LIMITS "ARG_OUT_OF_LIMITS" diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index a498ec18..121586e6 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -16,6 +16,23 @@ using namespace epee; #include "crypto/hash.h" #include "core_rpc_server_error_codes.h" +#define CHECK_RPC_LIMITS(var, limit) if(var > limit) {res.status = API_RETURN_CODE_ARG_OUT_OF_LIMITS; return true;} + + +#define RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS 4000 +#define RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS 500 +#define RPC_LIMIT_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_TXIDS 100 +#define RPC_LIMIT_COMMAND_RPC_CHECK_KEYIMAGES_IMAGES 1000 +#define RPC_LIMIT_COMMAND_RPC_GET_TRANSACTIONS_TXS_HASHES 5000 +#define RPC_LIMIT_COMMAND_RPC_GET_OFFERS_EX_LIMIT 1000 +#define RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DETAILS_COUNT 4000 +#define RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS 4000 +#define RPC_LIMIT_COMMAND_RPC_GET_VOTES 4000 +#define RPC_LIMIT_COMMAND_RPC_GET_ASSETS_LIST 1000 +#define RPC_LIMIT_COMMAND_RPC_GET_ALT_BLOCKS_DETAILS_COUNT 100 +#define RPC_LIMIT_COMMAND_RPC_FORCE_RELAY_RAW_TXS 100 +#define RPC_LIMIT_COMMAND_RPC_GET_ALIASES_COUNT 200 + namespace currency @@ -25,6 +42,7 @@ namespace currency const command_line::arg_descriptor arg_rpc_bind_ip ("rpc-bind-ip", "", "127.0.0.1"); const command_line::arg_descriptor arg_rpc_bind_port ("rpc-bind-port", "", std::to_string(RPC_DEFAULT_PORT)); const command_line::arg_descriptor arg_rpc_ignore_offline_status ("rpc-ignore-offline", "Let rpc calls despite online/offline status"); + const command_line::arg_descriptor arg_rpc_enable_admin_api ("rpc-enable-admin-api", "Enable API commands that can alter pool or daemon state (reset pool, remove txs, etc.)"); } //----------------------------------------------------------------------------------- void core_rpc_server::init_options(boost::program_options::options_description& desc) @@ -32,6 +50,8 @@ namespace currency command_line::add_arg(desc, arg_rpc_bind_ip); command_line::add_arg(desc, arg_rpc_bind_port); command_line::add_arg(desc, arg_rpc_ignore_offline_status); + command_line::add_arg(desc, arg_rpc_enable_admin_api); + } //------------------------------------------------------------------------------------------------------------------------------ core_rpc_server::core_rpc_server(core& cr, nodetool::node_server >& p2p, @@ -45,11 +65,23 @@ namespace currency bool core_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); + if (m_bind_ip == "0.0.0.0") + { + LOG_PRINT_COLOR("!!!!!!IMPORTANT!!!!!!: We strongly advise against binding the server to 0.0.0.0 and exposing it directly" << ENDL + << "to the public internet. The server is meant solely for internal functionality and lacks the security features" << ENDL + << "required for publicly facing services. If you need to offer a public API, always place it behind secure" << ENDL + << "proxies that are properly configured with security measures--such as rate limiting--to protect the service.", LOG_LEVEL_0, LOG_COLOR_RED); + } m_port = command_line::get_arg(vm, arg_rpc_bind_port); if (command_line::has_arg(vm, arg_rpc_ignore_offline_status)) { m_ignore_offline_status = command_line::get_arg(vm, arg_rpc_ignore_offline_status); } + if (command_line::has_arg(vm, arg_rpc_enable_admin_api)) + { + m_enabled_admin_api = command_line::get_arg(vm, arg_rpc_enable_admin_api); + } + return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -281,6 +313,7 @@ namespace currency bool core_rpc_server::on_get_blocks_direct(const COMMAND_RPC_GET_BLOCKS_DIRECT::request& req, COMMAND_RPC_GET_BLOCKS_DIRECT::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.block_ids.size(), RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS); if (req.block_ids.back() != m_core.get_blockchain_storage().get_block_id_by_height(0)) { @@ -319,6 +352,7 @@ namespace currency bool core_rpc_server::on_get_blocks(const COMMAND_RPC_GET_BLOCKS_FAST::request& req, COMMAND_RPC_GET_BLOCKS_FAST::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.block_ids.size(), RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DIRECT_BLOCK_IDS); LOG_PRINT_L2("[on_get_blocks]: Prevalidating...."); if (req.block_ids.back() != m_core.get_blockchain_storage().get_block_id_by_height(0)) { @@ -369,6 +403,8 @@ namespace currency bool core_rpc_server::on_get_random_outs(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS_LEGACY::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS); + res.status = API_RETURN_CODE_FAIL; COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request req_native = AUTO_VAL_INIT(req_native); @@ -420,6 +456,7 @@ namespace currency bool core_rpc_server::on_get_random_outs1(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS); res.status = API_RETURN_CODE_FAIL; if(!m_core.get_random_outs_for_amounts(req, res)) @@ -434,6 +471,12 @@ namespace currency bool core_rpc_server::on_get_random_outs3(const COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::request& req, COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS3::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.amounts.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS); + for (const auto& item : req.amounts) + { + CHECK_RPC_LIMITS(item.global_offsets.size(), RPC_LIMIT_COMMAND_RPC_GET_RANDOM_OUTPUTS_FOR_AMOUNTS); + } + res.status = API_RETURN_CODE_FAIL; if (!m_core.get_blockchain_storage().get_random_outs_for_amounts3(req, res)) { @@ -447,6 +490,8 @@ namespace currency bool core_rpc_server::on_get_indexes(const COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::request& req, COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.txids.size(), RPC_LIMIT_COMMAND_RPC_GET_TX_GLOBAL_OUTPUTS_INDEXES_TXIDS); + res.tx_global_outs.resize(req.txids.size()); size_t i = 0; for (auto& txid : req.txids) @@ -498,6 +543,8 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_check_keyimages(const COMMAND_RPC_CHECK_KEYIMAGES::request& req, COMMAND_RPC_CHECK_KEYIMAGES::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.images.size(), RPC_LIMIT_COMMAND_RPC_CHECK_KEYIMAGES_IMAGES); + m_core.get_blockchain_storage().check_keyimages(req.images, res.images_stat); res.status = API_RETURN_CODE_OK; return true; @@ -506,6 +553,8 @@ namespace currency bool core_rpc_server::on_get_transactions(const COMMAND_RPC_GET_TRANSACTIONS::request& req, COMMAND_RPC_GET_TRANSACTIONS::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.txs_hashes.size(), RPC_LIMIT_COMMAND_RPC_GET_TRANSACTIONS_TXS_HASHES); + std::vector vh; for(const auto& tx_hex_str : req.txs_hashes) { @@ -548,6 +597,9 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_offers_ex(const COMMAND_RPC_GET_OFFERS_EX::request& req, COMMAND_RPC_GET_OFFERS_EX::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { + CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.filter.limit, RPC_LIMIT_COMMAND_RPC_GET_OFFERS_EX_LIMIT); + m_of.get_offers_ex(req.filter, res.offers, res.total_offers, m_core.get_blockchain_storage().get_core_runtime_config().get_core_time()); res.status = API_RETURN_CODE_OK; return true; @@ -624,6 +676,8 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_rpc_get_blocks_details(const COMMAND_RPC_GET_BLOCKS_DETAILS::request& req, COMMAND_RPC_GET_BLOCKS_DETAILS::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_BLOCKS_DETAILS_COUNT); + m_core.get_blockchain_storage().get_main_blocks_rpc_details(req.height_start, req.count, req.ignore_transactions, res.blocks); res.status = API_RETURN_CODE_OK; return true; @@ -683,6 +737,9 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_pool_txs_details(const COMMAND_RPC_GET_POOL_TXS_DETAILS::request& req, COMMAND_RPC_GET_POOL_TXS_DETAILS::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.ids.size(), RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS); + + if (!req.ids.size()) { m_core.get_tx_pool().get_all_transactions_details(res.txs); @@ -697,6 +754,7 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_pool_txs_brief_details(const COMMAND_RPC_GET_POOL_TXS_BRIEF_DETAILS::request& req, COMMAND_RPC_GET_POOL_TXS_BRIEF_DETAILS::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.ids.size(), RPC_LIMIT_COMMAND_RPC_GET_POOL_TXS_DETAILS_IDS); if (!req.ids.size()) { m_core.get_tx_pool().get_all_transactions_brief_details(res.txs); @@ -730,6 +788,8 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_votes(const COMMAND_RPC_GET_VOTES::request& req, COMMAND_RPC_GET_VOTES::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.h_end - req.h_start, RPC_LIMIT_COMMAND_RPC_GET_VOTES); + if (!m_core.get_blockchain_storage().get_pos_votes(req.h_start, req.h_end, res.votes)) { res.status = API_RETURN_CODE_INTERNAL_ERROR; @@ -754,6 +814,8 @@ namespace currency bool core_rpc_server::on_get_assets_list(const COMMAND_RPC_GET_ASSETS_LIST::request& req, COMMAND_RPC_GET_ASSETS_LIST::response& res, connection_context& cntx) { CHECK_CORE_READY(); + CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ASSETS_LIST); + if (!m_core.get_blockchain_storage().get_assets(req.offset, req.count, res.assets)) { res.status = API_RETURN_CODE_NOT_FOUND; @@ -857,6 +919,8 @@ namespace currency //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_alt_blocks_details(const COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::request& req, COMMAND_RPC_GET_ALT_BLOCKS_DETAILS::response& res, connection_context& cntx) { + CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ALT_BLOCKS_DETAILS_COUNT); + m_core.get_blockchain_storage().get_alt_blocks_rpc_details(req.offset, req.count, res.blocks); res.status = API_RETURN_CODE_OK; return true; @@ -934,7 +998,8 @@ namespace currency } //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_force_relaey_raw_txs(const COMMAND_RPC_FORCE_RELAY_RAW_TXS::request& req, COMMAND_RPC_FORCE_RELAY_RAW_TXS::response& res, connection_context& cntx) - { + { + CHECK_RPC_LIMITS(req.txs_as_hex.size(), RPC_LIMIT_COMMAND_RPC_FORCE_RELAY_RAW_TXS); CHECK_CORE_READY(); NOTIFY_OR_INVOKE_NEW_TRANSACTIONS::request r = AUTO_VAL_INIT(r); @@ -1371,10 +1436,11 @@ namespace currency res.status = API_RETURN_CODE_OK; return true; } - //------------------------------------------------------------------------------------------------------------------------------ bool core_rpc_server::on_get_aliases(const COMMAND_RPC_GET_ALIASES::request& req, COMMAND_RPC_GET_ALIASES::response& res, epee::json_rpc::error& error_resp, connection_context& cntx) { + CHECK_RPC_LIMITS(req.count, RPC_LIMIT_COMMAND_RPC_GET_ALIASES_COUNT); + if(!check_core_ready()) { error_resp.code = CORE_RPC_ERROR_CODE_CORE_BUSY; diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index fa5313e3..3dc4049d 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -171,9 +171,10 @@ namespace currency MAP_JON_RPC_WE("get_alt_block_details", on_get_alt_block_details, COMMAND_RPC_GET_BLOCK_DETAILS) MAP_JON_RPC ("get_alt_blocks_details", on_get_alt_blocks_details, COMMAND_RPC_GET_ALT_BLOCKS_DETAILS) // - MAP_JON_RPC ("reset_transaction_pool", on_reset_transaction_pool, COMMAND_RPC_RESET_TX_POOL) - MAP_JON_RPC ("remove_tx_from_pool", on_remove_tx_from_pool, COMMAND_RPC_REMOVE_TX_FROM_POOL) - MAP_JON_RPC ("get_current_core_tx_expiration_median", on_get_current_core_tx_expiration_median, COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN) + MAP_JON_RPC_CONDITIONAL("reset_transaction_pool", on_reset_transaction_pool, COMMAND_RPC_RESET_TX_POOL, m_enabled_admin_api) + MAP_JON_RPC_CONDITIONAL("remove_tx_from_pool", on_remove_tx_from_pool, COMMAND_RPC_REMOVE_TX_FROM_POOL, m_enabled_admin_api) + + MAP_JON_RPC("get_current_core_tx_expiration_median", on_get_current_core_tx_expiration_median, COMMAND_RPC_GET_CURRENT_CORE_TX_EXPIRATION_MEDIAN) // MAP_JON_RPC_WE("marketplace_global_get_offers_ex", on_get_offers_ex, COMMAND_RPC_GET_OFFERS_EX) MAP_JON_RPC_WE("validate_signature", on_validate_signature, COMMAND_VALIDATE_SIGNATURE) @@ -200,6 +201,7 @@ namespace currency std::string m_port; std::string m_bind_ip; bool m_ignore_offline_status; + bool m_enabled_admin_api = false; epee::net_utils::http::i_chain_handler* m_prpc_chain_handler = nullptr; }; } diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 72b2a298..2e571212 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1494,7 +1494,7 @@ namespace currency struct COMMAND_RPC_RESET_TX_POOL { - DOC_COMMAND("Clears transaction pool."); + DOC_COMMAND("Clears transaction pool. (Admin API, requires CLI option, disabled by default)"); struct request { @@ -1516,7 +1516,7 @@ namespace currency struct COMMAND_RPC_REMOVE_TX_FROM_POOL { - DOC_COMMAND("Removes specified transactions from the transaction pool, typically to clear out transactions that are no longer valid or needed."); + DOC_COMMAND("Removes specified transactions from the transaction pool, typically to clear out transactions that are no longer valid or needed. (Admin API, requires CLI option, disabled by default)"); struct request { diff --git a/src/version.h.in b/src/version.h.in index 69d040b4..d509b997 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -8,6 +8,6 @@ #define PROJECT_REVISION "7" #define PROJECT_VERSION PROJECT_MAJOR_VERSION "." PROJECT_MINOR_VERSION "." PROJECT_REVISION -#define PROJECT_VERSION_BUILD_NO 411 +#define PROJECT_VERSION_BUILD_NO 416 #define PROJECT_VERSION_BUILD_NO_STR STRINGIFY_EXPAND(PROJECT_VERSION_BUILD_NO) #define PROJECT_VERSION_LONG PROJECT_VERSION "." PROJECT_VERSION_BUILD_NO_STR "[" BUILD_COMMIT_ID "]"