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/fs.h> 15 #include <util/fs_helpers.h> 16 #include <util/strencodings.h> 17 #include <util/string.h> 18 #include <walletinitinterface.h> 19 20 #include <algorithm> 21 #include <iterator> 22 #include <map> 23 #include <memory> 24 #include <optional> 25 #include <set> 26 #include <string> 27 #include <vector> 28 29 using util::SplitString; 30 using util::TrimStringView; 31 32 /** WWW-Authenticate to present with 401 Unauthorized response */ 33 static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; 34 35 /** Simple one-shot callback timer to be used by the RPC mechanism to e.g. 36 * re-lock the wallet. 37 */ 38 class HTTPRPCTimer : public RPCTimerBase 39 { 40 public: 41 HTTPRPCTimer(struct event_base* eventBase, std::function<void()>& func, int64_t millis) : 42 ev(eventBase, false, func) 43 { 44 struct timeval tv; 45 tv.tv_sec = millis/1000; 46 tv.tv_usec = (millis%1000)*1000; 47 ev.trigger(&tv); 48 } 49 private: 50 HTTPEvent ev; 51 }; 52 53 class HTTPRPCTimerInterface : public RPCTimerInterface 54 { 55 public: 56 explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base) 57 { 58 } 59 const char* Name() override 60 { 61 return "HTTP"; 62 } 63 RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override 64 { 65 return new HTTPRPCTimer(base, func, millis); 66 } 67 private: 68 struct event_base* base; 69 }; 70 71 72 /* Pre-base64-encoded authentication token */ 73 static std::string strRPCUserColonPass; 74 /* Stored RPC timer interface (for unregistration) */ 75 static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; 76 /* List of -rpcauth values */ 77 static std::vector<std::vector<std::string>> g_rpcauth; 78 /* RPC Auth Whitelist */ 79 static std::map<std::string, std::set<std::string>> g_rpc_whitelist; 80 static bool g_rpc_whitelist_default = false; 81 82 static void JSONErrorReply(HTTPRequest* req, UniValue objError, const JSONRPCRequest& jreq) 83 { 84 // Sending HTTP errors is a legacy JSON-RPC behavior. 85 Assume(jreq.m_json_version != JSONRPCVersion::V2); 86 87 // Send error reply from json-rpc error object 88 int nStatus = HTTP_INTERNAL_SERVER_ERROR; 89 int code = objError.find_value("code").getInt<int>(); 90 91 if (code == RPC_INVALID_REQUEST) 92 nStatus = HTTP_BAD_REQUEST; 93 else if (code == RPC_METHOD_NOT_FOUND) 94 nStatus = HTTP_NOT_FOUND; 95 96 std::string strReply = JSONRPCReplyObj(NullUniValue, std::move(objError), jreq.id, jreq.m_json_version).write() + "\n"; 97 98 req->WriteHeader("Content-Type", "application/json"); 99 req->WriteReply(nStatus, strReply); 100 } 101 102 //This function checks username and password against -rpcauth 103 //entries from config file. 104 static bool multiUserAuthorized(std::string strUserPass) 105 { 106 if (strUserPass.find(':') == std::string::npos) { 107 return false; 108 } 109 std::string strUser = strUserPass.substr(0, strUserPass.find(':')); 110 std::string strPass = strUserPass.substr(strUserPass.find(':') + 1); 111 112 for (const auto& vFields : g_rpcauth) { 113 std::string strName = vFields[0]; 114 if (!TimingResistantEqual(strName, strUser)) { 115 continue; 116 } 117 118 std::string strSalt = vFields[1]; 119 std::string strHash = vFields[2]; 120 121 static const unsigned int KEY_SIZE = 32; 122 unsigned char out[KEY_SIZE]; 123 124 CHMAC_SHA256(reinterpret_cast<const unsigned char*>(strSalt.data()), strSalt.size()).Write(reinterpret_cast<const unsigned char*>(strPass.data()), strPass.size()).Finalize(out); 125 std::vector<unsigned char> hexvec(out, out+KEY_SIZE); 126 std::string strHashFromPass = HexStr(hexvec); 127 128 if (TimingResistantEqual(strHashFromPass, strHash)) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135 static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut) 136 { 137 if (!strAuth.starts_with("Basic ")) 138 return false; 139 std::string_view strUserPass64 = TrimStringView(std::string_view{strAuth}.substr(6)); 140 auto userpass_data = DecodeBase64(strUserPass64); 141 std::string strUserPass; 142 if (!userpass_data) return false; 143 strUserPass.assign(userpass_data->begin(), userpass_data->end()); 144 145 if (strUserPass.find(':') != std::string::npos) 146 strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(':')); 147 148 // Check if authorized under single-user field. 149 // (strRPCUserColonPass is empty when -norpccookiefile is specified). 150 if (!strRPCUserColonPass.empty() && TimingResistantEqual(strUserPass, strRPCUserColonPass)) { 151 return true; 152 } 153 return multiUserAuthorized(strUserPass); 154 } 155 156 static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req) 157 { 158 // JSONRPC handles only POST 159 if (req->GetRequestMethod() != HTTPRequest::POST) { 160 req->WriteReply(HTTP_BAD_METHOD, "JSONRPC server handles only POST requests"); 161 return false; 162 } 163 // Check authorization 164 std::pair<bool, std::string> authHeader = req->GetHeader("authorization"); 165 if (!authHeader.first) { 166 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); 167 req->WriteReply(HTTP_UNAUTHORIZED); 168 return false; 169 } 170 171 JSONRPCRequest jreq; 172 jreq.context = context; 173 jreq.peerAddr = req->GetPeer().ToStringAddrPort(); 174 if (!RPCAuthorized(authHeader.second, jreq.authUser)) { 175 LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", jreq.peerAddr); 176 177 /* Deter brute-forcing 178 If this results in a DoS the user really 179 shouldn't have their RPC port exposed. */ 180 UninterruptibleSleep(std::chrono::milliseconds{250}); 181 182 req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); 183 req->WriteReply(HTTP_UNAUTHORIZED); 184 return false; 185 } 186 187 try { 188 // Parse request 189 UniValue valRequest; 190 if (!valRequest.read(req->ReadBody())) 191 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); 192 193 // Set the URI 194 jreq.URI = req->GetURI(); 195 196 UniValue reply; 197 bool user_has_whitelist = g_rpc_whitelist.count(jreq.authUser); 198 if (!user_has_whitelist && g_rpc_whitelist_default) { 199 LogPrintf("RPC User %s not allowed to call any methods\n", jreq.authUser); 200 req->WriteReply(HTTP_FORBIDDEN); 201 return false; 202 203 // singleton request 204 } else if (valRequest.isObject()) { 205 jreq.parse(valRequest); 206 if (user_has_whitelist && !g_rpc_whitelist[jreq.authUser].count(jreq.strMethod)) { 207 LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, jreq.strMethod); 208 req->WriteReply(HTTP_FORBIDDEN); 209 return false; 210 } 211 212 // Legacy 1.0/1.1 behavior is for failed requests to throw 213 // exceptions which return HTTP errors and RPC errors to the client. 214 // 2.0 behavior is to catch exceptions and return HTTP success with 215 // RPC errors, as long as there is not an actual HTTP server error. 216 const bool catch_errors{jreq.m_json_version == JSONRPCVersion::V2}; 217 reply = JSONRPCExec(jreq, catch_errors); 218 219 if (jreq.IsNotification()) { 220 // Even though we do execute notifications, we do not respond to them 221 req->WriteReply(HTTP_NO_CONTENT); 222 return true; 223 } 224 225 // array of requests 226 } else if (valRequest.isArray()) { 227 // Check authorization for each request's method 228 if (user_has_whitelist) { 229 for (unsigned int reqIdx = 0; reqIdx < valRequest.size(); reqIdx++) { 230 if (!valRequest[reqIdx].isObject()) { 231 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); 232 } else { 233 const UniValue& request = valRequest[reqIdx].get_obj(); 234 // Parse method 235 std::string strMethod = request.find_value("method").get_str(); 236 if (!g_rpc_whitelist[jreq.authUser].count(strMethod)) { 237 LogPrintf("RPC User %s not allowed to call method %s\n", jreq.authUser, strMethod); 238 req->WriteReply(HTTP_FORBIDDEN); 239 return false; 240 } 241 } 242 } 243 } 244 245 // Execute each request 246 reply = UniValue::VARR; 247 for (size_t i{0}; i < valRequest.size(); ++i) { 248 // Batches never throw HTTP errors, they are always just included 249 // in "HTTP OK" responses. Notifications never get any response. 250 UniValue response; 251 try { 252 jreq.parse(valRequest[i]); 253 response = JSONRPCExec(jreq, /*catch_errors=*/true); 254 } catch (UniValue& e) { 255 response = JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version); 256 } catch (const std::exception& e) { 257 response = JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id, jreq.m_json_version); 258 } 259 if (!jreq.IsNotification()) { 260 reply.push_back(std::move(response)); 261 } 262 } 263 // Return no response for an all-notification batch, but only if the 264 // batch request is non-empty. Technically according to the JSON-RPC 265 // 2.0 spec, an empty batch request should also return no response, 266 // However, if the batch request is empty, it means the request did 267 // not contain any JSON-RPC version numbers, so returning an empty 268 // response could break backwards compatibility with old RPC clients 269 // relying on previous behavior. Return an empty array instead of an 270 // empty response in this case to favor being backwards compatible 271 // over complying with the JSON-RPC 2.0 spec in this case. 272 if (reply.size() == 0 && valRequest.size() > 0) { 273 req->WriteReply(HTTP_NO_CONTENT); 274 return true; 275 } 276 } 277 else 278 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); 279 280 req->WriteHeader("Content-Type", "application/json"); 281 req->WriteReply(HTTP_OK, reply.write() + "\n"); 282 } catch (UniValue& e) { 283 JSONErrorReply(req, std::move(e), jreq); 284 return false; 285 } catch (const std::exception& e) { 286 JSONErrorReply(req, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq); 287 return false; 288 } 289 return true; 290 } 291 292 static bool InitRPCAuthentication() 293 { 294 if (gArgs.GetArg("-rpcpassword", "") == "") 295 { 296 std::optional<fs::perms> cookie_perms{std::nullopt}; 297 auto cookie_perms_arg{gArgs.GetArg("-rpccookieperms")}; 298 if (cookie_perms_arg) { 299 auto perm_opt = InterpretPermString(*cookie_perms_arg); 300 if (!perm_opt) { 301 LogError("Invalid -rpccookieperms=%s; must be one of 'owner', 'group', or 'all'.", *cookie_perms_arg); 302 return false; 303 } 304 cookie_perms = *perm_opt; 305 } 306 307 assert(strRPCUserColonPass.empty()); // Only support initializing once 308 if (!GenerateAuthCookie(&strRPCUserColonPass, cookie_perms)) { 309 return false; 310 } 311 if (strRPCUserColonPass.empty()) { 312 LogInfo("RPC authentication cookie file generation is disabled."); 313 } else { 314 LogInfo("Using random cookie authentication."); 315 } 316 } else { 317 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"); 318 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); 319 } 320 321 if (!gArgs.GetArgs("-rpcauth").empty()) { 322 LogInfo("Using rpcauth authentication.\n"); 323 for (const std::string& rpcauth : gArgs.GetArgs("-rpcauth")) { 324 std::vector<std::string> fields{SplitString(rpcauth, ':')}; 325 const std::vector<std::string> salt_hmac{SplitString(fields.back(), '$')}; 326 if (fields.size() == 2 && salt_hmac.size() == 2) { 327 fields.pop_back(); 328 fields.insert(fields.end(), salt_hmac.begin(), salt_hmac.end()); 329 g_rpcauth.push_back(fields); 330 } else { 331 LogPrintf("Invalid -rpcauth argument.\n"); 332 return false; 333 } 334 } 335 } 336 337 g_rpc_whitelist_default = gArgs.GetBoolArg("-rpcwhitelistdefault", !gArgs.GetArgs("-rpcwhitelist").empty()); 338 for (const std::string& strRPCWhitelist : gArgs.GetArgs("-rpcwhitelist")) { 339 auto pos = strRPCWhitelist.find(':'); 340 std::string strUser = strRPCWhitelist.substr(0, pos); 341 bool intersect = g_rpc_whitelist.count(strUser); 342 std::set<std::string>& whitelist = g_rpc_whitelist[strUser]; 343 if (pos != std::string::npos) { 344 std::string strWhitelist = strRPCWhitelist.substr(pos + 1); 345 std::vector<std::string> whitelist_split = SplitString(strWhitelist, ", "); 346 std::set<std::string> new_whitelist{ 347 std::make_move_iterator(whitelist_split.begin()), 348 std::make_move_iterator(whitelist_split.end())}; 349 if (intersect) { 350 std::set<std::string> tmp_whitelist; 351 std::set_intersection(new_whitelist.begin(), new_whitelist.end(), 352 whitelist.begin(), whitelist.end(), std::inserter(tmp_whitelist, tmp_whitelist.end())); 353 new_whitelist = std::move(tmp_whitelist); 354 } 355 whitelist = std::move(new_whitelist); 356 } 357 } 358 359 return true; 360 } 361 362 bool StartHTTPRPC(const std::any& context) 363 { 364 LogDebug(BCLog::RPC, "Starting HTTP RPC server\n"); 365 if (!InitRPCAuthentication()) 366 return false; 367 368 auto handle_rpc = [context](HTTPRequest* req, const std::string&) { return HTTPReq_JSONRPC(context, req); }; 369 RegisterHTTPHandler("/", true, handle_rpc); 370 if (g_wallet_init_interface.HasWalletSupport()) { 371 RegisterHTTPHandler("/wallet/", false, handle_rpc); 372 } 373 struct event_base* eventBase = EventBase(); 374 assert(eventBase); 375 httpRPCTimerInterface = std::make_unique<HTTPRPCTimerInterface>(eventBase); 376 RPCSetTimerInterface(httpRPCTimerInterface.get()); 377 return true; 378 } 379 380 void InterruptHTTPRPC() 381 { 382 LogDebug(BCLog::RPC, "Interrupting HTTP RPC server\n"); 383 } 384 385 void StopHTTPRPC() 386 { 387 LogDebug(BCLog::RPC, "Stopping HTTP RPC server\n"); 388 UnregisterHTTPHandler("/", true); 389 if (g_wallet_init_interface.HasWalletSupport()) { 390 UnregisterHTTPHandler("/wallet/", false); 391 } 392 if (httpRPCTimerInterface) { 393 RPCUnsetTimerInterface(httpRPCTimerInterface.get()); 394 httpRPCTimerInterface.reset(); 395 } 396 }