Files
wxWidgets/src/msw/secretstore.cpp
Vadim Zeitlin 2fffbde096 Change wxSecretStore API to allow retrieving the username
The old API didn't make any sense for the most common case when both the
user name and password need to be stored, as it required providing the
user name as input, which couldn't work (but somehow this went
unnoticed for more than a year...).

Fix this by returning the username, and not only the password, from
Load() instead of taking it as parameter and removing this parameter
from Delete() as well.

Also improve the documentation, notably include a simple example of
using this class.

Notice that this is a backwards-incompatible change, but the old API was
really badly broken and didn't appear in 3.1.0 yet, so the breakage is
both unavoidable and, hopefully, shouldn't affect much code.
Nevertheless, a special wxHAS_SECRETSTORE_LOAD_USERNAME symbol is added
to allow testing for it if necessary.
2017-07-17 18:26:20 +02:00

159 lines
5.0 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Name: src/msw/secretstore.cpp
// Purpose: wxSecretStore implementation for MSW.
// Author: Vadim Zeitlin
// Created: 2016-05-27
// Copyright: (c) 2016 Vadim Zeitlin <vadim@wxwidgets.org>
// Licence: wxWindows licence
///////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// for compilers that support precompilation, includes "wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#if wxUSE_SECRETSTORE
#include "wx/secretstore.h"
#include "wx/private/secretstore.h"
#include "wx/log.h" // wxSysErrorMsgStr()
// Somewhat surprisingly, wincred.h is not self-contained and relies on some
// standard Windows macros being defined without including the headers defining
// them on its own, so we must include <windows.h> (from our private header)
// before including it.
#include "wx/msw/private.h"
#include <wincred.h>
namespace
{
// ============================================================================
// wxSecretStoreImpl for MSW
// ============================================================================
// Helper class to ensure that a CREDENTIAL pointer is always freed.
class CredentialPtr
{
public:
explicit CredentialPtr(CREDENTIAL* cred) : m_cred(cred) { }
~CredentialPtr() { ::CredFree(m_cred); }
private:
CREDENTIAL* const m_cred;
wxDECLARE_NO_COPY_CLASS(CredentialPtr);
};
class wxSecretStoreMSWImpl : public wxSecretStoreImpl
{
public:
virtual bool Save(const wxString& service,
const wxString& user,
const wxSecretValueImpl& secret,
wxString& errmsg) wxOVERRIDE
{
CREDENTIAL cred;
wxZeroMemory(cred);
cred.Type = CRED_TYPE_GENERIC;
cred.TargetName = const_cast<TCHAR*>(static_cast<const TCHAR*>(service.t_str()));
cred.UserName = const_cast<TCHAR*>(static_cast<const TCHAR*>(user.t_str()));
cred.CredentialBlobSize = secret.GetSize();
cred.CredentialBlob = static_cast<BYTE *>(const_cast<void*>(secret.GetData()));
// We could also use CRED_PERSIST_ENTERPRISE here to store the password
// in the roaming section of the profile and this could arguably be
// more useful. However it might also be unexpected if the user really
// only wants to store the password on the local machine, so choose
// security over convenience here. Maybe in the future we should have
// an option for the password persistence scope.
cred.Persist = CRED_PERSIST_LOCAL_MACHINE;
if ( !::CredWrite(&cred, 0) )
{
errmsg = wxSysErrorMsgStr();
return false;
}
return true;
}
virtual bool Load(const wxString& service,
wxString* user,
wxSecretValueImpl** secret,
wxString& errmsg) const wxOVERRIDE
{
CREDENTIAL* pcred = NULL;
if ( !::CredRead(service.t_str(), CRED_TYPE_GENERIC, 0, &pcred) || !pcred )
{
// Not having the password for this service/user combination is not
// an error, but anything else is.
if ( ::GetLastError() != ERROR_NOT_FOUND )
errmsg = wxSysErrorMsgStr();
return false;
}
CredentialPtr ensureFree(pcred);
*user = pcred->UserName;
*secret = new wxSecretValueGenericImpl(pcred->CredentialBlobSize,
pcred->CredentialBlob);
return true;
}
virtual bool Delete(const wxString& service,
wxString& errmsg) wxOVERRIDE
{
if ( !::CredDelete(service.t_str(), CRED_TYPE_GENERIC, 0) )
{
// Same logic as in Load() above.
if ( ::GetLastError() != ERROR_NOT_FOUND )
errmsg = wxSysErrorMsgStr();
return false;
}
return true;
}
};
} // anonymous namespace
// ============================================================================
// MSW-specific implementation of common methods
// ============================================================================
/* static */
wxSecretValueImpl* wxSecretValue::NewImpl(size_t size, const void *data)
{
return new wxSecretValueGenericImpl(size, data);
}
/* static */
void wxSecretValue::Wipe(size_t size, void *data)
{
::SecureZeroMemory(data, size);
}
/* static */
wxSecretStore wxSecretStore::GetDefault()
{
// There is only a single store under Windows anyhow.
return wxSecretStore(new wxSecretStoreMSWImpl());
}
#endif // wxUSE_SECRETSTORE