我有一个使用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参数,看起来有某种冒充机制正在进行中。但目前还不清楚是谁冒充了谁。涉及三个用户凭证:
ServerApp在服务器PC上运行的用户(在dcomcnfg中配置)。
ClientApp在连接时指定其凭据的用户。
ClientApp在客户端PC上运行其凭据的用户。
无论你怎么看,如果涉及到#3,那么一个用户太多了。如果DCOM要在服务器PC上识别/模仿#3,为什么我需要指定#2的凭证?到了什么程度?
DCOM模仿#2似乎是合乎逻辑的,因为这是我明确指定的凭据。但为什么第二次登录尝试呢?
有人可以解释模仿的确切方式吗以及是否有办法忽略它并以dcomcnfg中指定的用户身份运行?
答案 0 :(得分:13)
回答我自己的问题。经过大量探索后,很明显 DCOM有两种不同的识别案例:
由于未知原因,#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
方法,它可以返回新实例。