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

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