使用 C++ 设置身份验证

WMI 的 IWbemLocator::ConnectServer 的主要任务之一是返回指向 IWbemServices 代理的指针。 通过 IWbemServices 代理,可以访问 WMI 基础结构的功能。 但是,指向 IWbemServices 代理的指针具有客户端应用程序进程的标识,而不是 IWbemServices 进程的标识。 因此,如果你尝试使用指针访问 IWbemServices,可能会收到拒绝访问错误代码,例如 E_ACCESSDENIED。 为避免拒绝访问错误,必须通过调用 CoSetProxyBlanket 接口设置新指针的标识。

提供程序可以在命名空间上设置安全性,以便不返回任何数据,除非在与该命名空间的连接中使用数据包隐私性 (PktPrivacy)。 这可以确保数据在通过网络传输时经过加密。 如果你尝试设置更低的身份验证级别,将收到拒绝访问消息。 有关详细信息,请参阅设置命名空间安全描述符

有关在脚本中设置身份验证的详细信息,请参阅使用 VBScript 设置默认进程安全级别

在远程 IUnknown 接口上设置安全性

在某些情况下,需要对服务器拥有更多访问权限,而不能仅设置指向代理的指针。 有时,可能需要与代理的 IUnknown 接口建立安全连接。 使用 IUnknown 可以在远程系统上查询接口和其他所需技术。

当代理位于远程计算机上时,服务器会将对代理 IUnknown 接口的所有调用委托给 IUnknown 接口。 例如,如果在代理上调用 QueryInterface,而请求的接口不是代理的一部分,则代理会将调用发送到远程服务器。 反过来,远程服务器会检查相应的接口支持。 如果服务器确实支持该接口,则 COM 会将新代理封送回客户端,以便应用程序可以使用新接口。

如果客户端对远程服务器不拥有访问权限,但正在使用拥有权限的用户的凭据,则会出现问题。 在这种情况下,尝试访问远程服务器上的 QueryInterface 都会失败。 代理上的最终释放也失败了,因为当前用户无权访问远程服务器。 这种情况的一个症状是在客户端应用程序最终代理释放失败之前有一到两秒的延迟。 失败的原因是 COM 尝试使用当前用户的默认安全设置访问远程服务器,而这些设置不包括最初有权访问服务器的已修改凭据。 有关详细信息,请参阅在 IWbemServices 和其他代理上设置安全性

为了避免连接失败,请使用 CoSetProxyBlanket 在从 IUnknown 返回的指针上显式设置安全身份验证。 使用 CoSetProxyBlanket 可以确保远程服务器收到正确的身份验证标识。

以下代码示例演示如何使用 CoSetProxyBlanket 访问远程 IUnknown 接口。

SEC_WINNT_AUTH_IDENTITY_W* pAuthIdentity = 
   new SEC_WINNT_AUTH_IDENTITY_W;
ZeroMemory(pAuthIdentity, sizeof(SEC_WINNT_AUTH_IDENTITY_W));

pAuthIdentity->User = new WCHAR[32];
StringCbCopyW(pAuthIdentity->User,sizeof(L"MyUser"),L"MyUser");
pAuthIdentity->UserLength = wcslen(pAuthIdentity->User);

pAuthIdentity->Domain = new WCHAR[32];
StringCbCopyW(pAuthIdentity->Domain,sizeof(L"MyDomain"),L"MyDomain");
pAuthIdentity->DomainLength = wcslen(pAuthIdentity->Domain);

pAuthIdentity->Password = new WCHAR[32];
pAuthIdentity->Password[0] = NULL;
pAuthIdentity->PasswordLength = wcslen( pAuthIdentity->Password);

pAuthIdentity->Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

IWbemServices* pWbemServices = 0;

// Set proxy security
hr = CoSetProxyBlanket(pWbemServices, 
                       RPC_C_AUTHN_DEFAULT, 
                       RPC_C_AUTHZ_NONE, 
                       COLE_DEFAULT_PRINCIPAL, 
                       RPC_C_AUTHN_LEVEL_DEFAULT, 
                       RPC_C_IMP_LEVEL_IMPERSONATE, 
                       pAuthIdentity, 
                       EOAC_NONE 
);
if (FAILED(hr))
{
   cout << "Count not set proxy blanket. Error code = 0x"
        << hex << hr << endl;
   pWbemServices->Release();
   return 1;
}

// Set IUnknown security
IUnknown*    pUnk = NULL;
pWbemServices->QueryInterface(IID_IUnknown, (void**) &pUnk);

hr = CoSetProxyBlanket(pUnk, 
                       RPC_C_AUTHN_DEFAULT, 
                       RPC_C_AUTHZ_NONE, 
                       COLE_DEFAULT_PRINCIPAL, 
                       RPC_C_AUTHN_LEVEL_DEFAULT, 
                       RPC_C_IMP_LEVEL_IMPERSONATE, 
                       pAuthIdentity, 
                       EOAC_NONE 
);
if (FAILED(hr))
{
   cout << "Count not set proxy blanket. Error code = 0x"
        << hex << hr << endl;
   pUnk->Release();
   pWbemServices->Release();
   delete [] pAuthIdentity->User;
   delete [] pAuthIdentity->Domain;
   delete [] pAuthIdentity->Password;
   delete pAuthIdentity;   
   return 1;
}

// cleanup IUnknown
pUnk->Release();

//
// Perform a bunch of operations
//

// Cleanup
pWbemServices->Release();

delete [] pAuthIdentity->User;
delete [] pAuthIdentity->Domain;
delete [] pAuthIdentity->Password;

delete pAuthIdentity;

注意

在代理的 IUnknown 接口上设置安全性时,COM 会创建一个在调用 CoUninitialize 之前无法释放的代理副本。

 

在 WMI 中设置身份验证