Skip to content

Commit

Permalink
init: add option for rpccookie permissions
Browse files Browse the repository at this point in the history
Add a bitcoind launch option `-rpccookieperms` to configure the file
permissions of the cookie on Unix systems.

Co-authored-by: Will Clark <[email protected]>
  • Loading branch information
aureleoules and willcl-ark committed Feb 5, 2024
1 parent 36816dc commit 7456c3a
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 7 deletions.
36 changes: 35 additions & 1 deletion src/httprpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,46 @@ static bool HTTPReq_JSONRPC(const std::any& context, HTTPRequest* req)
return true;
}

static std::optional<unsigned> StringToOctal(const std::string& str)
{
unsigned ret = 0;
for (char c : str) {
if (c < '0' || c > '7') return std::nullopt;
ret = (ret << 3) | (c - '0');
}
return ret;
}

static auto ConvertPermsToOctal(const std::string& str) noexcept -> std::optional<unsigned>
{
// Don't permit setting special bits as they're not relevant to cookie files
if (str.length() == 3) return StringToOctal(str);
return std::nullopt;
}

static bool InitRPCAuthentication()
{
if (gArgs.GetArg("-rpcpassword", "") == "")
{
LogPrintf("Using random cookie authentication.\n");
if (!GenerateAuthCookie(&strRPCUserColonPass)) {

fs::perms cookie_perms{DEFAULT_COOKIE_PERMS};
auto cookie_perms_arg{gArgs.GetArg("-rpccookieperms")};
if (cookie_perms_arg) {
#ifdef WIN32
LogPrintf("Unable to set unix-style file permissions on cookie via -rpccookieperms using Windows systems\n");
return false;
#else
auto perms{ConvertPermsToOctal(*cookie_perms_arg)};
if (!perms) {
LogPrintf("Invalid -rpccookieperms=%s; must be a 3 digit octal number (e.g. 400, 440 or 444).\n", *cookie_perms_arg);
return false;
}
cookie_perms = static_cast<fs::perms>(*perms);
#endif
}

if (!GenerateAuthCookie(&strRPCUserColonPass, cookie_perms)) {
return false;
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/httprpc.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* marked as read-only, as is the case here.
* Differs from defaults derived from umask in util/system.cpp
*/
const auto DEFAULT_COOKIE_PERMS{fs::perms::owner_read};
const fs::perms DEFAULT_COOKIE_PERMS{fs::perms::owner_read};

/** Start HTTP RPC subsystem.
* Precondition; HTTP and RPC has been started.
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ void SetupServerArgs(ArgsManager& argsman)
argsman.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. Do not expose the RPC server to untrusted networks such as the public internet! This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost)", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcdoccheck", strprintf("Throw a non-fatal error at runtime if the documentation for an RPC is incorrect (default: %u)", DEFAULT_RPC_DOC_CHECK), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpccookieperms=<octal>", strprintf("Set the permissions on the RPC auth cookie file to the specified octal value on unix systems only (default: %u)", PermsToOctalString(DEFAULT_COOKIE_PERMS).c_str()), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
argsman.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC);
argsman.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC);
argsman.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC);
Expand Down
14 changes: 10 additions & 4 deletions src/rpc/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,23 +82,28 @@ static fs::path GetAuthCookieFile(bool temp=false)

static bool g_generated_cookie = false;

bool GenerateAuthCookie(std::string *cookie_out)
bool GenerateAuthCookie(std::string* cookie_out, fs::perms cookie_perms)
{
const size_t COOKIE_SIZE = 32;
unsigned char rand_pwd[COOKIE_SIZE];
GetRandBytes(rand_pwd);
std::string cookie = COOKIEAUTH_USER + ":" + HexStr(rand_pwd);

/** the umask determines what permissions are used to create this file -
* these are set to 0077 in common/system.cpp.
*/
std::ofstream file;
fs::path filepath_tmp = GetAuthCookieFile(true);
file.open(filepath_tmp);
if (!file.is_open()) {
LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
return false;
}

std::error_code code;
fs::permissions(filepath_tmp, cookie_perms, fs::perm_options::replace, code);
if (code) {
LogPrintf("Unable to set permissions on cookie authentication file %s\n", fs::PathToString(filepath_tmp));
return false;
}

file << cookie;
file.close();

Expand All @@ -109,6 +114,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
}
g_generated_cookie = true;
LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
LogPrintf("Permissions used for cookie: %s\n", PermsToString(fs::status(filepath).permissions()));

if (cookie_out)
*cookie_out = cookie;
Expand Down
3 changes: 2 additions & 1 deletion src/rpc/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@
#include <string>

#include <univalue.h>
#include <util/fs.h>

UniValue JSONRPCRequestObj(const std::string& strMethod, const UniValue& params, const UniValue& id);
UniValue JSONRPCReplyObj(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);

/** Generate a new RPC authentication cookie and write it to disk */
bool GenerateAuthCookie(std::string *cookie_out);
bool GenerateAuthCookie(std::string* cookie_out, fs::perms cookie_perms);
/** Read the RPC authentication cookie from disk */
bool GetAuthCookie(std::string *cookie_out);
/** Delete RPC authentication cookie from disk */
Expand Down

0 comments on commit 7456c3a

Please sign in to comment.