Condividi tramite


Rappresentazione di un client

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

Le sezioni seguenti sono descritte in questo argomento:

WMI viene in genere eseguito come servizio amministrativo a livello di sicurezza elevato, usando il contesto di sicurezza LocalServer. L'uso di un servizio amministrativo fornisce a WMI i mezzi per accedere alle informazioni con privilegi. Quando si chiama un provider per informazioni, WMI passa l'identificatore 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 avere l'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 correttamente l'applicazione client, l'applicazione client e il provider devono soddisfare i criteri seguenti:

Registrazione di un provider per l'impersonificazione

WMI passa solo il SID di un'applicazione client ai provider registrati come provider di impersonazione. Per consentire al provider di eseguire l'impersonificazione, è necessario modificare il processo di registrazione del provider.

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

Per registrare un provider per l'impersonificazione

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

    La proprietà ImpersonationLevel documenta se il provider supporta o meno la personificazione. 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 le chiamate di impersonazione per controllare le operazioni eseguite per conto dell'utente.

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

Nota

Se si registra un provider con la proprietà __Win32ProviderInitializeAsAdminFirst impostato 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 ha esito negativo, 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 l'impersonificazione.

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

Impostazione dei livelli di impersonificazione all'interno di un provider

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

La funzioneCoImpersonateClientconsente 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 amministratore, creando così una potenziale vulnerabilità alla sicurezza. Se il provider deve temporaneamente operare come amministratore o eseguire manualmente il controllo di accesso, chiamare CoRevertToSelf.

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

Nota

Lo scopo di CoImpersonateClient e CoRevertToSelf consiste nell'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 contesto di 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 problema principale per l'impostazione della rappresentazione per un provider è la ri-entrabilità. In questo contesto, la reentrancy si verifica quando un provider effettua una chiamata a WMI per ottenere informazioni e attende fino a quando WMI richiama il 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 problemi di sicurezza nel provider, è necessario effettuare chiamate rientranti in WMI solo sotto l'identità 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 a livello utente quando WMI è in esecuzione, in genere come LocalSystem, una chiamata reentrante a WMI dopo aver chiamato CoRevertToSelf potrebbe fornire all'utente e a tutti i provider chiamati molte più capacità di quante dovrebbero avere.

Nota

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

 

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 ha 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 consultare la fonte di dati sottostante per determinare come gestire i messaggi di accesso negato. È responsabilità del provider replicare tale filosofia nell'ambiente WMI. Per altre informazioni, vedere Segnalazione Istanze Parziali e Segnalazione Enumerazioni Parziali.

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 un rifiuto a causa di un basso livello di impersonificazione e un rifiuto dovuto a 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.

Segnalazione di istanze parziali

Un'occorrenza comune di un messaggio di accesso negato è quando WMI non può fornire tutte le informazioni per riempire un'istanza. 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 di accesso.

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

  • Fallire l'intera operazione con WBEM_E_ACCESS_DENIED e non restituire alcuna istanza.

    Restituire 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 il ritorno di WBEM_E_ACCESS_DENIED non apra una falla nella sicurezza dell'azienda.

 

Creazione di report di enumerazioni parziali

Un'altra occorrenza comune di una violazione di accesso è quando WMI non può restituire tutta un'enumerazione. 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 all'esterno 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 riconosce 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.

  • Fallire l'intera operazione con WBEM_E_ACCESS_DENIED e non restituire alcuna istanza.

    Il provider può facoltativamente includere un oggetto errore che descrive la situazione al client. Si noti che alcuni provider possono accedere alle origini dati in modo seriale e potrebbero non riscontrare negazioni finché non si arriva a un certo punto nell'enumerazione.

  • Restituisci tutte le istanze a cui è possibile accedere e restituisci anche il codice di stato senza errore WBEM_S_ACCESS_DENIED.

    Il provider deve prendere nota del rifiuto durante l'enumerazione e può continuare a fornire le istanze, terminando con il codice di stato di non errore. 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. È possibile che un provider abbia 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 del COM, non è possibile trasferire 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, è invece necessario usare un codice di stato non di errore.

Debug del codice di accesso negato

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

Per impostazione predefinita, l'unico altro livello di impersonificazione 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, perché 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 impersonato 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 del namespace

la protezione del provider