Fabcoin Core  0.16.2
P2P Digital Currency
httprpc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2017 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 
20 
21 #include <boost/algorithm/string.hpp> // boost::trim
22 
24 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
25 
29 class HTTPRPCTimer : public RPCTimerBase
30 {
31 public:
32  HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) :
33  ev(eventBase, false, NULL, func)
34  {
35  struct timeval tv;
36  tv.tv_sec = millis/1000;
37  tv.tv_usec = (millis%1000)*1000;
38  ev.trigger(&tv);
39  }
40 private:
42 };
43 
45 {
46 public:
47  HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
48  {
49  }
50  const char* Name() override
51  {
52  return "HTTP";
53  }
54  RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override
55  {
56  return new HTTPRPCTimer(base, func, millis);
57  }
58 private:
59  struct event_base* base;
60 };
61 
62 
63 /* Pre-base64-encoded authentication token */
64 static std::string strRPCUserColonPass;
65 /* Stored RPC timer interface (for unregistration) */
66 static HTTPRPCTimerInterface* httpRPCTimerInterface = 0;
67 
68 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
69 {
70  // Send error reply from json-rpc error object
71  int nStatus = HTTP_INTERNAL_SERVER_ERROR;
72  int code = find_value(objError, "code").get_int();
73 
74  if (code == RPC_INVALID_REQUEST)
75  nStatus = HTTP_BAD_REQUEST;
76  else if (code == RPC_METHOD_NOT_FOUND)
77  nStatus = HTTP_NOT_FOUND;
78 
79  std::string strReply = JSONRPCReply(NullUniValue, objError, id);
80 
81  if (req->isChunkMode()) {
82  // in chunk mode, we assume that the handler had already set the response content-type
83  req->Chunk(strReply);
84  req->ChunkEnd();
85  } else {
86  req->WriteHeader("Content-Type", "application/json");
87  req->WriteReply(nStatus, strReply);
88  }
89 }
90 
91 //This function checks username and password against -rpcauth
92 //entries from config file.
93 static bool multiUserAuthorized(std::string strUserPass)
94 {
95  if (strUserPass.find(":") == std::string::npos) {
96  return false;
97  }
98  std::string strUser = strUserPass.substr(0, strUserPass.find(":"));
99  std::string strPass = strUserPass.substr(strUserPass.find(":") + 1);
100 
101  for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
102  //Search for multi-user login/pass "rpcauth" from config
103  std::vector<std::string> vFields;
104  boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
105  if (vFields.size() != 3) {
106  //Incorrect formatting in config file
107  continue;
108  }
109 
110  std::string strName = vFields[0];
111  if (!TimingResistantEqual(strName, strUser)) {
112  continue;
113  }
114 
115  std::string strSalt = vFields[1];
116  std::string strHash = vFields[2];
117 
118  static const unsigned int KEY_SIZE = 32;
119  unsigned char out[KEY_SIZE];
120 
121  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);
122  std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
123  std::string strHashFromPass = HexStr(hexvec);
124 
125  if (TimingResistantEqual(strHashFromPass, strHash)) {
126  return true;
127  }
128  }
129  return false;
130 }
131 
132 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
133 {
134  if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
135  return false;
136  if (strAuth.substr(0, 6) != "Basic ")
137  return false;
138  std::string strUserPass64 = strAuth.substr(6);
139  boost::trim(strUserPass64);
140  std::string strUserPass = DecodeBase64(strUserPass64);
141 
142  if (strUserPass.find(":") != std::string::npos)
143  strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":"));
144 
145  //Check if authorized under single-user field
146  if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
147  return true;
148  }
149  return multiUserAuthorized(strUserPass);
150 }
151 
152 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
153 {
154  // JSONRPC handles only POST
155  if (req->GetRequestMethod() != HTTPRequest::POST) {
156  req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
157  return false;
158  }
159  // Check authorization
160  std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
161  if (!authHeader.first) {
162  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
164  return false;
165  }
166 
167  JSONRPCRequest jreq(req);
168  if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
169  LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString());
170 
171  /* Deter brute-forcing
172  If this results in a DoS the user really
173  shouldn't have their RPC port exposed. */
174  MilliSleep(250);
175 
176  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
178  return false;
179  }
180 
181  try {
182  // Parse request
183  UniValue valRequest;
184  if (!valRequest.read(req->ReadBody()))
185  throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
186 
187  // Set the URI
188  jreq.URI = req->GetURI();
189 
190  std::string strReply;
191  // singleton request
192  if (valRequest.isObject()) {
193  jreq.parse(valRequest);
194 
195  UniValue result = tableRPC.execute(jreq);
196 
197  if (jreq.isLongPolling) {
198  jreq.PollReply(result);
199  return true;
200  }
201 
202  // Send reply
203  strReply = JSONRPCReply(result, NullUniValue, jreq.id);
204 
205  // array of requests
206  } else if (valRequest.isArray())
207  strReply = JSONRPCExecBatch(valRequest.get_array());
208  else
209  throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
210 
211  req->WriteHeader("Content-Type", "application/json");
212  req->WriteReply(HTTP_OK, strReply);
213  } catch (const UniValue& objError) {
214  JSONErrorReply(req, objError, jreq.id);
215  return false;
216  } catch (const std::exception& e) {
217  JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
218  return false;
219  }
220  return true;
221 }
222 
223 static bool InitRPCAuthentication()
224 {
225  if (gArgs.GetArg("-rpcpassword", "") == "")
226  {
227  LogPrintf("No rpcpassword set - using random cookie authentication\n");
228  if (!GenerateAuthCookie(&strRPCUserColonPass)) {
230  _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
232  return false;
233  }
234  } else {
235  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");
236  strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
237  }
238  return true;
239 }
240 
242 {
243  LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
244  if (!InitRPCAuthentication())
245  return false;
246 
247  RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
248 #ifdef ENABLE_WALLET
249  // ifdef can be removed once we switch to better endpoint support and API versioning
250  RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
251 #endif
252  assert(EventBase());
253  httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase());
254  RPCSetTimerInterface(httpRPCTimerInterface);
255  return true;
256 }
257 
259 {
260  LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
261 }
262 
264 {
265  LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
266  UnregisterHTTPHandler("/", true);
267  if (httpRPCTimerInterface) {
268  RPCUnsetTimerInterface(httpRPCTimerInterface);
269  delete httpRPCTimerInterface;
270  httpRPCTimerInterface = 0;
271  }
272 }
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:512
RPC timer "driver".
Definition: server.h:157
#define function(a, b, c, d, k, s)
HTTPRPCTimer(struct event_base *eventBase, std::function< void(void)> &func, int64_t millis)
Definition: httprpc.cpp:32
std::vector< unsigned char > DecodeBase64(const char *p, bool *pfInvalid)
HTTPRPCTimerInterface(struct event_base *_base)
Definition: httprpc.cpp:47
void MilliSleep(int64_t n)
Definition: utiltime.cpp:60
const char * Name() override
Implementation name.
Definition: httprpc.cpp:50
bool read(const char *raw, size_t len)
HTTPEvent ev
Definition: httprpc.cpp:41
std::string JSONRPCExecBatch(const UniValue &vReq)
Definition: server.cpp:453
std::pair< bool, std::string > GetHeader(const std::string &hdr)
Get the request header specified by hdr, or an empty string.
Definition: httpserver.cpp:629
Event class.
Definition: httpserver.h:159
bool StartHTTPRPC()
Start HTTP RPC subsystem.
Definition: httprpc.cpp:241
std::string GetURI()
Get requested URI.
Definition: httpserver.cpp:760
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
struct event_base * base
Definition: httprpc.cpp:59
void Chunk(const std::string &chunk)
Start chunk transfer.
Definition: httpserver.cpp:689
A hasher class for HMAC-SHA-256.
Definition: hmac_sha256.h:14
bool isArray() const
Definition: univalue.h:85
std::vector< std::string > GetArgs(const std::string &strArg)
Definition: util.cpp:490
assert(len-trim+(2 *lenIndices)<=WIDTH)
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition: httprpc.cpp:258
std::string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition: protocol.cpp:48
bytes code
Definition: SmartVM.cpp:45
RequestMethod GetRequestMethod()
Get request method.
Definition: httpserver.cpp:765
void ChunkEnd()
End chunk transfer.
Definition: httpserver.cpp:671
void PollReply(const UniValue &result)
Return the JSON result of a long poll request.
Definition: server.cpp:389
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition: server.cpp:576
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:786
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
CRPCTable tableRPC
Definition: server.cpp:599
#define LogPrintf(...)
Definition: util.h:153
bool isLongPolling
Definition: server.h:64
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:719
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition: httprpc.cpp:29
const Object_type::value_type::Value_type & find_value(const Object_type &obj, const String_type &name)
UniValue id
Definition: server.h:57
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:792
std::string ToString() const
Definition: netaddress.cpp:596
bool isChunkMode()
Definition: httpserver.cpp:625
CService GetPeer()
Get CService (address:ip) for the origin of the http request.
Definition: httpserver.cpp:746
struct event_base * EventBase()
Return evhttp event base.
Definition: httpserver.cpp:519
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition: httprpc.cpp:263
#define LogPrint(category,...)
Definition: util.h:164
void parse(const UniValue &valRequest)
Definition: server.cpp:400
ArgsManager gArgs
Definition: util.cpp:94
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition: protocol.cpp:81
const UniValue & get_array() const
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:664
void trigger(struct timeval *tv)
Trigger the event.
Definition: httpserver.cpp:546
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:504
std::string URI
Definition: server.h:61
std::string authUser
Definition: server.h:62
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set the factory function for timers.
Definition: server.cpp:571
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:148
const UniValue NullUniValue
Definition: univalue.cpp:15
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:75
#define e(i)
Definition: sha.cpp:733
bool isObject() const
Definition: univalue.h:86
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:640
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:37
UniValue JSONRPCError(int code, const std::string &message)
Definition: protocol.cpp:54
In-flight HTTP request.
Definition: httpserver.h:59
CClientUIInterface uiInterface
Definition: ui_interface.cpp:8
RPCTimerBase * NewTimer(std::function< void(void)> &func, int64_t millis) override
Factory function for timers.
Definition: httprpc.cpp:54
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result...
Definition: util.h:71