Merge pull request #666 from nobled/secstrings

Implement an mlock()'d string class for storing passphrases
This commit is contained in:
Gavin Andresen 2011-11-30 17:30:54 -08:00
commit a7120a3647
9 changed files with 40 additions and 54 deletions

View File

@ -1453,21 +1453,16 @@ Value walletpassphrase(const Array& params, bool fHelp)
throw JSONRPCError(-17, "Error: Wallet is already unlocked."); throw JSONRPCError(-17, "Error: Wallet is already unlocked.");
// Note that the walletpassphrase is stored in params[0] which is not mlock()ed // Note that the walletpassphrase is stored in params[0] which is not mlock()ed
string strWalletPass; SecureString strWalletPass;
strWalletPass.reserve(100); strWalletPass.reserve(100);
mlock(&strWalletPass[0], strWalletPass.capacity()); // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
strWalletPass = params[0].get_str(); // Alternately, find a way to make params[0] mlock()'d to begin with.
strWalletPass = params[0].get_str().c_str();
if (strWalletPass.length() > 0) if (strWalletPass.length() > 0)
{ {
if (!pwalletMain->Unlock(strWalletPass)) if (!pwalletMain->Unlock(strWalletPass))
{
fill(strWalletPass.begin(), strWalletPass.end(), '\0');
munlock(&strWalletPass[0], strWalletPass.capacity());
throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
}
fill(strWalletPass.begin(), strWalletPass.end(), '\0');
munlock(&strWalletPass[0], strWalletPass.capacity());
} }
else else
throw runtime_error( throw runtime_error(
@ -1493,15 +1488,15 @@ Value walletpassphrasechange(const Array& params, bool fHelp)
if (!pwalletMain->IsCrypted()) if (!pwalletMain->IsCrypted())
throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); throw JSONRPCError(-15, "Error: running with an unencrypted wallet, but walletpassphrasechange was called.");
string strOldWalletPass; // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
SecureString strOldWalletPass;
strOldWalletPass.reserve(100); strOldWalletPass.reserve(100);
mlock(&strOldWalletPass[0], strOldWalletPass.capacity()); strOldWalletPass = params[0].get_str().c_str();
strOldWalletPass = params[0].get_str();
string strNewWalletPass; SecureString strNewWalletPass;
strNewWalletPass.reserve(100); strNewWalletPass.reserve(100);
mlock(&strNewWalletPass[0], strNewWalletPass.capacity()); strNewWalletPass = params[1].get_str().c_str();
strNewWalletPass = params[1].get_str();
if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1)
throw runtime_error( throw runtime_error(
@ -1509,17 +1504,7 @@ Value walletpassphrasechange(const Array& params, bool fHelp)
"Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>."); "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>.");
if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass))
{
fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect."); throw JSONRPCError(-14, "Error: The wallet passphrase entered was incorrect.");
}
fill(strNewWalletPass.begin(), strNewWalletPass.end(), '\0');
fill(strOldWalletPass.begin(), strOldWalletPass.end(), '\0');
munlock(&strOldWalletPass[0], strOldWalletPass.capacity());
munlock(&strNewWalletPass[0], strNewWalletPass.capacity());
return Value::null; return Value::null;
} }
@ -1564,10 +1549,11 @@ Value encryptwallet(const Array& params, bool fHelp)
throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command"); throw runtime_error("Not Yet Implemented: use GUI to encrypt wallet, not RPC command");
#endif #endif
string strWalletPass; // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
// Alternately, find a way to make params[0] mlock()'d to begin with.
SecureString strWalletPass;
strWalletPass.reserve(100); strWalletPass.reserve(100);
mlock(&strWalletPass[0], strWalletPass.capacity()); strWalletPass = params[0].get_str().c_str();
strWalletPass = params[0].get_str();
if (strWalletPass.length() < 1) if (strWalletPass.length() < 1)
throw runtime_error( throw runtime_error(
@ -1575,13 +1561,7 @@ Value encryptwallet(const Array& params, bool fHelp)
"Encrypts the wallet with <passphrase>."); "Encrypts the wallet with <passphrase>.");
if (!pwalletMain->EncryptWallet(strWalletPass)) if (!pwalletMain->EncryptWallet(strWalletPass))
{
fill(strWalletPass.begin(), strWalletPass.end(), '\0');
munlock(&strWalletPass[0], strWalletPass.capacity());
throw JSONRPCError(-16, "Error: Failed to encrypt the wallet."); throw JSONRPCError(-16, "Error: Failed to encrypt the wallet.");
}
fill(strWalletPass.begin(), strWalletPass.end(), '\0');
munlock(&strWalletPass[0], strWalletPass.capacity());
// BDB seems to have a bad habit of writing old data into // BDB seems to have a bad habit of writing old data into
// slack space in .dat files; that is bad if the old data is // slack space in .dat files; that is bad if the old data is

