LCOV - code coverage report
Current view: top level - src - httprpc.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 177 211 83.9 %
Date: 2021-06-29 14:35:33 Functions: 13 13 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 57 78 73.1 %

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

Generated by: LCOV version 1.14