如何在DCOM中进行模拟?

时间:2011-05-25 10:52:51

标签: windows com remoting dcom

我有一个使用OLE自动化编组器的DCOM客户端和服务器应用程序。它们在同一台PC上运行时工作正常,但当服务器位于不在同一域的不同PC上时,我得到E_ACCESSDENIED(0x80070005)。

服务器PC配置了dcomcnfg,以便为我在客户端上指定的登录名和密码的用户提供对任何DCOM对象的所有访问权限。 ServerApp及其类型库已在服务器pc上注册。

类型库也在客户端PC上注册。我直接在ClientApp中指定服务器名称,因此据我所知,客户端PC上不需要dcomcnfg配置。

具有服务器名称,登录名,域名和密码的CreateInstanceEx()工作正常。它返回IUnknown,同时在服务器PC上启动ServerApp。

但是当我尝试使用QueryInterface()获取服务器支持的接口时,我得到了E_ACCESSDENIED。

分析安全事件日志,我有两条记录:

首先,我在ClientApp中指定其凭据的用户成功进行网络登录。当我调用CreateInstanceEx()时会发生这种情况。

接下来,我在客户端PC上登录的用户失败登录尝试。由于两台PC不在域中,因此服务器PC不知道该用户。

现在,为什么这个用户会登录到服务器,特别是当我调用所有东西的QueryInterface时?

研究CreateInterfaceEx参数,看起来有某种冒充机制正在进行中。但目前还不清楚是谁冒充了谁。涉及三个用户凭证:

  1. ServerApp在服务器PC上运行的用户(在dcomcnfg中配置)。

  2. ClientApp在连接时指定其凭据的用户。

  3. ClientApp在客户端PC上运行其凭据的用户。

  4. 无论你怎么看,如果涉及到#3,那么一个用户太多了。如果DCOM要在服务器PC上识别/模仿#3,为什么我需要指定#2的凭证?到了什么程度?

    DCOM模仿#2似乎是合乎逻辑的,因为这是我明确指定的凭据。但为什么第二次登录尝试呢?

    有人可以解释模仿的确切方式吗以及是否有办法忽略它并以dcomcnfg中指定的用户身份运行?

2 个答案:

答案 0 :(得分:13)

回答我自己的问题。经过大量探索后,很明显 DCOM有两种不同的识别案例

  1. 对象创建授权(CoCreateInstanceEx)
  2. 方法调用的授权。
  3. 由于未知原因,#2不会继承#1设置。默认情况下,它使用客户端进程的凭据,因此会出现奇怪的登录信息。

    有两种方法可以为#2指定凭据。第一个是 CoSetProxyBlanket 。它仅为指定代理(marshaller-unmarshaller)设置凭据:

    CoCreateInstanceEx(IID_IObject1, /*login, pass*/, obj1); //Success!
    //Logged in and recevied IObject1 proxy in obj1
    
    obj1->DoSomething();
    //IObject1 proxy in obj1 now tries to login under process credentials.
    //Failure! E_ACCESSDENIED
    
    CoSetProxyBlanket(obj1, /*login, pass*/); //Success!
    //IObject1 proxy is now authorized.
    
    obj1->DoSomething(); //Success!
    obj1->QueryInterface(IID_IObject2, obj2); //Success!
    
    obj2->DoSomethingElse(); //Failure!
    //This different proxy for IObject2 have not yet been authorized.
    
    CoSetProxyBlanket(obj2, /*login, pass*/);
    //etc.
    

    值得注意的是,虽然CoCreateInstanceEx要求模拟级别至少为IMPERSONATE,但CoSetProxyBlanket似乎不能处理IDENTIFY以外的任何内容。

    另一种选择是使用 CoInitializeSecurity 为整个过程设置默认凭据。然后,您不必在每个代理上调用CoSetProxyBlanket:

    CoInitializeSecurity(/* login, pass */);
    CoCreateInstanceEx(IID_IUnknown, /*login, pass*/, obj); //Success!
    obj->DoSomething(); //Success!
    

    在客户端上使用CoInitializeSecurity时,您必须指定 asAuthSvc ,即使MSDN说您没有。

    这种方法的缺点显然是,如果您有来自不同PC的多个DCOM对象,则必须在此调用中指定所有凭据,并且每次打开时都可能针对每台计算机尝试这些凭据。不同的代理人。

    当您从DLL运行时它也不可靠(如果进程具有不同的默认安全性,该怎么办?)。因此,在每次调用返回之前,最好实现一个CoSetsProxyBlanket的QueryInterface包装器。

答案 1 :(得分:0)

对于那些在Delphi工作的人来说,有一个小笔记可以节省你的大量时间。完成obj as ISomeInterface操作后,您必须为新实例调用CoSetProxyBlanket。这可能不是很明显,但我们都知道as运算符调用QueryInterface方法,它可以返回新实例。