Compartilhar via


Representando um cliente

Quando um aplicativo de usuário solicita dados de objetos no sistema por meio de um provedor WMI, a representação significa que o provedor apresenta credenciais que representam o nível de segurança do cliente em vez do do provedor. A representação impede que um cliente obtenha acesso não autorizado às informações no sistema.

As seções a seguir são discutidas neste tópico:

O WMI normalmente é executado como um serviço administrativo em um alto nível de segurança, usando o contexto de segurança do LocalServer. O uso de um serviço administrativo fornece ao WMI os meios para acessar informações privilegiadas. Ao chamar um provedor para obter informações, o WMI passa seu SID (identificador de segurança) para o provedor, permitindo que o provedor acesse informações no mesmo nível de segurança alto.

Durante o processo de inicialização do aplicativo WMI, o sistema operacional Windows fornece ao aplicativo WMI o contexto de segurança do usuário que iniciou o processo. O contexto de segurança do usuário normalmente é um nível de segurança menor do que o LocalServer, portanto, o usuário pode não ter permissão para acessar todas as informações disponíveis para o WMI. Quando o aplicativo de usuário solicita informações dinâmicas, o WMI passa o SID do usuário para o provedor correspondente. Se escrito adequadamente, o provedor tenta acessar informações com o SID do usuário, em vez do SID do provedor.

Para que o provedor represente com êxito o aplicativo cliente, o aplicativo cliente e o provedor devem atender aos seguintes critérios:

Registrando um provedor para personificação

O WMI passa apenas o SID de um aplicativo cliente para os provedores que se registraram como provedores de representação. Habilitar um provedor para executar a representação requer que você modifique o processo de registro do provedor.

O procedimento a seguir descreve como registrar um provedor para impersonação. O procedimento pressupõe que você já entenda o processo de registro. Para obter mais informações sobre o processo de registro, consulte Registrando um provedor.

Para registrar um provedor para representação

  1. Defina a propriedade ImpersonationLevel da classe __Win32Provider que representa seu provedor como 1.

    A propriedade ImpersonationLevel documenta se o provedor dá suporte à representação ou não. Definir ImpersonationLevel como 0 indica que o provedor não representa o cliente e executa todas as operações solicitadas no mesmo contexto de usuário que o WMI. Definir ImpersonationLevel como 1 indica que o provedor usa chamadas de representação para verificar as operações executadas em nome do cliente.

  2. Defina a propriedade PerUserInitialization da mesma classe __Win32Provider como TRUE.

Observação

Se você registrar um provedor com a propriedade __Win32ProviderInitializeAsAdminFirst definida como TRUE, o provedor usará o token de segurança de thread no nível de administração somente durante a fase de inicialização. Embora uma chamada para CoImpersonateClient não falhe, o provedor usa o contexto de segurança do WMI e não do cliente.

 

O exemplo de código a seguir mostra como registrar um provedor para suplantação.

instance of __Win32Provider
{
    CLSID = "{FD4F53E0-65DC-11d1-AB64-00C04FD9159E}";
    ImpersonationLevel = 1;
    Name = "MS_NT_EVENTLOG_PROVIDER";
    PerUserInitialization = TRUE;
};

Definindo níveis de impersonação em um provedor

Se você registrar um provedor com a propriedade de classe __Win32ProviderImpersonationLevel definida como 1, o WMI chamará seu provedor para representar vários clientes. Para lidar com essas chamadas, use as funções CoImpersonateClient e CoRevertToSelf COM na implementação da interface IWbemServices .

A função CoImpersonateClient permite que um servidor represente o cliente que fez a chamada. Ao fazer uma chamada para CoImpersonateClient em sua implementação de IWbemServices, você permite que seu provedor defina o token de thread do provedor para corresponder ao token de thread do cliente e, portanto, representar o cliente. Se você não chamar CoImpersonateClient, seu provedor executará o código em um nível de segurança de administrador, criando assim uma vulnerabilidade de segurança em potencial. Se o provedor precisar agir temporariamente como administrador ou executar a verificação de acesso manualmente, chame CoRevertToSelf.

Em contraste com CoImpersonateClient, CoRevertToSelf é uma função COM que lida com níveis de representação de thread. Nesse caso, CoRevertToSelf altera o nível de representação de volta para a configuração de representação original. Em geral, o provedor é inicialmente um administrador e alterna entre CoImpersonateClient e CoRevertToSelf , dependendo se ele está fazendo uma chamada que representa o chamador ou suas próprias chamadas. É responsabilidade do provedor fazer essas chamadas corretamente para não expor uma falha de segurança ao usuário final. Por exemplo, o provedor deve chamar apenas funções nativas do Windows dentro da sequência de código representada.

