Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
httprpc.cpp
Go to the documentation of this file.
1// Copyright (c) 2015-2016 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <httprpc.h>
6
7#include <chainparams.h>
8#include <common/args.h>
9#include <config.h>
10#include <crypto/hmac_sha256.h>
11#include <logging.h>
12#include <rpc/protocol.h>
13#include <util/strencodings.h>
14#include <util/string.h>
15#include <util/translation.h>
16#include <walletinitinterface.h>
17
18#include <boost/algorithm/string.hpp>
19
20#include <algorithm>
21#include <cstdio>
22#include <functional>
23#include <iterator>
24#include <map>
25#include <memory>
26#include <set>
27#include <string>
28
30static const char *WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
31
34
39class HTTPRPCTimer : public RPCTimerBase {
40public:
41 HTTPRPCTimer(struct event_base *eventBase, std::function<void()> &func,
43 : ev(eventBase, false, func) {
44 struct timeval tv;
45 tv.tv_sec = millis / 1000;
46 tv.tv_usec = (millis % 1000) * 1000;
47 ev.trigger(&tv);
48 }
49
50private:
52};
53
55public:
57
58 const char *Name() override { return "HTTP"; }
59
60 RPCTimerBase *NewTimer(std::function<void()> &func,
61 int64_t millis) override {
62 return new HTTPRPCTimer(base, func, millis);
63 }
64
65private:
67};
68
69/* Pre-base64-encoded authentication token */
70static std::string strRPCUserColonPass;
71/* Pre-base64-encoded authentication token */
72static std::string strRPCCORSDomain;
73/* Stored RPC timer interface (for unregistration) */
74static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
75/* RPC Auth Whitelist */
76static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
77static bool g_rpc_whitelist_default = false;
78
80 const UniValue &id) {
81 // Send error reply from json-rpc error object.
83 int code = objError.find_value("code").getInt<int>();
84
85 if (code == RPC_INVALID_REQUEST) {
87 } else if (code == RPC_METHOD_NOT_FOUND) {
89 }
90
91 std::string strReply = JSONRPCReply(NullUniValue, objError, id);
92
93 req->WriteHeader("Content-Type", "application/json");
95}
96
97/*
98 * This function checks username and password against -rpcauth entries from
99 * config file.
100 */
101static bool multiUserAuthorized(std::string strUserPass) {
102 if (strUserPass.find(':') == std::string::npos) {
103 return false;
104 }
105 std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
106 std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
107
108 for (const std::string &strRPCAuth : gArgs.GetArgs("-rpcauth")) {
109 // Search for multi-user login/pass "rpcauth" from config
110 std::vector<std::string> vFields;
111 boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
112 if (vFields.size() != 3) {
113 // Incorrect formatting in config file
114 continue;
115 }
116
117 std::string strName = vFields[0];
119 continue;
120 }
121
122 std::string strSalt = vFields[1];
123 std::string strHash = vFields[2];
124
125 static const unsigned int KEY_SIZE = 32;
126 uint8_t out[KEY_SIZE];
127
128 CHMAC_SHA256(reinterpret_cast<const uint8_t *>(strSalt.data()),
129 strSalt.size())
130 .Write(reinterpret_cast<const uint8_t *>(strPass.data()),
131 strPass.size())
132 .Finalize(out);
133 std::vector<uint8_t> hexvec(out, out + KEY_SIZE);
134 std::string strHashFromPass = HexStr(hexvec);
135
137 return true;
138 }
139 }
140 return false;
141}
142
143static bool RPCAuthorized(const std::string &strAuth,
144 std::string &strAuthUsernameOut) {
145 // Belt-and-suspenders measure if InitRPCAuthentication was not called.
146 if (strRPCUserColonPass.empty()) {
147 return false;
148 }
149
150 if (strAuth.substr(0, 6) != "Basic ") {
151 return false;
152 }
153
154 std::string_view strUserPass64 =
155 TrimStringView(std::string_view{strAuth}.substr(6));
157 std::string strUserPass;
158 if (!userpass_data) {
159 return false;
160 }
161 strUserPass.assign(userpass_data->begin(), userpass_data->end());
162
163 if (strUserPass.find(':') != std::string::npos) {
164 strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
165 }
166
167 // Check if authorized under single-user field
169 return true;
170 }
172}
173
174static bool checkCORS(HTTPRequest *req) {
175 // https://www.w3.org/TR/cors/#resource-requests
176
177 // 1. If the Origin header is not present terminate this set of steps.
178 // The request is outside the scope of this specification.
179 std::pair<bool, std::string> origin = req->GetHeader("origin");
180 if (!origin.first) {
181 return false;
182 }
183
184 // 2. If the value of the Origin header is not a case-sensitive match for
185 // any of the values in list of origins do not set any additional headers
186 // and terminate this set of steps.
187 // Note: Always matching is acceptable since the list of origins can be
188 // unbounded.
189 if (origin.second != strRPCCORSDomain) {
190 return false;
191 }
192
194 // 6.2 Preflight Request
195 // In response to a preflight request the resource indicates which
196 // methods and headers (other than simple methods and simple
197 // headers) it is willing to handle and whether it supports
198 // credentials.
199 // Resources must use the following set of steps to determine which
200 // additional headers to use in the response:
201
202 // 3. Let method be the value as result of parsing the
203 // Access-Control-Request-Method header.
204 // If there is no Access-Control-Request-Method header or if parsing
205 // failed, do not set any additional headers and terminate this set
206 // of steps. The request is outside the scope of this specification.
207 std::pair<bool, std::string> method =
208 req->GetHeader("access-control-request-method");
209 if (!method.first) {
210 return false;
211 }
212
213 // 4. Let header field-names be the values as result of parsing
214 // the Access-Control-Request-Headers headers.
215 // If there are no Access-Control-Request-Headers headers let header
216 // field-names be the empty list.
217 // If parsing failed do not set any additional headers and terminate
218 // this set of steps. The request is outside the scope of this
219 // specification.
220 std::pair<bool, std::string> header_field_names =
221 req->GetHeader("access-control-request-headers");
222
223 // 5. If method is not a case-sensitive match for any of the
224 // values in list of methods do not set any additional headers
225 // and terminate this set of steps.
226 // Note: Always matching is acceptable since the list of methods
227 // can be unbounded.
228 if (method.second != "POST") {
229 return false;
230 }
231
232 // 6. If any of the header field-names is not a ASCII case-
233 // insensitive match for any of the values in list of headers do not
234 // set any additional headers and terminate this set of steps.
235 // Note: Always matching is acceptable since the list of headers can
236 // be unbounded.
237 const std::string &list_of_headers = "authorization,content-type";
238
239 // 7. If the resource supports credentials add a single
240 // Access-Control-Allow-Origin header, with the value of the Origin
241 // header as value, and add a single
242 // Access-Control-Allow-Credentials header with the case-sensitive
243 // string "true" as value.
244 req->WriteHeader("Access-Control-Allow-Origin", origin.second);
245 req->WriteHeader("Access-Control-Allow-Credentials", "true");
246
247 // 8. Optionally add a single Access-Control-Max-Age header with as
248 // value the amount of seconds the user agent is allowed to cache
249 // the result of the request.
250
251 // 9. If method is a simple method this step may be skipped.
252 // Add one or more Access-Control-Allow-Methods headers consisting
253 // of (a subset of) the list of methods.
254 // If a method is a simple method it does not need to be listed, but
255 // this is not prohibited.
256 // Note: Since the list of methods can be unbounded, simply
257 // returning the method indicated by
258 // Access-Control-Request-Method (if supported) can be enough.
259 req->WriteHeader("Access-Control-Allow-Methods", method.second);
260
261 // 10. If each of the header field-names is a simple header and none
262 // is Content-Type, this step may be skipped.
263 // Add one or more Access-Control-Allow-Headers headers consisting
264 // of (a subset of) the list of headers.
265 req->WriteHeader("Access-Control-Allow-Headers",
268 req->WriteReply(HTTP_OK);
269 return true;
270 }
271
272 // 6.1 Simple Cross-Origin Request, Actual Request, and Redirects
273 // In response to a simple cross-origin request or actual request the
274 // resource indicates whether or not to share the response.
275 // If the resource has been relocated, it indicates whether to share its
276 // new URL.
277 // Resources must use the following set of steps to determine which
278 // additional headers to use in the response:
279
280 // 3. If the resource supports credentials add a single
281 // Access-Control-Allow-Origin header, with the value of the Origin
282 // header as value, and add a single Access-Control-Allow-Credentials
283 // header with the case-sensitive string "true" as value.
284 req->WriteHeader("Access-Control-Allow-Origin", origin.second);
285 req->WriteHeader("Access-Control-Allow-Credentials", "true");
286
287 // 4. If the list of exposed headers is not empty add one or more
288 // Access-Control-Expose-Headers headers, with as values the header
289 // field names given in the list of exposed headers.
290 req->WriteHeader("Access-Control-Expose-Headers", "WWW-Authenticate");
291
292 return false;
293}
294
296 // First, check and/or set CORS headers
297 if (checkCORS(req)) {
298 return true;
299 }
300
301 // JSONRPC handles only POST
302 if (req->GetRequestMethod() != HTTPRequest::POST) {
304 "JSONRPC server handles only POST requests");
305 return false;
306 }
307 // Check authorization
308 std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
309 if (!authHeader.first) {
310 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
312 return false;
313 }
314
317 jreq.peerAddr = req->GetPeer().ToString();
318 if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
319 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n",
320 jreq.peerAddr);
321
328 std::chrono::milliseconds{RPC_AUTH_BRUTE_FORCE_DELAY});
329
330 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
332 return false;
333 }
334
335 try {
336 // Parse request
338 if (!valRequest.read(req->ReadBody())) {
339 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
340 }
341
342 // Set the URI
343 jreq.URI = req->GetURI();
344
345 std::string strReply;
346 bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
348 LogPrintf("RPC User %s not allowed to call any methods\n",
349 jreq.authUser);
351 return false;
352
353 // singleton request
354 } else if (valRequest.isObject()) {
355 jreq.parse(valRequest);
356 if (user_has_whitelist &&
357 !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
358 LogPrintf("RPC User %s not allowed to call method %s\n",
359 jreq.authUser, jreq.strMethod);
361 return false;
362 }
364
365 // Send reply
366 strReply = JSONRPCReply(result, NullUniValue, jreq.id);
367
368 // array of requests
369 } else if (valRequest.isArray()) {
370 if (user_has_whitelist) {
371 for (unsigned int reqIdx = 0; reqIdx < valRequest.size();
372 reqIdx++) {
373 if (!valRequest[reqIdx].isObject()) {
375 "Invalid Request object");
376 } else {
377 const UniValue &request = valRequest[reqIdx].get_obj();
378 // Parse method
379 std::string strMethod =
380 request.find_value("method").get_str();
381 if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
382 LogPrintf(
383 "RPC User %s not allowed to call method %s\n",
384 jreq.authUser, strMethod);
386 return false;
387 }
388 }
389 }
390 }
392 valRequest.get_array());
393 } else {
394 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
395 }
396
397 req->WriteHeader("Content-Type", "application/json");
399 } catch (const UniValue &objError) {
400 JSONErrorReply(req, objError, jreq.id);
401 return false;
402 } catch (const std::exception &e) {
404 return false;
405 }
406 return true;
407}
408
410 if (gArgs.GetArg("-rpcpassword", "") == "") {
411 LogPrintf("Using random cookie authentication.\n");
413 return false;
414 }
415 } else {
416 LogPrintf("Config options rpcuser and rpcpassword will soon be "
417 "deprecated. Locally-run instances may remove rpcuser to use "
418 "cookie-based auth, or may be replaced with rpcauth. Please "
419 "see share/rpcauth for rpcauth auth generation.\n");
420 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" +
421 gArgs.GetArg("-rpcpassword", "");
422 }
423
424 strRPCCORSDomain = gArgs.GetArg("-rpccorsdomain", "");
425
426 if (gArgs.GetArg("-rpcauth", "") != "") {
427 LogPrintf("Using rpcauth authentication.\n");
428 }
429
430 g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault",
431 gArgs.IsArgSet("-rpcwhitelist"));
432 for (const std::string &strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
433 auto pos = strRPCWhitelist.find(':');
434 std::string strUser = strRPCWhitelist.substr(0, pos);
435 bool intersect = g_rpc_whitelist.count(strUser);
436 std::set<std::string> &whitelist = g_rpc_whitelist[strUser];
437 if (pos != std::string::npos) {
438 std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
439 std::set<std::string> new_whitelist;
440 boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
441 if (intersect) {
442 std::set<std::string> tmp_whitelist;
443 std::set_intersection(
444 new_whitelist.begin(), new_whitelist.end(),
445 whitelist.begin(), whitelist.end(),
446 std::inserter(tmp_whitelist, tmp_whitelist.end()));
447 new_whitelist = std::move(tmp_whitelist);
448 }
449 whitelist = std::move(new_whitelist);
450 }
451 }
452
453 return true;
454}
455
457 LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
458 if (!InitRPCAuthentication()) {
459 return false;
460 }
461
462 const std::function<bool(Config &, HTTPRequest *, const std::string &)>
463 &rpcFunction =
465 &httpRPCRequestProcessor, std::placeholders::_2);
468 RegisterHTTPHandler("/wallet/", false, rpcFunction);
469 }
470 struct event_base *eventBase = EventBase();
472 httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase);
474 return true;
475}
476
478 LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
479}
480
482 LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
483 UnregisterHTTPHandler("/", true);
485 UnregisterHTTPHandler("/wallet/", false);
486 }
489 httpRPCTimerInterface.reset();
490 }
491}
ArgsManager gArgs
Definition args.cpp:38
std::vector< std::string > GetArgs(const std::string &strArg) const
Return a vector of strings of the given argument.
Definition args.cpp:371
bool IsArgSet(const std::string &strArg) const
Return true if the given argument has been manually set.
Definition args.cpp:381
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition args.cpp:494
bool GetBoolArg(const std::string &strArg, bool fDefault) const
Return boolean argument or default value.
Definition args.cpp:556
A hasher class for HMAC-SHA-256.
Definition hmac_sha256.h:14
CHMAC_SHA256 & Write(const uint8_t *data, size_t len)
Definition hmac_sha256.h:23
void Finalize(uint8_t hash[OUTPUT_SIZE])
std::string ToString() const
Event class.
Definition httpserver.h:136
void trigger(struct timeval *tv)
Trigger the event.
bool ProcessHTTPRequest(HTTPRequest *request)
Definition httprpc.cpp:295
const std::any & context
Definition httprpc.h:23
RPCServer & rpcServer
Definition httprpc.h:18
static bool DelegateHTTPRequest(HTTPRPCRequestProcessor *requestProcessor, HTTPRequest *request)
Definition httprpc.h:29
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition httprpc.cpp:39
HTTPRPCTimer(struct event_base *eventBase, std::function< void()> &func, int64_t millis)
Definition httprpc.cpp:41
HTTPEvent ev
Definition httprpc.cpp:51
const char * Name() override
Implementation name.
Definition httprpc.cpp:58
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis) override
Factory function for timers.
Definition httprpc.cpp:60
struct event_base * base
Definition httprpc.cpp:66
HTTPRPCTimerInterface(struct event_base *_base)
Definition httprpc.cpp:56
In-flight HTTP request.
Definition httpserver.h:71
std::pair< bool, std::string > GetHeader(const std::string &hdr) const
Get the request header specified by hdr, or an empty string.
std::string GetURI() const
Get requested URI.
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
RequestMethod GetRequestMethod() const
Get request method.
std::string ReadBody()
Read request body.
CService GetPeer() const
Get CService (address:ip) for the origin of the http request.
std::any context
Definition request.h:39
UniValue ExecuteCommand(const Config &config, const JSONRPCRequest &request) const
Attempts to execute an RPC command from the given request.
Definition server.cpp:71
Opaque base class for timers returned by NewTimerFunc.
Definition server.h:92
RPC timer "driver".
Definition server.h:100
const std::string & get_str() const
const UniValue & find_value(std::string_view key) const
Definition univalue.cpp:229
const UniValue & get_obj() const
virtual bool HasWalletSupport() const =0
Is the wallet component enabled.
const WalletInitInterface & g_wallet_init_interface
static bool checkCORS(HTTPRequest *req)
Definition httprpc.cpp:174
static const char * WWW_AUTH_HEADER_DATA
WWW-Authenticate to present with 401 Unauthorized response.
Definition httprpc.cpp:30
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition httprpc.cpp:477
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition httprpc.cpp:481
static std::unique_ptr< HTTPRPCTimerInterface > httpRPCTimerInterface
Definition httprpc.cpp:74
static std::string strRPCCORSDomain
Definition httprpc.cpp:72
static bool multiUserAuthorized(std::string strUserPass)
Definition httprpc.cpp:101
static bool g_rpc_whitelist_default
Definition httprpc.cpp:77
static bool RPCAuthorized(const std::string &strAuth, std::string &strAuthUsernameOut)
Definition httprpc.cpp:143
static std::string strRPCUserColonPass
Definition httprpc.cpp:70
static bool InitRPCAuthentication()
Definition httprpc.cpp:409
static std::map< std::string, std::set< std::string > > g_rpc_whitelist
Definition httprpc.cpp:76
bool StartHTTPRPC(HTTPRPCRequestProcessor &httpRPCRequestProcessor)
Start HTTP RPC subsystem.
Definition httprpc.cpp:456
static const int64_t RPC_AUTH_BRUTE_FORCE_DELAY
RPC auth failure delay to make brute-forcing expensive.
Definition httprpc.cpp:33
static void JSONErrorReply(HTTPRequest *req, const UniValue &objError, const UniValue &id)
Definition httprpc.cpp:79
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
static struct event_base * eventBase
HTTP module state.
struct event_base * EventBase()
Return evhttp event base.
#define LogPrint(category,...)
Definition logging.h:238
#define LogPrintf(...)
Definition logging.h:227
@ RPC
Definition logging.h:47
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
std::string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition request.cpp:52
UniValue JSONRPCError(int code, const std::string &message)
Definition request.cpp:58
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition request.cpp:82
@ HTTP_BAD_REQUEST
Definition protocol.h:12
@ HTTP_BAD_METHOD
Definition protocol.h:16
@ HTTP_OK
Definition protocol.h:11
@ HTTP_UNAUTHORIZED
Definition protocol.h:13
@ HTTP_NOT_FOUND
Definition protocol.h:15
@ HTTP_FORBIDDEN
Definition protocol.h:14
@ HTTP_INTERNAL_SERVER_ERROR
Definition protocol.h:17
@ RPC_PARSE_ERROR
Definition protocol.h:34
@ RPC_METHOD_NOT_FOUND
Definition protocol.h:29
@ RPC_INVALID_REQUEST
Standard JSON-RPC 2.0 errors.
Definition protocol.h:26
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition server.cpp:658
std::string JSONRPCExecBatch(const Config &config, RPCServer &rpcServer, const JSONRPCRequest &jreq, const UniValue &vReq)
Definition server.cpp:435
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set the factory function for timers.
Definition server.cpp:654
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
std::string_view TrimStringView(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
Definition string.h:28
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition time.cpp:23
const UniValue NullUniValue
Definition univalue.cpp:16
std::string HexStr(const Span< const uint8_t > s)
Convert a span of bytes to a lower-case hexadecimal string.
std::optional< std::vector< uint8_t > > DecodeBase64(std::string_view str)
assert(!tx.IsCoinBase())