使用自定义身份调用 QueryInterface

bru*_*eng 5 c++ com dcom opc

问题:

我成功地在代理上调用 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;
Run Code Online (Sandbox Code Playgroud)

然后我可以使用此服务器信息进行调用,获取我的 OPC 服务器 ( ) 的CoCreateInstanceEx句柄 ( )。m_IOPCServerIID_IOPCServer

获得句柄后,我发现有必要通过以下调用再次设置更多权限(请参阅DCOM 中的模拟如何工作? ):

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);
Run Code Online (Sandbox Code Playgroud)

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

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

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

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
Run Code Online (Sandbox Code Playgroud)

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

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

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

hr = m_IOPCItemMgt->QueryInterface(IID_IOPCSyncIO, (void**)&m_IOPCSyncIO);
Run Code Online (Sandbox Code Playgroud)

但这样做的同时也提供了authIdentity。这可能吗?可以通过 CoCreateInstanceEx 或 CoGetClassObject 或其他 COM 调用来完成吗?

小智 1

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