14

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

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

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

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

但是当我尝试查询服务器支持的接口时,我得到 E_ACCESSDENIED。

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

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

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

现在,为什么这个用户会登录服务器,尤其是当我调用所有事物的 QueryInterface 时?

研究 CreateInterfaceEx 参数,似乎存在某种模拟机制。但目前还不清楚谁冒充谁。涉及三个用户凭据:

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

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

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

不管你怎么看,如果涉及#3,那就是一个用户太多了。如果 DCOM 无论如何都要在服务器 PC 上识别/模拟 #3,为什么我需要指定 #2 的凭据?到什么地步?

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

有人可以解释一下模拟是如何工作的,以及是否有办法忽略它并以 dcomcnfg 中指定的用户身份运行?

4

2 回答 2

13

回答我自己的问题。经过多次探索,很明显DCOM 有两种不同的识别情况

  1. 对象创建授权 (CoCreateInstanceEx)
  2. 方法调用的授权。

由于未知的原因,#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 包装器可能会更好。

于 2011-05-25T13:37:17.567 回答
0

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

于 2018-02-02T09:58:36.357 回答