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