|
15 | 15 | #include <util/translation.h>
|
16 | 16 | #include <walletinitinterface.h>
|
17 | 17 |
|
| 18 | +#include <algorithm> |
| 19 | +#include <iterator> |
| 20 | +#include <map> |
18 | 21 | #include <memory>
|
19 | 22 | #include <stdio.h>
|
| 23 | +#include <set> |
| 24 | +#include <string> |
20 | 25 |
|
21 | 26 | #include <boost/algorithm/string.hpp> // boost::trim
|
22 | 27 |
|
@@ -64,6 +69,9 @@ class HTTPRPCTimerInterface : public RPCTimerInterface
|
64 | 69 | static std::string strRPCUserColonPass;
|
65 | 70 | /* Stored RPC timer interface (for unregistration) */
|
66 | 71 | static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
|
| 72 | +/* RPC Auth Whitelist */ |
| 73 | +static std::map<std::string, std::set<std::string>> g_rpc_whitelist; |
| 74 | +static bool g_rpc_whitelist_default = false; |
67 | 75 |
|
68 | 76 | static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
|
69 | 77 | {
|
@@ -183,18 +191,45 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
|
183 | 191 | jreq.URI = req->GetURI();
|
184 | 192 |
|
185 | 193 | std::string strReply;
|
| 194 | + bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser); |
| 195 | + if (!user_has_whitelist && g_rpc_whitelist_default) { |
| 196 | + LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser); |
| 197 | + req->WriteReply(HTTP_FORBIDDEN); |
| 198 | + return false; |
| 199 | + |
186 | 200 | // singleton request
|
187 |
| - if (valRequest.isObject()) { |
| 201 | + } else if (valRequest.isObject()) { |
188 | 202 | jreq.parse(valRequest);
|
189 |
| - |
| 203 | + if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) { |
| 204 | + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod); |
| 205 | + req->WriteReply(HTTP_FORBIDDEN); |
| 206 | + return false; |
| 207 | + } |
190 | 208 | UniValue result = tableRPC.execute(jreq);
|
191 | 209 |
|
192 | 210 | // Send reply
|
193 | 211 | strReply = JSONRPCReply(result, NullUniValue, jreq.id);
|
194 | 212 |
|
195 | 213 | // array of requests
|
196 |
| - } else if (valRequest.isArray()) |
| 214 | + } else if (valRequest.isArray()) { |
| 215 | + if (user_has_whitelist) { |
| 216 | + for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) { |
| 217 | + if (!valRequest[reqIdx].isObject()) { |
| 218 | + throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); |
| 219 | + } else { |
| 220 | + const UniValue& request = valRequest[reqIdx].get_obj(); |
| 221 | + // Parse method |
| 222 | + std::string strMethod = find_value(request, "method").get_str(); |
| 223 | + if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { |
| 224 | + LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); |
| 225 | + req->WriteReply(HTTP_FORBIDDEN); |
| 226 | + return false; |
| 227 | + } |
| 228 | + } |
| 229 | + } |
| 230 | + } |
197 | 231 | strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
|
| 232 | + } |
198 | 233 | else
|
199 | 234 | throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
|
200 | 235 |
|
@@ -229,6 +264,27 @@ static bool InitRPCAuthentication()
|
229 | 264 | {
|
230 | 265 | LogPrintf("Using rpcauth authentication.\n");
|
231 | 266 | }
|
| 267 | + |
| 268 | + g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist")); |
| 269 | + for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { |
| 270 | + auto pos = strRPCWhitelist.find(':'); |
| 271 | + std::string strUser = strRPCWhitelist.substr(0, pos); |
| 272 | + bool intersect = g_rpc_whitelist.count(strUser); |
| 273 | + std::set<std::string>& whitelist = g_rpc_whitelist[strUser]; |
| 274 | + if (pos != std::string::npos) { |
| 275 | + std::string strWhitelist = strRPCWhitelist.substr(pos + 1); |
| 276 | + std::set<std::string> new_whitelist; |
| 277 | + boost::split(new_whitelist, strWhitelist, boost::is_any_of(", ")); |
| 278 | + if (intersect) { |
| 279 | + std::set<std::string> tmp_whitelist; |
| 280 | + std::set_intersection(new_whitelist.begin(), new_whitelist.end(), |
| 281 | + whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end())); |
| 282 | + new_whitelist = std::move(tmp_whitelist); |
| 283 | + } |
| 284 | + whitelist = std::move(new_whitelist); |
| 285 | + } |
| 286 | + } |
| 287 | + |
232 | 288 | return true;
|
233 | 289 | }
|
234 | 290 |
|
|
0 commit comments