// // 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. // // CSampleProvider implements ICredentialProvider, which is the main // interface that logonUI uses to decide which tiles to display. // In this sample, we will display one tile that uses each of the nine // available UI controls. #include #include "CSampleProvider.h" #include "CSampleCredential.h" #include "guid.h" CSampleProvider::CSampleProvider(): _cRef(1), _pCredential(nullptr), _pCredProviderUserArray(nullptr) { DllAddRef(); } CSampleProvider::~CSampleProvider() { if (_pCredential != nullptr) { _pCredential->Release(); _pCredential = nullptr; } if (_pCredProviderUserArray != nullptr) { _pCredProviderUserArray->Release(); _pCredProviderUserArray = nullptr; } DllRelease(); } // SetUsageScenario is the provider's cue that it's going to be asked for tiles // in a subsequent call. HRESULT CSampleProvider::SetUsageScenario( CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus, DWORD /*dwFlags*/) { HRESULT hr; // Decide which scenarios to support here. Returning E_NOTIMPL simply tells the caller // that we're not designed for that scenario. switch (cpus) { case CPUS_LOGON: case CPUS_UNLOCK_WORKSTATION: // The reason why we need _fRecreateEnumeratedCredentials is because ICredentialProviderSetUserArray::SetUserArray() is called after ICredentialProvider::SetUsageScenario(), // while we need the ICredentialProviderUserArray during enumeration in ICredentialProvider::GetCredentialCount() _cpus = cpus; _fRecreateEnumeratedCredentials = true; hr = S_OK; break; case CPUS_CHANGE_PASSWORD: case CPUS_CREDUI: hr = E_NOTIMPL; break; default: hr = E_INVALIDARG; break; } return hr; } // SetSerialization takes the kind of buffer that you would normally return to LogonUI for // an authentication attempt. It's the opposite of ICredentialProviderCredential::GetSerialization. // GetSerialization is implement by a credential and serializes that credential. Instead, // SetSerialization takes the serialization and uses it to create a tile. // // SetSerialization is called for two main scenarios. The first scenario is in the credui case // where it is prepopulating a tile with credentials that the user chose to store in the OS. // The second situation is in a remote logon case where the remote client may wish to // prepopulate a tile with a username, or in some cases, completely populate the tile and // use it to logon without showing any UI. // // If you wish to see an example of SetSerialization, please see either the SampleCredentialProvider // sample or the SampleCredUICredentialProvider sample. [The logonUI team says, "The original sample that // this was built on top of didn't have SetSerialization. And when we decided SetSerialization was // important enough to have in the sample, it ended up being a non-trivial amount of work to integrate // it into the main sample. We felt it was more important to get these samples out to you quickly than to // hold them in order to do the work to integrate the SetSerialization changes from SampleCredentialProvider // into this sample.] HRESULT CSampleProvider::SetSerialization( _In_ CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION const * /*pcpcs*/) { return E_NOTIMPL; } // Called by LogonUI to give you a callback. Providers often use the callback if they // some event would cause them to need to change the set of tiles that they enumerated. HRESULT CSampleProvider::Advise( _In_ ICredentialProviderEvents * /*pcpe*/, _In_ UINT_PTR /*upAdviseContext*/) { return E_NOTIMPL; } // Called by LogonUI when the ICredentialProviderEvents callback is no longer valid. HRESULT CSampleProvider::UnAdvise() { return E_NOTIMPL; } // Called by LogonUI to determine the number of fields in your tiles. This // does mean that all your tiles must have the same number of fields. // This number must include both visible and invisible fields. If you want a tile // to have different fields from the other tiles you enumerate for a given usage // scenario you must include them all in this count and then hide/show them as desired // using the field descriptors. HRESULT CSampleProvider::GetFieldDescriptorCount( _Out_ DWORD *pdwCount) { *pdwCount = SFI_NUM_FIELDS; return S_OK; } // Gets the field descriptor for a particular field. HRESULT CSampleProvider::GetFieldDescriptorAt( DWORD dwIndex, _Outptr_result_nullonfailure_ CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **ppcpfd) { HRESULT hr; *ppcpfd = nullptr; // Verify dwIndex is a valid field. if ((dwIndex < SFI_NUM_FIELDS) && ppcpfd) { hr = FieldDescriptorCoAllocCopy(s_rgCredProvFieldDescriptors[dwIndex], ppcpfd); } else { hr = E_INVALIDARG; } return hr; } // Sets pdwCount to the number of tiles that we wish to show at this time. // Sets pdwDefault to the index of the tile which should be used as the default. // The default tile is the tile which will be shown in the zoomed view by default. If // more than one provider specifies a default the last used cred prov gets to pick // the default. If *pbAutoLogonWithDefault is TRUE, LogonUI will immediately call // GetSerialization on the credential you've specified as the default and will submit // that credential for authentication without showing any further UI. HRESULT CSampleProvider::GetCredentialCount( _Out_ DWORD *pdwCount, _Out_ DWORD *pdwDefault, _Out_ BOOL *pbAutoLogonWithDefault) { *pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT; *pbAutoLogonWithDefault = FALSE; if (_fRecreateEnumeratedCredentials) { _fRecreateEnumeratedCredentials = false; _ReleaseEnumeratedCredentials(); _CreateEnumeratedCredentials(); } *pdwCount = 1; return S_OK; } // Returns the credential at the index specified by dwIndex. This function is called by logonUI to enumerate // the tiles. HRESULT CSampleProvider::GetCredentialAt( DWORD dwIndex, _Outptr_result_nullonfailure_ ICredentialProviderCredential **ppcpc) { HRESULT hr = E_INVALIDARG; *ppcpc = nullptr; if ((dwIndex == 0) && ppcpc) { hr = _pCredential->QueryInterface(IID_PPV_ARGS(ppcpc)); } return hr; } // This function will be called by LogonUI after SetUsageScenario succeeds. // Sets the User Array with the list of users to be enumerated on the logon screen. HRESULT CSampleProvider::SetUserArray(_In_ ICredentialProviderUserArray *users) { if (_pCredProviderUserArray) { _pCredProviderUserArray->Release(); } _pCredProviderUserArray = users; _pCredProviderUserArray->AddRef(); return S_OK; } void CSampleProvider::_CreateEnumeratedCredentials() { switch (_cpus) { case CPUS_LOGON: case CPUS_UNLOCK_WORKSTATION: { _EnumerateCredentials(); break; } default: break; } } void CSampleProvider::_ReleaseEnumeratedCredentials() { if (_pCredential != nullptr) { _pCredential->Release(); _pCredential = nullptr; } } HRESULT CSampleProvider::_EnumerateCredentials() { HRESULT hr = E_UNEXPECTED; if (_pCredProviderUserArray != nullptr) { DWORD dwUserCount; _pCredProviderUserArray->GetCount(&dwUserCount); if (dwUserCount > 0) { ICredentialProviderUser *pCredUser; hr = _pCredProviderUserArray->GetAt(0, &pCredUser); if (SUCCEEDED(hr)) { _pCredential = new(std::nothrow) CSampleCredential(); if (_pCredential != nullptr) { hr = _pCredential->Initialize(_cpus, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs, pCredUser); if (FAILED(hr)) { _pCredential->Release(); _pCredential = nullptr; } } else { hr = E_OUTOFMEMORY; } pCredUser->Release(); } } } return hr; } // Boilerplate code to create our provider. HRESULT CSample_CreateInstance(_In_ REFIID riid, _Outptr_ void **ppv) { HRESULT hr; CSampleProvider *pProvider = new(std::nothrow) CSampleProvider(); if (pProvider) { hr = pProvider->QueryInterface(riid, ppv); pProvider->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }