Sdílet prostřednictvím


Zosobnění klienta

Když aplikace uživatele požaduje data z objektů v systému prostřednictvím zprostředkovatele rozhraní WMI, zosobnění znamená, že zprostředkovatel zobrazí přihlašovací údaje, které představují úroveň zabezpečení klienta, a nikoli zprostředkovatele. Zosobnění brání klientovi v získání neoprávněného přístupu k informacím v systému.

V tomto tématu jsou popsány následující části:

WMI se obvykle spouští jako administrativní služba na vysoké úrovni zabezpečení s využitím kontextu zabezpečení LocalServer. Použití služby pro správu poskytuje rozhraní WMI prostředky pro přístup k privilegovaným informacím. Při volání zprostředkovatele informací rozhraní WMI předá poskytovateli identifikátor zabezpečení (SID), který poskytovateli umožní přístup k informacím na stejné vysoké úrovni zabezpečení.

Během procesu spuštění aplikace rozhraní WMI poskytuje operační systém Windows aplikaci WMI kontext zabezpečení uživatele, který proces zahájil. Kontext zabezpečení uživatele je obvykle nižší úroveň zabezpečení než LocalServer, takže uživatel nemusí mít oprávnění pro přístup ke všem informacím dostupným pro rozhraní WMI. Když uživatelová aplikace požádá o dynamické informace, rozhraní WMI předá identifikátor SID uživatele odpovídajícímu poskytovateli. Pokud je to správně napsané, poskytovatel se pokusí získat přístup k informacím pomocí identifikátoru SID uživatele, a ne identifikátoru SID zprostředkovatele.

Aby zprostředkovatel úspěšně zosobněl klientskou aplikaci, musí klientská aplikace a zprostředkovatel splňovat následující kritéria:

Registrace poskytovatele pro imitaci

Rozhraní WMI předává identifikátor SID klientské aplikace jenom poskytovatelům, kteří se zaregistrovali jako poskytovatelé zosobnění. Abyste umožnili poskytovateli předstírat identitu, je nutné upravit proces jeho registrace.

Následující postup popisuje, jak zaregistrovat zprostředkovatele pro předstírání identity. Tento postup předpokládá, že už rozumíte procesu registrace. Další informace o procesu registrace naleznete v tématu Registrace poskytovatele.

Registrace zprostředkovatele pro zosobnění

  1. Nastavte vlastnost ImpersonationLevel třídy __Win32Provider, která představuje vašeho zprostředkovatele na hodnotu 1.

    Vlastnost ImpersonationLevel dokumentuje, zda poskytovatel podporuje impersonaci nebo ne. Nastavení zosobněníLevel na 0 označuje, že zprostředkovatel nezosobní klienta a provádí všechny požadované operace ve stejném kontextu uživatele jako rozhraní WMI. Nastavení ImpersonationLevel na 1 označuje, že poskytovatel používá volání zosobnění ke kontrole operací provedených jménem klienta.

  2. Nastavte vlastnost PerUserInitialization stejné třídy __Win32Provider na TRUE.

Poznámka

Pokud zaregistrujete poskytovatele ve vlastnosti __Win32ProviderInitializeAsAdminFirst nastavenou na TRUE, pak zprostředkovatel používá token zabezpečení vlákna na úrovni správy pouze během fáze inicializace. I když volání CoImpersonateClient ne­selže, poskytovatel používá kontext zabezpečení rozhraní WMI, a nikoli klienta.

 

Následující příklad kódu ukazuje, jak zaregistrovat zprostředkovatele pro zosobnění.

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

Nastavení úrovní impersonace v rámci poskytovatele

Pokud zaregistrujete poskytovatele ve vlastnosti třídy __Win32ProviderImpersonationLevel nastavenou na hodnotu 1, služba WMI zavolá vašeho poskytovatele, aby se vydával za různé klienty. Ke zpracování těchto volání použijte CoImpersonateClient a CoRevertToSelf funkce COM ve vaší implementaci rozhraní IWbemServices.

Funkce CoImpersonateClient umožňuje serveru zosobnit klienta, který hovor provedl. Umístěním volání CoImpersonateClient do vaší implementace IWbemServicesumožníte poskytovateli nastavit token vlákna poskytovatele tak, aby odpovídal tokenu vlákna klienta a tím imitovat klienta. Pokud nevoláte CoImpersonateClient, váš poskytovatel spustí kód na úrovni zabezpečení správce, čímž se vytvoří potenciální ohrožení zabezpečení. Pokud váš poskytovatel dočasně potřebuje jednat jako správce nebo provést kontrolu přístupu ručně, zavolejte CoRevertToSelf.

Na rozdíl od CoImpersonateClient, CoRevertToSelf je funkce COM, která zpracovává úrovně zosobnění vlákna. V tomto případě CoRevertToSelf změní úroveň zosobnění zpět na původní nastavení zosobnění. Obecně platí, že poskytovatel je zpočátku správcem a přepíná se mezi CoImpersonateClient a CoRevertToSelf v závislosti na tom, zda provádí volání, které představuje volajícího, nebo vlastní volání. Je zodpovědností poskytovatele, aby tato volání správně umístil, aby koncový uživatel nezpřístupnil bezpečnostní díru. Poskytovatel by například měl volat pouze nativní funkce systému Windows v rámci impersonované sekvence kódu.

Poznámka

Účelem CoImpersonateClient a CoRevertToSelf je nastavit zabezpečení pro poskytovatele. Pokud zjistíte, že vaše zosobnění selhalo, měli byste prostřednictvím rozhraní WMI vrátit adekvátní kód dokončení pomocí IWbemObjectSink::SetStatus. Další informace naleznete v tématu Zpracování zpráv o odepření přístupu ve zprostředkovateli.

 

Udržování úrovní zabezpečení ve zprostředkovateli

Zprostředkovatelé nemohou volat CoImpersonateClient jednorázově v implementaci služby IWbemServices a předpokládat, že přihlašovací údaje zosobnění zůstanou po dobu trvání poskytovatele. Místo toho volejte CoImpersonateClient několikrát během implementace, aby rozhraní WMI zabránilo změně přihlašovacích údajů.

Hlavní obavou při nastavení zosobnění poskytovatele je znovuzavolání. V tomto kontextu je znovuvolání, když poskytovatel zavolá na WMI pro informace a čeká, až WMI zavolá zpět k poskytovateli. Vlákno provádění v podstatě opustí kód zprostředkovatele, aby se do něj později znovu vrátilo. Opětovný vstup je součástí návrhu COM a obecně není problémem. Když však vlákno spuštění vstoupí do rozhraní WMI, vlákno převezme úrovně zosobnění rozhraní WMI. Když se vlákno vrátí poskytovateli, je nutné obnovit úrovně zosobnění jiným voláním CoImpersonateClient.

Abyste se chránili před bezpečnostními dírami u vašeho poskytovatele, měli byste provádět znovuvstupné volání do WMI pouze při napodobování klienta. To znamená, že volání WMI by měla být provedena po volání CoImpersonateClient a před voláním CoRevertToSelf. Vzhledem k tomu, že CoRevertToSelf způsobí, že je úroveň zastupování stanovena na uživatelskou úroveň, která běží službu WMI, obvykle LocalSystem, reentrantní volání na WMI po volání CoRevertToSelf může uživateli a všem volaným zprostředkovatelům poskytnout mnohem více funkcí, než by měli mít.

Poznámka

Pokud voláte systémovou funkci nebo jinou metodu rozhraní, není zaručeno zachování kontextu volání.

 

Zpracování zpráv o odepření přístupu ve zprostředkovateli

Většina chybových zpráv o odepření přístupu se zobrazí, když klient požádá o třídu nebo informace, ke kterým nemá přístup. Pokud zprostředkovatel vrátí chybovou zprávu o odepření přístupu službě WMI a rozhraní WMI tuto zprávu předá klientovi, klient může odvodit, že informace existují. V některých situacích to může být porušení zabezpečení. Váš poskytovatel by proto neměl zprávu rozšířit do klienta. Místo toho by neměla být vystavena sada tříd, které by poskytovatel poskytl. Podobně by měl poskytovatel dynamické instance zavolat základní zdroj dat, aby určil, jak řešit zprávy o zamítnutí přístupu. Za replikaci této filozofie do prostředí WMI zodpovídá poskytovatel. Další informace naleznete v hlášení částečných instancí a hlášení částečných výčtů.

Když určíte, jak má váš poskytovatel zpracovávat zprávy o odepření přístupu, musíte napsat a ladit kód. Při ladění je často vhodné rozlišovat mezi odepřením kvůli nízké úrovni zosobnění a odepřením kvůli chybě v kódu. K určení rozdílu můžete použít jednoduchý test v kódu. Další informace naleznete v tématu Ladění kódu odepření přístupu.

Hlášení částečných instancí

Jedním z běžných výskytů zprávy o odepření přístupu je, když rozhraní WMI nemůže poskytnout všechny informace k vyplnění instance. Klient může mít například autoritu k zobrazení objektu pevného disku, ale nemusí mít oprávnění zjistit, kolik místa je k dispozici na samotné jednotce pevného disku. Váš poskytovatel musí určit, jak zpracovat jakoukoli situaci, kdy poskytovatel nemůže zcela vyplnit instanci vlastnostmi z důvodu porušení přístupu.

Rozhraní WMI nevyžaduje jednotnou odpověď pro klienty s částečným přístupem k instanci. Místo toho WMI verze 1.x umožňuje poskytovateli jednu z následujících možností:

  • Selžte celou operaci s WBEM_E_ACCESS_DENIED a nevracejte žádné instance.

    Vraťte objekt chyby spolu s WBEM_E_ACCESS_DENIED, který popisuje důvod odepření.

  • Vrátí všechny dostupné vlastnosti a vyplní nedostupné vlastnosti null.

Poznámka

Ujistěte se, že vrácení WBEM_E_ACCESS_DENIED nevytváří ve vaší společnosti bezpečnostní problém.

 

Hlášení částečných výčtů

Dalším běžným výskytem narušení přístupu je, že rozhraní WMI nemůže vrátit všechny výčty. Klient může mít například přístup k zobrazení všech objektů počítače místní sítě, ale nemusí mít přístup k zobrazení počítačových objektů mimo jeho doménu. Váš poskytovatel musí určit, jak zpracovat jakoukoli situaci, kdy se výčet nedá dokončit kvůli narušení přístupu.

Stejně jako u zprostředkovatele instance WMI není vyžadována jediná odpověď na částečný výčet. Místo toho WMI verze 1.x umožňuje poskytovateli jednu z následujících možností:

  • Vraťte WBEM_S_NO_ERROR pro všechny instance, ke kterým má poskytovatel přístup.

    Pokud použijete tuto možnost, uživatel neví, že některé instance nebyly k dispozici. Řada poskytovatelů, například těch, kteří používají jazyk SQL (Structured Query Language) se zabezpečením na úrovni řádků, vrací úspěšné částečné výsledky pomocí úrovně zabezpečení volajícího k definování sady výsledků.

  • Celá operace se nezdaří s WBEM_E_ACCESS_DENIED a nevrátí žádné instance.

    Poskytovatel může volitelně zahrnout objekt chyby, který popisuje situaci klientovi. Všimněte si, že někteří zprostředkovatelé mohou přistupovat k zdrojům dat sériově a nemusí narazit na odepření, dokud neprojde výčtem.

  • Vrátí všechny instance, ke kterým lze získat přístup, a vrátí také stavový kód bez chyby, WBEM_S_ACCESS_DENIED.

    Zprostředkovatel by měl během výčtu poznamenat zamítnutí a může pokračovat v poskytování instancí, přičemž ukončí s kódem stavu bez chyby. Poskytovatel se může také rozhodnout ukončit výčet při prvním odepření. Odůvodněním této možnosti je, že různí poskytovatelé mají různá paradigmata načítání. Zprostředkovatel už možná zprostředkoval instance dříve, než bylo zjištěno porušení přístupu. Někteří poskytovatelé se můžou rozhodnout pokračovat v poskytování jiných instancí a jiní můžou chtít ukončit.

Vzhledem ke struktuře modelu COM nelze během chyby přesměrovat žádné informace, s výjimkou objektu chyby. Proto nelze vrátit informace i kód chyby. Pokud se rozhodnete vrátit informace, musíte místo toho použít nechybný stavový kód.

Ladění kódu odepření přístupu

Některé aplikace mohou používat úrovně zosobnění nižší než RPC_C_IMP_LEVEL_IMPERSONATE. V takovém případě většina volání zosobnění, která poskytovatel provede pro klientskou aplikaci, selže. Pokud chcete úspěšně navrhnout a implementovat poskytovatele, musíte mít na paměti tuto myšlenku.

Ve výchozím nastavení je jedinou další úrovní impersonace, která má přístup k poskytovateli, RPC_C_IMP_LEVEL_IDENTIFY. V případech, kdy klientská aplikace používá RPC_C_IMP_LEVEL_IDENTIFY, CoImpersonateClient nevrací kód chyby. Místo toho zprostředkovatel zosobňuje klienta pouze pro účely identifikace. Proto většina metod Windows volaných poskytovatelem vrátí zprávu o odepření přístupu. To je neškodné v praxi, protože uživatelé nebudou moci dělat nic nevhodného. Při vývoji zprostředkovatele však může být užitečné vědět, zda byl klient skutečně zastoupen nebo ne.

Kód vyžaduje následující odkazy a příkazy #include, aby se správně zkompiloval.

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

Následující příklad kódu ukazuje, jak zjistit, jestli poskytovatel úspěšně zosobněl klientskou aplikaci.

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

vývoj zprostředkovatele WMI

nastavení popisovačů zabezpečení

zabezpečení vašeho poskytovatele