Line data Source code
1 : // Copyright (c) 2015-2017 The Bitcoin Core developers
2 : // Copyright (c) 2017-2021 The PIVX Core developers
3 : // Distributed under the MIT software license, see the accompanying
4 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 :
6 : #include "httprpc.h"
7 :
8 : #include "chainparams.h"
9 : #include "crypto/hmac_sha256.h"
10 : #include "guiinterface.h"
11 : #include "httpserver.h"
12 : #include "key_io.h"
13 : #include "rpc/protocol.h"
14 : #include "rpc/server.h"
15 : #include "random.h"
16 : #include "sync.h"
17 : #include "util/system.h"
18 : #include "utilstrencodings.h"
19 :
20 : #include <boost/algorithm/string.hpp> // boost::trim
21 :
22 : /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
23 : * re-lock the wellet.
24 : */
25 : class HTTPRPCTimer : public RPCTimerBase
26 : {
27 : public:
28 14 : HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
29 14 : ev(eventBase, false, func)
30 : {
31 14 : struct timeval tv;
32 14 : tv.tv_sec = millis/1000;
33 14 : tv.tv_usec = (millis%1000)*1000;
34 14 : ev.trigger(&tv);
35 14 : }
36 : private:
37 : HTTPEvent ev;
38 : };
39 :
40 : class HTTPRPCTimerInterface : public RPCTimerInterface
41 : {
42 : public:
43 375 : explicit HTTPRPCTimerInterface(struct event_base* base) : base(base)
44 : {
45 : }
46 14 : const char* Name()
47 : {
48 14 : return "HTTP";
49 : }
50 14 : RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis)
51 : {
52 14 : return new HTTPRPCTimer(base, func, millis);
53 : }
54 : private:
55 : struct event_base* base;
56 : };
57 :
58 :
59 : /* Pre-base64-encoded authentication token */
60 : static std::string strRPCUserColonPass;
61 : /* Stored RPC timer interface (for unregistration) */
62 : static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
63 :
64 287 : static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
65 : {
66 : // Send error reply from json-rpc error object
67 287 : int nStatus = HTTP_INTERNAL_SERVER_ERROR;
68 287 : int code = find_value(objError, "code").get_int();
69 :
70 287 : if (code == RPC_INVALID_REQUEST)
71 : nStatus = HTTP_BAD_REQUEST;
72 287 : else if (code == RPC_METHOD_NOT_FOUND)
73 1 : nStatus = HTTP_NOT_FOUND;
74 :
75 287 : std::string strReply = JSONRPCReply(NullUniValue, objError, id);
76 :
77 574 : req->WriteHeader("Content-Type", "application/json");
78 287 : req->WriteReply(nStatus, strReply);
79 287 : }
80 :
81 : //This function checks username and password against -rpcauth
82 : //entries from config file.
83 18 : static bool multiUserAuthorized(std::string strUserPass)
84 : {
85 18 : if (strUserPass.find(':') == std::string::npos) {
86 : return false;
87 : }
88 36 : std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
89 18 : std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
90 :
91 57 : for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
92 : //Search for multi-user login/pass "rpcauth" from config
93 42 : std::vector<std::string> vFields;
94 42 : boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
95 42 : if (vFields.size() != 3) {
96 : //Incorrect formatting in config file
97 0 : continue;
98 : }
99 :
100 45 : std::string strName = vFields[0];
101 42 : if (!TimingResistantEqual(strName, strUser)) {
102 72 : continue;
103 : }
104 :
105 9 : std::string strSalt = vFields[1];
106 9 : std::string strHash = vFields[2];
107 :
108 6 : static const unsigned int KEY_SIZE = 32;
109 6 : unsigned char out[KEY_SIZE];
110 :
111 6 : 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);
112 9 : std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
113 9 : std::string strHashFromPass = HexStr(hexvec);
114 :
115 6 : if (TimingResistantEqual(strHashFromPass, strHash)) {
116 6 : return true;
117 : }
118 : }
119 15 : return false;
120 : }
121 :
122 298166 : static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
123 : {
124 298166 : if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
125 : return false;
126 596332 : if (strAuth.substr(0, 6) != "Basic ")
127 : return false;
128 596332 : std::string strUserPass64 = strAuth.substr(6);
129 298166 : boost::trim(strUserPass64);
130 596332 : std::string strUserPass = DecodeBase64(strUserPass64);
131 :
132 298166 : if (strUserPass.find(':') != std::string::npos)
133 298166 : strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
134 :
135 : //Check if authorized under single-user field
136 298166 : if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
137 : return true;
138 : }
139 54 : return multiUserAuthorized(strUserPass);
140 : }
141 :
142 298166 : static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
143 : {
144 : // JSONRPC handles only POST
145 298166 : if (req->GetRequestMethod() != HTTPRequest::POST) {
146 0 : req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
147 0 : return false;
148 : }
149 : // Check authorization
150 596332 : std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
151 298166 : if (!authHeader.first) {
152 0 : req->WriteReply(HTTP_UNAUTHORIZED);
153 0 : return false;
154 : }
155 :
156 596332 : JSONRPCRequest jreq;
157 298166 : if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
158 30 : LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
159 :
160 : /* Deter brute-forcing
161 : If this results in a DoS the user really
162 : shouldn't have their RPC port exposed. */
163 15 : MilliSleep(250);
164 :
165 15 : req->WriteReply(HTTP_UNAUTHORIZED);
166 15 : return false;
167 : }
168 :
169 298151 : try {
170 : // Parse request
171 298151 : UniValue valRequest;
172 894740 : if (!valRequest.read(req->ReadBody()))
173 0 : throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
174 :
175 : // Set the URI
176 298151 : jreq.URI = req->GetURI();
177 :
178 596302 : std::string strReply;
179 : // singleton request
180 298151 : if (valRequest.isObject()) {
181 298150 : jreq.parse(valRequest);
182 :
183 298150 : UniValue result = tableRPC.execute(jreq);
184 :
185 : // Send reply
186 297863 : strReply = JSONRPCReply(result, NullUniValue, jreq.id);
187 :
188 : // array of requests
189 1 : } else if (valRequest.isArray())
190 1 : strReply = JSONRPCExecBatch(valRequest.get_array());
191 : else
192 0 : throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
193 :
194 595728 : req->WriteHeader("Content-Type", "application/json");
195 297864 : req->WriteReply(HTTP_OK, strReply);
196 287 : } catch (const UniValue& objError) {
197 287 : JSONErrorReply(req, objError, jreq.id);
198 287 : return false;
199 0 : } catch (const std::exception& e) {
200 0 : JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
201 0 : return false;
202 : }
203 297864 : return true;
204 : }
205 :
206 375 : static bool InitRPCAuthentication()
207 : {
208 750 : if (gArgs.GetArg("-rpcpassword", "") == "")
209 : {
210 374 : LogPrintf("No rpcpassword set - using random cookie authentication\n");
211 374 : if (!GenerateAuthCookie(&strRPCUserColonPass)) {
212 0 : uiInterface.ThreadSafeMessageBox(
213 0 : _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
214 : "", CClientUIInterface::MSG_ERROR);
215 0 : return false;
216 : }
217 : } else {
218 1 : 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");
219 1 : strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
220 : }
221 : return true;
222 : }
223 :
224 375 : bool StartHTTPRPC()
225 : {
226 375 : LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
227 375 : if (!InitRPCAuthentication())
228 : return false;
229 :
230 750 : RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
231 : #ifdef ENABLE_WALLET
232 : // ifdef can be removed once we switch to better endpoint support and API versioning
233 750 : RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
234 : #endif
235 375 : assert(EventBase());
236 375 : httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(EventBase());
237 375 : RPCSetTimerInterface(httpRPCTimerInterface.get());
238 375 : return true;
239 : }
240 :
241 378 : void InterruptHTTPRPC()
242 : {
243 378 : LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
244 378 : }
245 :
246 378 : void StopHTTPRPC()
247 : {
248 378 : LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
249 378 : UnregisterHTTPHandler("/", true);
250 378 : if (httpRPCTimerInterface) {
251 375 : RPCUnsetTimerInterface(httpRPCTimerInterface.get());
252 375 : httpRPCTimerInterface.reset();
253 : }
254 378 : }
|