PRCYCoin  2.0.0.7rc1
P2P Digital Currency
httprpc.cpp
Go to the documentation of this file.
1 // Copyright (c) 2015-2017 The Bitcoin Core developers
2 // Copyright (c) 2015-2018 The PIVX developers
3 // Copyright (c) 2018-2020 The DAPS Project developers
4 // Distributed under the MIT software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include "httprpc.h"
8 
9 #include "base58.h"
10 #include "chainparams.h"
11 #include "httpserver.h"
12 #include "rpc/protocol.h"
13 #include "rpc/server.h"
14 #include "random.h"
15 #include "sync.h"
16 #include "util.h"
17 #include "utilstrencodings.h"
18 #include "guiinterface.h"
19 #include "crypto/hmac_sha256.h"
20 #include <stdio.h>
21 
22 #include <boost/algorithm/string.hpp> // boost::trim
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  for (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 
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 
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);
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);
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 
210 static bool InitRPCAuthentication()
211 {
212  if (mapArgs["-rpcpassword"] == "")
213  {
214  LogPrintf("No rpcpassword set - using random cookie authentication\n");
215  if (!GenerateAuthCookie(&strRPCUserColonPass)) {
217  _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode
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 
229 {
230  LogPrint(BCLog::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 
243 {
244  LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
245 }
246 
248 {
249  LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
250  UnregisterHTTPHandler("/", true);
251  if (httpRPCTimerInterface) {
252  RPCUnsetTimerInterface(httpRPCTimerInterface);
253  delete httpRPCTimerInterface;
254  httpRPCTimerInterface = 0;
255  }
256 }
RPC_METHOD_NOT_FOUND
@ RPC_METHOD_NOT_FOUND
Definition: protocol.h:34
StartHTTPRPC
bool StartHTTPRPC()
Start HTTP RPC subsystem.
Definition: httprpc.cpp:228
RPC_INVALID_REQUEST
@ RPC_INVALID_REQUEST
Standard JSON-RPC 2.0 errors.
Definition: protocol.h:33
HTTPRPCTimer
Simple one-shot callback timer to be used by the RPC mechanism to e.g.
Definition: httprpc.cpp:30
CHMAC_SHA256::Finalize
void Finalize(unsigned char hash[OUTPUT_SIZE])
Definition: hmac_sha256.cpp:29
hmac_sha256.h
HTTPRequest::GetPeer
CService GetPeer()
Get CService (address:ip) for the origin of the http request.
Definition: httpserver.cpp:655
UnregisterHTTPHandler
void UnregisterHTTPHandler(const std::string &prefix, bool exactMatch)
Unregister handler for prefix.
Definition: httpserver.cpp:701
HTTP_BAD_METHOD
@ HTTP_BAD_METHOD
Definition: protocol.h:25
HTTPRPCTimerInterface
Definition: httprpc.cpp:45
RPCTimerInterface
RPC timer "driver".
Definition: server.h:88
sync.h
RPCUnsetTimerInterface
void RPCUnsetTimerInterface(RPCTimerInterface *iface)
Unset factory function for timers.
Definition: server.cpp:622
uiInterface
CClientUIInterface uiInterface
Definition: init.cpp:101
NullUniValue
const UniValue NullUniValue
Definition: univalue.cpp:78
HTTPRPCTimer::ev
HTTPEvent ev
Definition: httprpc.cpp:42
CHMAC_SHA256
A hasher class for HMAC-SHA-256.
Definition: hmac_sha256.h:14
EventBase
struct event_base * EventBase()
Return evhttp event base.
Definition: httpserver.cpp:540
tableRPC
const CRPCTable tableRPC
Definition: server.cpp:636
mapArgs
std::map< std::string, std::string > mapArgs
Definition: util.cpp:111
HTTP_BAD_REQUEST
@ HTTP_BAD_REQUEST
Definition: protocol.h:21
HTTPRPCTimerInterface::HTTPRPCTimerInterface
HTTPRPCTimerInterface(struct event_base *base)
Definition: httprpc.cpp:48
RPCTimerBase
Opaque base class for timers returned by NewTimerFunc.
Definition: server.h:79
protocol.h
TimingResistantEqual
bool TimingResistantEqual(const T &a, const T &b)
Timing-attack-resistant comparison.
Definition: utilstrencodings.h:132
JSONRequest::id
UniValue id
Definition: server.h:40
JSONRequest
Definition: server.h:37
UniValue::read
bool read(const char *raw, size_t len)
Definition: univalue_read.cpp:253
InterruptHTTPRPC
void InterruptHTTPRPC()
Interrupt HTTP RPC subsystem.
Definition: httprpc.cpp:242
GenerateAuthCookie
bool GenerateAuthCookie(std::string *cookie_out)
Generate a new RPC authentication cookie and write it to disk.
Definition: protocol.cpp:82
chainparams.h
HTTPRPCTimerInterface::NewTimer
RPCTimerBase * NewTimer(boost::function< void(void)> &func, int64_t millis)
Factory function for timers.
Definition: httprpc.cpp:55
guiinterface.h
HTTPRequest::WriteHeader
void WriteHeader(const std::string &hdr, const std::string &value)
Write output header.
Definition: httpserver.cpp:616
UniValue
Definition: univalue.h:19
JSONRequest::params
UniValue params
Definition: server.h:42
DecodeBase64
std::vector< unsigned char > DecodeBase64(const char *p, bool *pfInvalid)
Definition: utilstrencodings.cpp:150
JSONRPCExecBatch
std::string JSONRPCExecBatch(const UniValue &vReq)
Definition: server.cpp:561
BCLog::RPC
@ RPC
Definition: logging.h:47
HTTPRequest::POST
@ POST
Definition: httpserver.h:70
random.h
CService::ToString
std::string ToString() const
Definition: netaddress.cpp:568
CRPCTable::execute
UniValue execute(const std::string &method, const UniValue &params) const
Execute a method.
Definition: server.cpp:569
CHMAC_SHA256::Write
CHMAC_SHA256 & Write(const unsigned char *data, size_t len)
Definition: hmac_sha256.h:24
HTTPRequest
In-flight HTTP request.
Definition: httpserver.h:57
_
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
Definition: guiinterface.h:119
HTTPRPCTimerInterface::Name
const char * Name()
Implementation name.
Definition: httprpc.cpp:51
CClientUIInterface::ThreadSafeMessageBox
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: guiinterface.h:80
HTTPRequest::ReadBody
std::string ReadBody()
Read request body.
Definition: httpserver.cpp:596
StopHTTPRPC
void StopHTTPRPC()
Stop HTTP RPC subsystem.
Definition: httprpc.cpp:247
HexStr
std::string HexStr(const T itbegin, const T itend, bool fSpaces=false)
Definition: utilstrencodings.h:85
JSONRPCError
UniValue JSONRPCError(int code, const std::string &message)
Definition: protocol.cpp:60
LogPrintf
#define LogPrintf(...)
Definition: logging.h:147
HTTPRequest::GetHeader
std::pair< bool, std::string > GetHeader(const std::string &hdr)
Get the request header specified by hdr, or an empty string.
Definition: httpserver.cpp:585
HTTP_UNAUTHORIZED
@ HTTP_UNAUTHORIZED
Definition: protocol.h:22
JSONRequest::parse
void parse(const UniValue &valRequest)
Definition: server.cpp:509
LogPrint
#define LogPrint(category,...)
Definition: logging.h:162
UniValue::isArray
bool isArray() const
Definition: univalue.h:83
CClientUIInterface::MSG_ERROR
@ MSG_ERROR
Definition: guiinterface.h:76
JSONRequest::strMethod
std::string strMethod
Definition: server.h:41
httprpc.h
HTTP_OK
@ HTTP_OK
Definition: protocol.h:20
UniValue::get_int
int get_int() const
Definition: univalue.cpp:316
HTTPEvent::trigger
void trigger(struct timeval *tv)
Trigger the event.
Definition: httpserver.cpp:564
HTTPEvent
Event class.
Definition: httpserver.h:130
HTTPRPCTimer::HTTPRPCTimer
HTTPRPCTimer(struct event_base *eventBase, boost::function< void(void)> &func, int64_t millis)
Definition: httprpc.cpp:33
JSONRPCReply
std::string JSONRPCReply(const UniValue &result, const UniValue &error, const UniValue &id)
Definition: protocol.cpp:54
HTTP_NOT_FOUND
@ HTTP_NOT_FOUND
Definition: protocol.h:24
MilliSleep
void MilliSleep(int64_t n)
Definition: utiltime.cpp:45
base58.h
HTTPRequest::GetRequestMethod
RequestMethod GetRequestMethod()
Get request method.
Definition: httpserver.cpp:674
utilstrencodings.h
find_value
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:279
httpserver.h
UniValue::get_array
const UniValue & get_array() const
Definition: univalue.cpp:353
server.h
mapMultiArgs
std::map< std::string, std::vector< std::string > > mapMultiArgs
Definition: util.cpp:112
RegisterHTTPHandler
void RegisterHTTPHandler(const std::string &prefix, bool exactMatch, const HTTPRequestHandler &handler)
Register handler for prefix.
Definition: httpserver.cpp:695
HTTPRequest::WriteReply
void WriteReply(int nStatus, const std::string &strReply="")
Write HTTP reply.
Definition: httpserver.cpp:628
util.h
RPC_PARSE_ERROR
@ RPC_PARSE_ERROR
Definition: protocol.h:37
RPCSetTimerInterface
void RPCSetTimerInterface(RPCTimerInterface *iface)
Set factory function for timers.
Definition: server.cpp:618
UniValue::isObject
bool isObject() const
Definition: univalue.h:84
HTTPRPCTimerInterface::base
struct event_base * base
Definition: httprpc.cpp:60
HTTP_INTERNAL_SERVER_ERROR
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:26