(ez még egy régebbi történet, csak most jutottam el oda, hogy leírjam)
Az egyik IRF háziban jött elő, hogy egy hallgató meg szeretett volna nyitni egy szöveges fájlt a WMI provider kódján belül, de a következő hibát kapta:
.NET Runtime version 2.0.50727.3082 - Unexpected exception thrown from the provider: System.IO.IOException: Either a required impersonation level was not provided, or the provided impersonation level is invalid.
A fenti hibához egyébként a következő hibakód tartozik: ERROR_BAD_IMPERSONATION_LEVEL 1346 (0x542)
Mi az a megszemélyesítés (impersonation)?
A megszemélyesítés (impersonation) beállítás mondja meg, hogy ha a providernek valamilyen további műveletet (pl. fájl olvasás, hálózati kapcsolat) kell végrehajtania, azt kinek a nevében tegye. Használja-e azt a felhasználót, akinek a nevében fut a provider, vagy használja a WMI hívást kiadó felhasználó adatait (ebben az esetben beszélünk megszemélyesítésről)?
A WMI DCOM-ot használ a távoli hívásokhoz, itt a következő beállítási lehetőségeink vannak:
- 0 – Default: Reads the local registry for the default impersonation level.
- 1 – Anonymous: Hides the credentials of the caller. Calls to WMI may fail with this impersonation level.
- 2 – Identify: Allows objects to query the credentials of the caller. Calls to WMI may fail with this impersonation level.
- 3 – Impersonate: Allows objects to use the credentials of the caller. This is the recommended impersonation level for WMI Scripting API calls.
- 4 – Delegate: Allows objects to permit other objects to use the credentials of the caller. This impersonation, which will work with WMI Scripting API calls but may constitute an unnecessary security risk, is supported only under Windows 2000
Hogyan lehet beállítani a megszemélyesítést a providerben?
Általános WMI provider esetén az ImpersonationLevel tulajdonságot kéne beállítani a __Win32Provider CIM osztályon (lásd: Impersonating a Client).
Mi viszont a .NET-es WMI Provider Extensions kiegészítést használtuk, amiben .NET-es attribútumok segítségével lehet megadni a beállításokat, és ezek utána akkor az installutil-es telepítés során átkerülnek a CIM osztály definíciójába. Itt a WmiConfigurationAttribute IdentifyLevel tulajdonsága az, ami elvileg a megszemélyesítést befolyásolja. A leírás szerint:
Gets or sets a value that specifies whether the WMI provider can impersonate its callers. If the value is false, the provider cannot impersonate, and if the value is true, the provider can impersonate.
(Azonban látni fogjuk, hogy ez nem teljesen így működik.)
Hogyan lehet beállítani a megszemélyesítést a kliensben?
A fenti beállítások még csak azt mondják meg, hogy engedélyezi-e egyáltalán a megszemélyesítést a provider, ezen kívül még a WMI kérés kliensének is meg kell adni, hogy milyen szintet akar használni.
Powershell esetén ezt a Get-WmiObject cmdlet Impersonation paraméterében lehet megadni, 0-4 érétket vár a fent leírtaknak megfelelően.
Hogyan lehet akkor megoldani a problémát?
A fentiek alapján akkor úgy gondoltam, hogy true-ra kell állítani az IdentifyLevel értékét, majd 3-at (Impersonate) kell megadni az Impersonation paraméternek, és megy is minden rendben. Hát nem:)
A providerben továbbra is a fenti kivételt kaptam, a kliens pedig egyszerűen nem látta annak a tulajdonságnak az értékét, amiben a fájlművelet volt. Ezért kicsit kibővítettem a kódot, hogy kiírja a hívás során, hogy kinek a nevében fut és mi az aktuális ImpersonationLevel. Ez lett a kimenet:
Provider started Provider running by: THEFAMILY\zoltan.micskei Bind method called Get Property1 Get property2 Current identity: THEFAMILY\vito.mascarpone, Impersonation level: Identification System.IO.IOException: Unknown error "1346".
Látszik, hogy a hívó személyét megkapja rendesen a provider, azonban nem Impersonate az aktuális szint, hanem Identification. No most akkor mit is csinál az IdentifyLevel? Ha wbemtest.exe segítségével megnézzük a provider példányának MOF reprezentációját, akkor ezt látjuk:
instance of WmiProviderSample { Property1 = "Some string"; Property2 = NULL; };
A .NET WMI Extensions úgy működik, hogy létrehoz minden providerhez a WMI_extension osztály egy példányát, és abban tárol még információkat a providerhez:
instance of WMI_extension { AssemblyName = "WmiProviderSample, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; AssemblyPath = "file:///C:/temp/WmiProviderSample/WmiProviderSample.exe"; CLRVersion = "v2.0.50727"; CLSID = "{54D8502C-527D-43f7-A506-A9DA075E229C}"; HostingModel = "Decoupled:COM"; Name = "WmiProviderSample, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; SecurityDescriptor = "O:WDG:WDD:(A;;0x1;;;WD)"; };
Hát itt nyoma sincs az Impersonation beállításnak. A WMI_extension a __Win32Provider osztályból származik, annak van ImpersonationLevel attribútuma, de az alapértelmezés szerint nulla, így ez a provider példány tényleg nem fogja engedni a megszemélyesítést.
Kipróbáltam, hogy mi a különbség, ha az IdentifyLevel értékét false állítom, ekkor így nézett ki a megfelelő objektum (vigyázat, ahhoz, hogy ez a változtatás életbe lépjen, a providert az installutil segítségével újra kell telepíteni):
instance of WMI_extension { AssemblyName = "WmiProviderSample, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; AssemblyPath = "file:///C:/temp/WmiProviderSample/WmiProviderSample.exe"; CLRVersion = "v2.0.50727"; CLSID = "{54D8502C-527D-43f7-A506-A9DA075E229C}"; HostingModel = "Decoupled:COM:FoldIdentity(FALSE)"; Name = "WmiProviderSample, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"; SecurityDescriptor = "O:WDG:WDD:(A;;0x1;;;WD)"; };
Félkövérrel kiemeltem a különbséget. Mit csinál ez a FoldIdentity?
The following example shows the FoldIdentity specifier for the HostingModel property set to FALSE, which allows the provider to impersonate the client.
Decoupled:Com:FoldIdentity(FALSE)
Gratulálunk:-) Újra lefuttatva a kérést most már rendesen ment a megszemélyesítés, a kérést a hívó nevében hajtotta végre a provider, és így sikeresen le tudta kérdezni az adott tulajdonságot.
A megoldás tehát annyi, hogy – amennyire én látom – hibás az MSDN dokumentáció, és az IdentifyLevel false értéke engedélyezi a megszemélyesítést.