diff --git a/src/JsonRpcServer/JsonRpcServer.cpp b/src/JsonRpcServer/JsonRpcServer.cpp index 3f77d2801f..101d763889 100755 --- a/src/JsonRpcServer/JsonRpcServer.cpp +++ b/src/JsonRpcServer/JsonRpcServer.cpp @@ -37,11 +37,12 @@ namespace CryptoNote { -JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup) : +JsonRpcServer::JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup, PaymentService::Configuration& config) : HttpServer(sys, loggerGroup), system(sys), stopEvent(stopEvent), - logger(loggerGroup, "JsonRpcServer") + logger(loggerGroup, "JsonRpcServer"), + config(config) { } @@ -164,6 +165,23 @@ void JsonRpcServer::makeMethodNotFoundResponse(Common::JsonValue& resp) { resp.insert("error", error); } +void JsonRpcServer::makeInvalidPasswordResponse(Common::JsonValue& resp) { + using Common::JsonValue; + + JsonValue error(JsonValue::OBJECT); + + JsonValue code; + code = static_cast(-32604); + + JsonValue message; + message = "Invalid or no rpc password"; + + error.insert("code", code); + error.insert("message", message); + + resp.insert("error", error); +} + void JsonRpcServer::fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp) { resp.insert("result", v); } diff --git a/src/JsonRpcServer/JsonRpcServer.h b/src/JsonRpcServer/JsonRpcServer.h index ab6cee2660..97f2690d8c 100755 --- a/src/JsonRpcServer/JsonRpcServer.h +++ b/src/JsonRpcServer/JsonRpcServer.h @@ -24,6 +24,7 @@ #include "Logging/ILogger.h" #include "Logging/LoggerRef.h" #include "Rpc/HttpServer.h" +#include "PaymentGateService/PaymentServiceConfiguration.h" namespace CryptoNote { @@ -43,7 +44,7 @@ namespace CryptoNote { class JsonRpcServer : HttpServer { public: - JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup); + JsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, Logging::ILogger& loggerGroup, PaymentService::Configuration& config); JsonRpcServer(const JsonRpcServer&) = delete; void start(const std::string& bindAddress, uint16_t bindPort); @@ -51,12 +52,14 @@ class JsonRpcServer : HttpServer { protected: static void makeErrorResponse(const std::error_code& ec, Common::JsonValue& resp); static void makeMethodNotFoundResponse(Common::JsonValue& resp); + static void makeInvalidPasswordResponse(Common::JsonValue& resp); static void makeGenericErrorReponse(Common::JsonValue& resp, const char* what, int errorCode = -32001); static void fillJsonResponse(const Common::JsonValue& v, Common::JsonValue& resp); static void prepareJsonResponse(const Common::JsonValue& req, Common::JsonValue& resp); static void makeJsonParsingErrorResponse(Common::JsonValue& resp); virtual void processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) = 0; + PaymentService::Configuration& config; private: // HttpServer diff --git a/src/PaymentGate/PaymentServiceJsonRpcServer.cpp b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp index 324cbd1aaf..2c106dc291 100755 --- a/src/PaymentGate/PaymentServiceJsonRpcServer.cpp +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.cpp @@ -25,10 +25,12 @@ #include "Serialization/JsonInputValueSerializer.h" #include "Serialization/JsonOutputStreamSerializer.h" +#include "Rpc/JsonRpc.h" + namespace PaymentService { -PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup) - : JsonRpcServer(sys, stopEvent, loggerGroup) +PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup, PaymentService::Configuration& config) + : JsonRpcServer(sys, stopEvent, loggerGroup, config) , service(service) , logger(loggerGroup, "PaymentServiceJsonRpcServer") { @@ -60,6 +62,23 @@ PaymentServiceJsonRpcServer::PaymentServiceJsonRpcServer(System::Dispatcher& sys void PaymentServiceJsonRpcServer::processJsonRpcRequest(const Common::JsonValue& req, Common::JsonValue& resp) { try { prepareJsonResponse(req, resp); + + if (!config.legacySecurity) { + std::string clientPassword; + if (!req.contains("password")) { + makeInvalidPasswordResponse(resp); + return; + } + if (!req("password").isString()) { + makeInvalidPasswordResponse(resp); + return; + } + clientPassword = req("password").getString(); + if (clientPassword != config.rpcPassword) { + makeInvalidPasswordResponse(resp); + return; + } + } if (!req.contains("method")) { logger(Logging::WARNING) << "Field \"method\" is not found in json request: " << req; diff --git a/src/PaymentGate/PaymentServiceJsonRpcServer.h b/src/PaymentGate/PaymentServiceJsonRpcServer.h index 49b47dbd12..569ee5d909 100755 --- a/src/PaymentGate/PaymentServiceJsonRpcServer.h +++ b/src/PaymentGate/PaymentServiceJsonRpcServer.h @@ -31,7 +31,7 @@ class WalletService; class PaymentServiceJsonRpcServer : public CryptoNote::JsonRpcServer { public: - PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup); + PaymentServiceJsonRpcServer(System::Dispatcher& sys, System::Event& stopEvent, WalletService& service, Logging::ILogger& loggerGroup, PaymentService::Configuration& config); PaymentServiceJsonRpcServer(const PaymentServiceJsonRpcServer&) = delete; protected: diff --git a/src/PaymentGateService/PaymentGateService.cpp b/src/PaymentGateService/PaymentGateService.cpp index f2b79fd990..5f576de804 100755 --- a/src/PaymentGateService/PaymentGateService.cpp +++ b/src/PaymentGateService/PaymentGateService.cpp @@ -290,7 +290,7 @@ void PaymentGateService::runWalletService(const CryptoNote::Currency& currency, std::cout << "Address: " << address << std::endl; } } else { - PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger); + PaymentService::PaymentServiceJsonRpcServer rpcServer(*dispatcher, *stopEvent, *service, logger, config.gateConfiguration); rpcServer.start(config.gateConfiguration.bindAddress, config.gateConfiguration.bindPort); Logging::LoggerRef(logger, "PaymentGateService")(Logging::INFO, Logging::BRIGHT_WHITE) << "JSON-RPC server stopped, stopping wallet service..."; diff --git a/src/PaymentGateService/PaymentServiceConfiguration.cpp b/src/PaymentGateService/PaymentServiceConfiguration.cpp index 22edf15216..adc859997c 100755 --- a/src/PaymentGateService/PaymentServiceConfiguration.cpp +++ b/src/PaymentGateService/PaymentServiceConfiguration.cpp @@ -42,12 +42,16 @@ Configuration::Configuration() { bindPort = 0; secretViewKey = ""; secretSpendKey = ""; + rpcPassword = ""; + legacySecurity = false; } void Configuration::initOptions(boost::program_options::options_description& desc) { desc.add_options() ("bind-address", po::value()->default_value("0.0.0.0"), "payment service bind address") ("bind-port", po::value()->default_value(8070), "payment service bind port") + ("rpc-password", po::value(), "Specify the password to access the rpc server.") + ("rpc-legacy-security", "Enable legacy mode (no password for RPC). WARNING: INSECURE. USE ONLY AS A LAST RESORT.") ("container-file,w", po::value(), "container file") ("container-password,p", po::value(), "container password") ("generate-container,g", "generate new container file with one wallet and exit") @@ -152,6 +156,24 @@ void Configuration::init(const boost::program_options::variables_map& options) { throw ConfigurationError("container-file parameter are required"); } } + + // If generating a container skip the authentication parameters. + if (generateNewContainer) { + return; + } + + // Check for the authentication parameters + if ((options.count("rpc-password") == 0) && (options.count("rpc-legacy-security") == 0)) { + throw ConfigurationError("Please specify an RPC password or use the --rpc-legacy-security flag."); + } + + if (options.count("rpc-legacy-security") != 0) { + legacySecurity = true; + } + else { + rpcPassword = options["rpc-password"].as(); + } + } } //namespace PaymentService diff --git a/src/PaymentGateService/PaymentServiceConfiguration.h b/src/PaymentGateService/PaymentServiceConfiguration.h index f628d528d8..44c9e3cc5c 100755 --- a/src/PaymentGateService/PaymentServiceConfiguration.h +++ b/src/PaymentGateService/PaymentServiceConfiguration.h @@ -38,6 +38,7 @@ struct Configuration { std::string bindAddress; uint16_t bindPort; + std::string rpcPassword; std::string containerFile; std::string containerPassword; @@ -53,6 +54,7 @@ struct Configuration { bool testnet; bool printAddresses; bool syncFromZero; + bool legacySecurity; size_t logLevel; }; diff --git a/src/Rpc/JsonRpc.cpp b/src/Rpc/JsonRpc.cpp index c9cd54d560..d855191da8 100755 --- a/src/Rpc/JsonRpc.cpp +++ b/src/Rpc/JsonRpc.cpp @@ -31,6 +31,7 @@ JsonRpcError::JsonRpcError(int c) : code(c) { case errMethodNotFound: message = "Method not found"; break; case errInvalidParams: message = "Invalid params"; break; case errInternalError: message = "Internal error"; break; + case errInvalidPassword: message = "Invalid or no password supplied"; break; default: message = "Unknown error"; break; } } diff --git a/src/Rpc/JsonRpc.h b/src/Rpc/JsonRpc.h index f98fee9afe..0f4da62852 100755 --- a/src/Rpc/JsonRpc.h +++ b/src/Rpc/JsonRpc.h @@ -37,6 +37,7 @@ const int errInvalidRequest = -32600; const int errMethodNotFound = -32601; const int errInvalidParams = -32602; const int errInternalError = -32603; +const int errInvalidPassword = -32604; class JsonRpcError: public std::exception { public: @@ -62,6 +63,7 @@ class JsonRpcError: public std::exception { }; typedef boost::optional OptionalId; +typedef boost::optional OptionalPassword; class JsonRpcRequest { public: @@ -84,6 +86,10 @@ class JsonRpcRequest { if (psReq.contains("id")) { id = psReq("id"); } + + if (psReq.contains("password")) { + password = psReq("password"); + } return true; } @@ -112,6 +118,10 @@ class JsonRpcRequest { const OptionalId& getId() const { return id; } + + const OptionalPassword& getPassword() const { + return password; + } std::string getBody() { psReq.set("jsonrpc", std::string("2.0")); @@ -123,6 +133,7 @@ class JsonRpcRequest { Common::JsonValue psReq; OptionalId id; + OptionalPassword password; std::string method; }; diff --git a/src/SimpleWallet/SimpleWallet.cpp b/src/SimpleWallet/SimpleWallet.cpp index 482cd89698..201c4afd47 100755 --- a/src/SimpleWallet/SimpleWallet.cpp +++ b/src/SimpleWallet/SimpleWallet.cpp @@ -1329,6 +1329,18 @@ int main(int argc, char* argv[]) { if (command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_bind_port)) { //runs wallet with rpc interface + + /* + If the rpc interface is run, ensure that either legacy mode or an RPC + password is set. + */ + + if (!command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_password) && + !command_line::has_arg(vm, Tools::wallet_rpc_server::arg_rpc_legacy_security)) { + logger(ERROR, BRIGHT_RED) << "Required RPC password is not set."; + return 1; + } + if (!command_line::has_arg(vm, arg_wallet_file)) { logger(ERROR, BRIGHT_RED) << "Wallet file not set."; return 1; diff --git a/src/Wallet/WalletRpcServer.cpp b/src/Wallet/WalletRpcServer.cpp index 5895e82525..9d72a5c46d 100755 --- a/src/Wallet/WalletRpcServer.cpp +++ b/src/Wallet/WalletRpcServer.cpp @@ -36,11 +36,16 @@ namespace Tools { const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_port = { "rpc-bind-port", "Starts wallet as rpc server for wallet operations, sets bind port for server", 0, true }; const command_line::arg_descriptor wallet_rpc_server::arg_rpc_bind_ip = { "rpc-bind-ip", "Specify ip to bind rpc server", "127.0.0.1" }; +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_password = { "rpc-password", "Specify the password to access the rpc server.", "", true }; +const command_line::arg_descriptor wallet_rpc_server::arg_rpc_legacy_security = { "rpc-legacy-security", "Enable legacy mode (no password for RPC). WARNING: INSECURE. USE ONLY AS A LAST RESORT.", false}; const command_line::arg_descriptor arg_allow_extended_rpc = {"allow-extended-rpc", "Allow RPC access to the wallet address and view/spend keys", false}; + void wallet_rpc_server::init_options(boost::program_options::options_description& desc) { 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_password); + command_line::add_arg(desc, arg_rpc_legacy_security); command_line::add_arg(desc, arg_allow_extended_rpc); } //------------------------------------------------------------------------------------------------------------------------------ @@ -80,6 +85,10 @@ void wallet_rpc_server::send_stop_signal() { bool wallet_rpc_server::handle_command_line(const boost::program_options::variables_map& vm) { m_bind_ip = command_line::get_arg(vm, arg_rpc_bind_ip); m_port = command_line::get_arg(vm, arg_rpc_bind_port); + m_legacy = command_line::get_arg(vm, arg_rpc_legacy_security); + if (!m_legacy) { + m_password = command_line::get_arg(vm, arg_rpc_password); + } m_allow_extended_rpc = command_line::get_arg(vm, arg_allow_extended_rpc); return true; } @@ -99,10 +108,25 @@ void wallet_rpc_server::processRequest(const CryptoNote::HttpRequest& request, C JsonRpcRequest jsonRequest; JsonRpcResponse jsonResponse; + std::string clientPassword; try { jsonRequest.parseRequest(request.getBody()); jsonResponse.setId(jsonRequest.getId()); + + if (!m_legacy) { + const JsonRpc::OptionalPassword& clientPasswordObject = jsonRequest.getPassword(); + if (!clientPasswordObject.is_initialized()) { + throw JsonRpcError(errInvalidPassword); + } + if (!clientPasswordObject.get().isString()) { + throw JsonRpcError(errInvalidPassword); + } + clientPassword = clientPasswordObject.get().getString(); + if (clientPassword != m_password) { + throw JsonRpcError(errInvalidPassword); + } + } static std::unordered_map s_methods = { { "getbalance", makeMemberMethod(&wallet_rpc_server::on_getbalance) }, diff --git a/src/Wallet/WalletRpcServer.h b/src/Wallet/WalletRpcServer.h index cf172f8e4d..0a73c829cc 100755 --- a/src/Wallet/WalletRpcServer.h +++ b/src/Wallet/WalletRpcServer.h @@ -53,6 +53,8 @@ namespace Tools static const command_line::arg_descriptor arg_rpc_bind_port; static const command_line::arg_descriptor arg_rpc_bind_ip; + static const command_line::arg_descriptor arg_rpc_password; + static const command_line::arg_descriptor arg_rpc_legacy_security; private: @@ -77,7 +79,9 @@ namespace Tools CryptoNote::INode& m_node; uint16_t m_port; bool m_allow_extended_rpc; + bool m_legacy; std::string m_bind_ip; + std::string m_password; CryptoNote::Currency& m_currency; const std::string m_walletFilename;