Share via


Rappresentazione di un client

Quando un'applicazione utente richiede dati da oggetti nel sistema tramite un provider WMI, la rappresentazione indica che il provider presenta le credenziali che rappresentano il livello di sicurezza del client anziché il provider. La rappresentazione impedisce a un client di ottenere l'accesso non autorizzato alle informazioni sul sistema.

In questo argomento vengono illustrate le sezioni seguenti:

WMI viene in genere eseguito come servizio amministrativo a un livello di sicurezza elevato, usando il contesto di sicurezza LocalServer. L'uso di un servizio amministrativo offre a WMI i mezzi per accedere alle informazioni con privilegi. Quando si chiama un provider per informazioni, WMI passa il relativo ID di sicurezza (SID) al provider, consentendo al provider di accedere alle informazioni allo stesso livello di sicurezza elevato.

Durante il processo di avvio dell'applicazione WMI, il sistema operativo Windows fornisce all'applicazione WMI il contesto di sicurezza dell'utente che ha iniziato il processo. Il contesto di sicurezza dell'utente è in genere un livello di sicurezza inferiore a LocalServer, pertanto l'utente potrebbe non disporre dell'autorizzazione per accedere a tutte le informazioni disponibili per WMI. Quando l'applicazione utente richiede informazioni dinamiche, WMI passa il SID dell'utente al provider corrispondente. Se scritto in modo appropriato, il provider tenta di accedere alle informazioni con il SID utente, anziché il SID del provider.

Affinché il provider impersoni l'applicazione client, l'applicazione client e il provider devono soddisfare i criteri seguenti:

Registrazione di un provider per la rappresentazione

WMI passa solo il SID di un'applicazione client ai provider registrati come provider di rappresentazione. Per consentire a un provider di eseguire la rappresentazione, è necessario modificare il processo di registrazione del provider.

La procedura seguente descrive come registrare un provider per la rappresentazione. La procedura presuppone che l'utente abbia già compreso il processo di registrazione. Per altre informazioni sul processo di registrazione, vedere Registrazione di un provider.

Per registrare un provider per la rappresentazione

  1. Impostare la proprietà ImpersonationLevel della classe __Win32Provider che rappresenta il provider su 1.

    La proprietà ImpersonationLevel indica se il provider supporta o meno la rappresentazione. L'impostazione di ImpersonationLevel su 0 indica che il provider non rappresenta il client ed esegue tutte le operazioni richieste nello stesso contesto utente di WMI. L'impostazione di ImpersonationLevel su 1 indica che il provider usa chiamate di rappresentazione per controllare le operazioni eseguite per conto del client.

  2. Impostare la proprietà PerUserInitialization della stessa classe __Win32Provider su TRUE.

Nota

Se si registra un provider con la proprietà __Win32ProviderInitializeAsAdminFirst impostata su TRUE, il provider usa il token di sicurezza del thread a livello di amministrazione solo durante la fase di inizializzazione. Anche se una chiamata a CoImpersonateClient non riesce, il provider usa il contesto di sicurezza di WMI e non del client.

 

Nell'esempio di codice seguente viene illustrato come registrare un provider per la rappresentazione.

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

Impostazione dei livelli di rappresentazione all'interno di un provider

Se si registra un provider con la proprietà della classe __Win32ProviderImpersonationLevel impostata su 1, WMI chiama il provider per rappresentare vari client. Per gestire queste chiamate, usare le funzioni COM CoImpersonateClient e CoRevertToSelf nell'implementazione dell'interfaccia IWbemServices .

La funzione CoImpersonateClient consente a un server di rappresentare il client che ha effettuato la chiamata. Inserendo una chiamata a CoImpersonateClient nell'implementazione di IWbemServices, è possibile consentire al provider di impostare il token di thread del provider in modo che corrisponda al token di thread del client e quindi rappresentare il client. Se non si chiama CoImpersonateClient, il provider esegue il codice a livello di sicurezza a livello di amministratore, creando così una potenziale vulnerabilità di sicurezza. Se il provider deve agire temporaneamente come amministratore o eseguire manualmente il controllo di accesso, chiama CoRevertToSelf.

