BSHA3  0.17.99
P2P Blockchain, based on Bitcoin
httprpc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2018 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 <httpserver.h>
9 #include <key_io.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 <walletinitinterface.h>
18 #include <crypto/hmac_sha256.h>
19 #include <stdio.h>
20 
21 #include <memory>
22 
23 #include <boost/algorithm/string.hpp> // boost::trim
24 
26 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
27 
31 class HTTPRPCTimer : public RPCTimerBase
32 {
33 public:
34  HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) :
35  ev(eventBase, false, func)
36  {
37  struct timeval tv;
38  tv.tv_sec = millis/1000;
39  tv.tv_usec = (millis%1000)*1000;
40  ev.trigger(&tv);
41  }
42 private:
44 };
45 
47 {
48 public:
49  explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
50  {
51  }
52  const char* Name() override
53  {
54  return "HTTP";
55  }
56  RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
57  {
58  return new HTTPRPCTimer(base, func, millis);
59  }
60 private:
61  struct event_base* base;
62 };
63 
64 
65 /* Pre-base64-encoded authentication token */
66 static std::string strRPCUserColonPass;
67 /* Stored RPC timer interface (for unregistration) */
68 static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
69 
70 static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
71 {
72  // Send error reply from json-rpc error object
73  int nStatus = HTTP_INTERNAL_SERVER_ERROR;
74  int code = find_value(objError, "code").get_int();
75 
76  if (code == RPC_INVALID_REQUEST)
77  nStatus = HTTP_BAD_REQUEST;
78  else if (code == RPC_METHOD_NOT_FOUND)
79  nStatus = HTTP_NOT_FOUND;
80 
81  std::string strReply = JSONRPCReply(NullUniValue, objError, id);
82 
83  req->WriteHeader("Content-Type", "application/json");
84  req->WriteReply(nStatus, strReply);
85 }
86 
87 //This function checks username and password against -rpcauth
88 //entries from config file.
89 static bool multiUserAuthorized(std::string strUserPass)
90 {
91  if (strUserPass.find(':') == std::string::npos) {
92  return false;
93  }
94  std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
95  std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
96 
97  for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
98  //Search for multi-user login/pass "rpcauth" from config
99  std::vector<std::string> vFields;
100  boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
101  if (vFields.size() != 3) {
102  //Incorrect formatting in config file
103  continue;
104  }
105 
106  std::string strName = vFields[0];
107  if (!TimingResistantEqual(strName, strUser)) {
108  continue;
109  }
110 
111  std::string strSalt = vFields[1];
112  std::string strHash = vFields[2];
113 
114  static const unsigned int KEY_SIZE = 32;
115  unsigned char out[KEY_SIZE];
116 
117  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);
118  std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
119  std::string strHashFromPass = HexStr(hexvec);
120 
121  if (TimingResistantEqual(strHashFromPass, strHash)) {
122  return true;
123  }
124  }
125  return false;
126 }
127 
128 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
129 {
130  if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
131  return false;
132  if (strAuth.substr(0, 6) != "Basic ")
133  return false;
134  std::string strUserPass64 = strAuth.substr(6);
135  boost::trim(strUserPass64);
136  std::string strUserPass = DecodeBase64(strUserPass64);
137 
138  if (strUserPass.find(':') != std::string::npos)
139  strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
140 
141  //Check if authorized under single-user field
142  if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
143  return true;
144  }
145  return multiUserAuthorized(strUserPass);
146 }
147 
148 static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &)
149 {
150  // JSONRPC handles only POST
151  if (req->GetRequestMethod() != HTTPRequest::POST) {
152  req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
153  return false;
154  }
155  // Check authorization
156  std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
157  if (!authHeader.first) {
158  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
160  return false;
161  }
162 
163  JSONRPCRequest jreq;
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  MilliSleep(250);
172 
173  req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
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  // singleton request
189  if (valRequest.isObject()) {
190  jreq.parse(valRequest);
191 
192  UniValue result = tableRPC.execute(jreq);
193 
194  // Send reply
195  strReply = JSONRPCReply(result, NullUniValue, jreq.id);
196 
197  // array of requests
198  } else if (valRequest.isArray())
199  strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
200  else
201  throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
202 
203  req->WriteHeader("Content-Type", "application/json");
204  req->WriteReply(HTTP_OK, strReply);
205  } catch (const UniValue& objError) {
206  JSONErrorReply(req, objError, jreq.id);
207  return false;
208  } catch (const std::exception& e) {
209  JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
210  return false;
211  }
212  return true;
213 }
214 
215 static bool InitRPCAuthentication()
216 {
217  if (gArgs.GetArg("-rpcpassword", "") == "")
218  {
219  LogPrintf("No rpcpassword set - using random cookie authentication.\n");
220  if (!GenerateAuthCookie(&strRPCUserColonPass)) {
221  uiInterface.ThreadSafeMessageBox(
222  _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
224  return false;
225  }
226  } else {
227  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");
228  strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
229  }
230  if (gArgs.GetArg("-rpcauth","") != "")
231  {
232  LogPrintf("Using rpcauth authentication.\n");
233  }
234  return true;
235 }
236 
238 {
239  LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
240  if (!InitRPCAuthentication())
241  return false;
242 
243  RegisterHTTPHandler("/", true, HTTPReq_JSONRPC);
245  RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC);
246  }
247  struct event_base* eventBase = EventBase();
248  assert(eventBase);
249  httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase);
250  RPCSetTimerInterface(httpRPCTimerInterface.get());
251  return true;
252 }
253 
255 {
256  LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
257 }
258 
260 {
261  LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
262  UnregisterHTTPHandler("/", true);
264  UnregisterHTTPHandler("/wallet/", false);
265  }
266  if (httpRPCTimerInterface) {
267  RPCUnsetTimerInterface(httpRPCTimerInterface.get());
268  httpRPCTimerInterface.reset();
269  }
270 }
bool isObject() const
Definition: univalue.h:85
RPC timer "driver".
Definition: server.h:101
std::vector< unsigned char > DecodeBase64(const char *p, bool *pfInvalid)
HTTPRPCTimerInterface(struct event_base *_base)
Definition: httprpc.cpp:49
void MilliSleep(int64_t n)
Definition: utiltime.cpp:61
const char * Name() override
Implementation name.
Definition: httprpc.cpp:52
bool read(const char *raw, size_t len)
HTTPEvent ev
Definition: httprpc.cpp:43
Event class.
Definition: httpserver.h:130
bool StartHTTPRPC()
Start HTTP RPC subsystem.
Definition: httprpc.cpp:237
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
struct event_base * base
Definition: httprpc.cpp:61
A hasher class for HMAC-SHA-256.
Definition: hmac_sha256.h:14
RPCTimerBase * NewTimer(std::function< void()> &func, int64_t millis) override
Factory function for timers.
Definition: httprpc.cpp:56
const UniValue & get_array() const
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition: httprpc.cpp:254
std::string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition: protocol.cpp:45
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition: server.cpp:533
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:234
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:642
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:469
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
CRPCTable tableRPC
Definition: server.cpp:556
std::string peerAddr
Definition: server.h:48
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:575
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition: httprpc.cpp:31
RequestMethod GetRequestMethod() const
Get request method.
Definition: httpserver.cpp:621
std::string JSONRPCExecBatch(const JSONRPCRequest &jreq, const UniValue &vReq)
Definition: server.cpp:410
UniValue id
Definition: server.h:42
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:648
int get_int() const
struct event_base * EventBase()
Return evhttp event base.
Definition: httpserver.cpp:487
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition: httprpc.cpp:259
const WalletInitInterface & g_wallet_init_interface
Definition: dummywallet.cpp:35
void parse(const UniValue &valRequest)
Definition: server.cpp:347
ArgsManager gArgs
Definition: util.cpp:88
HTTPRPCTimer(struct event_base *eventBase, std::function< void()> &func, int64_t millis)
Definition: httprpc.cpp:34
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition: protocol.cpp:76
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:563
void trigger(struct timeval *tv)
Trigger the event.
Definition: httpserver.cpp:511
virtual bool HasWalletSupport() const =0
Is the wallet component enabled.
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
Definition: util.cpp:526
std::string URI
Definition: server.h:46
std::string authUser
Definition: server.h:47
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set the factory function for timers.
Definition: server.cpp:528
std::pair< bool, std::string > GetHeader(const std::string &hdr) const
Get the request header specified by hdr, or an empty string.
Definition: httpserver.cpp:532
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:92
const UniValue NullUniValue
Definition: univalue.cpp:13
CService GetPeer() const
Get CService (address:ip) for the origin of the http request.
Definition: httpserver.cpp:602
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:543
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:37
std::string ToString() const
Definition: netaddress.cpp:574
UniValue JSONRPCError(int code, const std::string &message)
Definition: protocol.cpp:51
In-flight HTTP request.
Definition: httpserver.h:57
CClientUIInterface uiInterface
std::vector< std::string > GetArgs(const std::string &strArg) const
Return a vector of strings of the given argument.
Definition: util.cpp:483
bool isArray() const
Definition: univalue.h:84
std::string GetURI() const
Get requested URI.
Definition: httpserver.cpp:616
std::string _(const char *psz)
Translation function.
Definition: util.h:50