Dogecoin Core  1.14.2
P2P Digital Currency
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 "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 
25 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
26 
30 class HTTPRPCTimer : public RPCTimerBase
31 {
32 public:
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:
43 };
44 
46 {
47 public:
48  HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
49  {
50  }
51  const char* Name()
52  {
53  return "HTTP";
54  }
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 
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.
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.at("-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  static const unsigned int KEY_SIZE = 32;
116  unsigned char out[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 
130 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
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  if (strUserPass.find(":") != std::string::npos)
141  strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
142 
143  //Check if authorized under single-user field
144  if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
145  return true;
146  }
147  return multiUserAuthorized(strUserPass);
148 }
149 
150 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
151 {
152  // JSONRPC handles only POST
153  if (req->GetRequestMethod() != HTTPRequest::POST) {
154  req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
155  return false;
156  }
157  // Check authorization
158  std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
159  if (!authHeader.first) {
160  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
162  return false;
163  }
164 
165  JSONRPCRequest jreq;
166  if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
167  LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
168 
169  /* Deter brute-forcing
170  If this results in a DoS the user really
171  shouldn't have their RPC port exposed. */
172  MilliSleep(250);
173 
174  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
176  return false;
177  }
178 
179  try {
180  // Parse request
181  UniValue valRequest;
182  if (!valRequest.read(req->ReadBody()))
183  throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
184 
185  // Set the URI
186  jreq.URI = req->GetURI();
187 
188  std::string strReply;
189  // singleton request
190  if (valRequest.isObject()) {
191  jreq.parse(valRequest);
192 
193  UniValue result = tableRPC.execute(jreq);
194 
195  // Send reply
196  strReply = JSONRPCReply(result, NullUniValue, jreq.id);
197 
198  // array of requests
199  } else if (valRequest.isArray())
200  strReply = JSONRPCExecBatch(valRequest.get_array());
201  else
202  throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
203 
204  req->WriteHeader("Content-Type", "application/json");
205  req->WriteReply(HTTP_OK, strReply);
206  } catch (const UniValue& objError) {
207  JSONErrorReply(req, objError, jreq.id);
208  return false;
209  } catch (const std::exception& e) {
210  JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
211  return false;
212  }
213  return true;
214 }
215 
216 static bool InitRPCAuthentication()
217 {
218  if (GetArg("-rpcpassword", "") == "")
219  {
220  LogPrintf("No rpcpassword set - using random cookie authentication\n");
221  if (!GenerateAuthCookie(&strRPCUserColonPass)) {
223  _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
225  return false;
226  }
227  } else {
228  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");
229  strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
230  }
231  return true;
232 }
233 
235 {
236  LogPrint("rpc", "Starting HTTP RPC server\n");
237  if (!InitRPCAuthentication())
238  return false;
239 
240  RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
241 
242  assert(EventBase());
243  httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
244  RPCSetTimerInterface(httpRPCTimerInterface);
245  return true;
246 }
247 
249 {
250  LogPrint("rpc", "Interrupting HTTP RPC server\n");
251 }
252 
254 {
255  LogPrint("rpc", "Stopping HTTP RPC server\n");
256  UnregisterHTTPHandler("/", true);
257  if (httpRPCTimerInterface) {
258  RPCUnsetTimerInterface(httpRPCTimerInterface);
259  delete httpRPCTimerInterface;
260  httpRPCTimerInterface = 0;
261  }
262 }
boost::signals2::signal< bool(const std::string &message, const std::string &caption, unsigned int style), boost::signals2::last_value< bool > > ThreadSafeMessageBox
Show message box.
Definition: ui_interface.h:77
A hasher class for HMAC-SHA-512.
Definition: hmac_sha256.h:16
void Finalize(unsigned char hash[OUTPUT_SIZE])
Definition: hmac_sha256.cpp:29
CHMAC_SHA256 & Write(const unsigned char *data, size_t len)
Definition: hmac_sha256.h:29
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:479
std::string ToString() const
Definition: netaddress.cpp:568
Event class.
Definition: httpserver.h:127
void trigger(struct timeval *tv)
Trigger the event.
Definition: httpserver.cpp:536
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition: httprpc.cpp:31
HTTPRPCTimer(struct event_base *eventBase, boost::function< void(void)> &func, int64_t millis)
Definition: httprpc.cpp:33
HTTPEvent ev
Definition: httprpc.cpp:42
RPCTimerBase * NewTimer(boost::function< void(void)> &func, int64_t millis)
Factory function for timers.
Definition: httprpc.cpp:55
const char * Name()
Implementation name.
Definition: httprpc.cpp:51
struct event_base * base
Definition: httprpc.cpp:60
HTTPRPCTimerInterface(struct event_base *_base)
Definition: httprpc.cpp:48
In-flight HTTP request.
Definition: httpserver.h:54
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:600
std::string GetURI()
Get requested URI.
Definition: httpserver.cpp:628
CService GetPeer()
Get CService (address:ip) for the origin of the http request.
Definition: httpserver.cpp:614
std::pair< bool, std::string > GetHeader(const std::string &hdr)
Get the request header specified by hdr, or an empty string.
Definition: httpserver.cpp:557
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:588
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:568
RequestMethod GetRequestMethod()
Get request method.
Definition: httpserver.cpp:633
UniValue id
Definition: server.h:50
void parse(const UniValue &valRequest)
Definition: server.cpp:374
std::string URI
Definition: server.h:54
std::string authUser
Definition: server.h:55
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:100
RPC timer "driver".
Definition: server.h:109
bool isArray() const
Definition: univalue.h:83
bool read(const char *raw)
const UniValue & get_array() const
Definition: univalue.cpp:351
bool isObject() const
Definition: univalue.h:84
int get_int() const
Definition: univalue.cpp:314
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition: httprpc.cpp:248
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition: httprpc.cpp:253
bool StartHTTPRPC()
Start HTTP RPC subsystem.
Definition: httprpc.cpp:234
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:660
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:654
struct event_base * EventBase()
Return evhttp event base.
Definition: httpserver.cpp:512
UniValue JSONRPCError(int code, const string &message)
Definition: protocol.cpp:56
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition: protocol.cpp:78
string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition: protocol.cpp:50
@ HTTP_BAD_REQUEST
Definition: protocol.h:21
@ HTTP_BAD_METHOD
Definition: protocol.h:25
@ HTTP_OK
Definition: protocol.h:20
@ HTTP_UNAUTHORIZED
Definition: protocol.h:22
@ HTTP_NOT_FOUND
Definition: protocol.h:24
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:26
@ RPC_PARSE_ERROR
Definition: protocol.h:38
@ RPC_METHOD_NOT_FOUND
Definition: protocol.h:35
@ RPC_INVALID_REQUEST
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:34
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition: server.cpp:546
std::string JSONRPCExecBatch(const UniValue &vReq)
Definition: server.cpp:428
CRPCTable tableRPC
Definition: server.cpp:569
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set the factory function for timers.
Definition: server.cpp:541
CClientUIInterface uiInterface
Definition: ui_interface.cpp:8
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:277
const UniValue NullUniValue
Definition: univalue.cpp:78
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:395
const map< string, vector< string > > & mapMultiArgs
Definition: util.cpp:112
#define LogPrint(category,...)
Definition: util.h:76
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
Definition: util.h:62
#define LogPrintf(...)
Definition: util.h:82
vector< unsigned char > DecodeBase64(const char *p, bool *pfInvalid)
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
void MilliSleep(int64_t n)
Definition: utiltime.cpp:62