Observação

A finalidade de CoImpersonateClient e CoRevertToSelf é definir a segurança para um provedor. Se você determinar que sua representação falhou, você deverá retornar um código de conclusão apropriado ao WMI por meio de IWbemObjectSink::SetStatus. Para obter mais informações, consulte Como lidar com mensagens de acesso negadas em um provedor.

 

Mantendo níveis de segurança em um provedor

Os provedores não podem chamar CoImpersonateClient uma única vez em uma implementação de IWbemServices e pressupor que as credenciais de representação permaneçam em vigor durante a duração do provedor. Em vez disso, chame CoImpersonateClient várias vezes durante o curso de uma implementação para impedir que o WMI altere as credenciais.

A principal preocupação para definir a impersonation para um provedor é a reentrância. Nesse contexto, a reentrada é quando um provedor faz uma chamada ao WMI para obter informações e aguarda até que o WMI retorne ao provedor. Em essência, o fluxo de execução sai do código do provedor, apenas para retornar ao código em um momento posterior. A reentrada faz parte do design do COM e geralmente não é uma preocupação. No entanto, quando o thread de execução entra no WMI, o thread assume os níveis de representação do WMI. Quando o thread retorna ao provedor, você deve redefinir os níveis de representação com outra chamada para CoImpersonateClient.

Para se proteger contra vulnerabilidades de segurança em seu provedor, você deve fazer chamadas reentrantes no WMI somente ao assumir a identidade do cliente. Ou seja, as chamadas para o WMI devem ser feitas depois que você chamar CoImpersonateClient e antes de chamar CoRevertToSelf. Como o CoRevertToSelf faz com que a representação seja definida para o nível de usuário que o WMI está executando, geralmente o LocalSystem, chamadas reentrantes ao WMI após chamar CoRevertToSelf podem dar ao usuário e a todos os provedores chamados, muito mais capacidades do que deveriam ter.

Observação

Se você chamar uma função do sistema ou outro método de interface, o contexto de chamada não será garantido para ser mantido.

 

Manipulando mensagens de acesso negado em um provedor

A maioria das mensagens de erro negadas pelo Access aparece quando um cliente solicita uma classe ou informações às quais não tem acesso. Se o provedor retornar uma mensagem de erro de Acesso Negado ao WMI e o WMI passar a mensagem de erro para o cliente, o cliente poderá inferir que as informações existem. Em algumas situações, isso pode ser uma violação de segurança. Portanto, seu provedor não deve propagar a mensagem para o cliente. Em vez disso, o conjunto de classes que o provedor teria fornecido não deve ser exposto. Da mesma forma, um provedor de instância dinâmica deve chamar a fonte de dados subjacente para determinar como lidar com mensagens negadas pelo Access. É responsabilidade do provedor replicar essa filosofia no ambiente WMI. Para obter mais informações, consulte Relatando Instâncias Parciais e Relatando Enumerações Parciais.

Ao determinar como seu provedor deve lidar com mensagens negadas pelo Access, você deve escrever e depurar seu código. Durante a depuração, geralmente é conveniente distinguir entre uma recusa devido à baixa impersonação e uma recusa devido a um erro em seu código. Você pode usar um teste simples em seu código para determinar a diferença. Para obter mais informações, consulte Depurando seu Código de Acesso Negado.

Relatório de Instâncias Parciais

Uma ocorrência comum de mensagem de 'Acesso Negado' é quando o WMI não pode fornecer todas as informações para preencher uma instância. Por exemplo, um cliente pode ter autoridade para exibir um objeto de unidade de disco rígido, mas pode não ter autoridade para ver quanto espaço está disponível na própria unidade de disco rígido. Seu provedor deve determinar como lidar com qualquer situação quando o provedor não pode preencher completamente uma instância com propriedades devido a uma violação de acesso.

O WMI não requer uma única resposta aos clientes que têm acesso parcial a uma instância. Em vez disso, o WMI versão 1.x permite ao provedor uma das seguintes opções:

  • Falhe toda a operação com WBEM_E_ACCESS_DENIED e não retorne instâncias.

    Retorne um objeto de erro junto com WBEM_E_ACCESS_DENIED, para descrever o motivo da negação.

  • Retorne todas as propriedades disponíveis e preencha as propriedades indisponíveis com NULL.

Observação

Certifique-se de que retornar WBEM_E_ACCESS_DENIED não crie uma falha de segurança em sua empresa.

 

Relatando enumerações parciais

