VBoxCredProvCredential.cpp revision 8e2c60d8d3ffea86ce10a66f138ce1afef9f597c
/* $Id$ */
/** @file
* VBoxCredProvCredential - Class for keeping and handling the passed credentials.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#ifndef WIN32_NO_STATUS
# include <ntstatus.h>
# define WIN32_NO_STATUS
#endif
#include <intsafe.h>
#include "VBoxCredentialProvider.h"
#include "VBoxCredProvProvider.h"
#include "VBoxCredProvCredential.h"
#include "VBoxCredProvUtils.h"
#include <lm.h>
#include <iprt/initterm.h>
m_cRefs(1),
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Created\n");
}
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Destroying\n");
Reset();
}
VBoxCredProvCredential::AddRef(void)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::AddRef: Returning refcount=%ld\n",
cRefs);
return cRefs;
}
VBoxCredProvCredential::Release(void)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::Release: Returning refcount=%ld\n",
cRefs);
if (!cRefs)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Calling destructor\n");
delete this;
}
return cRefs;
}
{
if (ppvInterface)
{
if ( IID_IUnknown == interfaceID
{
*ppvInterface = static_cast<IUnknown*>(this);
}
else
{
*ppvInterface = NULL;
hr = E_NOINTERFACE;
}
}
else
hr = E_INVALIDARG;
return hr;
}
/**
* Assigns or copies a RTUTF16 string to a UNICODE_STRING.
*
* When fCopy is false, this does *not* copy its contents
* and only assigns its code points to the destination!
* When fCopy is true, the actual string buffer gets copied.
*
* Does not take terminating \0 into account.
*
* @return HRESULT
* @param pUnicodeDest Unicode string assigning the UTF16 string to.
* @param pwszSource UTF16 string to assign.
* @param fCopy Whether to just assign or copy the actual buffer
* contents from source -> dest.
* @todo r=bird: It appears that fCopy == true is never used, which is
* fortunate as it (a) doesn't check for there being room in the
* buffer, (b) terminate the string (which is customary, even if not
* strictly necessary), and (c) overwrites MaximumLength.
*/
VBoxCredProvCredential::RTUTF16ToUnicode(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource, bool fCopy)
{
if (fCopy)
{
AssertFailed(/*see todo*/);
}
else /* Just assign the buffer. */
return S_OK;
}
VBoxCredProvCredential::AllocateLogonPackage(const KERB_INTERACTIVE_UNLOCK_LOGON &rUnlockLogon, PBYTE *ppPackage, DWORD *pcbPackage)
{
/*
* First, allocate enough space for the logon structure itself and separate
* string buffers right after it to store the actual user, password and domain
* credentials.
*/
#ifdef DEBUG
VBoxCredProvVerbose(3, "VBoxCredProvCredential::AllocateLogonPackage: Allocating %ld bytes (%d bytes credentials)\n",
#endif
if (!pLogon)
return E_OUTOFMEMORY;
/* Let our byte buffer point to the end of our allocated structure so that it can
* be used to store the credential data sequentially in a binary blob
* (without terminating \0). */
/* The buffer of the packed destination string does not contain the actual
* string content but a relative offset starting at the given
* KERB_INTERACTIVE_UNLOCK_LOGON structure. */
*pcbPackage = cbLogon;
return S_OK;
}
/**
* Resets (wipes) stored credentials.
*
* @return HRESULT
*/
VBoxCredProvCredential::Reset(void)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::Reset: Wiping credentials user=%ls, pw=%ls, domain=%ls\n",
#ifdef DEBUG
#else
L"XXX" /* Don't show any passwords in release mode. */,
#endif
3 /* Passes */);
if (m_pEvents)
{
/* Note: On Windows 8, set "this" to "nullptr". */
}
return hr;
}
/**
* Checks and retrieves credentials provided by the host + does account lookup on eventually
* renamed user accounts.
*
* @return IPRT status code.
*/
int
{
int rc = VbglR3CredentialsQueryAvailability();
if (RT_SUCCESS(rc))
{
/*
* Set status to "terminating" to let the host know this module now
* tries to receive and use passed credentials so that credentials from
* the host won't be sent twice.
*/
VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Retrieved credentials with rc=%Rrc\n", rc);
}
if (RT_SUCCESS(rc))
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: User=%ls, Password=%ls, Domain=%ls\n",
#ifdef DEBUG
#else
L"XXX" /* Don't show any passwords in release mode. */,
#endif
/*
* In case we got a "display name" (e.g. "John Doe")
* instead of the real user name (e.g. "jdoe") we have
* to translate the data first ...
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Translated account name %ls -> %ls\n",
{
3 /* Passes */);
}
}
else
{
/*
* Okay, no display name, but maybe it's a
* principal name from which we have to extract the domain from?
* (jdoe@my-domain.sub.net.com -> jdoe in domain my-domain.sub.net.com.)
*/
&pwszAcount, &pwszDomain))
{
/* Update user name. */
{
3 /* Passes */);
}
/* Update domain. */
{
3 /* Passes */);
}
VBoxCredProvVerbose(0, "VBoxCredProvCredential::RetrieveCredentials: Extracted account data pwszAccount=%ls, pwszDomain=%ls\n",
}
}
}
return rc;
}
/**
* Initializes this credential with the current credential provider
* usage scenario.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::Initialize: enmUsageScenario=%ld\n", enmUsageScenario);
return S_OK;
}
/**
* Called by LogonUI when it needs this credential's advice.
*
* At the moment we only grab the credential provider events so that we can
* trigger a re-enumeration of the credentials later.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::Advise: pEvents=0x%p\n",
pEvents);
if (m_pEvents)
{
}
if (m_pEvents)
return S_OK;
}
/**
* Called by LogonUI when it's finished with handling this credential.
*
* We only need to release the credential provider events, if any.
*/
VBoxCredProvCredential::UnAdvise(void)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::UnAdvise\n");
if (m_pEvents)
{
}
return S_OK;
}
/**
* Called by LogonUI when a user profile (tile) has been selected.
*
* As we don't want Winlogon to try logging in immediately we set pfAutoLogon
* to FALSE (if set).
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetSelected\n");
/*
* Don't do auto logon here because it would retry too often with
* every credential field (user name, password, domain, ...) which makes
* winlogon wait before new login attempts can be made.
*/
if (pfAutoLogon)
*pfAutoLogon = FALSE;
return S_OK;
}
/**
* Called by LogonUI when a user profile (tile) has been unselected again.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetDeselected\n");
Reset();
if (m_pEvents)
return S_OK;
}
/**
* Called by LogonUI to retrieve the (interactive) state of a UI field.
*/
VBoxCredProvCredential::GetFieldState(DWORD dwFieldID, CREDENTIAL_PROVIDER_FIELD_STATE *pFieldState,
{
if ( (dwFieldID < VBOXCREDPROV_NUM_FIELDS)
&& pFieldState
{
}
else
hr = E_INVALIDARG;
return hr;
}
/**
* Searches the account name based on a display (real) name (e.g. "John Doe" -> "jdoe").
* Result "ppwszAccoutName" needs to be freed with CoTaskMemFree!
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Getting account name for \"%ls\" ...\n",
/** @todo Do we need ADS support (e.g. TranslateNameW) here? */
DWORD dwEntriesRead = 0;
DWORD dwTotalEntries = 0;
DWORD dwResumeHandle = 0;
do
{
if ( rcStatus == NERR_Success
|| rcStatus == ERROR_MORE_DATA)
{
{
for (DWORD i = 0; i < dwEntriesRead; i++)
{
/*
* Search for the "display name" - that might be
* "John Doe" or something similar the user recognizes easier
* and may not the same as the "account" name (e.g. "jdoe").
*/
if ( pCurBuf
{
/*
* Copy the real user name (e.g. "jdoe") to our
* output buffer.
*/
{
}
else
VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName: Error copying data, hr=%08x\n", hr);
break;
}
pCurBuf++;
}
}
{
}
}
{
}
VBoxCredProvVerbose(0, "VBoxCredProvCredential::TranslateAccountName returned rcStatus=%ld, fFound=%RTbool\n",
return fFound;
#if 0
&& cbLen > 0)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Translated ADS name has %u characters\n", cbLen));
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetAccountName: Real ADS account name of '%ls' is '%ls'\n",
}
else
{
dwErr = GetLastError();
}
}
else
dwErr = GetLastError();
/* The above method for looking up in ADS failed, try another one. */
{
}
#endif
}
/**
* Extracts the actual account name & domain from a (raw) account data string.
*
* This might be a principal or FQDN string.
*/
VBoxCredProvCredential::ExtractAccoutData(PWSTR pwszAccountData, PWSTR *ppwszAccoutName, PWSTR *ppwszDomain)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Getting account name for \"%ls\" ...\n",
/* Try to figure out whether this is a principal name (user@domain). */
&& pPos != pwszAccountData)
{
{
*pPos++; /* Skip @, point to domain name (if any). */
&& *pPos != L'\0')
{
{
}
else
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Error copying domain data, hr=%08x\n", hr);
}
else
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: No domain name found!\n");
}
}
else
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: Error copying account data, hr=%08x\n", hr);
{
if (pwszDomain)
}
}
else
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ExtractAccoutData: No valid principal account name found!\n");
}
/**
* Returns the value of a specified LogonUI field.
*
* @return IPRT status code.
* @param dwFieldID Field ID to get value for.
* @param ppwszString Pointer that receives the actual value of the specified field.
*/
{
if ( dwFieldID < VBOXCREDPROV_NUM_FIELDS
&& ppwszString)
{
switch (dwFieldID)
{
/* Fill in standard value to make Winlogon happy. */
break;
default:
if ( m_apwszCredentials[dwFieldID]
else /* Fill in an empty value. */
break;
}
#ifdef DEBUG
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetStringValue: dwFieldID=%ld, ppwszString=%ls\n",
dwFieldID, *ppwszString);
#endif
}
else
hr = E_INVALIDARG;
return hr;
}
/**
* Returns back the field ID of which the submit button should be put next to.
*
* We always want to be the password field put next to the submit button
* currently.
*
* @return HRESULT
* @param dwFieldID Field ID of the submit button.
* @param pdwAdjacentTo Field ID where to put the submit button next to.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld\n",
/* Validate parameters. */
&& pdwAdjacentTo)
{
/* pdwAdjacentTo is a pointer to the fieldID you want the submit button to appear next to. */
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSubmitButtonValue: dwFieldID=%ld, *pdwAdjacentTo=%ld\n",
}
else
hr = E_INVALIDARG;
return hr;
}
/**
* Sets the value of a specified field. Currently not used.
*
* @return HRESULT
* @param dwFieldID Field to set value for.
* @param pcwzString Actual value to set.
*/
{
#ifdef DEBUG
VBoxCredProvVerbose(0, "VBoxCredProvCredential::SetStringValue: dwFieldID=%ld, pcwzString=%ls\n",
#endif
/* Do more things here later. */
return hr;
}
{
/* We don't do own bitmaps. */
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
VBoxCredProvCredential::GetComboBoxValueCount(DWORD dwFieldID, DWORD *pcItems, DWORD *pdwSelectedItem)
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
/**
* Does the actual authentication stuff to attempt a login.
*
* @return HRESULT
* @param pcpGetSerializationResponse Credential serialization response.
* @param pcpCredentialSerialization Details about the current credential.
* @param ppwszOptionalStatusText Text to set. Optional.
* @param pcpsiOptionalStatusIcon Status icon to set. Optional.
*/
VBoxCredProvCredential::GetSerialization(CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *pcpGetSerializationResponse,
{
/* Save a pointer to the interactive logon struct. */
#ifdef DEBUG
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSerialization: Username=%ls, Password=%ls, Domain=%ls\n",
#endif
/* Do we have a domain name set? */
{
false /* Just assign, no copy */);
}
else /* No domain (FQDN) given, try local computer name. */
{
{
/* Is a domain name missing? Then use the name of the local computer. */
false /* Just assign, no copy */);
VBoxCredProvVerbose(0, "VBoxCredProvCredential::GetSerialization: Local computer name=%ls\n",
}
else
}
{
/* Fill in the username and password. */
{
false /* Just assign, no copy */);
{
false /* Just assign, no copy */);
{
/* Set credential type according to current usage scenario. */
switch (m_enmUsageScenario)
{
case CPUS_UNLOCK_WORKSTATION:
break;
case CPUS_LOGON:
break;
case CPUS_CREDUI:
break;
default:
break;
}
{
if (SUCCEEDED(HRESULT_FROM_NT(s)))
{
{
{
}
}
{
if (FAILED(HRESULT_FROM_NT(s)))
hr = HRESULT_FROM_NT(s);
}
}
{
/* We're done -- let the logon UI know. */
}
}
}
}
}
}
return hr;
}
/**
* Called by LogonUI after a logon attempt was made -- here we could set an additional status
*
* Currently not used.
*
* @return HRESULT
* @param ntStatus NT status of logon attempt reported by Winlogon.
* @param ntSubStatus NT substatus of logon attempt reported by Winlogon.
* @param ppwszOptionalStatusText Pointer that receives the optional status text.
* @param pcpsiOptionalStatusIcon Pointer that receives the optional status icon.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential::ReportResult: ntStatus=%ld, ntSubStatus=%ld\n",
return S_OK;
}