Configurar a segurança em uma chamada assíncrona

Chamadas assíncronas apresentam sérios riscos à segurança, porque um retorno de chamada para o coletor pode não ser resultado da chamada assíncrona pelo aplicativo ou script original. A segurança em conexões remotas baseia-se na criptografia da comunicação entre o cliente e o provedor no computador remoto. No C++, você pode definir a criptografia por meio do parâmetro de nível de autenticação na chamada para CoInitializeSecurity. No script, defina AuthenticationLevel na conexão moniker ou em um objeto SWbemSecurity. Para obter mais informações, consulte Definir o nível de segurança do processo padrão usando VBScript.

Os riscos à segurança para chamadas assíncronas existem, porque o WMI reduz o nível de autenticação em um retorno de chamada até que o retorno de chamada seja bem-sucedido. Em uma chamada assíncrona de saída, o cliente pode definir o nível de autenticação na conexão com o WMI. O WMI recupera as configurações de segurança na chamada do cliente e tenta fazer o retorno de chamada com o mesmo nível de autenticação. O retorno de chamada é sempre iniciado no nível RPC_C_AUTHN_LEVEL_PKT_PRIVACY. Se o retorno de chamada falhar, o WMI reduzirá o nível de autenticação para um nível em que o retorno de chamada pode ter êxito, se necessário, para RPC_C_AUTHN_LEVEL_NONE. No contexto de chamadas no sistema local em que o serviço de autenticação não é Kerberos, o retorno de chamada sempre é retornado em RPC_C_AUTHN_LEVEL_NONE.

O nível mínimo de autenticação é RPC_C_AUTHN_LEVEL_PKT (wbemAuthenticationLevelPkt para script). No entanto, você pode especificar um nível mais alto, como RPC_C_AUTHN_LEVEL_PKT_PRIVACY (wbemAuthenticationLevelPktPrivacy). É recomendável que scripts ou aplicativos cliente definam o nível de autenticação como RPC_C_AUTHN_LEVEL_DEFAULT (wbemAuthenticationLevelDefault), que permite que o nível de autenticação seja negociado para o nível especificado pelo servidor.

O valor do Registro HKEY_LOCAL_MACHINE\Software\Microsoft\WBEM\CIMOM\UnsecAppAccessControlDefault controla se o WMI verifica se há um nível de autenticação aceitável em retornos de chamada. Esse é o único mecanismo para proteger a segurança do coletor em chamadas assíncronas feitas no script ou no Visual Basic. Por padrão, essa chave do Registro é definida como zero. Se a chave do Registro for zero, o WMI não verificará os níveis de autenticação. Para proteger chamadas assíncronas no script, defina a chave do Registro como 1. Os clientes C++ podem chamar IWbemUnsecuredApartment::CreateSinkStub para controlar o acesso ao coletor. O valor é criado em qualquer lugar por padrão.

Os seguintes tópicos fornecem exemplos de configuração da segurança de chamada assíncrona:

Configurar a segurança de chamada assíncrona no C++

O método IWbemUnsecuredApartment::CreateSinkStub é semelhante ao método IUnsecuredApartment::CreateObjectStub e cria um coletor em um processo separado, Unsecapp.exe, para receber retornos de chamada. No entanto, o método CreateSinkStub tem um parâmetro dwFlag que especifica como o processo separado manipula o controle de acesso.

O parâmetro dwFlag especifica uma das seguintes ações para Unsecapp.exe:

  • Usar a configuração da chave do Registro para determinar se deve ou não verificar o acesso.
  • Ignorar a chave do Registro e sempre verificar o acesso.
  • Ignorar a chave do Registro e nunca verificar o acesso.

O exemplo de código neste tópico requer que a instrução #include a seguir sejam compiladas corretamente.

#include <wbemidl.h>

O procedimento a seguir descreve como executar uma chamada assíncrona com IUnsecuredApartment.

Para executar uma chamada assíncrona com IWbemUnsecuredApartment

  1. Crie um processo dedicado com uma chamada para CoCreateInstance.

    O exemplo de código a seguir chama CoCreateInstance para criar um processo dedicado.

    CLSID                    CLSID_WbemUnsecuredApartment;
    IWbemUnsecuredApartment* pUnsecApp = NULL;
    
    CoCreateInstance(CLSID_WbemUnsecuredApartment, 
                     NULL, 
                     CLSCTX_LOCAL_SERVER, 
                     IID_IWbemUnsecuredApartment, 
                     (void**)&pUnsecApp);
    
  2. Instancie o objeto de coletor.

    O exemplo de código a seguir cria um novo objeto de coletor.

    CMySink* pSink = new CMySink;
    pSink->AddRef();
    
  3. Crie um stub para o coletor.

    Um stub é uma função de wrapper produzida no coletor.

    O exemplo de código a seguir cria um stub para o coletor.

    LPCWSTR          wszReserved = NULL;           
    IWbemObjectSink* pStubSink   = NULL;
    IUnknown*        pStubUnk    = NULL; 
    
    pUnsecApp->CreateSinkStub(pSink,
                              WBEM_FLAG_UNSECAPP_CHECK_ACCESS,  //Authenticate callbacks regardless of registry key
                              wszReserved,
                              &pStubSink);
    
  4. Libere o ponteiro do objeto de coletor.

    Você pode liberar o ponteiro do objeto, porque o stub agora é proprietário do ponteiro.

    O exemplo de código a seguir libera o ponteiro do objeto.

    pSink->Release();
    
  5. Use o stub em qualquer chamada assíncrona.

    Quando terminar a chamada, libere a contagem de referência local.

    O exemplo de código a seguir usa o stub em uma chamada assíncrona.

    // pServices is an IWbemServices* object
    pServices->CreateInstanceEnumAsync(strClassName, 0, NULL, pStubSink);
    

    Às vezes, pode ser necessário cancelar uma chamada assíncrona depois de fazer a chamada. Se você precisar cancelar a chamada, cancele-a com o mesmo ponteiro que originalmente fez a chamada.

    O exemplo de código a seguir descreve como cancelar uma chamada assíncrona.

    pServices->CancelAsyncCall(pStubSink);
    
  6. Libere a contagem de referência local quando terminar de usar a chamada assíncrona.

    Libere o ponteiro pStubSink somente depois de confirmar que a chamada assíncrona não deve ser cancelada. Além disso, não libere o pStubSink depois que o WMI liberar o ponteiro do coletor pSink. A liberação de pStubSink após pSink cria uma contagem de referência circular na qual o coletor e o stub permanecem na memória para sempre. Em vez disso, um possível local para liberar o ponteiro é na chamada IWbemObjectSink::SetStatus, feita pelo WMI para informar que a chamada assíncrona original está concluída.

  7. Quando terminar, não inicialize o COM usando uma chamada para Release().

    O exemplo de código a seguir mostra como chamar Release() no ponteiro pUnsecApp.

    pUnsecApp->Release();
    

Para obter mais informações sobre os parâmetros e a função CoInitializeSecurity, consulte a documentação de COM.