Outra ocorrência comum de uma violação de acesso é quando o WMI não pode retornar toda uma enumeração. Por exemplo, um cliente pode ter acesso para exibir todos os objetos de computador de rede local, mas pode não ter acesso para exibir objetos de computador fora de seu domínio. Seu provedor deve determinar como lidar com qualquer situação quando uma enumeração não pode ser concluída devido a uma violação de acesso.

Assim como acontece com um provedor de instância, o WMI não requer uma única resposta a uma enumeração parcial. Em vez disso, o WMI versão 1.x permite a um provedor uma das seguintes opções:

  • Retorne WBEM_S_NO_ERROR para todas as instâncias que o provedor pode acessar.

    Se você usar essa opção, o usuário não estará ciente de que algumas instâncias não estavam disponíveis. Vários provedores, como aqueles que usam SQL (Structured Query Language) com segurança em nível de linha, retornam resultados parciais bem-sucedidos usando o nível de segurança do chamador para definir o conjunto de resultados.

  • Falhe toda a operação com WBEM_E_ACCESS_DENIED e não retorne instâncias.

    Opcionalmente, o provedor pode incluir um objeto de erro que descreve a situação para o cliente. Observe que alguns provedores podem acessar fontes de dados serialmente e podem não encontrar negações até a parte da enumeração.

  • Retorne todas as instâncias que podem ser acessadas, mas também retorne o código de status sem erro WBEM_S_ACCESS_DENIED.

    O provedor deve observar a negação durante a enumeração e pode continuar fornecendo instâncias, terminando com o código de status nonerror. O provedor também pode optar por encerrar a enumeração na primeira negação. A justificativa para essa opção é que provedores diferentes têm paradigmas de recuperação diferentes. Um provedor pode já ter entregue instâncias antes de descobrir uma violação de acesso. Alguns provedores podem optar por continuar fornecendo outras instâncias e outros podem desejar encerrar.

Devido à estrutura do COM, você não pode fazer marshalback de nenhuma informação durante um erro, exceto para um objeto de erro. Portanto, você não pode retornar informações e um código de erro. Se você optar por retornar informações, deverá usar um código de status sem erro.

Depurando seu código de acesso negado

Alguns aplicativos podem usar níveis de representação inferiores ao RPC_C_IMP_LEVEL_IMPERSONATE. Nesse caso, a maioria das chamadas de imitação feitas pelo provedor para o aplicativo cliente falhará. Para projetar e implementar um provedor com êxito, você deve ter essa ideia em mente.

Por padrão, o único outro nível de representação que pode acessar um provedor é RPC_C_IMP_LEVEL_IDENTIFY. Nos casos em que um aplicativo cliente usa RPC_C_IMP_LEVEL_IDENTIFY, CoImpersonateClient não retorna um código de erro. Em vez disso, o provedor representa o cliente apenas para fins de identificação. Portanto, a maioria dos métodos do Windows chamados pelo provedor retornará uma mensagem de acesso negado. Isso é inofensivo na prática, pois os usuários não poderão fazer nada inadequado. No entanto, pode ser útil durante o desenvolvimento do provedor determinar se o cliente foi realmente impersonado ou não.

O código requer as seguintes referências e instruções #include para compilar corretamente.

#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <wbemidl.h>

O exemplo de código a seguir mostra como determinar se um provedor representou com êxito um aplicativo cliente.

DWORD dwImp = 0;
HANDLE hThreadTok;
DWORD dwBytesReturned;
BOOL bRes;

// You must call this before trying to open a thread token!
CoImpersonateClient();

bRes = OpenThreadToken(
    GetCurrentThread(),
    TOKEN_QUERY,
    TRUE,
    &hThreadTok
);

if (bRes == FALSE)
{
    printf("Unable to read thread token (%d)\n", GetLastError());
    return 0;
}

bRes = GetTokenInformation(
    hThreadTok,
    TokenImpersonationLevel, 
    &dwImp,
    sizeof(DWORD),
    &dwBytesReturned
);

if (!bRes)
{
    printf("Unable to read impersonation level\n");
    CloseHandle(hThreadTok);
    return 0;
}

switch (dwImp)
{
case SecurityAnonymous:
    printf("SecurityAnonymous\n");
    break;

case SecurityIdentification:
    printf("SecurityIdentification\n");
    break;

case SecurityImpersonation:
    printf("SecurityImpersonation\n");
    break;

case SecurityDelegation:
    printf("SecurityDelegation\n");
    break;

default:
    printf("Error. Unable to determine impersonation level\n");
    break;
}

CloseHandle(hThreadTok);

Desenvolver um provedor WMI

Definindo descritores de segurança do namespace

Protegendo seu provedor