You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

httprpc.cpp 8.3KB

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