A differenza di CoImpersonateClient, CoRevertToSelf è una funzione COM che gestisce i livelli di rappresentazione dei thread. In questo caso , CoRevertToSelf modifica il livello di rappresentazione nell'impostazione di rappresentazione originale. In generale, il provider è inizialmente un amministratore e alterna tra CoImpersonateClient e CoRevertToSelf a seconda che stia effettuando una chiamata che rappresenta il chiamante o le proprie chiamate. È responsabilità del provider effettuare correttamente queste chiamate in modo da non esporre un foro di sicurezza all'utente finale. Ad esempio, il provider deve chiamare solo funzioni di Windows native all'interno della sequenza di codice rappresentata.

Nota

Lo scopo di CoImpersonateClient e CoRevertToSelf è impostare la sicurezza per un provider. Se si determina che la rappresentazione non è riuscita, è necessario restituire un codice di completamento appropriato a WMI tramite IWbemObjectSink::SetStatus. Per altre informazioni, vedere Gestione dei messaggi di accesso negato in un provider.

 

Gestione dei livelli di sicurezza in un provider

I provider non possono chiamare CoImpersonateClient una volta in un'implementazione di IWbemServices e presuppongono che le credenziali di rappresentazione rimangano applicate per la durata del provider. Chiamare invece CoImpersonateClient più volte durante il corso di un'implementazione per impedire a WMI di modificare le credenziali.

Il principale problema per l'impostazione della rappresentazione per un provider è la reentrancy. In questo contesto, la reentrancy è quando un provider effettua una chiamata a WMI per informazioni e attende fino a quando WMI non viene richiamato nel provider. In sostanza, il thread di esecuzione lascia il codice del provider, solo per immettere nuovamente il codice in un secondo momento. Reentry fa parte della progettazione di COM e in genere non è un problema. Tuttavia, quando il thread di esecuzione entra in WMI, il thread assume i livelli di rappresentazione di WMI. Quando il thread torna al provider, è necessario reimpostare i livelli di rappresentazione con un'altra chiamata a CoImpersonateClient.

Per proteggersi da fori di sicurezza nel provider, è necessario effettuare chiamate reentrant in WMI solo durante la rappresentazione del client. Ovvero, le chiamate a WMI devono essere effettuate dopo aver chiamato CoImpersonateClient e prima di chiamare CoRevertToSelf. Poiché CoRevertToSelf fa sì che la rappresentazione sia impostata su WMI a livello di utente sia in esecuzione, in genere LocalSystem, le chiamate reentrant a WMI dopo aver chiamato CoRevertToSelf potrebbero fornire all'utente e a qualsiasi provider chiamato molte più funzionalità di quelle che devono avere.

Nota

Se si chiama una funzione di sistema o un altro metodo di interfaccia, non è garantito che venga mantenuto il contesto di chiamata.

 

Gestione dei messaggi di accesso negato in un provider

La maggior parte dei messaggi di errore Accesso negato viene visualizzata quando un client richiede una classe o informazioni a cui non hanno accesso. Se il provider restituisce un messaggio di errore Accesso negato a WMI e WMI lo passa al client, il client può dedurre che le informazioni esistono. In alcune situazioni, può trattarsi di una violazione della sicurezza. Pertanto, il provider non deve propagare il messaggio al client. Al contrario, il set di classi che il provider avrebbe fornito non deve essere esposto. Analogamente, un provider di istanze dinamiche deve chiamare all'origine dati sottostante per determinare come gestire i messaggi di accesso negato. È responsabilità del provider replicare tale filosofia nell'ambiente WMI. Per altre informazioni, vedere Reporting Partial Instances and Reporting Partial Enumerations.For more information, see Reporting Partial Instances and Reporting Partial Enumerations.

Quando si determina come il provider deve gestire i messaggi di accesso negato, è necessario scrivere ed eseguire il debug del codice. Durante il debug, spesso è utile distinguere tra una negazione a causa di una rappresentazione bassa e una negazione a causa di un errore nel codice. È possibile usare un semplice test nel codice per determinare la differenza. Per altre informazioni, vedere Debug del codice di accesso negato.

Creazione di report di istanze parziali

Un'occorrenza comune di un messaggio di accesso negato è quando WMI non può fornire tutte le informazioni per compilare un'istanza di . Ad esempio, un client può avere l'autorità per visualizzare un oggetto unità disco rigido, ma potrebbe non avere l'autorità per vedere la quantità di spazio disponibile nell'unità disco rigido stessa. Il provider deve determinare come gestire qualsiasi situazione quando il provider non può riempire completamente un'istanza con proprietà a causa di una violazione dell'accesso.