View File

@ -15,7 +15,7 @@
#include "main.h" #include "main.h"
#include "util.h" #include "util.h"
bool CCrypter::SetKeyFromPassphrase(const std::string& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod) bool CCrypter::SetKeyFromPassphrase(const SecureString& strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod)
{ {
if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE) if (nRounds < 1 || chSalt.size() != WALLET_CRYPTO_SALT_SIZE)
return false; return false;

View File

@ -65,7 +65,7 @@ private:
bool fKeySet; bool fKeySet;
public: public:
bool SetKeyFromPassphrase(const std::string &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod); bool SetKeyFromPassphrase(const SecureString &strKeyData, const std::vector<unsigned char>& chSalt, const unsigned int nRounds, const unsigned int nDerivationMethod);
bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext); bool Encrypt(const CKeyingMaterial& vchPlaintext, std::vector<unsigned char> &vchCiphertext);
bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext); bool Decrypt(const std::vector<unsigned char>& vchCiphertext, CKeyingMaterial& vchPlaintext);
bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV); bool SetKey(const CKeyingMaterial& chNewKey, const std::vector<unsigned char>& chNewIV);

View File

@ -71,16 +71,17 @@ void AskPassphraseDialog::setModel(WalletModel *model)
void AskPassphraseDialog::accept() void AskPassphraseDialog::accept()
{ {
std::string oldpass, newpass1, newpass2; SecureString oldpass, newpass1, newpass2;
if(!model) if(!model)
return; return;
// TODO: mlock memory / munlock on return so they will not be swapped out, really need "mlockedstring" wrapper class to do this safely
oldpass.reserve(MAX_PASSPHRASE_SIZE); oldpass.reserve(MAX_PASSPHRASE_SIZE);
newpass1.reserve(MAX_PASSPHRASE_SIZE); newpass1.reserve(MAX_PASSPHRASE_SIZE);
newpass2.reserve(MAX_PASSPHRASE_SIZE); newpass2.reserve(MAX_PASSPHRASE_SIZE);
oldpass.assign(ui->passEdit1->text().toStdString()); // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string)
newpass1.assign(ui->passEdit2->text().toStdString()); // Alternately, find a way to make this input mlock()'d to begin with.
newpass2.assign(ui->passEdit3->text().toStdString()); oldpass.assign(ui->passEdit1->text().toStdString().c_str());
newpass1.assign(ui->passEdit2->text().toStdString().c_str());
newpass2.assign(ui->passEdit3->text().toStdString().c_str());
switch(mode) switch(mode)
{ {

View File

@ -200,7 +200,7 @@ WalletModel::EncryptionStatus WalletModel::getEncryptionStatus() const
} }
} }
bool WalletModel::setWalletEncrypted(bool encrypted, const std::string &passphrase) bool WalletModel::setWalletEncrypted(bool encrypted, const SecureString &passphrase)
{ {
if(encrypted) if(encrypted)
{ {
@ -214,7 +214,7 @@ bool WalletModel::setWalletEncrypted(bool encrypted, const std::string &passphra
} }
} }
bool WalletModel::setWalletLocked(bool locked, const std::string &passPhrase) bool WalletModel::setWalletLocked(bool locked, const SecureString &passPhrase)
{ {
if(locked) if(locked)
{ {
@ -228,7 +228,7 @@ bool WalletModel::setWalletLocked(bool locked, const std::string &passPhrase)
} }
} }
bool WalletModel::changePassphrase(const std::string &oldPass, const std::string &newPass) bool WalletModel::changePassphrase(const SecureString &oldPass, const SecureString &newPass)
{ {
bool retval; bool retval;
CRITICAL_BLOCK(wallet->cs_wallet) CRITICAL_BLOCK(wallet->cs_wallet)

View File

@ -2,7 +2,8 @@
#define WALLETMODEL_H #define WALLETMODEL_H
#include <QObject> #include <QObject>
#include <string>
#include "util.h"
class OptionsModel; class OptionsModel;
class AddressTableModel; class AddressTableModel;
@ -72,10 +73,10 @@ public:
SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients); SendCoinsReturn sendCoins(const QList<SendCoinsRecipient> &recipients);
// Wallet encryption // Wallet encryption
bool setWalletEncrypted(bool encrypted, const std::string &passphrase); bool setWalletEncrypted(bool encrypted, const SecureString &passphrase);
// Passphrase only needed when unlocking // Passphrase only needed when unlocking
bool setWalletLocked(bool locked, const std::string &passPhrase=std::string()); bool setWalletLocked(bool locked, const SecureString &passPhrase=SecureString());
bool changePassphrase(const std::string &oldPass, const std::string &newPass); bool changePassphrase(const SecureString &oldPass, const SecureString &newPass);
// RAI object for unlocking wallet, returned by requestUnlock() // RAI object for unlocking wallet, returned by requestUnlock()
class UnlockContext class UnlockContext

View File

@ -292,6 +292,10 @@ public:
// This is exactly like std::string, but with a custom allocator.
// (secure_allocator<> is defined in serialize.h)
typedef std::basic_string<char, std::char_traits<char>, secure_allocator<char> > SecureString;

View File

@ -42,7 +42,7 @@ bool CWallet::AddCryptedKey(const vector<unsigned char> &vchPubKey, const vector
return false; return false;
} }
bool CWallet::Unlock(const string& strWalletPassphrase) bool CWallet::Unlock(const SecureString& strWalletPassphrase)
{ {
if (!IsLocked()) if (!IsLocked())
return false; return false;
@ -63,7 +63,7 @@ bool CWallet::Unlock(const string& strWalletPassphrase)
return false; return false;
} }
bool CWallet::ChangeWalletPassphrase(const string& strOldWalletPassphrase, const string& strNewWalletPassphrase) bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase)
{ {
bool fWasLocked = IsLocked(); bool fWasLocked = IsLocked();
@ -122,7 +122,7 @@ public:
) )
}; };
bool CWallet::EncryptWallet(const string& strWalletPassphrase) bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase)
{ {
if (IsCrypted()) if (IsCrypted())
return false; return false;

View File

@ -70,9 +70,9 @@ public:
// Adds an encrypted key to the store, without saving it to disk (used by LoadWallet) // Adds an encrypted key to the store, without saving it to disk (used by LoadWallet)
bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); } bool LoadCryptedKey(const std::vector<unsigned char> &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) { return CCryptoKeyStore::AddCryptedKey(vchPubKey, vchCryptedSecret); }
bool Unlock(const std::string& strWalletPassphrase); bool Unlock(const SecureString& strWalletPassphrase);
bool ChangeWalletPassphrase(const std::string& strOldWalletPassphrase, const std::string& strNewWalletPassphrase); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase);
bool EncryptWallet(const std::string& strWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase);
bool AddToWallet(const CWalletTx& wtxIn); bool AddToWallet(const CWalletTx& wtxIn);
bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false); bool AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate = false);