DCOM中的身份模拟是如何工作的?

15

我有一个使用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. 在服务器PC上运行ServerApp的用户(如在dcomcnfg中配置的)。

  2. 连接时ClientApp指定的用户凭证。

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

无论如何,如果涉及#3,则有一位多余的用户。如果DCOM要在服务器PC上识别/模拟#3,那么我为什么需要指定#2的凭据?有什么意义吗?

DCOM应该模拟#2很合乎逻辑,因为这是我明确指定的凭据。但是为什么还会出现第二次登录尝试呢?

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

2个回答

14

回答自己的问题。经过大量探索,发现DCOM有两种不同的身份验证情况:

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

由于原因未知,第2种情况不继承第1种情况的设置。默认情况下,它使用客户端进程的凭据,因此导致奇怪的登录。

有两种方法可以为第2种情况指定凭据。第一种方法是CoSetProxyBlanket。它仅为指定的代理(编组-解编组器)设置凭据:

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时,即使MSDN说不需要,您仍需要指定asAuthSvc。

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

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


0
对于那些使用Delphi编程的人来说,有一个小提示可以节省大量时间。在执行 obj as ISomeInterface 操作后,你必须为新实例调用 CoSetProxyBlanket。这可能不是很明显,但我们都知道 as 运算符调用了QueryInterface方法,它可能会返回新实例。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接