mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 13:03:17 +01:00
Merge pull request #6388
0937290
doc: mention RPC random cookie authentication in release notes (Wladimir J. van der Laan)71cbeaa
rpc: Implement random-cookie based authentication (Wladimir J. van der Laan)
This commit is contained in:
commit
fd5dfda939
@ -4,6 +4,21 @@ release-notes at release time)
|
|||||||
Notable changes
|
Notable changes
|
||||||
===============
|
===============
|
||||||
|
|
||||||
|
Random-cookie RPC authentication
|
||||||
|
---------------------------------
|
||||||
|
|
||||||
|
When no `-rpcpassword` is specified, the daemon now uses a special 'cookie'
|
||||||
|
file for authentication. This file is generated with random content when the
|
||||||
|
daemon starts, and deleted when it exits. Its contents are used as
|
||||||
|
authentication token. Read access to this file controls who can access through
|
||||||
|
RPC. By default it is stored in the data directory but its location can be
|
||||||
|
overridden with the option `-rpccookiefile`.
|
||||||
|
|
||||||
|
This is similar to Tor's CookieAuthentication: see
|
||||||
|
https://www.torproject.org/docs/tor-manual.html.en
|
||||||
|
|
||||||
|
This allows running bitcoind without having to do any manual configuration.
|
||||||
|
|
||||||
Example header
|
Example header
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
@ -97,12 +97,6 @@ static bool AppInitRPC(int argc, char* argv[])
|
|||||||
|
|
||||||
UniValue CallRPC(const string& strMethod, const UniValue& params)
|
UniValue CallRPC(const string& strMethod, const UniValue& 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
|
// Connect to localhost
|
||||||
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
||||||
boost::asio::io_service io_service;
|
boost::asio::io_service io_service;
|
||||||
@ -116,10 +110,24 @@ UniValue CallRPC(const string& strMethod, const UniValue& params)
|
|||||||
if (!fConnected)
|
if (!fConnected)
|
||||||
throw CConnectionFailed("couldn't connect to server");
|
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
|
// HTTP basic authentication
|
||||||
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
|
||||||
map<string, string> mapRequestHeaders;
|
map<string, string> mapRequestHeaders;
|
||||||
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
|
mapRequestHeaders["Authorization"] = string("Basic ") + EncodeBase64(strRPCUserColonPass);
|
||||||
|
|
||||||
// Send request
|
// Send request
|
||||||
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "rpcprotocol.h"
|
#include "rpcprotocol.h"
|
||||||
|
|
||||||
#include "clientversion.h"
|
#include "clientversion.h"
|
||||||
|
#include "random.h"
|
||||||
#include "tinyformat.h"
|
#include "tinyformat.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "utilstrencodings.h"
|
#include "utilstrencodings.h"
|
||||||
@ -13,6 +14,7 @@
|
|||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
@ -287,3 +289,68 @@ UniValue JSONRPCError(int code, const string& message)
|
|||||||
error.push_back(Pair("message", message));
|
error.push_back(Pair("message", message));
|
||||||
return error;
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include <boost/iostreams/stream.hpp>
|
#include <boost/iostreams/stream.hpp>
|
||||||
#include <boost/asio.hpp>
|
#include <boost/asio.hpp>
|
||||||
#include <boost/asio/ssl.hpp>
|
#include <boost/asio/ssl.hpp>
|
||||||
|
#include <boost/filesystem.hpp>
|
||||||
|
|
||||||
#include "univalue/univalue.h"
|
#include "univalue/univalue.h"
|
||||||
|
|
||||||
@ -165,4 +166,13 @@ UniValue JSONRPCReplyObj(const UniValue& result, const UniValue& error, const Un
|
|||||||
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
|
std::string JSONRPCReply(const UniValue& result, const UniValue& error, const UniValue& id);
|
||||||
UniValue JSONRPCError(int code, const std::string& message);
|
UniValue 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
|
#endif // BITCOIN_RPCPROTOCOL_H
|
||||||
|
@ -597,27 +597,18 @@ void StartRPCThreads()
|
|||||||
strAllowed += subnet.ToString() + " ";
|
strAllowed += subnet.ToString() + " ";
|
||||||
LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
|
LogPrint("rpc", "Allowing RPC connections from: %s\n", strAllowed);
|
||||||
|
|
||||||
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
|
|
||||||
if (mapArgs["-rpcpassword"] == "")
|
if (mapArgs["-rpcpassword"] == "")
|
||||||
{
|
{
|
||||||
unsigned char rand_pwd[32];
|
LogPrintf("No rpcpassword set - using random cookie authentication\n");
|
||||||
GetRandBytes(rand_pwd, 32);
|
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
|
||||||
uiInterface.ThreadSafeMessageBox(strprintf(
|
uiInterface.ThreadSafeMessageBox(
|
||||||
_("To use bitcoind, or the -server option to bitcoin-qt, you must set an rpcpassword in the configuration file:\n"
|
_("Error: A fatal internal error occured, see debug.log for details"), // Same message as AbortNode
|
||||||
"%s\n"
|
"", CClientUIInterface::MSG_ERROR);
|
||||||
"It is recommended you use the following random password:\n"
|
StartShutdown();
|
||||||
"rpcuser=bitcoinrpc\n"
|
return;
|
||||||
"rpcpassword=%s\n"
|
}
|
||||||
"(you do not need to remember this password)\n"
|
} else {
|
||||||
"The username and password MUST NOT be the same.\n"
|
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
|
||||||
"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 \"Bitcoin Alert\" admin@foo.com\n"),
|
|
||||||
GetConfigFile().string(),
|
|
||||||
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)),
|
|
||||||
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE);
|
|
||||||
StartShutdown();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(rpc_io_service == NULL);
|
assert(rpc_io_service == NULL);
|
||||||
@ -768,6 +759,8 @@ void StopRPCThreads()
|
|||||||
}
|
}
|
||||||
deadlineTimers.clear();
|
deadlineTimers.clear();
|
||||||
|
|
||||||
|
DeleteAuthCookie();
|
||||||
|
|
||||||
rpc_io_service->stop();
|
rpc_io_service->stop();
|
||||||
g_rpcSignals.Stopped();
|
g_rpcSignals.Stopped();
|
||||||
if (rpc_worker_group != NULL)
|
if (rpc_worker_group != NULL)
|
||||||
|
Loading…
Reference in New Issue
Block a user