aboutsummaryrefslogtreecommitdiff
path: root/src/httprpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/httprpc.cpp')
-rw-r--r--src/httprpc.cpp107
1 files changed, 81 insertions, 26 deletions
diff --git a/src/httprpc.cpp b/src/httprpc.cpp
index 46cd890f3..4d4973614 100644
--- a/src/httprpc.cpp
+++ b/src/httprpc.cpp
@@ -1,23 +1,27 @@
-// Copyright (c) 2015-2018 The Bitcoin Core developers
+// Copyright (c) 2015-2019 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <httprpc.h>
#include <chainparams.h>
+#include <crypto/hmac_sha256.h>
#include <httpserver.h>
-#include <key_io.h>
#include <rpc/protocol.h>
#include <rpc/server.h>
-#include <random.h>
-#include <sync.h>
-#include <util.h>
-#include <utilstrencodings.h>
#include <ui_interface.h>
-#include <crypto/hmac_sha256.h>
-#include <stdio.h>
-
+#include <util/strencodings.h>
+#include <util/system.h>
+#include <util/translation.h>
+#include <walletinitinterface.h>
+
+#include <algorithm>
+#include <iterator>
+#include <map>
#include <memory>
+#include <stdio.h>
+#include <set>
+#include <string>
#include <boost/algorithm/string.hpp> // boost::trim
@@ -30,7 +34,7 @@ static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
class HTTPRPCTimer : public RPCTimerBase
{
public:
- HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
+ HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) :
ev(eventBase, false, func)
{
struct timeval tv;
@@ -52,7 +56,7 @@ public:
{
return "HTTP";
}
- RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
+ RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
{
return new HTTPRPCTimer(base, func, millis);
}
@@ -65,6 +69,9 @@ private:
static std::string strRPCUserColonPass;
/* Stored RPC timer interface (for unregistration) */
static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
+/* RPC Auth Whitelist */
+static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
+static bool g_rpc_whitelist_default = false;
static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
@@ -113,7 +120,7 @@ static bool multiUserAuthorized(std::string strUserPass)
static const unsigned int KEY_SIZE = 32;
unsigned char out[KEY_SIZE];
- CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
+ CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.data()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.data()), strPass.size()).Finalize(out);
std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
std::string strHashFromPass = HexStr(hexvec);
@@ -167,7 +174,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
/* Deter brute-forcing
If this results in a DoS the user really
shouldn't have their RPC port exposed. */
- MilliSleep(250);
+ UninterruptibleSleep(std::chrono::milliseconds{250});
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
@@ -184,18 +191,45 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
jreq.URI = req->GetURI();
std::string strReply;
+ bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
+ if (!user_has_whitelist && g_rpc_whitelist_default) {
+ LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser);
+ req->WriteReply(HTTP_FORBIDDEN);
+ return false;
+
// singleton request
- if (valRequest.isObject()) {
+ } else if (valRequest.isObject()) {
jreq.parse(valRequest);
-
+ if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
+ LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod);
+ req->WriteReply(HTTP_FORBIDDEN);
+ return false;
+ }
UniValue result = tableRPC.execute(jreq);
// Send reply
strReply = JSONRPCReply(result, NullUniValue, jreq.id);
// array of requests
- } else if (valRequest.isArray())
+ } else if (valRequest.isArray()) {
+ if (user_has_whitelist) {
+ for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) {
+ if (!valRequest[reqIdx].isObject()) {
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
+ } else {
+ const UniValue& request = valRequest[reqIdx].get_obj();
+ // Parse method
+ std::string strMethod = find_value(request, "method").get_str();
+ if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
+ LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod);
+ req->WriteReply(HTTP_FORBIDDEN);
+ return false;
+ }
+ }
+ }
+ }
strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
+ }
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
@@ -218,7 +252,7 @@ static bool InitRPCAuthentication()
LogPrintf("No rpcpassword set - using random cookie authentication.\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
- _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
+ _("Error: A fatal internal error occurred, see debug.log for details").translated, // Same message as AbortNode
"", CClientUIInterface::MSG_ERROR);
return false;
}
@@ -230,6 +264,27 @@ static bool InitRPCAuthentication()
{
LogPrintf("Using rpcauth authentication.\n");
}
+
+ g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist"));
+ for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
+ auto pos = strRPCWhitelist.find(':');
+ std::string strUser = strRPCWhitelist.substr(0, pos);
+ bool intersect = g_rpc_whitelist.count(strUser);
+ std::set<std::string>& whitelist = g_rpc_whitelist[strUser];
+ if (pos != std::string::npos) {
+ std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
+ std::set<std::string> new_whitelist;
+ boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
+ if (intersect) {
+ std::set<std::string> tmp_whitelist;
+ std::set_intersection(new_whitelist.begin(), new_whitelist.end(),
+ whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end()));
+ new_whitelist = std::move(tmp_whitelist);
+ }
+ whitelist = std::move(new_whitelist);
+ }
+ }
+
return true;
}
@@ -240,12 +295,12 @@ bool StartHTTPRPC()
return false;
RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
-#ifdef ENABLE_WALLET
- // ifdef can be removed once we switch to better endpoint support and API versioning
- RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
-#endif
- assert(EventBase());
- httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase());
+ if (g_wallet_init_interface.HasWalletSupport()) {
+ RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
+ }
+ struct event_base* eventBase = EventBase();
+ assert(eventBase);
+ httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase);
RPCSetTimerInterface(httpRPCTimerInterface.get());
return true;
}
@@ -259,9 +314,9 @@ void StopHTTPRPC()
{
LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
UnregisterHTTPHandler("/", true);
-#ifdef ENABLE_WALLET
- UnregisterHTTPHandler("/wallet/", false);
-#endif
+ if (g_wallet_init_interface.HasWalletSupport()) {
+ UnregisterHTTPHandler("/wallet/", false);
+ }
if (httpRPCTimerInterface) {
RPCUnsetTimerInterface(httpRPCTimerInterface.get());
httpRPCTimerInterface.reset();