windows DCOM 中的模拟是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6123301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How does impersonation in DCOM work?
提问by himself
I have a DCOM client and server applications which use OLE automation marshaller. They work fine when run on the same PC but when the server is on a different PC not in the same domain I get E_ACCESSDENIED (0x80070005).
我有一个使用 OLE 自动化编组器的 DCOM 客户端和服务器应用程序。它们在同一台 PC 上运行时工作正常,但是当服务器位于不在同一域中的另一台 PC 上时,我得到 E_ACCESSDENIED (0x80070005)。
Server PC is configured with dcomcnfg to give all access to any DCOM object to the user whose login and password I specify on the client. ServerApp and its type library are registered on the server pc.
服务器 PC 配置了 dcomcnfg,以将所有 DCOM 对象的访问权限授予我在客户端指定其登录名和密码的用户。ServerApp 及其类型库注册在服务器 pc 上。
Type library is also registered on the client PC. I specify server name directly in the ClientApp so no dcomcnfg configuration is needed on the Client PC as far as I understand.
类型库也在客户端 PC 上注册。我直接在 ClientApp 中指定服务器名称,因此据我所知,客户端 PC 上不需要 dcomcnfg 配置。
CreateInstanceEx() with server name, login, domain and password works fine. It returns IUnknown and at the same time starts ServerApp on server PC.
带有服务器名称、登录名、域和密码的 CreateInstanceEx() 工作正常。它返回 IUnknown,同时在服务器 PC 上启动 ServerApp。
But when I try to QueryInterface() for the interface which server supports, I get E_ACCESSDENIED.
但是当我尝试为服务器支持的接口使用 QueryInterface() 时,我得到 E_ACCESSDENIED。
Analyzing the Security Event Log, I have two records there:
分析安全事件日志,我有两条记录:
First, a successful network login by the user whose credentials I specify in ClientApp. This happens when I call CreateInstanceEx().
首先,我在 ClientApp 中指定凭据的用户成功登录网络。当我调用 CreateInstanceEx() 时会发生这种情况。
Next, a failed login attempt by the user under which I'm logged in on a client PC. Since two PCs are not in a domain, this user is unknown to server PC.
接下来,我在客户端 PC 上登录的用户尝试登录失败。由于两台 PC 不在域中,因此服务器 PC 不知道该用户。
Now, why the heck would THIS user be logging into server, especially when I call QueryInterface of all things?
现在,为什么这个用户会登录到服务器,尤其是当我调用所有事物的 QueryInterface 时?
Studying CreateInterfaceEx params, it appears there's some kind of impersonation mechanism going on. But it's unclear who impersonates who. There are THREE user credentials involved:
研究 CreateInterfaceEx 参数,似乎存在某种模拟机制。但不清楚是谁冒充谁。涉及三个用户凭据:
User under which ServerApp runs on the server PC (as configured in dcomcnfg).
User whose credentials ClientApp specifies when connecting.
User under whose credentials ClientApp runs on client PC.
ServerApp 在服务器 PC 上运行的用户(在 dcomcnfg 中配置)。
ClientApp 在连接时指定其凭据的用户。
其凭据 ClientApp 在客户端 PC 上运行的用户。
No matter how you look at it, if #3 is involved it's one user too much. If DCOM is going to identify/impersonate #3 on server PC anyway, why do I need to specify #2's credentials? To what point?
不管你怎么看,如果涉及到#3,那就是一个用户太多了。如果 DCOM 无论如何要在服务器 PC 上识别/模拟 #3,为什么我需要指定 #2 的凭据?到什么地步?
It would have seem logical for DCOM to impersonate #2 because this is what I have explicitly specified as my credentials. But why the second login attempt then?
DCOM 模拟 #2 似乎是合乎逻辑的,因为这是我明确指定为我的凭据的内容。但是为什么要进行第二次登录尝试呢?
Can someone please explain how exactly the impersonation works,and also if there's a way to just ignore it and run as user which is specified in dcomcnfg?
有人可以解释一下模拟是如何工作的,以及是否有办法忽略它并以 dcomcnfg 中指定的用户身份运行?
采纳答案by himself
Answering my own question. After much exploration it became apparent that DCOM has TWO different identification cases:
回答我自己的问题。经过多次探索,很明显DCOM 有两种不同的识别案例:
- Authorization for object creation (CoCreateInstanceEx)
- Authorization for method calls.
- 对象创建授权 (CoCreateInstanceEx)
- 方法调用授权。
For reasons unknown, #2 doesn't inherit #1 settings. By default it uses the credentials of the client process, hence strange logins.
由于未知原因,#2 不继承 #1 设置。默认情况下,它使用客户端进程的凭据,因此登录很奇怪。
There are two ways to specify credentials for #2. First one is CoSetProxyBlanket. It sets credentials for a specified proxy (marshaller-unmarshaller) only:
有两种方法可以为 #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.
It's important to note that while CoCreateInstanceEx requires impersonation level to be at least IMPERSONATE, CoSetProxyBlanket doesn't seem to work on anything except IDENTIFY.
需要注意的是,虽然 CoCreateInstanceEx 要求模拟级别至少为 IMPERSONATE,但 CoSetProxyBlanket 似乎除了 IDENTIFY 之外没有任何作用。
Another option is to use CoInitializeSecurityto set default credentials for the whole process. Then you don't have to call CoSetProxyBlanket on every proxy:
另一种选择是使用CoInitializeSecurity为整个过程设置默认凭据。然后你不必在每个代理上调用 CoSetProxyBlanket:
CoInitializeSecurity(/* login, pass */);
CoCreateInstanceEx(IID_IUnknown, /*login, pass*/, obj); //Success!
obj->DoSomething(); //Success!
When using CoInitializeSecurity on the client you have to specify asAuthSvctoo, even though MSDN says you don't.
在客户端上使用 CoInitializeSecurity 时,您也必须指定asAuthSvc,即使 MSDN 说您没有。
The drawback of this method is obviously that if you have several DCOM objects from different PCs you're going to have to specify all the credentials in this call and those are probably going to be tried against every computer every time you open a different proxy.
这种方法的缺点显然是,如果您有来自不同 PC 的多个 DCOM 对象,您将必须在此调用中指定所有凭据,并且每次打开不同的代理时,这些凭据可能会在每台计算机上进行尝试。
It also is not reliable when you're running from a DLL (what if a process has different default security?). So, it's probably better to implement a QueryInterface wrapper which CoSetsProxyBlanket before returning from every call.
当您从 DLL 运行时,它也不可靠(如果进程具有不同的默认安全性怎么办?)。因此,最好在每次调用返回之前实现 CoSetsProxyBlanket 的 QueryInterface 包装器。
回答by Rustem Zinnatullin
For those who are working in Delphi there is one little note that can save a lot of your time. After you did obj as ISomeInterface
operation, you have to call CoSetProxyBlanket
for the new instance. This could be not very obvious, but all we know that as
operator calls QueryInterface
method, and it can return new instance.
对于那些在 Delphi 工作的人来说,有一个小笔记可以节省你很多时间。obj as ISomeInterface
操作完成后,必须调用CoSetProxyBlanket
新实例。这可能不是很明显,但我们都知道as
操作符调用QueryInterface
方法,并且它可以返回新实例。