// // 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) 2006 Microsoft Corporation. All rights reserved. // // Modifications (c) 2009-2010 Oracle Corporation // #include #include #include #include "VBoxCredProv.h" #include "VBoxCredential.h" #include "guid.h" VBoxCredProv::VBoxCredProv(void) : m_cRef(1), m_pPoller(NULL), m_pCred(NULL), m_pCredProvEvents(NULL), m_fGotCredentials(false) { LONG l = DllAddRef(); int rc = RTR3Init(); /* Never terminate the runtime! */ if (RT_FAILURE(rc)) LogRel(("VBoxCredProv: Could not init runtime! rc = %Rrc\n", rc)); rc = VbglR3Init(); if (RT_FAILURE(rc)) LogRel(("VBoxCredProv: Could not init guest library! rc = %Rrc\n", rc)); if (l == 1) /* First instance? */ LogRel(("VBoxCredProv: Loaded.\n")); Log(("VBoxCredProv: DLL refcount (load) = %ld\n", l)); } VBoxCredProv::~VBoxCredProv(void) { Log(("VBoxCredProv::~VBoxCredProv\n")); if (m_pCred != NULL) { m_pCred->Release(); m_pCred = NULL; } if (m_pPoller != NULL) { m_pPoller->Shutdown(); delete m_pPoller; m_pPoller = NULL; } LONG lRefCount = DllGetRefCount(); if (lRefCount == 1) /* First (=last) instance unloaded? */ LogRel(("VBoxCredProv: Unloaded.\n")); Log(("VBoxCredProv: DLL refcount (unload) = %ld\n", lRefCount)); VbglR3Term(); DllRelease(); } /* * SetUsageScenario is the provider's cue that it's going to be asked for tiles * in a subsequent call. This call happens after the user pressed CTRL+ALT+DEL * and we need to handle the CPUS_LOGON event. */ HRESULT VBoxCredProv::SetUsageScenario(CREDENTIAL_PROVIDER_USAGE_SCENARIO cpUsageScenario, DWORD dwFlags) { UNREFERENCED_PARAMETER(dwFlags); HRESULT hr; m_cpUsageScenario = cpUsageScenario; /* Decide which scenarios to support here. Returning E_NOTIMPL simply tells the caller * that we're not designed for that scenario. */ switch (m_cpUsageScenario) { case CPUS_LOGON: case CPUS_UNLOCK_WORKSTATION: if (m_pPoller == NULL) { m_pPoller = new VBoxCredPoller(); Assert(m_pPoller); if (false == m_pPoller->Initialize(this)) Log(("VBoxCredProv::SetUsageScenario: Could not initialize credentials poller thread!\n")); } if (m_pCred == NULL) { m_pCred = new VBoxCredential(this); /* All stuff allocated? */ if (m_pCred != NULL) { hr = m_pCred->Initialize(m_cpUsageScenario, s_rgCredProvFieldDescriptors, s_rgFieldStatePairs); } else { hr = E_OUTOFMEMORY; Log(("VBoxCredProv::SetUsageScenario: Out of memory!\n")); } } else { /* All set up already! */ hr = S_OK; } /* If we did fail -> cleanup */ if (FAILED(hr)) { if (m_pCred != NULL) { m_pCred->Release(); m_pCred = NULL; } } break; case CPUS_CHANGE_PASSWORD: case CPUS_CREDUI: case CPUS_PLAP: hr = E_NOTIMPL; break; default: hr = E_INVALIDARG; break; } Log(("VBoxCredProv::SetUsageScenario returned 0x%08x (cpUS: %d, Flags: %ld)\n", hr, cpUsageScenario, dwFlags)); 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 credential. * * 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. */ STDMETHODIMP VBoxCredProv::SetSerialization(const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpCredentialSerialization) { UNREFERENCED_PARAMETER(pcpCredentialSerialization); 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 (visible UI elements) * that they enumerated. */ HRESULT VBoxCredProv::Advise(ICredentialProviderEvents *pcpEvents, UINT_PTR upAdviseContext) { Log(("VBoxCredProv::Advise\n")); if (m_pCredProvEvents != NULL) m_pCredProvEvents->Release(); m_pCredProvEvents = pcpEvents; Assert(m_pCredProvEvents); m_pCredProvEvents->AddRef(); /* * Save advice context for later use when binding to * certain ICredentialProviderEvents events. */ m_upAdviseContext = upAdviseContext; return S_OK; } /* Called by LogonUI when the ICredentialProviderEvents callback is no longer valid. */ HRESULT VBoxCredProv::UnAdvise() { Log(("VBoxCredProv::UnAdvise\n")); if (m_pCredProvEvents != NULL) { m_pCredProvEvents->Release(); m_pCredProvEvents = NULL; } return S_OK; } /* * 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 VBoxCredProv::GetFieldDescriptorCount(DWORD *pdwCount) { Assert(pdwCount); *pdwCount = SFI_NUM_FIELDS; Log(("VBoxCredProv::GetFieldDescriptorCount: %ld\n", *pdwCount)); return S_OK; } /* Gets the field descriptor for a particular field. */ HRESULT VBoxCredProv::GetFieldDescriptorAt(DWORD dwIndex, CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR **ppcpFieldDescriptor) { /* Verify dwIndex is a valid field */ HRESULT hr; if ( dwIndex < SFI_NUM_FIELDS && ppcpFieldDescriptor) { hr = FieldDescriptorCoAllocCopy(s_rgCredProvFieldDescriptors[dwIndex], ppcpFieldDescriptor); } else { hr = E_INVALIDARG; } Log(("VBoxCredProv::GetFieldDescriptorAt: hr=0x%08x, index=%ld, ppcpfd=%p\n", hr, dwIndex, ppcpFieldDescriptor)); 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 tile the behavior is the last used cred * prov gets to specify the default tile to be displayed * * 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 VBoxCredProv::GetCredentialCount(DWORD *pdwCount, DWORD *pdwDefault, BOOL *pbAutoLogonWithDefault) { Assert(pdwCount); Assert(pdwDefault); Assert(pbAutoLogonWithDefault); bool fGotCredentials = false; /* Poller thread create/active? */ if ( m_pPoller && m_pCred) { fGotCredentials = m_pPoller->QueryCredentials(m_pCred); } if (fGotCredentials) { *pdwCount = 1; /* This provider always has the same number of credentials (1). */ *pdwDefault = 0; /* The credential we provide is *always* at index 0! */ *pbAutoLogonWithDefault = TRUE; /* We always at least try to auto-login (if password is correct). */ } else { *pdwCount = 0; *pdwDefault = CREDENTIAL_PROVIDER_NO_DEFAULT; *pbAutoLogonWithDefault = FALSE; } Log(("VBoxCredProv::GetCredentialCount: *pdwCount=%ld, *pdwDefault=%ld, *pbAutoLogonWithDefault=%s\n", *pdwCount, *pdwDefault, *pbAutoLogonWithDefault ? "true" : "false")); return S_OK; } /* * Returns the credential at the index specified by dwIndex. This function is called by logonUI to enumerate * the tiles. */ HRESULT VBoxCredProv::GetCredentialAt(DWORD dwIndex, ICredentialProviderCredential **ppCredProvCredential) { HRESULT hr; Log(("VBoxCredProv::GetCredentialAt: Index=%ld, ppCredProvCredential=%p\n", dwIndex, ppCredProvCredential)); if (m_pCred == NULL) { Log(("VBoxCredProv::GetCredentialAt: No credentials available.\n")); return E_INVALIDARG; } /* Validate parameters (we only have one credential). */ if( dwIndex == 0 && ppCredProvCredential) { hr = m_pCred->QueryInterface(IID_ICredentialProviderCredential, reinterpret_cast(ppCredProvCredential)); } else { Log(("VBoxCredProv::GetCredentialAt: More than one credential not supported!\n")); hr = E_INVALIDARG; } return hr; } /* Do a credential re-enumeration if we got the event to do so. */ void VBoxCredProv::OnCredentialsProvided() { Log(("VBoxCredProv::OnCredentialsProvided\n")); if (m_pCredProvEvents != NULL) m_pCredProvEvents->CredentialsChanged(m_upAdviseContext); } /* Creates our provider. This happens *before* CTRL-ALT-DEL was pressed! */ HRESULT VBoxCredProv_CreateInstance(REFIID riid, void** ppv) { HRESULT hr; VBoxCredProv* pProvider = new VBoxCredProv(); if (pProvider) { hr = pProvider->QueryInterface(riid, ppv); pProvider->Release(); } else { hr = E_OUTOFMEMORY; } return hr; }