Personifikacja klienta
Gdy aplikacja użytkownika żąda danych z obiektów w systemie za pośrednictwem dostawcy WMI, personifikacja oznacza, że dostawca przedstawia poświadczenia reprezentujące poziom zabezpieczeń klienta, a nie dostawcy. Personifikacja uniemożliwia klientowi uzyskanie nieautoryzowanego dostępu do informacji w systemie.
W tym temacie omówiono następujące sekcje:
- Rejestrowanie dostawcy do podszywania się
- Ustawianie Poziomów Impersonacji w Dostawcy
- utrzymywanie poziomów zabezpieczeń u dostawcy
- Zarządzanie komunikatami odmowy dostępu u dostawcy
- raportowania wystąpień częściowych
- Raportowanie częściowych wyliczeń
- Debugowanie Twojego kodu odmowy dostępu
- Tematy pokrewne
Usługa WMI zazwyczaj działa jako usługa administracyjna na wysokim poziomie zabezpieczeń przy użyciu kontekstu zabezpieczeń LocalServer. Użycie usługi administracyjnej umożliwia WMI dostęp do informacji uprzywilejowanych. Podczas wywoływania dostawcy w celu uzyskania informacji, WMI przekazuje swój identyfikator zabezpieczeń (SID) do dostawcy, umożliwiając dostawcy dostęp do informacji na tym samym poziomie zabezpieczeń.
Podczas procesu uruchamiania aplikacji WMI system operacyjny Windows udostępnia aplikacji WMI kontekst zabezpieczeń użytkownika, który rozpoczął proces. Kontekst zabezpieczeń użytkownika jest zazwyczaj niższym poziomem zabezpieczeń niż LocalServer, więc użytkownik może nie mieć uprawnień dostępu do wszystkich informacji dostępnych w usłudze WMI. Gdy aplikacja użytkownika prosi o informacje dynamiczne, usługa WMI przekazuje identyfikator SID użytkownika do odpowiedniego dostawcy. Jeśli zostanie napisany odpowiednio, dostawca próbuje uzyskać dostęp do informacji przy użyciu identyfikatora SID użytkownika, a nie identyfikatora SID dostawcy.
Aby dostawca mógł się pomyślnie podszyć pod aplikację kliencką, aplikacja kliencka i dostawca muszą spełniać następujące kryteria:
- Aplikacja kliencka musi wywołać usługę WMI, ustawiając poziom zabezpieczeń dla połączenia COM na RPC_C_IMP_LEVEL_IMPERSONATE lub RPC_C_IMP_LEVEL_DELEGATE. Aby uzyskać więcej informacji, zobacz Utrzymanie zabezpieczeń WMI.
- Dostawca musi zarejestrować się w usłudze WMI jako dostawca personifikacji. Aby uzyskać więcej informacji, zobacz Rejestrowanie dostawcy pod kątem personifikacji.
- Dostawca musi przełączyć się na poziom zabezpieczeń aplikacji klienckiej przed uzyskaniem dostępu do informacji uprzywilejowanych. Aby uzyskać więcej informacji, zobacz Ustawianie poziomów personifikacji w ramach dostawcy.
- Dostawca musi poprawnie obsługiwać sytuacje błędów, jeśli dostęp do tych informacji zostanie odmówiony. Aby uzyskać więcej informacji, zobacz Obsługa komunikatów odmowy dostępu u dostawcy.
Rejestrowanie usługodawcy do podszywania się
WMI przekazuje SID aplikacji klienckiej tylko tym dostawcom, którzy zarejestrowali się jako dostawcy personifikacji. Aby umożliwić dostawcy wykonanie impersonacji, należy zmodyfikować proces rejestracji dostawcy.
Poniższa procedura opisuje sposób rejestrowania dostawcy w celu personifikacji. Procedura zakłada, że rozumiesz już proces rejestracji. Aby uzyskać więcej informacji na temat procesu rejestracji, zobacz Rejestrowanie dostawcy.
Aby zarejestrować dostawcę w celu personifikacji
Ustaw właściwość ImpersonationLevel klasy __Win32Provider reprezentującej dostawcę na 1.
Właściwość ImpersonationLevel określa, czy dostawca obsługuje uwierzytelnianie przez imitację. Ustawienie PoziomImpersonacji na 0 wskazuje, że dostawca nie impersonuje klienta i wykonuje wszystkie żądane operacje w tym samym kontekście użytkownika co WMI. Ustawienie PoziomImpersonacji na 1 wskazuje, że dostawca używa wywołań impersonacji w celu sprawdzenia operacji wykonywanych w imieniu klienta.
Ustaw właściwość PerUserInitialization tej samej klasy __Win32Provider na true.
Notatka
Jeśli zarejestrujesz dostawcę za pomocą właściwości __Win32ProviderInitializeAsAdminFirst ustawioną na true, dostawca używa tokenu zabezpieczeń wątku administracji tylko podczas fazy inicjowania. Pomimo że wywołanie CoImpersonateClient nie kończy się niepowodzeniem, dostawca korzysta z kontekstu zabezpieczeń WMI, a nie klienta.
W poniższym przykładzie kodu pokazano, jak zarejestrować dostawcę w celu personifikacji.
instance of __Win32Provider
{
CLSID = "{FD4F53E0-65DC-11d1-AB64-00C04FD9159E}";
ImpersonationLevel = 1;
Name = "MS_NT_EVENTLOG_PROVIDER";
PerUserInitialization = TRUE;
};
Ustawianie poziomów podszywania się w ramach dostawcy
Jeśli zarejestrujesz dostawcę za pomocą właściwości klasy __Win32ProviderImpersonationLevel ustawionej na 1, usługa WMI wywołuje dostawcę w celu podszywania się pod różnych klientów. Aby obsłużyć te wywołania, użyj funkcji CoImpersonateClient i CoRevertToSelf COM w implementacji interfejsu IWbemServices.
Funkcja CoImpersonateClient umożliwia serwerowi personifikację klienta, który wykonał wywołanie. Umieszczając wywołanie CoImpersonateClient w swojej implementacji IWbemServices, umożliwiasz dostawcy ustawienie tokenu wątku, aby dopasować go do tokenu wątku klienta i tym samym dokonać jego imitacji. Jeśli nie wywołasz CoImpersonateClient, dostawca wykonuje kod na poziomie zabezpieczeń administratora, tworząc potencjalną lukę w zabezpieczeniach. Jeśli dostawca tymczasowo musi działać jako administrator lub ręcznie przeprowadzić sprawdzanie dostępu, wywołaj CoRevertToSelf.
W przeciwieństwie do CoImpersonateClient, CoRevertToSelf to funkcja COM, która obsługuje poziomy imitacji wątków. W tym przypadku CoRevertToSelf zmienia poziom personifikacji z powrotem na oryginalne ustawienie personifikacji. Ogólnie rzecz biorąc, dostawca jest początkowo administratorem i przemiennie korzysta z CoImpersonateClient oraz CoRevertToSelf, w zależności od tego, czy wykonuje wywołanie reprezentujące dzwoniącego, czy swoje własne wywołania. Obowiązkiem dostawcy jest poprawne umieszczenie tych wywołań, aby nie narażać użytkownika końcowego na zagrożenie związane z luką w zabezpieczeniach. Na przykład dostawca powinien wywoływać tylko natywne funkcje systemu Windows w ramach personifikowanej sekwencji kodu.
Notatka
Celem CoImpersonateClient i CoRevertToSelf jest konfigurowanie zabezpieczeń dla dostawcy. Gdy stwierdzisz, że impersonacja nie powiodła się, powinieneś zwrócić odpowiedni kod ukończenia do WMI za pośrednictwem IWbemObjectSink::SetStatus. Aby uzyskać więcej informacji, zobacz Obsługa komunikatów o odmowie dostępu w dostawcy.
Utrzymywanie poziomów zabezpieczeń u dostawcy
Dostawcy nie mogą wywoływać CoImpersonateClient raz w implementacji IWbemServices, zakładając, że poświadczenia personifikacji pozostają ważne na czas działania dostawcy. Zamiast tego wywołaj CoImpersonateClient wiele razy w trakcie implementacji, aby usługa WMI nie zmieniała poświadczeń.
Głównym problemem dotyczącym ustawiania personifikacji dostawcy jest ponowne zalogowanie. W tym kontekście ponowne wejście ma miejsce, gdy dostawca wykonuje wywołanie do WMI w celu uzyskania informacji i czeka, aż WMI ponownie wywoła dostawcę. W istocie wątek wykonywania pozostawia kod dostawcy, tylko w celu ponownego przesyłania kodu w późniejszym terminie. Reentry jest integralną częścią projektu COM i zazwyczaj nie stanowi problemu. Jednak gdy wątek wykonywania przechodzi do WMI, wątek przejmuje poziomy podszywania się WMI. Gdy wątek powróci do usługodawcy, należy zresetować poziomy uwierzytelnienia, wykonując kolejne wywołanie CoImpersonateClient.
Aby chronić się przed lukami bezpieczeństwa u dostawcy, należy wykonać wywołania ponownego uwierzytelniania w usłudze WMI tylko podczas personifikacji klienta. Oznacza to, że wywołania WMI powinny być wykonywane po wywołaniu CoImpersonateClient i przed wywołaniem CoRevertToSelf. Ponieważ CoRevertToSelf powoduje, że przypisanie tożsamości jest ustawione na poziom użytkownika, na którym jest uruchomiona WMI, zazwyczaj LocalSystem, ponowne wywołania usługi WMI po wywołaniu CoRevertToSelf mogą dać użytkownikowi i dowolnym dostawcom o wiele więcej możliwości niż powinny mieć.
Notatka
Jeśli wywołasz funkcję systemową lub inną metodę interfejsu, kontekst wywołania nie jest gwarantowany.
Obsługa komunikatów odmowy dostępu u dostawcy
Większość komunikatów o błędzie odmowy dostępu jest wyświetlana, gdy klient żąda klasy lub informacji, do których nie ma dostępu. Jeśli dostawca zwróci komunikat o błędzie Odmowa dostępu do WMI, a WMI przekaże ten komunikat klientowi, klient może wywnioskować, że istnieją informacje. W niektórych sytuacjach może to być naruszenie zabezpieczeń. W związku z tym dostawca nie powinien propagować komunikatu do klienta. Zamiast tego zestaw klas dostarczonych przez dostawcę nie powinien być uwidoczniony. Podobnie dostawca dynamicznych wystąpień powinien odwołać się do podstawowego źródła danych, aby określić, jak obsłużyć komunikaty o odmowie dostępu. Jest to odpowiedzialność dostawcy za replikowanie tej filozofii do środowiska WMI. Aby uzyskać więcej informacji, zobacz także Reporting Partial Instances i Reporting Partial Enumerations.
Podczas określania sposobu obsługi komunikatów odmowy dostępu dostawcy należy napisać i debugować kod. Podczas debugowania często wygodnie jest odróżnić odmowę ze względu na niskie uwierzytelnienie od odmowy z powodu błędu w kodzie. Aby określić różnicę, możesz użyć prostego testu w kodzie. Aby uzyskać więcej informacji, zobacz Debugowanie kodu odmowy dostępu.
Raportowanie wystąpień częściowych
Jednym z typowych przypadków komunikatu o odmowie dostępu jest sytuacja, kiedy usługa WMI nie może podać wszystkich informacji potrzebnych do wypełnienia instancji. Na przykład klient może mieć uprawnienia do wyświetlania obiektu dysku twardego, ale może nie mieć uprawnień do sprawdzenia, ile miejsca jest dostępne na samym dysku twardym. Dostawca musi określić, jak obsługiwać każdą sytuację, gdy nie może całkowicie wypełnić instancji odpowiednimi właściwościami z powodu naruszenia dostępu.
Usługa WMI nie wymaga pojedynczej odpowiedzi od klientów, którzy mają częściowy dostęp do instancji. Zamiast tego usługa WMI w wersji 1.x umożliwia dostawcy jedną z następujących opcji:
Nie powiodła się cała operacja z WBEM_E_ACCESS_DENIED i nie zwrócono żadnych wystąpień.
Zwróć obiekt błędu razem z WBEM_E_ACCESS_DENIED, aby wyjaśnić przyczynę odmowy.
Zwróć wszystkie dostępne właściwości i wypełnij niedostępne właściwości za pomocą NULL.
Notatka
Upewnij się, że zwracanie WBEM_E_ACCESS_DENIED nie powoduje luk w zabezpieczeniach w przedsiębiorstwie.
Raportowanie częściowych wyliczeń
Innym typowym wystąpieniem naruszenia dostępu jest to, że usługa WMI nie może zwrócić wszystkich wyliczeń. Na przykład klient może mieć dostęp do wyświetlania wszystkich obiektów komputera sieci lokalnej, ale może nie mieć dostępu do wyświetlania obiektów komputerów poza jego domeną. Dostawca musi określić, jak obsługiwać każdą sytuację, gdy nie można ukończyć wyliczenia z powodu naruszenia dostępu.
Podobnie jak w przypadku dostawcy instancji, usługa WMI nie wymaga jednej odpowiedzi na częściowe wyliczenie. Zamiast tego usługa WMI w wersji 1.x umożliwia dostawcy jedną z następujących opcji:
Zwróć WBEM_S_NO_ERROR dla wszystkich wystąpień, do których dostawca może uzyskać dostęp.
Jeśli użyjesz tej opcji, użytkownik nie jest świadomy, że niektóre instancje były niedostępne. Niektórzy dostawcy, tacy jak ci, którzy korzystają z języka Structured Query Language (SQL) z zabezpieczeniami na poziomie wiersza, zwracają pomyślne częściowe wyniki, używając poziomu zabezpieczeń osoby wywołującej do zdefiniowania zestawu wyników.
Zakończyć całą operację niepowodzeniem z kodem WBEM_E_ACCESS_DENIED i nie zwrócić żadnych wystąpień.
Dostawca może opcjonalnie zawierać obiekt błędu opisujący sytuację klienta. Należy pamiętać, że niektórzy dostawcy mogą uzyskiwać dostęp do źródeł danych szeregowo i mogą nie napotkać odmowy do momentu częściowego wyliczenia.
Zwróć wszystkie wystąpienia, do których można uzyskać dostęp, oraz kod stanu bez błędów WBEM_S_ACCESS_DENIED.
Dostawca powinien odnotować odmowę podczas enumeracji i może kontynuować dostarczanie wystąpień, kończąc kodem stanu bez błędów. Dostawca może również zdecydować się na zakończenie wyliczania przy pierwszej odmowie. Uzasadnieniem tej opcji jest to, że różni dostawcy mają różne paradygmaty pobierania. Dostawca mógł już dostarczyć wystąpienia przed wykryciem naruszenia dostępu. Niektórzy dostawcy mogą zdecydować się na dalsze dostarczanie innych instancji, podczas gdy inni mogą zechcieć je zakończyć.
Ze względu na strukturę modelu COM nie można przesyłać z powrotem żadnych innych informacji podczas błędu, z wyjątkiem obiektu błędu. W związku z tym nie można zwrócić informacji i kodu błędu. Jeśli zdecydujesz się zwrócić informacje, musisz zamiast tego użyć kodu stanu nonerror.
Debugowanie kodu odmowy dostępu
Niektóre aplikacje mogą używać poziomów personifikacji niższych niż RPC_C_IMP_LEVEL_IMPERSONATE. W takim przypadku większość wywołań podszywania się wykonanych przez dostawcę dla aplikacji klienckiej zakończy się niepowodzeniem. Aby pomyślnie zaprojektować i wdrożyć dostawcę, należy pamiętać o tym.
Domyślnie jedynym innym poziomem podszywania się, który może uzyskiwać dostęp do usługodawcy, jest RPC_C_IMP_LEVEL_IDENTIFY. W przypadkach, gdy aplikacja kliencka używa RPC_C_IMP_LEVEL_IDENTIFY, CoImpersonateClient nie zwraca kodu błędu. Zamiast tego dostawca personifikuje klienta tylko do celów identyfikacji. W związku z tym większość metod systemu Windows wywoływanych przez dostawcę zwróci komunikat o odmowie dostępu. Jest to nieszkodliwe w praktyce, ponieważ użytkownicy nie będą mogli robić niczego nieodpowiedniego. Jednak może to być przydatne podczas opracowywania dostawcy, aby wiedzieć, czy klient był naprawdę personifikowany, czy nie.
Kod wymaga następujących odwołań i instrukcji #include w celu poprawnego skompilowania.
#define _WIN32_DCOM
#include <iostream>
using namespace std;
#include <wbemidl.h>
Poniższy przykład kodu pokazuje, jak określić, czy dostawca pomyślnie personifikował aplikację kliencką.
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);
Tematy pokrewne