Browse Source

Auto merge of #1999 - str4d:1950-random-cookie-rpc-auth, r=ebfull

rpc: Implement random-cookie based authentication

Cherry-picked from bitcoin/bitcoin#6388.

Closes #1950.
metaverse
zkbot 8 years ago
parent
commit
94f427a211
  1. 24
      src/bitcoin-cli.cpp
  2. 67
      src/rpcprotocol.cpp
  3. 10
      src/rpcprotocol.h
  4. 34
      src/rpcserver.cpp

24
src/bitcoin-cli.cpp

@ -96,12 +96,6 @@ static bool AppInitRPC(int argc, char* argv[])
Object CallRPC(const string& strMethod, const Array& params)
{
if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
throw runtime_error(strprintf(
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
"If the file does not exist, create it with owner-readable-only file permissions."),
GetConfigFile().string().c_str()));
// Connect to localhost
bool fUseSSL = GetBoolArg("-rpcssl", false);
boost::asio::io_service io_service;
@ -115,10 +109,24 @@ Object CallRPC(const string& strMethod, const Array& params)
if (!fConnected)
throw CConnectionFailed("couldn't connect to server");
// Find credentials to use
std::string strRPCUserColonPass;
if (mapArgs["-rpcpassword"] == "") {
// Try fall back to cookie-based authentication if no password is provided
if (!GetAuthCookie(&strRPCUserColonPass)) {
throw runtime_error(strprintf(
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
"If the file does not exist, create it with owner-readable-only file permissions."),
GetConfigFile().string().c_str()));
}
} else {
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
}
// HTTP basic authentication
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
map<string, string> mapRequestHeaders;
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
mapRequestHeaders["Authorization"] = string("Basic ") + EncodeBase64(strRPCUserColonPass);
// Send request
string strRequest = JSONRPCRequest(strMethod, params, 1);

67
src/rpcprotocol.cpp

@ -6,6 +6,7 @@
#include "rpcprotocol.h"
#include "clientversion.h"
#include "random.h"
#include "tinyformat.h"
#include "util.h"
#include "utilstrencodings.h"
@ -13,6 +14,7 @@
#include "version.h"
#include <stdint.h>
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
@ -288,3 +290,68 @@ Object JSONRPCError(int code, const string& message)
error.push_back(Pair("message", message));
return error;
}
/** Username used when cookie authentication is in use (arbitrary, only for
* recognizability in debugging/logging purposes)
*/
static const std::string COOKIEAUTH_USER = "__cookie__";
/** Default name for auth cookie file */
static const std::string COOKIEAUTH_FILE = ".cookie";
boost::filesystem::path GetAuthCookieFile()
{
boost::filesystem::path path(GetArg("-rpccookiefile", COOKIEAUTH_FILE));
if (!path.is_complete()) path = GetDataDir() / path;
return path;
}
bool GenerateAuthCookie(std::string *cookie_out)
{
unsigned char rand_pwd[32];
GetRandBytes(rand_pwd, 32);
std::string cookie = COOKIEAUTH_USER + ":" + EncodeBase64(&rand_pwd[0],32);
/** the umask determines what permissions are used to create this file -
* these are set to 077 in init.cpp unless overridden with -sysperms.
*/
std::ofstream file;
boost::filesystem::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", filepath.string());
return false;
}
file << cookie;
file.close();
LogPrintf("Generated RPC authentication cookie %s\n", filepath.string());
if (cookie_out)
*cookie_out = cookie;
return true;
}
bool GetAuthCookie(std::string *cookie_out)
{
std::ifstream file;
std::string cookie;
boost::filesystem::path filepath = GetAuthCookieFile();
file.open(filepath.string().c_str());
if (!file.is_open())
return false;
std::getline(file, cookie);
file.close();
if (cookie_out)
*cookie_out = cookie;
return true;
}
void DeleteAuthCookie()
{
try {
boost::filesystem::remove(GetAuthCookieFile());
} catch (const boost::filesystem::filesystem_error& e) {
LogPrintf("%s: Unable to remove random auth cookie file: %s\n", __func__, e.what());
}
}

10
src/rpcprotocol.h

@ -14,6 +14,7 @@
#include <boost/iostreams/stream.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <boost/filesystem.hpp>
#include "json/json_spirit_reader_template.h"
#include "json/json_spirit_utils.h"
@ -165,4 +166,13 @@ json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json
std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
json_spirit::Object JSONRPCError(int code, const std::string& message);
/** Get name of RPC authentication cookie file */
boost::filesystem::path GetAuthCookieFile();
/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
void DeleteAuthCookie();
#endif // BITCOIN_RPCPROTOCOL_H

34
src/rpcserver.cpp

@ -621,28 +621,18 @@ void StartRPCThreads()
strAllowed += subnet.ToString() + " ";
LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
if (((mapArgs["-rpcpassword"] == "") ||
(mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword())
if (mapArgs["-rpcpassword"] == "")
{
unsigned char rand_pwd[32];
GetRandBytes(rand_pwd, 32);
uiInterface.ThreadSafeMessageBox(strprintf(
_("To use zcashd you must set an rpcpassword in the configuration file:\n"
"%s\n"
"It is recommended you use the following random password:\n"
"rpcuser=zcashrpc\n"
"rpcpassword=%s\n"
"(you do not need to remember this password)\n"
"The username and password MUST NOT be the same.\n"
"If the file does not exist, create it with owner-readable-only file permissions.\n"
"It is also recommended to set alertnotify so you are notified of problems;\n"
"for example: alertnotify=echo %%s | mail -s \"Zcash Alert\" admin@foo.com\n"),
GetConfigFile().string(),
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)),
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE);
StartShutdown();
return;
LogPrintf("No rpcpassword set - using random cookie authentication\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
uiInterface.ThreadSafeMessageBox(
_("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode
"", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
} else {
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
}
assert(rpc_io_service == NULL);
@ -808,6 +798,8 @@ void StopRPCThreads()
}
deadlineTimers.clear();
DeleteAuthCookie();
rpc_io_service->stop();
g_rpcSignals.Stopped();
if (rpc_worker_group != NULL)

Loading…
Cancel
Save