我已经使用来自SampleWrapExistingCredentialProvider的VistaCredentialProviderSamples开发了一个定制的凭证提供程序。凭据提供程序实现了一个筛选器,可以过滤所有其他凭据提供程序,而我在登录时只看到我的凭据提供程序。问题是,如果我们使用远程桌面连接连接到它,用户名/密码就不会从windows RDP客户端传递给凭据提供程序,而我必须在RDP会话打开时再次输入它(与默认提供程序的行为不同)。
我正在尝试探索代码的哪一部分处理凭据提供程序从远程桌面客户端接受用户名/密码而不再询问的场景。附件是在RDP客户端上提供成功凭据后我的凭据提供者的屏幕截图。单击凭据提供程序的此图标后,将向凭据提供程序显示再次询问用户名和密码的请求。对于如何从RDP客户端接收凭据,任何帮助都将不胜感激。


我已经为CREDUI返回了S_OK。我的SetUsageScenario如下:
HRESULT CSampleProvider::SetUsageScenario(
CREDENTIAL_PROVIDER_USAGE_SCENARIO cpus,
DWORD dwFlags
)
{
HRESULT hr;
// Create the password credential provider and query its interface for an
// ICredentialProvider we can use. Once it's up and running, ask it about the
// usage scenario being provided.
IUnknown *pUnknown = NULL;
hr = ::CoCreateInstance(CLSID_PasswordCredentialProvider, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pUnknown));
if (SUCCEEDED(hr))
{
hr = pUnknown->QueryInterface(IID_PPV_ARGS(&(_pWrappedProvider)));
if (SUCCEEDED(hr))
{
hr = _pWrappedProvider->SetUsageScenario(cpus, dwFlags);
switch (cpus)
{
case CPUS_LOGON:
case CPUS_UNLOCK_WORKSTATION:
case CPUS_CREDUI:
{
hr = S_OK;
break;
}
case CPUS_CHANGE_PASSWORD:
default:
hr = E_INVALIDARG;
break;
}
}
}
if (FAILED(hr))
{
if (_pWrappedProvider != NULL)
{
_pWrappedProvider->Release();
_pWrappedProvider = NULL;
}
}
return hr;
}发布于 2019-07-14 19:38:09
用户名/密码不会从windows RDP客户端传递给凭据提供程序,我必须在RDP会话打开时再次输入它(与默认提供程序的行为不同)
windows不能通过某种魔术知道客户端的用户名/密码,它是通过rdp连接的。
在客户端开始时,某些凭据提供者必须创建CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION并将其传递给服务器。在它内部,clsidCredentialProvider说哪个具体的提供者收集这个序列化。
使用这个CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION必须做什么服务器?显然,将其传递给某个凭据提供者SetSerialization方法。但为了什么?为了所有人?不是的。同样,仅对于与clsidCredentialProvider完全匹配的提供者( clsid与CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION完全匹配)来说,这是显而易见的。此提供程序(如果存在且未过滤)必须记住此凭据,然后当称为GetCredentialCount时,请说它有无可靠的凭据(而不是CREDENTIAL_PROVIDER_NO_DEFAULT),并且通常已经准备好进行自动登录尝试。
密码提供程序从客户端(mstsc)创建序列化。__uuidof(PasswordCredentialProvider)或__uuidof(V1PasswordCredentialProvider) (如果客户端运行在win7上)在clsidCredentialProvider中也是如此。
但您可以在自筛选器中禁用此提供程序。因此,你自己打破了过程。
过滤器必须实现UpdateRemoteCredential方法。在这里,复制和更新传递了pcpcsIn。最重要的,也是其中的一部分--我们必须将替换 clsidCredentialProvider到self CLSID。因此,我们的SetSerialization方法将被调用。在这里,我们需要还原原始的CLSID,然后将它传递给包装的凭证。
同样重要的是-在GetCredentialCount内部-首先将其传递给包装凭证,然后执行*pbAutoLogonWithDefault = FALSE; -禁用自动说明-如果您需要额外的(OTP ?)客户提供的信息。
在UpdateRemoteCredential方法中,如果声明为const,则不能修改pcpcsIn。所以我们需要把更新的凭证写到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
从横向上分配更多的内存,最后保存原始的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内部设置(对于RDP,我不是在客户端设置CPUS_CREDUI,只是为了"run“)--我只知道这是远程连接,并保存此信息(_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;//!!!行
发布于 2019-05-28 08:26:51
根据官方文档:RDC和自定义凭据提供者
如果用户与非Microsoft凭据提供程序连接,则终端服务器上将再次提示您输入凭据(两次)。如果未启用NLA,则尽管在连接之前在客户端上使用不受支持的凭据提供程序输入,用户仍将被连接。您将留在登录屏幕上,在那里您可以使用本地身份验证所支持的任何凭据提供程序。在使用不受支持的凭据提供程序时,无法避免这两种身份验证。 话虽如此,如果您有自己的凭据提供程序,并且尝试在远程桌面连接到Vista盒(有此凭据提供程序),那么您需要登录两次。这是一个预期的行为,它是故意的,没有合法的方法来避免它。
https://stackoverflow.com/questions/56317142
复制相似问题