1 // Copyright (c) 2015-2020 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 <crypto/hmac_sha256.h>
9 #include <httpserver.h>
10 #include <rpc/protocol.h>
11 #include <rpc/server.h>
12 #include <util/strencodings.h>
13 #include <util/system.h>
14 #include <util/translation.h>
15 #include <walletinitinterface.h>
16
17 #include <algorithm>
18 #include <iterator>
19 #include <map>
20 #include <memory>
21 #include <stdio.h>
22 #include <set>
23 #include <string>
24
25 #include <boost/algorithm/string.hpp> // boost::trim
26
27 /** WWW-Authenticate to present with 401 Unauthorized response */
28 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
29
30 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
31 * re-lock the wallet.
32 */
33 class HTTPRPCTimer : public RPCTimerBase
34 {
35 public:
HTTPRPCTimer(struct event_base * eventBase,std::function<void ()> & func,int64_t millis)36 HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) :
37 ev(eventBase, false, func)
38 {
39 struct timeval tv;
40 tv.tv_sec = millis/1000;
41 tv.tv_usec = (millis%1000)*1000;
42 ev.trigger(&tv);
43 }
44 private:
45 HTTPEvent ev;
46 };
47
48 class HTTPRPCTimerInterface : public RPCTimerInterface
49 {
50 public:
HTTPRPCTimerInterface(struct event_base * _base)51 explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
52 {
53 }
Name()54 const char* Name() override
55 {
56 return "HTTP";
57 }
NewTimer(std::function<void ()> & func,int64_t millis)58 RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
59 {
60 return new HTTPRPCTimer(base, func, millis);
61 }
62 private:
63 struct event_base* base;
64 };
65
66
67 /* Pre-base64-encoded authentication token */
68 static std::string strRPCUserColonPass;
69 /* Stored RPC timer interface (for unregistration) */
70 static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
71 /* List of -rpcauth values */
72 static std::vector<std::vector<std::string>> g_rpcauth;
73 /* RPC Auth Whitelist */
74 static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
75 static bool g_rpc_whitelist_default = false;
76
JSONErrorReply(HTTPRequest * req,const UniValue & objError,const UniValue & id)77 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
78 {
79 // Send error reply from json-rpc error object
80 int nStatus = HTTP_INTERNAL_SERVER_ERROR;
81 int code = find_value(objError, "code").get_int();
82
83 if (code == RPC_INVALID_REQUEST)
84 nStatus = HTTP_BAD_REQUEST;
85 else if (code == RPC_METHOD_NOT_FOUND)
86 nStatus = HTTP_NOT_FOUND;
87
88 std::string strReply = JSONRPCReply(NullUniValue, objError, id);
89
90 req->WriteHeader("Content-Type", "application/json");
91 req->WriteReply(nStatus, strReply);
92 }
93
94 //This function checks username and password against -rpcauth
95 //entries from config file.
multiUserAuthorized(std::string strUserPass)96 static bool multiUserAuthorized(std::string strUserPass)
97 {
98 if (strUserPass.find(':') == std::string::npos) {
99 return false;
100 }
101 std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
102 std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
103
104 for (const auto& vFields : g_rpcauth) {
105 std::string strName = vFields[0];
106 if (!TimingResistantEqual(strName, strUser)) {
107 continue;
108 }
109
110 std::string strSalt = vFields[1];
111 std::string strHash = vFields[2];
112
113 static const unsigned int KEY_SIZE = 32;
114 unsigned char out[KEY_SIZE];
115
116 CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.data()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.data()), strPass.size()).Finalize(out);
117 std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
118 std::string strHashFromPass = HexStr(hexvec);
119
120 if (TimingResistantEqual(strHashFromPass, strHash)) {
121 return true;
122 }
123 }
124 return false;
125 }
126
RPCAuthorized(const std::string & strAuth,std::string & strAuthUsernameOut)127 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
128 {
129 if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
130 return false;
131 if (strAuth.substr(0, 6) != "Basic ")
132 return false;
133 std::string strUserPass64 = strAuth.substr(6);
134 boost::trim(strUserPass64);
135 std::string strUserPass = DecodeBase64(strUserPass64);
136
137 if (strUserPass.find(':') != std::string::npos)
138 strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
139
140 //Check if authorized under single-user field
141 if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
142 return true;
143 }
144 return multiUserAuthorized(strUserPass);
145 }
146
HTTPReq_JSONRPC(const std::any & context,HTTPRequest * req)147 static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
148 {
149 // JSONRPC handles only POST
150 if (req->GetRequestMethod() != HTTPRequest::POST) {
151 req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
152 return false;
153 }
154 // Check authorization
155 std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
156 if (!authHeader.first) {
157 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
158 req->WriteReply(HTTP_UNAUTHORIZED);
159 return false;
160 }
161
162 JSONRPCRequest jreq;
163 jreq.context = context;
164 jreq.peerAddr = req->GetPeer().ToString();
165 if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
166 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
167
168 /* Deter brute-forcing
169 If this results in a DoS the user really
170 shouldn't have their RPC port exposed. */
171 UninterruptibleSleep(std::chrono::milliseconds{250});
172
173 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
174 req->WriteReply(HTTP_UNAUTHORIZED);
175 return false;
176 }
177
178 try {
179 // Parse request
180 UniValue valRequest;
181 if (!valRequest.read(req->ReadBody()))
182 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
183
184 // Set the URI
185 jreq.URI = req->GetURI();
186
187 std::string strReply;
188 bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
189 if (!user_has_whitelist && g_rpc_whitelist_default) {
190 LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser);
191 req->WriteReply(HTTP_FORBIDDEN);
192 return false;
193
194 // singleton request
195 } else if (valRequest.isObject()) {
196 jreq.parse(valRequest);
197 if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
198 LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod);
199 req->WriteReply(HTTP_FORBIDDEN);
200 return false;
201 }
202 UniValue result = tableRPC.execute(jreq);
203
204 // Send reply
205 strReply = JSONRPCReply(result, NullUniValue, jreq.id);
206
207 // array of requests
208 } else if (valRequest.isArray()) {
209 if (user_has_whitelist) {
210 for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) {
211 if (!valRequest[reqIdx].isObject()) {
212 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
213 } else {
214 const UniValue& request = valRequest[reqIdx].get_obj();
215 // Parse method
216 std::string strMethod = find_value(request, "method").get_str();
217 if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
218 LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod);
219 req->WriteReply(HTTP_FORBIDDEN);
220 return false;
221 }
222 }
223 }
224 }
225 strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
226 }
227 else
228 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
229
230 req->WriteHeader("Content-Type", "application/json");
231 req->WriteReply(HTTP_OK, strReply);
232 } catch (const UniValue& objError) {
233 JSONErrorReply(req, objError, jreq.id);
234 return false;
235 } catch (const std::exception& e) {
236 JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
237 return false;
238 }
239 return true;
240 }
241
InitRPCAuthentication()242 static bool InitRPCAuthentication()
243 {
244 if (gArgs.GetArg("-rpcpassword", "") == "")
245 {
246 LogPrintf("Using random cookie authentication.\n");
247 if (!GenerateAuthCookie(&strRPCUserColonPass)) {
248 return false;
249 }
250 } else {
251 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/rpcauth for rpcauth auth generation.\n");
252 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
253 }
254 if (gArgs.GetArg("-rpcauth","") != "")
255 {
256 LogPrintf("Using rpcauth authentication.\n");
257 for (const std::string& rpcauth : gArgs.GetArgs("-rpcauth")) {
258 std::vector<std::string> fields;
259 boost::split(fields, rpcauth, boost::is_any_of(":$"));
260 if (fields.size() == 3) {
261 g_rpcauth.push_back(fields);
262 } else {
263 LogPrintf("Invalid -rpcauth argument.\n");
264 return false;
265 }
266 }
267 }
268
269 g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist"));
270 for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
271 auto pos = strRPCWhitelist.find(':');
272 std::string strUser = strRPCWhitelist.substr(0, pos);
273 bool intersect = g_rpc_whitelist.count(strUser);
274 std::set<std::string>& whitelist = g_rpc_whitelist[strUser];
275 if (pos != std::string::npos) {
276 std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
277 std::set<std::string> new_whitelist;
278 boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
279 if (intersect) {
280 std::set<std::string> tmp_whitelist;
281 std::set_intersection(new_whitelist.begin(), new_whitelist.end(),
282 whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end()));
283 new_whitelist = std::move(tmp_whitelist);
284 }
285 whitelist = std::move(new_whitelist);
286 }
287 }
288
289 return true;
290 }
291
StartHTTPRPC(const std::any & context)292 bool StartHTTPRPC(const std::any& context)
293 {
294 LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
295 if (!InitRPCAuthentication())
296 return false;
297
298 auto handle_rpc = [context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
299 RegisterHTTPHandler("/", true, handle_rpc);
300 if (g_wallet_init_interface.HasWalletSupport()) {
301 RegisterHTTPHandler("/wallet/", false, handle_rpc);
302 }
303 struct event_base* eventBase = EventBase();
304 assert(eventBase);
305 httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase);
306 RPCSetTimerInterface(httpRPCTimerInterface.get());
307 return true;
308 }
309
InterruptHTTPRPC()310 void InterruptHTTPRPC()
311 {
312 LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
313 }
314
StopHTTPRPC()315 void StopHTTPRPC()
316 {
317 LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
318 UnregisterHTTPHandler("/", true);
319 if (g_wallet_init_interface.HasWalletSupport()) {
320 UnregisterHTTPHandler("/wallet/", false);
321 }
322 if (httpRPCTimerInterface) {
323 RPCUnsetTimerInterface(httpRPCTimerInterface.get());
324 httpRPCTimerInterface.reset();
325 }
326 }
327