mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-02 09:46:52 -05:00
wallet: ensure that the passphrase is not deleted from memory when being used to rescan
`m_relock_mutex` is introduced so that the passphrase is not deleted from memory when the timeout provided in `walletpassphrase` is up, but the wallet is still rescanning.
This commit is contained in:
parent
66a86ebabb
commit
493b813e17
5 changed files with 18 additions and 10 deletions
|
@ -1655,6 +1655,10 @@ RPCHelpMan importdescriptors()
|
|||
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
|
||||
}
|
||||
|
||||
// Ensure that the wallet is not locked for the remainder of this RPC, as
|
||||
// the passphrase is used to top up the keypool.
|
||||
LOCK(pwallet->m_relock_mutex);
|
||||
|
||||
const UniValue& requests = main_request.params[0];
|
||||
const int64_t minimum_timestamp = 1;
|
||||
int64_t now = 0;
|
||||
|
|
|
@ -90,7 +90,7 @@ RPCHelpMan walletpassphrase()
|
|||
std::weak_ptr<CWallet> weak_wallet = wallet;
|
||||
pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] {
|
||||
if (auto shared_wallet = weak_wallet.lock()) {
|
||||
LOCK(shared_wallet->cs_wallet);
|
||||
LOCK2(shared_wallet->m_relock_mutex, shared_wallet->cs_wallet);
|
||||
// Skip if this is not the most recent rpcRunLater callback.
|
||||
if (shared_wallet->nRelockTime != relock_time) return;
|
||||
shared_wallet->Lock();
|
||||
|
@ -122,8 +122,6 @@ RPCHelpMan walletpassphrasechange()
|
|||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
if (!pwallet->IsCrypted()) {
|
||||
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
|
||||
}
|
||||
|
@ -132,6 +130,8 @@ RPCHelpMan walletpassphrasechange()
|
|||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before changing the passphrase.");
|
||||
}
|
||||
|
||||
LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
|
||||
|
||||
// TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
|
||||
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
|
||||
SecureString strOldWalletPass;
|
||||
|
@ -179,8 +179,6 @@ RPCHelpMan walletlock()
|
|||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
if (!pwallet->IsCrypted()) {
|
||||
throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called.");
|
||||
}
|
||||
|
@ -189,6 +187,8 @@ RPCHelpMan walletlock()
|
|||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before locking the wallet.");
|
||||
}
|
||||
|
||||
LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
|
||||
|
||||
pwallet->Lock();
|
||||
pwallet->nRelockTime = 0;
|
||||
|
||||
|
@ -227,8 +227,6 @@ RPCHelpMan encryptwallet()
|
|||
std::shared_ptr<CWallet> const pwallet = GetWalletForJSONRPCRequest(request);
|
||||
if (!pwallet) return UniValue::VNULL;
|
||||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
if (pwallet->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
|
||||
throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet does not contain private keys, nothing to encrypt.");
|
||||
}
|
||||
|
@ -241,6 +239,8 @@ RPCHelpMan encryptwallet()
|
|||
throw JSONRPCError(RPC_WALLET_ERROR, "Error: the wallet is currently being used to rescan the blockchain for related transactions. Please call `abortrescan` before encrypting the wallet.");
|
||||
}
|
||||
|
||||
LOCK2(pwallet->m_relock_mutex, pwallet->cs_wallet);
|
||||
|
||||
// TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
|
||||
// Alternately, find a way to make request.params[0] mlock()'d to begin with.
|
||||
SecureString strWalletPass;
|
||||
|
|
|
@ -880,6 +880,7 @@ RPCHelpMan rescanblockchain()
|
|||
std::optional<int> stop_height;
|
||||
uint256 start_block;
|
||||
|
||||
LOCK(pwallet->m_relock_mutex);
|
||||
{
|
||||
LOCK(pwallet->cs_wallet);
|
||||
EnsureWalletIsUnlocked(*pwallet);
|
||||
|
|
|
@ -551,7 +551,7 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
|
|||
bool fWasLocked = IsLocked();
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
LOCK2(m_relock_mutex, cs_wallet);
|
||||
Lock();
|
||||
|
||||
CCrypter crypter;
|
||||
|
@ -786,7 +786,7 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
|
|||
return false;
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
LOCK2(m_relock_mutex, cs_wallet);
|
||||
mapMasterKeys[++nMasterKeyMaxID] = kMasterKey;
|
||||
WalletBatch* encrypted_batch = new WalletBatch(GetDatabase());
|
||||
if (!encrypted_batch->TxnBegin()) {
|
||||
|
@ -3407,7 +3407,7 @@ bool CWallet::Lock()
|
|||
return false;
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
LOCK2(m_relock_mutex, cs_wallet);
|
||||
if (!vMasterKey.empty()) {
|
||||
memory_cleanse(vMasterKey.data(), vMasterKey.size() * sizeof(decltype(vMasterKey)::value_type));
|
||||
vMasterKey.clear();
|
||||
|
|
|
@ -488,6 +488,9 @@ public:
|
|||
|
||||
// Used to prevent concurrent calls to walletpassphrase RPC.
|
||||
Mutex m_unlock_mutex;
|
||||
// Used to prevent deleting the passphrase from memory when it is still in use.
|
||||
RecursiveMutex m_relock_mutex;
|
||||
|
||||
bool Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys = false);
|
||||
bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
|
||||
bool EncryptWallet(const SecureString& strWalletPassphrase);
|
||||
|
|
Loading…
Add table
Reference in a new issue