560 lines
15 KiB
C++
560 lines
15 KiB
C++
//
|
|
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
//
|
|
|
|
#ifndef WIN32_NO_STATUS
|
|
#include <ntstatus.h>
|
|
#define WIN32_NO_STATUS
|
|
#endif
|
|
#include <unknwn.h>
|
|
|
|
#include "CSampleCredential.h"
|
|
#include "CWrappedCredentialEvents.h"
|
|
#include "guid.h"
|
|
|
|
// CSampleCredential ////////////////////////////////////////////////////////
|
|
|
|
// NOTE: Please read the readme.txt file to understand when it's appropriate to
|
|
// wrap an another credential provider and when it's not.
|
|
CSampleCredential::CSampleCredential():
|
|
_cRef(1)
|
|
{
|
|
DllAddRef();
|
|
|
|
ZeroMemory(_rgCredProvFieldDescriptors, sizeof(_rgCredProvFieldDescriptors));
|
|
ZeroMemory(_rgFieldStatePairs, sizeof(_rgFieldStatePairs));
|
|
ZeroMemory(_rgFieldStrings, sizeof(_rgFieldStrings));
|
|
|
|
_pWrappedCredential = NULL;
|
|
_pWrappedCredentialEvents = NULL;
|
|
_pCredProvCredentialEvents = NULL;
|
|
|
|
_dwWrappedDescriptorCount = 0;
|
|
_dwDatabaseIndex = 0;
|
|
}
|
|
|
|
CSampleCredential::~CSampleCredential()
|
|
{
|
|
for (int i = 0; i < ARRAYSIZE(_rgFieldStrings); i++)
|
|
{
|
|
CoTaskMemFree(_rgFieldStrings[i]);
|
|
CoTaskMemFree(_rgCredProvFieldDescriptors[i].pszLabel);
|
|
}
|
|
|
|
_CleanupEvents();
|
|
|
|
if (_pWrappedCredential)
|
|
{
|
|
_pWrappedCredential->Release();
|
|
}
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
// Initializes one credential with the field information passed in. We also keep track
|
|
// of our wrapped credential and how many fields it has.
|
|
HRESULT CSampleCredential::Initialize(
|
|
__in const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR* rgcpfd,
|
|
__in const FIELD_STATE_PAIR* rgfsp,
|
|
__in ICredentialProviderCredential *pWrappedCredential,
|
|
__in DWORD dwWrappedDescriptorCount
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
// Grab the credential we're wrapping for future reference.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
_pWrappedCredential->Release();
|
|
}
|
|
_pWrappedCredential = pWrappedCredential;
|
|
_pWrappedCredential->AddRef();
|
|
|
|
// We also need to remember how many fields the inner credential has.
|
|
_dwWrappedDescriptorCount = dwWrappedDescriptorCount;
|
|
|
|
// Copy the field descriptors for each field. This is useful if you want to vary the field
|
|
// descriptors based on what Usage scenario the credential was created for.
|
|
for (DWORD i = 0; SUCCEEDED(hr) && i < ARRAYSIZE(_rgCredProvFieldDescriptors); i++)
|
|
{
|
|
_rgFieldStatePairs[i] = rgfsp[i];
|
|
hr = FieldDescriptorCopy(rgcpfd[i], &_rgCredProvFieldDescriptors[i]);
|
|
}
|
|
|
|
// Initialize the String value of all of our fields.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHStrDupW(L"I Work In:", &_rgFieldStrings[SFI_I_WORK_IN_STATIC]);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = SHStrDupW(L"Database", &_rgFieldStrings[SFI_DATABASE_COMBOBOX]);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// LogonUI calls this in order to give us a callback in case we need to notify it of
|
|
// anything. We'll also provide it to the wrapped credential.
|
|
HRESULT CSampleCredential::Advise(
|
|
__in ICredentialProviderCredentialEvents* pcpce
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
_CleanupEvents();
|
|
|
|
// We keep a strong reference on the real ICredentialProviderCredentialEvents
|
|
// to ensure that the weak reference held by the CWrappedCredentialEvents is valid.
|
|
_pCredProvCredentialEvents = pcpce;
|
|
_pCredProvCredentialEvents->AddRef();
|
|
|
|
_pWrappedCredentialEvents = new CWrappedCredentialEvents();
|
|
|
|
if (_pWrappedCredentialEvents != NULL)
|
|
{
|
|
_pWrappedCredentialEvents->Initialize(this, pcpce);
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->Advise(_pWrappedCredentialEvents);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// LogonUI calls this to tell us to release the callback.
|
|
// We'll also provide it to the wrapped credential.
|
|
HRESULT CSampleCredential::UnAdvise()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
_pWrappedCredential->UnAdvise();
|
|
}
|
|
|
|
_CleanupEvents();
|
|
|
|
return hr;
|
|
}
|
|
|
|
// LogonUI calls this function when our tile is selected (zoomed)
|
|
// If you simply want fields to show/hide based on the selected state,
|
|
// there's no need to do anything here - you can set that up in the
|
|
// field definitions. In fact, we're just going to hand it off to the
|
|
// wrapped credential in case it wants to do something.
|
|
HRESULT CSampleCredential::SetSelected(__out BOOL* pbAutoLogon)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->SetSelected(pbAutoLogon);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Similarly to SetSelected, LogonUI calls this when your tile was selected
|
|
// and now no longer is. We'll let the wrapped credential do anything it needs.
|
|
HRESULT CSampleCredential::SetDeselected()
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->SetDeselected();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Get info for a particular field of a tile. Called by logonUI to get information to
|
|
// display the tile. We'll check to see if it's for us or the wrapped credential, and then
|
|
// handle or route it as appropriate.
|
|
HRESULT CSampleCredential::GetFieldState(
|
|
__in DWORD dwFieldID,
|
|
__out CREDENTIAL_PROVIDER_FIELD_STATE* pcpfs,
|
|
__out CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE* pcpfis
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
// Make sure we have a wrapped credential.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
// Validate parameters.
|
|
if ((pcpfs != NULL) && (pcpfis != NULL))
|
|
{
|
|
// If the field is in the wrapped credential, hand it off.
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->GetFieldState(dwFieldID, pcpfs, pcpfis);
|
|
}
|
|
// Otherwise, we need to see if it's one of ours.
|
|
else
|
|
{
|
|
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
|
|
// If the field ID is valid, give it info it needs.
|
|
if (pfsp != NULL)
|
|
{
|
|
*pcpfs = pfsp->cpfs;
|
|
*pcpfis = pfsp->cpfis;
|
|
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Sets ppwsz to the string value of the field at the index dwFieldID. We'll check to see if
|
|
// it's for us or the wrapped credential, and then handle or route it as appropriate.
|
|
HRESULT CSampleCredential::GetStringValue(
|
|
__in DWORD dwFieldID,
|
|
__deref_out PWSTR* ppwsz
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
// Make sure we have a wrapped credential.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
// If this field belongs to the wrapped credential, hand it off.
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->GetStringValue(dwFieldID, ppwsz);
|
|
}
|
|
// Otherwise determine if we need to handle it.
|
|
else
|
|
{
|
|
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
|
|
if (pfsp != NULL)
|
|
{
|
|
hr = SHStrDupW(_rgFieldStrings[SFI_I_WORK_IN_STATIC], ppwsz);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
// Returns the number of items to be included in the combobox (pcItems), as well as the
|
|
// currently selected item (pdwSelectedItem). We'll check to see if it's for us or the
|
|
// wrapped credential, and then handle or route it as appropriate.
|
|
HRESULT CSampleCredential::GetComboBoxValueCount(
|
|
__in DWORD dwFieldID,
|
|
__out DWORD* pcItems,
|
|
__out_range(<,*pcItems) DWORD* pdwSelectedItem
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
// Make sure we have a wrapped credential.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
// If this field belongs to the wrapped credential, hand it off.
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->GetComboBoxValueCount(dwFieldID, pcItems, pdwSelectedItem);
|
|
}
|
|
// Otherwise determine if we need to handle it.
|
|
else
|
|
{
|
|
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
|
|
if (pfsp != NULL)
|
|
{
|
|
*pcItems = ARRAYSIZE(s_rgDatabases);
|
|
*pdwSelectedItem = _dwDatabaseIndex;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Called iteratively to fill the combobox with the string (ppwszItem) at index dwItem.
|
|
// We'll check to see if it's for us or the wrapped credential, and then handle or route
|
|
// it as appropriate.
|
|
HRESULT CSampleCredential::GetComboBoxValueAt(
|
|
__in DWORD dwFieldID,
|
|
__in DWORD dwItem,
|
|
__deref_out PWSTR* ppwszItem
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
// Make sure we have a wrapped credential.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
// If this field belongs to the wrapped credential, hand it off.
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->GetComboBoxValueAt(dwFieldID, dwItem, ppwszItem);
|
|
}
|
|
// Otherwise determine if we need to handle it.
|
|
else
|
|
{
|
|
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
|
|
if ((pfsp != NULL) && (dwItem < ARRAYSIZE(s_rgDatabases)))
|
|
{
|
|
hr = SHStrDupW(s_rgDatabases[dwItem], ppwszItem);
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Called when the user changes the selected item in the combobox. We'll check to see if
|
|
// it's for us or the wrapped credential, and then handle or route it as appropriate.
|
|
HRESULT CSampleCredential::SetComboBoxSelectedValue(
|
|
__in DWORD dwFieldID,
|
|
__in DWORD dwSelectedItem
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
// Make sure we have a wrapped credential.
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
// If this field belongs to the wrapped credential, hand it off.
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->SetComboBoxSelectedValue(dwFieldID, dwSelectedItem);
|
|
}
|
|
// Otherwise determine if we need to handle it.
|
|
else
|
|
{
|
|
FIELD_STATE_PAIR *pfsp = _LookupLocalFieldStatePair(dwFieldID);
|
|
if ((pfsp != NULL) && (dwSelectedItem < ARRAYSIZE(s_rgDatabases)))
|
|
{
|
|
_dwDatabaseIndex = dwSelectedItem;
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------
|
|
// 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. Even though
|
|
// we don't offer these field types ourselves, we need to pass along the request to the
|
|
// wrapped credential.
|
|
|
|
HRESULT CSampleCredential::GetBitmapValue(
|
|
__in DWORD dwFieldID,
|
|
__out HBITMAP* phbmp
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->GetBitmapValue(dwFieldID, phbmp);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSampleCredential::GetSubmitButtonValue(
|
|
__in DWORD dwFieldID,
|
|
__out DWORD* pdwAdjacentTo
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->GetSubmitButtonValue(dwFieldID, pdwAdjacentTo);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSampleCredential::SetStringValue(
|
|
__in DWORD dwFieldID,
|
|
__in PCWSTR pwz
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->SetStringValue(dwFieldID, pwz);
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT CSampleCredential::GetCheckboxValue(
|
|
__in DWORD dwFieldID,
|
|
__out BOOL* pbChecked,
|
|
__deref_out PWSTR* ppwszLabel
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
if (_IsFieldInWrappedCredential(dwFieldID))
|
|
{
|
|
hr = _pWrappedCredential->GetCheckboxValue(dwFieldID, pbChecked, ppwszLabel);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSampleCredential::SetCheckboxValue(
|
|
__in DWORD dwFieldID,
|
|
__in BOOL bChecked
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->SetCheckboxValue(dwFieldID, bChecked);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CSampleCredential::CommandLinkClicked(__in DWORD dwFieldID)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->CommandLinkClicked(dwFieldID);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
//------ end of methods for controls we don't have ourselves ----//
|
|
|
|
|
|
//
|
|
// Collect the username and password into a serialized credential for the correct usage scenario
|
|
// (logon/unlock is what's demonstrated in this sample). LogonUI then passes these credentials
|
|
// back to the system to log on.
|
|
//
|
|
HRESULT CSampleCredential::GetSerialization(
|
|
__out CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE* pcpgsr,
|
|
__out CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs,
|
|
__deref_out_opt PWSTR* ppwszOptionalStatusText,
|
|
__out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->GetSerialization(pcpgsr, pcpcs, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// ReportResult is completely optional. However, we will hand it off to the wrapped
|
|
// credential in case they want to handle it.
|
|
HRESULT CSampleCredential::ReportResult(
|
|
__in NTSTATUS ntsStatus,
|
|
__in NTSTATUS ntsSubstatus,
|
|
__deref_out_opt PWSTR* ppwszOptionalStatusText,
|
|
__out CREDENTIAL_PROVIDER_STATUS_ICON* pcpsiOptionalStatusIcon
|
|
)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (_pWrappedCredential != NULL)
|
|
{
|
|
hr = _pWrappedCredential->ReportResult(ntsStatus, ntsSubstatus, ppwszOptionalStatusText, pcpsiOptionalStatusIcon);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL CSampleCredential::_IsFieldInWrappedCredential(
|
|
__in DWORD dwFieldID
|
|
)
|
|
{
|
|
return (dwFieldID < _dwWrappedDescriptorCount);
|
|
}
|
|
|
|
FIELD_STATE_PAIR *CSampleCredential::_LookupLocalFieldStatePair(
|
|
__in DWORD dwFieldID
|
|
)
|
|
{
|
|
// Offset into the ID to account for the wrapped fields.
|
|
dwFieldID -= _dwWrappedDescriptorCount;
|
|
|
|
// If the index if valid, give it the info it wants.
|
|
if (dwFieldID < SFI_NUM_FIELDS)
|
|
{
|
|
return &(_rgFieldStatePairs[dwFieldID]);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void CSampleCredential::_CleanupEvents()
|
|
{
|
|
// Call Uninitialize before releasing our reference on the real
|
|
// ICredentialProviderCredentialEvents to avoid having an
|
|
// invalid reference.
|
|
if (_pWrappedCredentialEvents != NULL)
|
|
{
|
|
_pWrappedCredentialEvents->Uninitialize();
|
|
_pWrappedCredentialEvents->Release();
|
|
_pWrappedCredentialEvents = NULL;
|
|
}
|
|
|
|
if (_pCredProvCredentialEvents != NULL)
|
|
{
|
|
_pCredProvCredentialEvents->Release();
|
|
_pCredProvCredentialEvents = NULL;
|
|
}
|
|
}
|