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