имя пользователя / пароль не передаются от клиента Windows RDP к
Поставщик учетных данных и я должен ввести его снова, когда сеанс RDP
открывает (в отличие от поведения с поставщиком по умолчанию)
окна не могут каким-то волшебством узнать имя пользователя / пароль клиента, которые подключаются по rdp.
в начале на стороне клиента какой-либо поставщик учетных данных должен создать CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
и передать его на сервер. внутри него clsidCredentialProvider
говорят, какой конкретный поставщик собирает эту сериализацию.
что сервер должен делать с этим CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
? очевидно, передайте его какому-либо поставщику учетных данных SetSerialization
метод. но для чего? для всех ? нет. опять же, только для провайдера, который точно соответствует clsid clsidCredentialProvider
из CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION
. этот провайдер (если он существует и не отфильтрован) должен запомнить эти учетные данные, а затем при вызове GetCredentialCount
- сказать, что у него есть учетные данные по умолчанию (не CREDENTIAL_PROVIDER_NO_DEFAULT
), и он обычно готов к попытке автоматического входа с этим.
со стороны клиента (mstsc) поставщик паролей создает сериализацию. так будет __uuidof(PasswordCredentialProvider)
или __uuidof(V1PasswordCredentialProvider)
(если клиент работает на win7) в clsidCredentialProvider
.
но вы отключаете этот провайдер в самофильтре. в результате вы сами нарушаете процесс.
Фильтр
должен реализовывать метод UpdateRemoteCredential
. и вот скопируйте и обновите передали pcpcsIn . самая важная часть этого - мы должны заменить clsidCredentialProvider на собственный CLSID. в результате будет вызван наш метод SetSerialization
. здесь нам нужно восстановить оригинал CLSID , прежде чем передать его в упакованные учетные данные.
также важное место - внутри GetCredentialCount
- сначала передайте его в завернутые учетные данные, а затем выполните *pbAutoLogonWithDefault = FALSE;
- отключите автологон - вы не можете сделать это (автологон), если вам требуется дополнительный (OTP?) информация от клиента.
внутри UpdateRemoteCredential
метод, который мы не можем изменить pcpcsIn - если он объявлен как const. поэтому нам нужно написать наше обновление для pcpcsOut . потому что система не может знать, какой размер требуется для rgbSerialization
- нам нужно выделить самостоятельно. и система затем освобождает это. Явно нужно использовать CoTaskMemAlloc
для выделения rgbSerialization
.
так - все это можно понять и без какой-либо документации. однако, если все это было задокументировано - тоже будет неплохо.
код для 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;
}
если мы не знаем clsidCredentialProvider
- просто верните E_UNEXPECTED
иначе выделите больше (на sizeof (CLSID)) памяти и сохраните оригинал clsidCredentialProvider
в конце
сейчас 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);
}
восстановить исходное clsidCredentialProvider
и исправить cbSerialization
. также, потому что pcpcs->clsidCredentialProvider == __uuidof(CSampleProvider)
может быть установлен только внутри UpdateRemoteCredential
в моем случае (я не делаю CPUS_CREDUI
на стороне клиента для RDP, только для "запуска от имени администратора") - я просто знаю, что это удаленное соединение, и сохраните эту информацию (_IsRemoteSession = true;
) для не звонить WTSIsRemoteSession
наконец 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;
}
примечание очень важно *pbAutoLogonWithDefault = FALSE;//!!!
строка