使用自定义身份调用 QueryInterface

Posted

技术标签:

【中文标题】使用自定义身份调用 QueryInterface【英文标题】:Calling QueryInterface with custom identity 【发布时间】:2017-01-20 11:13:31 【问题描述】:

问题:

我在代理上成功调用 CoSetProxyBlanket(如果这是正确的术语),然后我在同一个代理上调用 QueryInterface,但我收到 0x80070005(“拒绝访问”)的结果。但是,如果我首先使用相同的凭据调用 CoInitializeSecurity (我试图避免),那么调用就会成功。

问题:

如何在不调用 CoInitializeSecurity 的情况下成功获取我需要的接口?据我了解,一个进程只能调用一次此方法,因此它与制作 dll 不兼容,通常可以用对 CoSetProxyBlanket 的调用来代替。

详情:

我正在尝试构建自己的 OPC 客户端,该客户端可以与运行在不同域中的计算机进行通信, 没有匹配的用户帐户。

首先,我使用在服务器上有效的域、用户名和密码创建身份结构:

COAUTHINFO      authInfo;
COAUTHIDENTITY  authIdentity;

authIdentity.Domain             = (unsigned short *) w_domain;
authIdentity.DomainLength       = wcslen( w_domain);
authIdentity.Flags              = SEC_WINNT_AUTH_IDENTITY_UNICODE;
authIdentity.Password           = (unsigned short *) w_password;
authIdentity.PasswordLength     = wcslen(w_password);
authIdentity.User               = (unsigned short *) w_username;
authIdentity.UserLength         = wcslen(w_username);

authInfo.dwAuthnLevel           = RPC_C_AUTHN_LEVEL_CALL;
authInfo.dwAuthnSvc             = RPC_C_AUTHN_WINNT;
authInfo.dwAuthzSvc             = RPC_C_AUTHZ_NONE;
authInfo.dwCapabilities         = EOAC_NONE;
authInfo.dwImpersonationLevel   = RPC_C_IMP_LEVEL_IMPERSONATE;
authInfo.pAuthIdentityData      = &authIdentity;
authInfo.pwszServerPrincName    = NULL;

ServerInfo.pAuthInfo = &authInfo;

然后我可以使用此服务器信息调用 CoCreateInstanceEx 并获取我的 OPC 服务器 (IID_IOPCServer) 的句柄 (m_IOPCServer)。

获得句柄后,我发现有必要再次通过此调用设置更多权限(参见How does impersonation in DCOM work?):

hr = CoSetProxyBlanket(m_IOPCServer, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 
         NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 
         &authIdentity, EOAC_NONE);

在此之后,我能够成功获取 OPC 项目组的句柄:

hr = m_IOPCServer->AddGroup(L"", FALSE, reqUptRate, clientHandle, 
         NULL, NULL, lcid, &m_hServerGroup, &revisedUptRate, 
         IID_IOPCItemMgt,(LPUNKNOWN*)&m_IOPCItemMgt);

但是,当我尝试使用此代码时:

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);

结果为 0x80070005(“拒绝访问”)。即使我在 m_IOPCItemMgt 上成功调用 CoSetProxyBlanket 也是如此。但是,如果我首先调用 CoInitializeSecurity,则调用成功。

我认为与How does impersonation in DCOM work? 相关的问题在于QueryInterface 函数是一种对象创建形式,因此它不使用与AddGroup 等其他方法调用相同的安全性。然而,在 Microsoft 参考文献QueryInterface 中,在给实现者的注释下,它听起来好像 QueryInterface 不应该检查 ACL,并且在返回值下,没有提到拒绝访问的可能性。我不认为这个问题是特定于实现的,因为我已经在一些著名的商业 OPC 服务器(例如 Matrikon Simulation Server)以及没有实现任何额外安全性的开源 LightOPC 上尝试了我的代码。

我猜我需要做的是找到一种方法来复制这个命令

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);

但同时提供authIdentity。这可能吗?可以通过 CoCreateInstanceEx 或 CoGetClassObject 或其他一些 COM 调用来完成吗?

【问题讨论】:

【参考方案1】:

无需过多介绍:CoInitializeSecurity 始终在每个进程中至少调用一次。这可以隐式或显式地完成。如果您的代码没有进行显式调用,则 DCOM 运行时会使用从注册表填充的参数为您执行此操作。您可以尝试调整适当的注册表值,以强制 DCOm 使用与显式调用中使用的值类似的值。保存这些值的注册表项是“HKEY_LOCAL_MACHINE\SOFTWARE\Classes\AppIDAppID_GUID” 这里描述了这个键:https://msdn.microsoft.com/en-us/library/windows/desktop/ms693736(v=vs.85).aspx

【讨论】:

这对我没有帮助。如何使用与进程不同的安全上下文调用 QueryInterface?例如,如何创建一个连接到多个 OPC 服务器的程序,每个服务器使用不同的凭据? 只是对您的“在返回值下,没有提及拒绝访问的可能性”的注释:在 COM 中,一组成功代码被视为接口合同的一部分,而一组错误代码代码不是。也就是说,即使没有记录,接口方法也可以返回它想要的任何错误,而不会破坏“接口契约”。几乎任何 (D)COM 方法都可以返回许多错误代码(例如 RPC_xxxx 错误代码),并且每种方法都没有记录它们。因此,记录在案的错误代码是“为了说明”,那些值得解释的,仅此而已。 您对 QuerryInterface 调用中的安全上下文没有太多控制权。您可能可以实现自定义代理并在代理中调整 CoInitializeSecurity,但每个代理都有一个安全上下文。更多关于自定义代理的信息:msdn.microsoft.com/en-us/library/windows/desktop/…【参考方案2】:

您必须在每个新的 COM 对象实例上调用 CoSetProxyBlanket,因此在您的情况下,即使是 m_IOPCItemMgt,您也必须调用它。

【讨论】:

【参考方案3】:

在使用QueryInterface之前需要在IUnknown接口中调用CoSetProxyBlanket

CComPtr<IUnknown> pUnknown;
hr = m_IOPCItemMgt->QueryInterface(IID_IUnknown, (void**)&pUnknown);
hr = CoSetProxyBlanket(pUnknown, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, 
         NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, 
         &authIdentity, EOAC_NONE);
hr = pUnknown->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);

您可以查看this了解更多信息。

【讨论】:

以上是关于使用自定义身份调用 QueryInterface的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security:未调用自定义 UserDetailsS​​ervice(使用 Auth0 身份验证)

Spring自定义身份验证过滤器和提供者不调用控制器方法

Spring Security 自定义 AuthenticationProvider 验证方法调用了两次

甚至没有调用自定义 Spring Security 身份验证提供程序?

使用 IdentityServer 与创建基于 JWT 的自定义身份验证

Keycloak 自定义用户联合和身份提供者工作顺序