VBoxCredProvCredential.cpp revision d821174790abf45486fad407c8aea102c97689d8
/* $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.
*/
#ifndef WIN32_NO_STATUS
#include <ntstatus.h>
#define WIN32_NO_STATUS
#endif
#include "VBoxCredentialProvider.h"
#include "VBoxCredProvProvider.h"
#include "VBoxCredProvCredential.h"
#include "VBoxCredProvUtils.h"
#include <lm.h>
m_cRefCount(1),
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Created, pProvider=%p\n",
}
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Destroying\n");
}
/* IUnknown overrides. */
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Increasing reference to %ld\n",
m_cRefCount + 1);
return m_cRefCount++;
}
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Decreasing reference to %ld\n",
if (!cRefCount)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Calling destructor\n");
delete this;
}
return cRefCount;
}
{
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.
*/
HRESULT VBoxCredProvCredential::RTUTF16toToUnicode(PUNICODE_STRING pUnicodeDest, PRTUTF16 pwszSource,
bool fCopy)
{
if (fCopy)
{
}
else /* Just assign the buffer. */
return S_OK;
}
HRESULT VBoxCredProvCredential::AllocateLogonPackage(const KERB_INTERACTIVE_UNLOCK_LOGON& UnlockLogon,
{
/*
* 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;
}
void VBoxCredProvCredential::Reset(void)
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Wiping credentials ...\n");
3 /* Passes */);
if (m_pEvents)
{
}
}
int VBoxCredProvCredential::RetrieveCredentials(void)
{
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.
*/
}
if (RT_SUCCESS(rc))
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Got credentials: 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: Translated account name %ls -> %ls\n",
}
else
{
/*
* Oky, 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. */
/* Update domain. */
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Extracted account data is pwszAccount=%ls, pwszDomain=%ls\n",
}
}
}
return rc;
}
/*
* Initializes one credential with the field information passed in.
* Set the value of the VBOXCREDPROV_FIELDID_USERNAME field to pwzUsername.
* Optionally takes a password for the SetSerialization case.
*/
{
return S_OK;
}
/*
* LogonUI calls this in order to give us a callback in case we need to notify it of anything.
* Store this callback pointer for later use.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: Advise\n");
return S_OK;
}
/* LogonUI calls this to tell us to release the callback. */
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: UnAdvise\n");
/*
* We're done with the current iteration, trigger a refresh of ourselves
* to reset credentials and to keep the logon UI clean (no stale entries anymore).
*/
Reset();
/*
* Force a re-iteration of the provider (which will give zero credentials
* to try out because we just reset our one and only a line above.
*/
if (m_pProvider)
if (m_pEvents)
return S_OK;
}
/*
* LogonUI calls this function when our tile is selected (zoomed).
* there's no need to do anything here - you can set that up in the
* field definitions. But if you want to do something
* more complicated, like change the contents of a field when the tile is
* selected, you would do it here.
*/
{
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.
*/
*pbAutoLogon = FALSE;
return S_OK;
}
/*
* Similarly to SetSelected, LogonUI calls this when your tile was selected
* and now no longer is. The most common thing to do here (which we do below)
* is to clear out the password field.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetDeselected\n");
3 /* Passes */);
if (m_pEvents)
return S_OK;
}
/*
* Gets info for a particular field of a tile. Called by logonUI to get information to
* display the tile.
*/
{
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 ( (nStatus == NERR_Success)
|| (nStatus == 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 nStatus=%ld, fFound=%s\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.
*/
BOOL 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");
}
/* Sets ppwsz to the string value of the field at the index dwFieldID. */
{
/* Check to make sure dwFieldID is a legitimate index. */
if ( dwFieldID < VBOXCREDPROV_NUM_FIELDS
&& ppwszString)
{
switch (dwFieldID)
{
/* Fill in standard value to make Winlogon happy. */
break;
default:
/*
* Make a copy of the string and return that, the caller is responsible for freeing it.
* Note that there can be empty fields (like a missing domain name); handle them
* by writing an empty string.
*/
if ( m_pwszCredentials[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;
}
/*
* Sets pdwAdjacentTo to the index of the field the submit button should be
* adjacent to. We recommend that the submit button is placed next to the last
* field which the user is required to enter information in. Optional fields
* should be below the submit button.
*/
{
/* 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 field which can accept a string as a value.
* This is called on each keystroke when a user types into an edit field.
*/
{
VBoxCredProvVerbose(0, "VBoxCredProvCredential: SetStringValue: dwFieldID=%ld, pcwzString=%ls\n",
/*
* We don't set any values into fields (e.g. the password, hidden
* by dots), instead keep it secret by resetting all credentials.
*/
Reset();
return S_OK;
}
/*
* The following methods are for logonUI to get the values of various UI elements and then communicate
* to the credential about what the user did in that field. However, these methods are not implemented
* because our tile doesn't contain these types of UI elements.
*/
{
/* We don't do own bitmaps. */
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
{
return E_NOTIMPL;
}
/*
* Collect the username and password into a serialized credential for the correct usage scenario
* LogonUI then passes these credentials back to the system to log on.
*/
HRESULT 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
{
/* Is a domain name missing? Then use the name of the local computer. */
{
false /* Just assign, no copy */);
}
else
false /* Just assign, no copy */);
/* 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_cpUS)
{
case CPUS_UNLOCK_WORKSTATION:
break;
case CPUS_LOGON:
break;
case CPUS_CREDUI:
break;
default:
break;
}
{
/* Allocate copies of, and package, the strings in a binary blob. */
}
{
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. */
}
}
}
}
}
}
else
return hr;
}
/*
* ReportResult is completely optional. Its purpose is to allow a credential to customize the string
* and the icon displayed in the case of a logon failure. For example, we have chosen to
* being disabled.
*/
{
/*
* Since NULL is a valid value for *ppwszOptionalStatusText and *pcpsiOptionalStatusIcon
* this function can't fail
*/
return S_OK;
}