Skip to content

Commit

Permalink
Add RPC Whitelist Feature from bitcoin#12248
Browse files Browse the repository at this point in the history
  • Loading branch information
JeremyRubin authored and luke-jr committed Jul 7, 2018
1 parent 9d9c418 commit 338deae
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
36 changes: 34 additions & 2 deletions src/httprpc.cpp
Expand Up @@ -63,6 +63,8 @@ class HTTPRPCTimerInterface : public RPCTimerInterface
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>> whitelistedRPC;

static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
{
Expand Down Expand Up @@ -184,15 +186,38 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
// singleton request
if (valRequest.isObject()) {
jreq.parse(valRequest);

if (whitelistedRPC.count(jreq.authUser) && !whitelistedRPC[jreq.authUser].count(jreq.strMethod)) {
LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod);
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
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 (whitelistedRPC.count(jreq.authUser)) {
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 (!whitelistedRPC[jreq.authUser].count(strMethod)) {
LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod);
req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
req->WriteReply(HTTP_UNAUTHORIZED);
return false;
}
}
}
}
strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
}
else
throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");

Expand Down Expand Up @@ -223,6 +248,13 @@ static bool InitRPCAuthentication()
LogPrintf("Config options rpcuser and rpcpassword will soon be deprecated. Locally-run instances may remove rpcuser to use cookie-based auth, or may be replaced with rpcauth. Please see share/rpcuser for rpcauth auth generation.\n");
strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
}

for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
std::string strUser = strRPCWhitelist.substr(0, strRPCWhitelist.find(':'));
std::string strWhitelist = strRPCWhitelist.substr(strRPCWhitelist.find(':') + 1);
boost::split(whitelistedRPC[strUser], strWhitelist, boost::is_any_of(","));
}

return true;
}

Expand Down
1 change: 1 addition & 0 deletions src/httprpc.h
Expand Up @@ -7,6 +7,7 @@

#include <string>
#include <map>
#include <set>

/** Start HTTP RPC subsystem.
* Precondition; HTTP and RPC has been started.
Expand Down

0 comments on commit 338deae

Please sign in to comment.