diff options
Diffstat (limited to 'src/httprpc.cpp')
| -rw-r--r-- | src/httprpc.cpp | 124 |
1 files changed, 68 insertions, 56 deletions
diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 2920aa26f..dbd09595c 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -1,10 +1,14 @@ +// Copyright (c) 2015-2016 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + #include "httprpc.h" #include "base58.h" #include "chainparams.h" #include "httpserver.h" -#include "rpcprotocol.h" -#include "rpcserver.h" +#include "rpc/protocol.h" +#include "rpc/server.h" #include "random.h" #include "sync.h" #include "util.h" @@ -12,18 +16,19 @@ #include "ui_interface.h" #include "crypto/hmac_sha256.h" #include <stdio.h> -#include "utilstrencodings.h" #include <boost/algorithm/string.hpp> // boost::trim -#include <boost/foreach.hpp> //BOOST_FOREACH + +/** WWW-Authenticate to present with 401 Unauthorized response */ +static const char* WWW_AUTH_HEADER_DATA = "Basic realm=\"jsonrpc\""; /** Simple one-shot callback timer to be used by the RPC mechanism to e.g. - * re-lock the wellet. + * re-lock the wallet. */ class HTTPRPCTimer : public RPCTimerBase { public: - HTTPRPCTimer(struct event_base* eventBase, boost::function<void(void)>& func, int64_t millis) : + HTTPRPCTimer(struct event_base* eventBase, std::function<void(void)>& func, int64_t millis) : ev(eventBase, false, func) { struct timeval tv; @@ -38,14 +43,14 @@ private: class HTTPRPCTimerInterface : public RPCTimerInterface { public: - HTTPRPCTimerInterface(struct event_base* base) : base(base) + explicit HTTPRPCTimerInterface(struct event_base* _base) : base(_base) { } - const char* Name() + const char* Name() override { return "HTTP"; } - RPCTimerBase* NewTimer(boost::function<void(void)>& func, int64_t millis) + RPCTimerBase* NewTimer(std::function<void(void)>& func, int64_t millis) override { return new HTTPRPCTimer(base, func, millis); } @@ -57,7 +62,7 @@ private: /* Pre-base64-encoded authentication token */ static std::string strRPCUserColonPass; /* Stored RPC timer interface (for unregistration) */ -static HTTPRPCTimerInterface* httpRPCTimerInterface = 0; +static std::unique_ptr<HTTPRPCTimerInterface> httpRPCTimerInterface; static void JSONErrorReply(HTTPRequest* req, const UniValue& objError, const UniValue& id) { @@ -86,41 +91,38 @@ static bool multiUserAuthorized(std::string strUserPass) std::string strUser = strUserPass.substr(0, strUserPass.find(":")); std::string strPass = strUserPass.substr(strUserPass.find(":") + 1); - if (mapMultiArgs.count("-rpcauth") > 0) { + for (const std::string& strRPCAuth : gArgs.GetArgs("-rpcauth")) { //Search for multi-user login/pass "rpcauth" from config - BOOST_FOREACH(std::string strRPCAuth, mapMultiArgs["-rpcauth"]) - { - std::vector<std::string> vFields; - boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); - if (vFields.size() != 3) { - //Incorrect formatting in config file - continue; - } - - std::string strName = vFields[0]; - if (!TimingResistantEqual(strName, strUser)) { - continue; - } - - std::string strSalt = vFields[1]; - std::string strHash = vFields[2]; - - unsigned int KEY_SIZE = 32; - unsigned char *out = new unsigned char[KEY_SIZE]; - - 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); - std::vector<unsigned char> hexvec(out, out+KEY_SIZE); - std::string strHashFromPass = HexStr(hexvec); - - if (TimingResistantEqual(strHashFromPass, strHash)) { - return true; - } + std::vector<std::string> vFields; + boost::split(vFields, strRPCAuth, boost::is_any_of(":$")); + if (vFields.size() != 3) { + //Incorrect formatting in config file + continue; + } + + std::string strName = vFields[0]; + if (!TimingResistantEqual(strName, strUser)) { + continue; + } + + std::string strSalt = vFields[1]; + std::string strHash = vFields[2]; + + static const unsigned int KEY_SIZE = 32; + unsigned char out[KEY_SIZE]; + + 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); + std::vector<unsigned char> hexvec(out, out+KEY_SIZE); + std::string strHashFromPass = HexStr(hexvec); + + if (TimingResistantEqual(strHashFromPass, strHash)) { + return true; } } return false; } -static bool RPCAuthorized(const std::string& strAuth) +static bool RPCAuthorized(const std::string& strAuth, std::string& strAuthUsernameOut) { if (strRPCUserColonPass.empty()) // Belt-and-suspenders measure if InitRPCAuthentication was not called return false; @@ -129,7 +131,10 @@ static bool RPCAuthorized(const std::string& strAuth) std::string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); std::string strUserPass = DecodeBase64(strUserPass64); - + + if (strUserPass.find(":") != std::string::npos) + strAuthUsernameOut = strUserPass.substr(0, strUserPass.find(":")); + //Check if authorized under single-user field if (TimingResistantEqual(strUserPass, strRPCUserColonPass)) { return true; @@ -147,11 +152,13 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) // Check authorization std::pair<bool, std::string> authHeader = req->GetHeader("authorization"); if (!authHeader.first) { + req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); req->WriteReply(HTTP_UNAUTHORIZED); return false; } - if (!RPCAuthorized(authHeader.second)) { + JSONRPCRequest jreq; + if (!RPCAuthorized(authHeader.second, jreq.authUser)) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", req->GetPeer().ToString()); /* Deter brute-forcing @@ -159,30 +166,33 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) shouldn't have their RPC port exposed. */ MilliSleep(250); + req->WriteHeader("WWW-Authenticate", WWW_AUTH_HEADER_DATA); req->WriteReply(HTTP_UNAUTHORIZED); return false; } - JSONRequest jreq; try { // Parse request UniValue valRequest; if (!valRequest.read(req->ReadBody())) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + // Set the URI + jreq.URI = req->GetURI(); + std::string strReply; // singleton request if (valRequest.isObject()) { jreq.parse(valRequest); - UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + UniValue result = tableRPC.execute(jreq); // Send reply strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests } else if (valRequest.isArray()) - strReply = JSONRPCExecBatch(valRequest.get_array()); + strReply = JSONRPCExecBatch(jreq, valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); @@ -200,7 +210,7 @@ static bool HTTPReq_JSONRPC(HTTPRequest* req, const std::string &) static bool InitRPCAuthentication() { - if (mapArgs["-rpcpassword"] == "") + if (gArgs.GetArg("-rpcpassword", "") == "") { LogPrintf("No rpcpassword set - using random cookie authentication\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { @@ -210,38 +220,40 @@ static bool InitRPCAuthentication() return false; } } else { - 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."); - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + 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"); + strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } return true; } bool StartHTTPRPC() { - LogPrint("rpc", "Starting HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Starting HTTP RPC server\n"); if (!InitRPCAuthentication()) return false; RegisterHTTPHandler("/", true, HTTPReq_JSONRPC); - +#ifdef ENABLE_WALLET + // ifdef can be removed once we switch to better endpoint support and API versioning + RegisterHTTPHandler("/wallet/", false, HTTPReq_JSONRPC); +#endif assert(EventBase()); - httpRPCTimerInterface = new HTTPRPCTimerInterface(EventBase()); - RPCRegisterTimerInterface(httpRPCTimerInterface); + httpRPCTimerInterface = MakeUnique<HTTPRPCTimerInterface>(EventBase()); + RPCSetTimerInterface(httpRPCTimerInterface.get()); return true; } void InterruptHTTPRPC() { - LogPrint("rpc", "Interrupting HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Interrupting HTTP RPC server\n"); } void StopHTTPRPC() { - LogPrint("rpc", "Stopping HTTP RPC server\n"); + LogPrint(BCLog::RPC, "Stopping HTTP RPC server\n"); UnregisterHTTPHandler("/", true); if (httpRPCTimerInterface) { - RPCUnregisterTimerInterface(httpRPCTimerInterface); - delete httpRPCTimerInterface; - httpRPCTimerInterface = 0; + RPCUnsetTimerInterface(httpRPCTimerInterface.get()); + httpRPCTimerInterface.reset(); } } |