Skip to content

Commit

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

Co-authored-by: Will Clark <[email protected]>
  • Loading branch information
aureleoules and willcl-ark committed Nov 21, 2023
1 parent d752349 commit c0d7779
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 3 deletions.
30 changes: 29 additions & 1 deletion src/httprpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,12 +240,40 @@ 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>
{
if ((str.length() == 3) || (str.length() == 4)) return StringToOctal(str);
return std::nullopt;
}

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

std::optional<fs::perms> cookie_perms;
auto cookie_perms_arg{gArgs.GetArg("-rpccookieperms")};
if (cookie_perms_arg) {
auto perms{ConvertPermsToOctal(*cookie_perms_arg)};
if (!perms) {
LogPrintf("Invalid -rpccookieperms=%s; must be an octal number (e.g. 0600 or 644).\n", *cookie_perms_arg);
return false;
}
cookie_perms = static_cast<fs::perms>(*perms);
}

if (!GenerateAuthCookie(&strRPCUserColonPass, cookie_perms)) {
return false;
}
} else {
Expand Down
1 change: 1 addition & 0 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,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>", "Set the permissions on the RPC auth cookie file to the specified octal value (default: 0600)", 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("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) (DEPRECATED) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), ArgsManager::ALLOW_ANY, OptionsCategory::RPC);
Expand Down
32 changes: 31 additions & 1 deletion src/rpc/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,26 @@ static fs::path GetAuthCookieFile(bool temp=false)
return AbsPathForConfigVal(gArgs, arg);
}

bool GenerateAuthCookie(std::string *cookie_out)
static std::string perms_to_str(fs::perms p)
{
char special = '-';
if ((p & fs::perms::set_uid) != fs::perms::none) special = '4';
if ((p & fs::perms::set_gid) != fs::perms::none) special = '2';
if ((p & fs::perms::sticky_bit) != fs::perms::none) special = '1';

return std::string(1, special) +
std::string((p & fs::perms::owner_read) != fs::perms::none ? "r" : "-") +
std::string((p & fs::perms::owner_write) != fs::perms::none ? "w" : "-") +
std::string((p & fs::perms::owner_exec) != fs::perms::none ? "x" : "-") +
std::string((p & fs::perms::group_read) != fs::perms::none ? "r" : "-") +
std::string((p & fs::perms::group_write) != fs::perms::none ? "w" : "-") +
std::string((p & fs::perms::group_exec) != fs::perms::none ? "x" : "-") +
std::string((p & fs::perms::others_read) != fs::perms::none ? "r" : "-") +
std::string((p & fs::perms::others_write) != fs::perms::none ? "w" : "-") +
std::string((p & fs::perms::others_exec) != fs::perms::none ? "x" : "-");
}

bool GenerateAuthCookie(std::string* cookie_out, std::optional<fs::perms> cookie_perms)
{
const size_t COOKIE_SIZE = 32;
unsigned char rand_pwd[COOKIE_SIZE];
Expand All @@ -97,6 +116,16 @@ bool GenerateAuthCookie(std::string *cookie_out)
LogPrintf("Unable to open cookie authentication file %s for writing\n", fs::PathToString(filepath_tmp));
return false;
}

if (cookie_perms) {
std::error_code code;
std::filesystem::permissions(filepath_tmp, *cookie_perms, std::filesystem::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 @@ -106,6 +135,7 @@ bool GenerateAuthCookie(std::string *cookie_out)
return false;
}
LogPrintf("Generated RPC authentication cookie %s\n", fs::PathToString(filepath));
LogPrintf("Permissions used for cookie: %s\n", perms_to_str(fs::status(filepath).permissions()));

if (cookie_out)
*cookie_out = cookie;
Expand Down
4 changes: 3 additions & 1 deletion src/rpc/request.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,19 @@
#define BITCOIN_RPC_REQUEST_H

#include <any>
#include <optional>
#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, std::optional<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 c0d7779

Please sign in to comment.