LCOV - code coverage report
Current view: top level - src - httprpc.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 160 186 86.0 %
Date: 2020-09-26 01:30:44 Functions: 24 24 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2015-2020 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 <crypto/hmac_sha256.h>
       9             : #include <httpserver.h>
      10             : #include <rpc/protocol.h>
      11             : #include <rpc/server.h>
      12             : #include <util/strencodings.h>
      13             : #include <util/system.h>
      14             : #include <util/translation.h>
      15             : #include <walletinitinterface.h>
      16             : 
      17             : #include <algorithm>
      18             : #include <iterator>
      19             : #include <map>
      20             : #include <memory>
      21             : #include <stdio.h>
      22             : #include <set>
      23             : #include <string>
      24             : 
      25             : #include <boost/algorithm/string.hpp> // boost::trim
      26             : 
      27             : /** WWW-Authenticate to present with 401 Unauthorized response */
      28             : static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\"";
      29             : 
      30             : /** Simple one-shot callback timer to be used by the RPC mechanism to e.g.
      31             :  * re-lock the wallet.
      32             :  */
      33         117 : class HTTPRPCTimer : public RPCTimerBase
      34             : {
      35             : public:
      36          78 :     HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) :
      37          39 :         ev(eventBase, false, func)
      38          78 :     {
      39          39 :         struct timeval tv;
      40          39 :         tv.tv_sec = millis/1000;
      41          39 :         tv.tv_usec = (millis%1000)*1000;
      42          39 :         ev.trigger(&tv);
      43          78 :     }
      44             : private:
      45             :     HTTPEvent ev;
      46             : };
      47             : 
      48        1575 : class HTTPRPCTimerInterface : public RPCTimerInterface
      49             : {
      50             : public:
      51        1050 :     explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base)
      52        1050 :     {
      53        1050 :     }
      54          39 :     const char* Name() override
      55             :     {
      56          39 :         return "HTTP";
      57             :     }
      58          39 :     RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override
      59             :     {
      60          39 :         return new HTTPRPCTimer(base, func, millis);
      61           0 :     }
      62             : private:
      63             :     struct event_base* base;
      64             : };
      65             : 
      66             : 
      67             : /* Pre-base64-encoded authentication token */
      68         640 : static std::string strRPCUserColonPass;
      69             : /* Stored RPC timer interface (for unregistration) */
      70         640 : static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface;
      71             : /* RPC Auth Whitelist */
      72         640 : static std::map<std::string, std::set<std::string>> g_rpc_whitelist;
      73             : static bool g_rpc_whitelist_default = false;
      74             : 
      75        1880 : static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id)
      76             : {
      77             :     // Send error reply from json-rpc error object
      78             :     int nStatus = HTTP_INTERNAL_SERVER_ERROR;
      79        1880 :     int code = find_value(objError, "code").get_int();
      80             : 
      81        1880 :     if (code == RPC_INVALID_REQUEST)
      82           0 :         nStatus = HTTP_BAD_REQUEST;
      83        1880 :     else if (code == RPC_METHOD_NOT_FOUND)
      84           9 :         nStatus = HTTP_NOT_FOUND;
      85             : 
      86        1880 :     std::string strReply = JSONRPCReply(NullUniValue, objError, id);
      87             : 
      88        1880 :     req->WriteHeader("Content-Type", "application/json");
      89        1880 :     req->WriteReply(nStatus, strReply);
      90        1880 : }
      91             : 
      92             : //This function checks username and password against -rpcauth
      93             : //entries from config file.
      94          30 : static bool multiUserAuthorized(std::string strUserPass)
      95             : {
      96          30 :     if (strUserPass.find(':') == std::string::npos) {
      97           0 :         return false;
      98             :     }
      99          30 :     std::string strUser = strUserPass.substr(0, strUserPass.find(':'));
     100          30 :     std::string strPass = strUserPass.substr(strUserPass.find(':') + 1);
     101             : 
     102         103 :     for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) {
     103             :         //Search for multi-user login/pass "rpcauth" from config
     104          73 :         std::vector<std::string> vFields;
     105          73 :         boost::split(vFields, strRPCAuth, boost::is_any_of(":$"));
     106          73 :         if (vFields.size() != 3) {
     107             :             //Incorrect formatting in config file
     108           0 :             continue;
     109             :         }
     110             : 
     111          73 :         std::string strName = vFields[0];
     112          73 :         if (!TimingResistantEqual(strName, strUser)) {
     113          57 :             continue;
     114             :         }
     115             : 
     116          16 :         std::string strSalt = vFields[1];
     117          16 :         std::string strHash = vFields[2];
     118             : 
     119             :         static const unsigned int KEY_SIZE = 32;
     120          16 :         unsigned char out[KEY_SIZE];
     121             : 
     122          16 :         CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.data()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.data()), strPass.size()).Finalize(out);
     123          16 :         std::vector<unsigned char> hexvec(out, out+KEY_SIZE);
     124          16 :         std::string strHashFromPass = HexStr(hexvec);
     125             : 
     126          16 :         if (TimingResistantEqual(strHashFromPass, strHash)) {
     127          13 :             return true;
     128             :         }
     129          73 :     }
     130          17 :     return false;
     131          30 : }
     132             : 
     133       79024 : static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut)
     134             : {
     135       79024 :     if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called
     136           0 :         return false;
     137       79024 :     if (strAuth.substr(0, 6) != "Basic ")
     138           1 :         return false;
     139       79023 :     std::string strUserPass64 = strAuth.substr(6);
     140       79023 :     boost::trim(strUserPass64);
     141       79023 :     std::string strUserPass = DecodeBase64(strUserPass64);
     142             : 
     143       79023 :     if (strUserPass.find(':') != std::string::npos)
     144       79023 :         strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':'));
     145             : 
     146             :     //Check if authorized under single-user field
     147       79023 :     if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) {
     148       78993 :         return true;
     149             :     }
     150          30 :     return multiUserAuthorized(strUserPass);
     151       79024 : }
     152             : 
     153       79024 : static bool HTTPReq_JSONRPC(const util::Ref& context, HTTPRequest* req)
     154             : {
     155             :     // JSONRPC handles only POST
     156       79024 :     if (req->GetRequestMethod() != HTTPRequest::POST) {
     157           0 :         req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests");
     158           0 :         return false;
     159             :     }
     160             :     // Check authorization
     161       79024 :     std::pair<bool, std::string> authHeader = req->GetHeader("authorization");
     162       79024 :     if (!authHeader.first) {
     163           0 :         req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
     164           0 :         req->WriteReply(HTTP_UNAUTHORIZED);
     165           0 :         return false;
     166             :     }
     167             : 
     168       79024 :     JSONRPCRequest jreq(context);
     169       79024 :     jreq.peerAddr = req->GetPeer().ToString();
     170       79024 :     if (!RPCAuthorized(authHeader.second, jreq.authUser)) {
     171          18 :         LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr);
     172             : 
     173             :         /* Deter brute-forcing
     174             :            If this results in a DoS the user really
     175             :            shouldn't have their RPC port exposed. */
     176          18 :         UninterruptibleSleep(std::chrono::milliseconds{250});
     177             : 
     178          18 :         req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA);
     179          18 :         req->WriteReply(HTTP_UNAUTHORIZED);
     180          18 :         return false;
     181             :     }
     182             : 
     183             :     try {
     184             :         // Parse request
     185       79006 :         UniValue valRequest;
     186       79006 :         if (!valRequest.read(req->ReadBody()))
     187           0 :             throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
     188             : 
     189             :         // Set the URI
     190       79006 :         jreq.URI = req->GetURI();
     191             : 
     192       79006 :         std::string strReply;
     193       79006 :         bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser);
     194       79006 :         if (!user_has_whitelist && g_rpc_whitelist_default) {
     195           0 :             LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser);
     196           0 :             req->WriteReply(HTTP_FORBIDDEN);
     197           0 :             return false;
     198             : 
     199             :         // singleton request
     200       79006 :         } else if (valRequest.isObject()) {
     201       78993 :             jreq.parse(valRequest);
     202       78993 :             if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) {
     203           5 :                 LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod);
     204           5 :                 req->WriteReply(HTTP_FORBIDDEN);
     205           5 :                 return false;
     206             :             }
     207       78988 :             UniValue result = tableRPC.execute(jreq);
     208             : 
     209             :             // Send reply
     210       77108 :             strReply = JSONRPCReply(result, NullUniValue, jreq.id);
     211             : 
     212             :         // array of requests
     213       79001 :         } else if (valRequest.isArray()) {
     214          13 :             if (user_has_whitelist) {
     215           0 :                 for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) {
     216           0 :                     if (!valRequest[reqIdx].isObject()) {
     217           0 :                         throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
     218             :                     } else {
     219           0 :                         const UniValue& request = valRequest[reqIdx].get_obj();
     220             :                         // Parse method
     221           0 :                         std::string strMethod = find_value(request, "method").get_str();
     222           0 :                         if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) {
     223           0 :                             LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod);
     224           0 :                             req->WriteReply(HTTP_FORBIDDEN);
     225           0 :                             return false;
     226             :                         }
     227           0 :                     }
     228             :                 }
     229             :             }
     230          13 :             strReply = JSONRPCExecBatch(jreq, valRequest.get_array());
     231             :         }
     232             :         else
     233           0 :             throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
     234             : 
     235       77121 :         req->WriteHeader("Content-Type", "application/json");
     236       77121 :         req->WriteReply(HTTP_OK, strReply);
     237       79006 :     } catch (const UniValue& objError) {
     238        1880 :         JSONErrorReply(req, objError, jreq.id);
     239             :         return false;
     240        1880 :     } catch (const std::exception& e) {
     241           0 :         JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
     242             :         return false;
     243        1880 :     }
     244       77121 :     return true;
     245       80904 : }
     246             : 
     247         526 : static bool InitRPCAuthentication()
     248             : {
     249         526 :     if (gArgs.GetArg("-rpcpassword", "") == "")
     250             :     {
     251         525 :         LogPrintf("Using random cookie authentication.\n");
     252         525 :         if (!GenerateAuthCookie(&strRPCUserColonPass)) {
     253           1 :             return false;
     254             :         }
     255             :     } else {
     256           1 :         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");
     257           1 :         strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
     258             :     }
     259         525 :     if (gArgs.GetArg("-rpcauth","") != "")
     260             :     {
     261           3 :         LogPrintf("Using rpcauth authentication.\n");
     262           3 :     }
     263             : 
     264         525 :     g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", gArgs.IsArgSet("-rpcwhitelist"));
     265         533 :     for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) {
     266           8 :         auto pos = strRPCWhitelist.find(':');
     267           8 :         std::string strUser = strRPCWhitelist.substr(0, pos);
     268           8 :         bool intersect = g_rpc_whitelist.count(strUser);
     269           8 :         std::set<std::string>& whitelist = g_rpc_whitelist[strUser];
     270           8 :         if (pos != std::string::npos) {
     271           7 :             std::string strWhitelist = strRPCWhitelist.substr(pos + 1);
     272           7 :             std::set<std::string> new_whitelist;
     273           7 :             boost::split(new_whitelist, strWhitelist, boost::is_any_of(", "));
     274           7 :             if (intersect) {
     275           1 :                 std::set<std::string> tmp_whitelist;
     276           1 :                 std::set_intersection(new_whitelist.begin(), new_whitelist.end(),
     277           1 :                        whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end()));
     278           1 :                 new_whitelist = std::move(tmp_whitelist);
     279           1 :             }
     280           7 :             whitelist = std::move(new_whitelist);
     281           7 :         }
     282           8 :     }
     283             : 
     284         525 :     return true;
     285         526 : }
     286             : 
     287         526 : bool StartHTTPRPC(const util::Ref& context)
     288             : {
     289         526 :     LogPrint(BCLog::RPC, "Starting HTTP RPC server\n");
     290         526 :     if (!InitRPCAuthentication())
     291           1 :         return false;
     292             : 
     293       79024 :     auto handle_rpc = [&context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); };
     294         525 :     RegisterHTTPHandler("/", true, handle_rpc);
     295         525 :     if (g_wallet_init_interface.HasWalletSupport()) {
     296         525 :         RegisterHTTPHandler("/wallet/", false, handle_rpc);
     297         525 :     }
     298         525 :     struct event_base* eventBase = EventBase();
     299         525 :     assert(eventBase);
     300         525 :     httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(eventBase);
     301         525 :     RPCSetTimerInterface(httpRPCTimerInterface.get());
     302             :     return true;
     303         526 : }
     304             : 
     305         529 : void InterruptHTTPRPC()
     306             : {
     307         529 :     LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n");
     308         529 : }
     309             : 
     310         529 : void StopHTTPRPC()
     311             : {
     312         529 :     LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n");
     313         529 :     UnregisterHTTPHandler("/", true);
     314         529 :     if (g_wallet_init_interface.HasWalletSupport()) {
     315         529 :         UnregisterHTTPHandler("/wallet/", false);
     316         529 :     }
     317         529 :     if (httpRPCTimerInterface) {
     318         525 :         RPCUnsetTimerInterface(httpRPCTimerInterface.get());
     319         525 :         httpRPCTimerInterface.reset();
     320         525 :     }
     321         529 : }

Generated by: LCOV version 1.15