在C++中,我找不到一种方法来适当地终止与远程服务器的WMI会话。任何释放IWbemServices
指针的尝试都会引发异常;到服务器的TCP连接一直保持建立状态,直到进程退出(在最后一次CoUninitialize
调用后它是打开的)。连接到本地计算机时不会出现此问题(引发异常)。
我看过一个类似的问题,问here,但微软的解决方案(检索IUnknown
指针并首先释放它)没有解决这个问题。
下面是代码(为了可读性,省略了错误检查):
HRESULT hRes = S_OK;
IWbemLocator* pWbemLocator = NULL;
IWbemServices* pWbemServices = NULL;
// these three pointers are already initialized...
PWCHAR wcUser; // L"theuser"
PWCHAR wcPass; // L"thepassword"
PWCHAR wcAuth; // L"ntlmdomain:THEDOMAIN"
std::wstring wstrNsPath = L"\\\\remoteserver\\ROOT\\CIMV2";
hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
hRes = CoCreateInstance(CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pWbemLocator);
// this returns S_OK :)
hRes = pWbemLocator->ConnectServer((BSTR)wstrNsPath.c_str(),
(BSTR)wcUser,
(BSTR)wcPass,
NULL,
WBEM_FLAG_CONNECT_USE_MAX_WAIT,
(BSTR)wcAuth,
NULL,
&pWbemServices);
hRes = CoSetProxyBlanket(pWbemServices,
RPC_C_AUTHN_DEFAULT,
RPC_C_AUTHZ_DEFAULT,
COLE_DEFAULT_PRINCIPAL,
RPC_C_IMP_LEVEL_IMPERSONATE,
RPC_C_AUTHN_LEVEL_DEFAULT,
NULL, // domain info already specified in 'wcAuth'
EOAC_NONE);
// do some queries..
// cleanup
pWbemServices->Release(); // on remote sessions, this throws an exception
pWbemLocator->Release();
CoUninitialize();
异常显示在调试输出(Visual Studio)中:
onecore\com\combase\dcomrem\call.cxx(1234)\combase.dll!0000ABCDEFABCDEF: (caller: 0000FEDCBAFEDCBA) ReturnHr(1) tid(4321) 80070005 Access is denied.
这是预期的行为吗?一旦会话被释放,客户端和服务器之间的连接是否不应该被终止?
我尝试遵循MSDN的建议,并在上面代码中的CoSetProxyBlanket()
调用之后添加了以下内容。这并没有改变什么。
IUnknown* pUnknown = NULL;
pWbemServices->QueryInterface(IID_IUnknown, (LPVOID*)&pUnknown);
if (pUnknown)
{
hRes = CoSetProxyBlanket(pUnknown,
RPC_C_AUTHN_DEFAULT,
RPC_C_AUTHZ_DEFAULT,
COLE_DEFAULT_PRINCIPAL,
RPC_C_IMP_LEVEL_IMPERSONATE,
RPC_C_AUTHN_LEVEL_DEFAULT,
NULL,
EOAC_NONE);
pUnknown->Release();
}
任何建议都非常感谢!
EDIT因此,在捕获会话数据包后,使用pAuthInfo == NULL
设置代理安全性似乎会导致由客户端计算机的当前登录用户发出请求。它忽略了我在调用ConnectServer
时提供的凭据。我知道COAUTHIDENTITY
结构允许您将正确的凭据传递给CoSetProxyBlanket
,但我希望避免将域作为单独的变量输入。换句话说,当向远程服务器发出请求时,是否有一种方法可以使用wcAuth
提取此信息?如果是这样,我如何区分本地与远程请求?
下面是Wireshark的输出,它让我相信这就是问题所在(参见数据包1617):
No. Time Source Destination Protocol Length Info
1615 162.221354 [CLIENT_IP] [SERVER_IP] DCERPC 174 Alter_context: call_id: 8, Fragment: Single, 1 context items: IRemUnknown2 V0.0 (32bit NDR), NTLMSSP_NEGOTIATE
1616 162.228517 [SERVER_IP] [CLIENT_IP] DCERPC 366 Alter_context_resp: call_id: 8, Fragment: Single, max_xmit: 5840 max_recv: 5840, 1 results: Acceptance, NTLMSSP_CHALLENGE
1617 162.229396 [CLIENT_IP] [SERVER_IP] DCERPC 612 AUTH3: call_id: 8, Fragment: Single, NTLMSSP_AUTH, User: .\[client_user]
1618 162.229495 [CLIENT_IP] [SERVER_IP] IRemUnknown2 182 RemRelease request Cnt=1 Refs=5-0
1619 162.235567 [SERVER_IP] [CLIENT_IP] TCP 60 49669 → 59905 [ACK] Seq=1606 Ack=4339 Win=64768 Len=0
1620 162.235567 [SERVER_IP] [CLIENT_IP] DCERPC 86 Fault: call_id: 8, Fragment: Single, Ctx: 0, status: nca_s_fault_access_denied
1条答案
按热度按时间blpfk2vs1#
我能够解决这个问题。如果未使用当前登录的用户令牌,则必须将
CoSetProxyBlanket
的pAuthInfo
参数指定为有效的COAUTHIDENITY
结构指针。IWbemLocator::ConnectServer
的文档实际上指出,最好的做法是在strUser
参数中包含域...如果这样做,则必须将权限字符串作为NULL
传递。需要注意的一点是,如果
ConnectServer
成功,您不必为确保正确性而疯狂地清理用户名字符串;登录要么有效要么无效(并中断/抛出异常,这取决于您如何处理错误)。换句话说,只需搜索字符串中的域分隔符('\\'
或'@'
),并将它们适当地拆分为域名和用户名。