1 // Copyright (c) 2015 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 "base58.h"
8 #include "chainparams.h"
9 #include "httpserver.h"
10 #include "rpc/protocol.h"
11 #include "rpc/server.h"
12 #include "random.h"
13 #include "sync.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
16 #include "ui_interface.h"
17 #include "crypto/hmac_sha256.h"
18 #include <stdio.h>
19 #include "utilstrencodings.h"
20 
21 #include <boost/algorithm/string.hpp> // boost::trim
22 #include <boost/foreach.hpp> //BOOST_FOREACH
23 
24 /** WWW-Authenticate to present with 401 Unauthorized response */
25 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
26 
27 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
28  * re-lock the wellet.
29  */
30 class HTTPRPCTimer : public RPCTimerBase
31 {
32 public:
HTTPRPCTimer(struct event_base * eventBase,boost::function<void (void)> & func,int64_t millis)33     HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) :
34         ev(eventBase, false, func)
35     {
36         struct timeval tv;
37         tv.tv_sec = millis/1000;
38         tv.tv_usec = (millis%1000)*1000;
39         ev.trigger(&tv);
40     }
41 private:
42     HTTPEvent ev;
43 };
44 
45 class HTTPRPCTimerInterface : public RPCTimerInterface
46 {
47 public:
HTTPRPCTimerInterface(struct event_base * base)48     HTTPRPCTimerInterface(struct event_base* base) : base(base)
49     {
50     }
Name()51     const char* Name()
52     {
53         return "HTTP";
54     }
NewTimer(boost::function<void (void)> & func,int64_t millis)55     RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis)
56     {
57         return new HTTPRPCTimer(base, func, millis);
58     }
59 private:
60     struct event_base* base;
61 };
62 
63 
64 /* Pre-base64-encoded authentication token */
65 static std::string strRPCUserColonPass;
66 /* Stored RPC timer interface (for unregistration) */
67 static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
68 
JSONErrorReply(HTTPRequest * req,const UniValue & objError,const UniValue & id)69 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
70 {
71     // Send error reply from json-rpc error object
72     int nStatus = HTTP_INTERNAL_SERVER_ERROR;
73     int code = find_value(objError, "code").get_int();
74 
75     if (code == RPC_INVALID_REQUEST)
76         nStatus = HTTP_BAD_REQUEST;
77     else if (code == RPC_METHOD_NOT_FOUND)
78         nStatus = HTTP_NOT_FOUND;
79 
80     std::string strReply = JSONRPCReply(NullUniValue, objError, id);
81 
82     req->WriteHeader("Content-Type", "application/json");
83     req->WriteReply(nStatus, strReply);
84 }
85 
86 //This function checks username and password against -rpcauth
87 //entries from config file.
multiUserAuthorized(std::string strUserPass)88 static bool multiUserAuthorized(std::string strUserPass)
89 {
90     if (strUserPass.find(":") == std::string::npos) {
91         return false;
92     }
93     std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
94     std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
95 
96     if (mapMultiArgs.count("-rpcauth") > 0) {
97         //Search for multi-user login/pass "rpcauth" from config
98         BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs["-rpcauth"])
99         {
100             std::vector<std::string> vFields;
101             boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
102             if (vFields.size() != 3) {
103                 //Incorrect formatting in config file
104                 continue;
105             }
106 
107             std::string strName = vFields[0];
108             if (!TimingResistantEqual(strName, strUser)) {
109                 continue;
110             }
111 
112             std::string strSalt = vFields[1];
113             std::string strHash = vFields[2];
114 
115             unsigned int KEY_SIZE = 32;
116             unsigned char *out = new unsigned char[KEY_SIZE];
117 
118             CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.c_str()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.c_str()), strPass.size()).Finalize(out);
119             std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
120             std::string strHashFromPass = HexStr(hexvec);
121 
122             if (TimingResistantEqual(strHashFromPass, strHash)) {
123                 return true;
124             }
125         }
126     }
127     return false;
128 }
129 
RPCAuthorized(const std::string & strAuth)130 static bool RPCAuthorized(const std::string& strAuth)
131 {
132     if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
133         return false;
134     if (strAuth.substr(0, 6) != "Basic ")
135         return false;
136     std::string strUserPass64 = strAuth.substr(6);
137     boost::trim(strUserPass64);
138     std::string strUserPass = DecodeBase64(strUserPass64);
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(HTTPRequest * req,const std::string &)147 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
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     if (!RPCAuthorized(authHeader.second)) {
163         LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
164 
165         /* Deter brute-forcing
166            If this results in a DoS the user really
167            shouldn't have their RPC port exposed. */
168         MilliSleep(250);
169 
170         req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
171         req->WriteReply(HTTP_UNAUTHORIZED);
172         return false;
173     }
174 
175     JSONRequest jreq;
176     try {
177         // Parse request
178         UniValue valRequest;
179         if (!valRequest.read(req->ReadBody()))
180             throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
181 
182         std::string strReply;
183         // singleton request
184         if (valRequest.isObject()) {
185             jreq.parse(valRequest);
186 
187             UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
188 
189             // Send reply
190             strReply = JSONRPCReply(result, NullUniValue, jreq.id);
191 
192         // array of requests
193         } else if (valRequest.isArray())
194             strReply = JSONRPCExecBatch(valRequest.get_array());
195         else
196             throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
197 
198         req->WriteHeader("Content-Type", "application/json");
199         req->WriteReply(HTTP_OK, strReply);
200     } catch (const UniValue& objError) {
201         JSONErrorReply(req, objError, jreq.id);
202         return false;
203     } catch (const std::exception& e) {
204         JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
205         return false;
206     }
207     return true;
208 }
209 
InitRPCAuthentication()210 static bool InitRPCAuthentication()
211 {
212     if (mapArgs["-rpcpassword"] == "")
213     {
214         LogPrintf("No rpcpassword set - using random cookie authentication\n");
215         if (!GenerateAuthCookie(&strRPCUserColonPass)) {
216             uiInterface.ThreadSafeMessageBox(
217                 _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
218                 "", CClientUIInterface::MSG_ERROR);
219             return false;
220         }
221     } else {
222         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");
223         strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
224     }
225     return true;
226 }
227 
StartHTTPRPC()228 bool StartHTTPRPC()
229 {
230     LogPrint("rpc", "Starting HTTP RPC server\n");
231     if (!InitRPCAuthentication())
232         return false;
233 
234     RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
235 
236     assert(EventBase());
237     httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
238     RPCSetTimerInterface(httpRPCTimerInterface);
239     return true;
240 }
241 
InterruptHTTPRPC()242 void InterruptHTTPRPC()
243 {
244     LogPrint("rpc", "Interrupting HTTP RPC server\n");
245 }
246 
StopHTTPRPC()247 void StopHTTPRPC()
248 {
249     LogPrint("rpc", "Stopping HTTP RPC server\n");
250     UnregisterHTTPHandler("/", true);
251     if (httpRPCTimerInterface) {
252         RPCUnsetTimerInterface(httpRPCTimerInterface);
253         delete httpRPCTimerInterface;
254         httpRPCTimerInterface = 0;
255     }
256 }
257