the username/password are not passed from windows RDP client to the
Credential provider and I have to enter it again when RDP session
opens (unlike the behavior with default provider)
windows can not by some magic know client username/password, which connecting by rdp.
at begin on client side some credential provider must create CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
and pass it to server. inside it clsidCredentialProvider
say which concrete provider collect this serialization.
what server must do with this CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
? obvious pass it to some credential provider SetSerialization
method. but for which ? for all ? no. again obvivous only for provider which clsid exactly matches clsidCredentialProvider
from CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
. this provider (if exist and not filtered) must remember this credentional and then when called GetCredentialCount
- say that it have dafault credentional (not CREDENTIAL_PROVIDER_NO_DEFAULT
) and it usually ready for auto logon attempt with this.
from client side (mstsc) the password provider create serialization. so will be __uuidof(PasswordCredentialProvider)
or __uuidof(V1PasswordCredentialProvider)
(if client run on win7) in clsidCredentialProvider
.
but you disable this providers in self filter. as result you yourself break process.
filter must implement UpdateRemoteCredential
method. and here copy and update passed pcpcsIn. most importand part of this - we must replace clsidCredentialProvider to self CLSID. as result our SetSerialization
method will be called. here we need restore original CLSID before pass it to wrapped credentional.
also important place - inside GetCredentialCount
- first pass it to wrapped credentional and then do *pbAutoLogonWithDefault = FALSE;
- disable autologon - you can not do this (autologon) if you require additional (OTP ?) information from client.
inside UpdateRemoteCredential
method we can not modify pcpcsIn - if declared as const. so we need write our update credentional to pcpcsOut. because system can not know which size is required for rgbSerialization
- we need allocate it yourself. and system then free it. obvivous need use CoTaskMemAlloc
for allocate rgbSerialization
.
so - all this can be understanded and without any documentation. however if all this was documented - will be not bad too.
so code for UpdateRemoteCredential
:
HRESULT STDMETHODCALLTYPE CSampleProvider::UpdateRemoteCredential(
/* [annotation][in] */
_In_ const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcsIn,
/* [annotation][out] */
_Out_ CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *pcpcsOut)
{
if (pcpcsIn->clsidCredentialProvider != __uuidof(PasswordCredentialProvider) &&
pcpcsIn->clsidCredentialProvider != __uuidof(V1PasswordCredentialProvider))
{
// we dont know format of serialization
return E_UNEXPECTED;
}
ULONG cbSerialization = pcpcsIn->cbSerialization;
if (pcpcsOut->rgbSerialization = (PBYTE)CoTaskMemAlloc(cbSerialization + sizeof(GUID)))
{
memcpy(pcpcsOut->rgbSerialization, pcpcsIn->rgbSerialization, cbSerialization);
memcpy(pcpcsOut->rgbSerialization + cbSerialization, &pcpcsIn->clsidCredentialProvider, sizeof(GUID));
pcpcsOut->cbSerialization = cbSerialization + sizeof(GUID);
pcpcsOut->ulAuthenticationPackage = pcpcsIn->ulAuthenticationPackage;
pcpcsOut->clsidCredentialProvider = __uuidof(CSampleProvider);
return S_OK;
}
return E_OUTOFMEMORY;
}
if we dont know the clsidCredentialProvider
- simply return E_UNEXPECTED
oterwise allocate more (on sizeof(CLSID)) memory and save original clsidCredentialProvider
in the end
now SetSerialization
:
HRESULT STDMETHODCALLTYPE CSampleProvider::SetSerialization(
__in const CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION* pcpcs
)
{
if (pcpcs->clsidCredentialProvider == __uuidof(CSampleProvider))
{
// can not query WTSIsRemoteSession, small optimization
_IsRemoteSession = true;
// we got this via ICredentialProviderFilter::UpdateRemoteCredential
ULONG cbSerialization = pcpcs->cbSerialization;
if (cbSerialization >= sizeof(GUID))
{
// restore original clsidCredentialProvider
cbSerialization -= sizeof(GUID);
memcpy(const_cast<GUID*>(&pcpcs->clsidCredentialProvider), pcpcs->rgbSerialization + cbSerialization, sizeof(GUID));
const_cast<CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION*>(pcpcs)->cbSerialization = cbSerialization;
}
}
return _pWrappedProvider->SetSerialization(pcpcs);
}
restore original clsidCredentialProvider
and fix cbSerialization
. also because pcpcs->clsidCredentialProvider == __uuidof(CSampleProvider)
can be set only inside UpdateRemoteCredential
in my case (i not do CPUS_CREDUI
on client side for RDP, only for "run as admin") - i just know that this is remote connection and save this information (_IsRemoteSession = true;
) for not call WTSIsRemoteSession
finally GetCredentialCount
:
HRESULT STDMETHODCALLTYPE CSampleProvider::GetCredentialCount(
__out DWORD* pdwCount,
__out_range(<,*pdwCount) DWORD* pdwDefault,
__out BOOL* pbAutoLogonWithDefault
)
{
HRESULT hr = _pWrappedProvider->GetCredentialCount(pdwCount, pdwDefault, pbAutoLogonWithDefault);
*pbAutoLogonWithDefault = FALSE;//!!!
return hr;
}
note very important *pbAutoLogonWithDefault = FALSE;//!!!
line