WMI non richiede una singola risposta ai client che hanno accesso parziale a un'istanza di . WMI versione 1.x consente invece al provider una delle opzioni seguenti:

  • Eseguire l'intera operazione con WBEM_E_ACCESS_DENIED e non restituire istanze.

    Restituisce un oggetto di errore insieme a WBEM_E_ACCESS_DENIED, per descrivere il motivo della negazione.

  • Restituisce tutte le proprietà disponibili e riempie le proprietà non disponibili con NULL.

Nota

Assicurarsi che la restituzione di WBEM_E_ACCESS_DENIED non crei un buco di sicurezza nell'azienda.

 

Enumerazioni parziali per la creazione di report

Un'altra occorrenza comune di una violazione di accesso è quando WMI non può restituire tutte le enumeratorie. Ad esempio, un client può avere accesso per visualizzare tutti gli oggetti computer di rete locali, ma potrebbe non avere accesso per visualizzare gli oggetti computer al di fuori del dominio. Il provider deve determinare come gestire qualsiasi situazione quando non è possibile completare un'enumerazione a causa di una violazione di accesso.

Come per un provider di istanze, WMI non richiede una singola risposta a un'enumerazione parziale. WMI versione 1.x consente invece a un provider una delle opzioni seguenti:

  • Restituisce WBEM_S_NO_ERROR per tutte le istanze a cui il provider può accedere.

    Se si usa questa opzione, l'utente non sa che alcune istanze non erano disponibili. Diversi provider, ad esempio quelli che usano Structured Query Language (SQL) con sicurezza a livello di riga, restituiscono risultati parziali con esito positivo usando il livello di sicurezza del chiamante per definire il set di risultati.

  • Eseguire l'intera operazione con WBEM_E_ACCESS_DENIED e non restituire istanze.

    Il provider può facoltativamente includere un oggetto errore che descrive la situazione al client. Si noti che alcuni provider possono accedere in modo seriale alle origini dati e potrebbero non riscontrare denial fino a quando non si passa attraverso l'enumerazione .

  • Restituisce tutte le istanze a cui è possibile accedere, ma restituisce anche il codice di stato nonerrore WBEM_S_ACCESS_DENIED.

    Il provider deve prendere nota della negazione durante l'enumerazione e può continuare a fornire istanze, terminando con il codice di stato nonerrore. Il provider può anche scegliere di terminare l'enumerazione al primo rifiuto. La giustificazione di questa opzione è che i diversi provider hanno paradigmi di recupero diversi. Un provider potrebbe aver già recapitato istanze prima di individuare una violazione di accesso. Alcuni provider possono scegliere di continuare a fornire altre istanze e altri potrebbero voler terminare.

A causa della struttura di COM, non è possibile eseguire il marshalling di alcuna informazione durante un errore, ad eccezione di un oggetto errore. Pertanto, non è possibile restituire sia informazioni che codice di errore. Se si sceglie di restituire informazioni, è necessario usare invece un codice di stato nonerrore.

Debug del codice di accesso negato

Alcune applicazioni possono usare livelli di rappresentazione inferiori a RPC_C_IMP_LEVEL_IMPERSONATE. In questo caso, la maggior parte delle chiamate di rappresentazione effettuate dal provider per l'applicazione client avrà esito negativo. Per progettare e implementare correttamente un provider, è necessario tenere presente questa idea.

Per impostazione predefinita, l'unico livello di rappresentazione che può accedere a un provider è RPC_C_IMP_LEVEL_IDENTIFY. Nei casi in cui un'applicazione client usa RPC_C_IMP_LEVEL_IDENTIFY, CoImpersonateClient non restituisce un codice di errore. Il provider rappresenta invece il client solo a scopo di identificazione. Pertanto, la maggior parte dei metodi di Windows chiamati dal provider restituirà un messaggio di accesso negato. Questo è innocuo in pratica, poiché gli utenti non saranno autorizzati a fare nulla di inappropriato. Tuttavia, può essere utile durante lo sviluppo del provider sapere se il client è stato effettivamente rappresentato o meno.

Il codice richiede che i riferimenti e le istruzioni #include seguenti vengano compilati correttamente.

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

Nell'esempio di codice seguente viene illustrato come determinare se un provider ha rappresentato correttamente un'applicazione client.

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);

Sviluppo di un provider WMI

Impostazione dei descrittori di sicurezza namepace

Protezione del provider