使用自定义标识调用QueryInterface

时间:2016-09-12 23:16:22

标签: 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;

然后我可以使用此服务器信息调用CoCreateInstanceEx获取到我的OPC服务器(m_IOPCServer)的句柄(IID_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,并且在返回值下,不会提到Access Denied作为可能性。我不认为这个问题是特定于实现的,因为我已经在一些众所周知的商业OPC服务器(例如Matrikon Simulation Server)上尝试了我的代码,以及没有实现任何额外安全性的开源LightOPC。

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

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

但在提供authIdentity的同时这样做。这可能吗?可以使用CoCreateInstanceEx或CoGetClassObject或其他一些COM调用吗?

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

您必须在每个新的COM对象实例上调用# The following does not crash, but we removed the while_loop, so the output is incorrect def f( x ): ... def body( g, x ): # Compute the gradient here grad = tf.gradients( g, x )[0] ... return ... return body(...) ,因此在您的情况下,您甚至必须为CoSetProxyBlanket调用它。

答案 2 :(得分:0)

使用CoSetProxyBlanket前需要在IUnknown界面调用QueryInterface

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了解更多信息。