A blogban leírtak a szerzők saját véleménye, és nem a munkáltatójuké.

Metódushívás decoupled, singleton .NET-es WMI provider esetén

Az IRF tárgy egyik házi feladatában (4D) WMI providert kell készíteniük a hallgatóknak, ami egy .NET-es egyszerű webszerver menedzselését teszi lehetővé. Az egyik hallgató futott bele abba a problémába, hogy implementált egy metódust, de amikor WMI-on keresztül meg akarta hívni PowerShellből, akkor a provider leállt, a PowerShell ablakban meg látszólag nem történt semmi. Ránézve a kódra és a PowerShell Get-WmiObject hívásra látszólag minden rendben volt, az Eseménynaplóban találtunk csak egy részletesebb hibát, de első ránézésre az se segített sokat:

.NET Runtime version 2.0.50727.3082 - System.NullReferenceException:
Object reference not set to an instance of an object.
   at System.Management.Instrumentation.AutomationType.CalculateKey(ObjectDictionarydict)
   at PublishProvider.Get(CallContext context, ICIMResultHandler resultHandler)
   at WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(String objectPath, Int32 flags, IWbemContext wbemContext, IWbemObjectSink wbemSink)

Így aztán meg kellett közelebbről is vizsgálni a kódot, és kicsit jobban elmerülni a .NET WMI Provider Extensions lelkivilágában. WMI Providert alapvetően C++ nyelven lehetett írni bizonyos COM interfészek implementálásával, később aztán .NET támogatás is megjelent hozzá később. A 3.5-ös .NET Frameworkben újraírták ezt a .NET-est részt, és így született meg a WMI Provider Extensions, amiben egyszerűen attribútumok segítségével lehet definiálni, hogy mik a tulajdonságai, metódusai a providernek, majd az installutil elvégzi a MOF fájl generálást és a provider beregisztrálását. (Egy egyszerű .NET-es provider futtatásáról már írtam korábban.)

A providernek nagyon egyszerű teszt kódja volt, egy sima decoupled, singleton provider: egy osztály a statikus Main metódussal, és egy másik a WMI attribútumokkal. Az MSDN-es példakódtól egy dologban különbözött a kód, a WMI-ba regisztrálandó osztályok megadásánál:

// teszt kod: publish/revoke használata:
WmiProviderOsztaly m = new WmiProviderOsztaly();
InstrumentationManager.Publish(m);

// MSDN peldakod: registertype (callback) hasznalata
InstrumentationManager.RegisterType(typeof(WmiProviderOsztaly));

A két módszerről túl sok leírás nincsen, az InstrumentationManager leírásában ennyi szerepel:

Decoupled providers are hosted by an application. Two methods can be used by the application to make instances of WMI classes available: publish/revoke or the callback method. The callback method uses the RegisterType and UnregisterType methods.

In the publish/revoke model, the WMI infrastructure provides default behavior for many of the methods you have to write yourself in the callback method. These include the enumeration and bind methods. In this model, the application creates instances and publishes them. The application is responsible for ensuring that the key properties of the classes are respected. The application is also responsible for deleting instances.

In the callback model, the WMI infrastructure expects the application to have methods that handle enumeration, binding and any other methods required to implement the functionality of the provider. It calls into the application for this functionality and fails if it does not exist or is not implemented properly. The application registers the type of its WMI classes with the infrastructure by calling RegisterType and indicates that it no longer wants the WMI classes exposed by calling UnregisterType.

A Publish/revoke módszert használva a providerben, ha PowerShellből lekérjük a WMI objektumot, akkor a hívást sikeresen végrehajtja, a tulajdonságokat le lehet kérdezni. Viszont metódushívásnál a Powershell egyszerűen megáll, és a fenti kivétel kerül bele az eseménynaplóba.

A kivétel a Visual Studio debuggerében

Megnéztem Visual Studio 2008-ból indítva a providert, hogy ad-e valami további részletet a kivételről, azonban túl sok plusz információt nem szolgáltatott. Kaptam egy FatalExecutionEngineError-t:

wmi-provider-vs-error

A local és a callstack ablakok sem adtak túl sok útmutatást:

wmi-error-callstack

Itt most csak a GetObjectAsync hívás látszik, a kivétel stack trace-ében lévő többi hívás nem.

A kivétel WinDbg alól

Jobb ötletem nem volt, mint hogy ránézni WinDbg-gal, hátha ott jobban látszik, hogy mit történik a nem menedzselt kód hívásakor. Elindítottam a WinDbg-ot, File / Open executable, megadtam az alkalmazást, beállítottam a kezdő könyvtárat, majd lássuk mit ad:

wmi-provider-windbg

A debugger rögtön megállítja a folyamatot a létrehozása után, indítsuk el (go parancs):

0:000> g

Erre elindul a provider, betölt egy csomó DLL-t, majd várni kezd a kérésekre. Ha meghívjuk a metódust, ami a hibát kiváltja, akkor a WinDbg a következő kivételt kapja el:

(8e0.ee0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=022f1ee4 ecx=012f3c40 edx=012fb1c8 esi=012fb2a0 edi=012fce30
eip=6d4e0159 esp=0356f038 ebp=0356f068 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
*** WARNING: Unable to verify checksum for C:\WINDOWS\assembly\NativeImages_v2.0.50727_32\System.Management.I#\0c0688825a79e72951210318eef63c82\System.Management.Instrumentation.ni.dll
System_Management_Instrumentation_ni+0x10159:
6d4e0159 8b5004          mov     edx,dword ptr [eax+4] ds:0023:00000004=????????

Lássuk hol járunk (kb – a stack listázására szolgáló parancs):

0:005> kb
ChildEBP RetAddr  Args to Child
0356f068 6d4dac62 012f2c14 00000000 012f3c40 System_Management_Instrumentation_ni+0x10159
0356f09c 6d4d40e5 012fce04 012fcd50 012fce24 System_Management_Instrumentation_ni+0xac62
0356f0e8 79f047fd 012fcd40 012fcd30 00000000 System_Management_Instrumentation_ni+0x40e5
0356f214 79f0462c 001e2a38 0356f40c 0356f484 mscorwks!COMToCLRWorkerBody+0x208
0356f270 79f62ac8 001e2a38 0356f40c 0356f484 mscorwks!COMToCLRWorkerDebuggerWrapper+0x37
0356f444 0097a832 001e2a38 0356f484 9455ff71 mscorwks!COMToCLRWorker+0x157
WARNING: Frame IP not in any known module. Following frames may be wrong.
0356f46c 599493ce 001d7e50 00000000 001e3150 0x97a832
0356f4dc 59949532 00000000 001d7b14 00000000 wmidcprv!CInterceptor_DecoupledClient::Helper_GetObjectAsync+0x119
0356f51c 59947161 01280038 001d7b14 00000000 wmidcprv!CInterceptor_DecoupledClient::GetObjectAsync+0x75
0356f550 77e799f4 001d7e78 000005c0 00000000 wmidcprv!CInterceptor_DecoupledClient::Internal_GetObjectAsync+0x51
0356f588 77ef421a 59947110 0356f59c 00000009 RPCRT4!Invoke+0x30
0356f9a8 77ef4bf3 001df740 001d8fe0 001dfa28 RPCRT4!NdrStubCall2+0x297
0356fa00 77600c15 001df740 001dfa28 001d8fe0 RPCRT4!CStdStubBuffer_Invoke+0xc6
0356fa40 77600bbf 001dfa28 001df638 001d5c28 ole32!SyncStubInvoke+0x33
0356fa88 7752ad31 001dfa28 001d8550 001df740 ole32!StubInvoke+0xa7
0356fb60 7752ac56 001d8fe0 00000000 001df740 ole32!CCtxComChnl::ContextInvoke+0xe3
0356fb7c 776007f5 001dfa28 00000001 001df740 ole32!MTAInvoke+0x1a
0356fbac 77602df3 001df9d0 001d8fe0 001df740 ole32!AppInvoke+0x9c
0356fc80 77600715 001df9d0 001c87a0 001df620 ole32!ComInvokeWithLockAndIPID+0x2c2
0356fccc 77e794bd 001dee3c 001df620 001dee3c ole32!ThreadInvoke+0x1cd
0356fd00 77e79422 776005d0 001dee3c 0356fdec RPCRT4!DispatchToStubInC+0x38
0356fd54 77e7934e 00000000 00000000 774ebfc8 RPCRT4!RPC_INTERFACE::DispatchToStubWorker+0x113
0356fd78 77e8a384 001dee3c 00000000 774ebfc8 RPCRT4!RPC_INTERFACE::DispatchToStub+0x84
0356fdb8 77e8a3c5 001dee3c 001dedf8 00000000 RPCRT4!RPC_INTERFACE::DispatchToStubWithObject+0xc0
0356fdf8 77e7bcc1 001df780 001c7ea8 001deb98 RPCRT4!LRPC_SCALL::DealWithRequestMessage+0x2cd
0356fe1c 77e7bc05 001c7ee4 0356fe38 001deb98 RPCRT4!LRPC_ADDRESS::DealWithLRPCRequest+0x16d
0356ff80 77e76caf 0356ffa8 77e76ad1 001c7ea8 RPCRT4!LRPC_ADDRESS::ReceiveLotsaCalls+0x310
0356ff88 77e76ad1 001c7ea8 7c900000 0027fae0 RPCRT4!RecvLotsaCallsWrapper+0xd
0356ffa8 77e76c97 001c5380 0356ffec 7c80b713 RPCRT4!BaseCachedThreadRoutine+0x79
0356ffb4 7c80b713 001deaf8 7c900000 0027fae0 RPCRT4!ThreadStartRoutine+0x1a
0356ffec 00000000 77e76c7d 001deaf8 00000000 KERNEL32!BaseThreadStart+0x37

Ennyit lát egy nem menedzselt debugger a .NET-es kódból:) Az mscorwks a .NET Framework futtatókörnyezet, utána pedig a System.Management.Instrumentation assembly-be hívunk bele. Hogy lássuk, hogy mi történik a .NET-es kódban, használni kell a SOS kiegészítést. (a SOS-ról egy rövid leírás)

0:005>.loadby sos mscorwks

Így már belelátunk a menedzselt hívási verembe is:

0:005> !clrstack
OS Thread Id: 0xee0 (5)
ESP       EIP
0356f038 6d4e0159 System.Management.Instrumentation.AutomationType.CalculateKey(System.Management.Instrumentation.ObjectDictionary)
0356f070 6d4dac62 PublishProvider.Get(System.Management.Instrumentation.CallContext, System.Management.Instrumentation.ICIMResultHandler)
0356f0a8 6d4d40e5 WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)
0356f328 79f047fd [GCFrame: 0356f328]
0356f484 79f047fd [ComMethodFrame: 0356f484]

Most pont ott vagyunk, ahol a NullReferenceException történt:) A !clrstack meg tudja mutatni a paraméterek értékét is (jobbára):

0:005> !clrstack -p
OS Thread Id: 0xee0 (5)
ESP       EIP
0356f038 6d4e0159 System.Management.Instrumentation.AutomationType.CalculateKey(System.Management.Instrumentation.ObjectDictionary)
    PARAMETERS:
        this = <no data>
        dict = 0x00000000

0356f070 6d4dac62 PublishProvider.Get(System.Management.Instrumentation.CallContext, System.Management.Instrumentation.ICIMResultHandler)
    PARAMETERS:
        this = <no data>
        context = <no data>
        resultHandler = <no data>

0356f0a8 6d4d40e5 WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)
    PARAMETERS:
        this = 0x012fab80
        objectPath = <no data>
        flags = <no data>
        wbemContext = <no data>
        wbemSink = 0x012fcd40

0356f328 79f047fd [GCFrame: 0356f328]
0356f484 79f047fd [ComMethodFrame: 0356f484]

Ott látszik, hogy a CalculateKey-nek átadott paraméter null, valószínűleg ez okozza a NullReferenceException-t.

Ha továbbléptetjük, akkor elérkezünk a nem kezelt kivételhez, amit megnézhetünk a !PrintException segítségével:

0:005> g
ModLoad: 5e3a0000 5e42d000   C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\diasymreader.dll
CLR: Managed code called FailFast, saying "System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Management.Instrumentation.AutomationType.CalculateKey(ObjectDictionary dict)
   at PublishProvider.Get(CallContext context, ICIMResultHandler resultHandler)
   at WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(String objectPath, Int32 flags, IWbemContext wbemContext, IWbemObjectSink wbemSink)"
(8e0.ee0): Break instruction exception - code 80000003 (first chance)
eax=00000001 ebx=00000000 ecx=00000001 edx=0356e29c esi=001d6788 edi=6d4d4253
eip=7c90120e esp=0356ddfc ebp=0356e2a8 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000202
ntdll!DbgBreakPoint:
7c90120e cc              int     3
0:005> !PrintException
Exception object: 012fce78
Exception type: System.NullReferenceException
Message: Object reference not set to an instance of an object.
InnerException: <none>
StackTrace (generated):
    SP       IP       Function
    0356F038 6D4E015A System_Management_Instrumentation_ni!System.Management.Instrumentation.AutomationType.CalculateKey(System.Management.Instrumentation.ObjectDictionary)+0x56
    0356F070 6D4DAC62 System_Management_Instrumentation_ni!PublishProvider.Get(System.Management.Instrumentation.CallContext, System.Management.Instrumentation.ICIMResultHandler)+0xde
    0356F0A8 6D4D40E5 System_Management_Instrumentation_ni!WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)+0x155

StackTraceString: <none>
HResult: 80004003

(Így utólag visszaolvasva a WinDbg most igazából felesleges volt, hisz csak menedzselt kódot néztünk, ezt ki lehetett volna úgy is deríteni, ha az AutomationType.CalculateKey-re állítunk be egy töréspontot a Visual Studio-ban. Nem baj, legalább láttunk WindDbg-ot is, jól jöhet még az, ha menedzselt és nem menedzselt kódot kell vegyesen debuggolni:)

Most már csak azt kéne kideríteni, hogy miért hívja meg a CalculateKey-t, ez egy singleton provider, tehát egy példány van belőle, így nincs szükség ManagementKey-ekre, hogy megkülönböztessük az egyes példányokat (kulcsokat használunk pl. amikor a Win32_Process által visszaadott folyamatokat meg akarjuk különböztetni).

A System.Management.Instrumentation osztályai reflektorfényben

A .NET Reflector eszköz segítségével lehet például megnézni a .NET Framework osztályainak kódját. Ha hozzáadjuk a System.Management.Instrumentation.dll-t (File / Open, majd a C:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5 könyvtárban találjuk), akkor kikereshető a PublishProvider Get metódusa, ami meghívja a CalculateKey-t:

wmi-publishprovider-reflector

Hát itt túl sok bonyolult logika nincsen, ha valamit vissza akar adni, akkor meg kell hívni a CalculateKey-t. Ez pedig a context.Keys-t adja át, ennek az értékét meg tudjuk nézni, ha pl. Visual Studio-ban beállítunk egy breakpointot a PublishProvider.Get metódusra:

wmi-provider-publishprovider-breakpoint

Ha meghívjuk a metódust, akkor szépen meg is áll itt, és meg tudjuk nézni a context változó értékét:

wmi-provider-context

A Keys, nem meglepő módon, null értékű, ebből utána tényleg nem lehet megtalálni a WMI objektumot:)

Most már csak az a kérdés maradt hátra, hogy miért csak metódushívás esetén jön ez elő, a tulajdonságok lekérdezése miért megy publish esetén is. Ha beállítunk egy töréspontot egy tulajdonságlekérdezésre, akkor ott a következő hívási láncot kapjuk:

wmi1.exe!wmi1.WmiTest.Property1.get() Line 25	C#
 	[Native to Managed Transition]
 	[Managed to Native Transition]
>	System.Management.Instrumentation.dll!System.Management.Instrumentation.MetaPropertyInfo.GetValue(object oTarget) + 0x65 bytes
 	System.Management.Instrumentation.dll!System.Management.Instrumentation.ResultHandler.AddObjectToResult(object oResult = {wmi1.Cica}, out WmiNative.IWbemClassObject pOutInstance = null) + 0x97 bytes
 	System.Management.Instrumentation.dll!System.Management.Instrumentation.ResultHandler.AddResults(object[] result = {object[1]}) + 0x6a bytes
 	System.Management.Instrumentation.dll!PublishProvider.Enumerate(System.Management.Instrumentation.CallContext context, System.Management.Instrumentation.ICIMResultHandler resultHandler = {System.Management.Instrumentation.ResultHandler}) + 0x10e bytes
 	System.Management.Instrumentation.dll!WmiNative.WbemProvider.WmiNative.IWbemServices.CreateInstanceEnumAsync(string className, int flags, WmiNative.IWbemContext wbemContext, WmiNative.IWbemObjectSink wbemSink = {System.__ComObject}) + 0x158 bytes
 	[Native to Managed Transition]

Itt van a trükk, a tulajdonságok lekérdezésénél nem a GetObjectAsync-et és a PublishProvider.Get-et hívja, hanem amikor először lekéri az objektumot, akkor kiolvassa az összes tulajdonságát.

Az utolsó utáni kérdés pedig az, hogy mi történik máshogy a RegisterType esetén, ott miért nem futunk bele ebbe a hibába. Megpróbáltam hozzáadni egy töréspontot a WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync metódusra, de Visual Studio-ból sehogy se akart ráfutni, se teljes névvel, se részkombinációkkal,  se IWbemServices.GetObjectAsync alakban (ehhez a projekt tulajdonságainál be kellett kapcsolni az Enable unmanaged code debugging opciót). Úgyhogy maradt a WinDbg (ha kalapács van a kezünkben, mindent szögnek nézünk alapon:-).

Itt kicsit trükkösebb a breakpoint hozzáadás. Lehet névvel, ezzel itt se ment, de lehet a metódus azonosítóját is megadni a breakpointnak. Ezt kell tehát megszerezni:

Elindítjuk a programot, majd g, utána pedig ha betöltötte a .NET-es dll-eket is, akkor nyomunk egy Debug / Break parancsot. Majd (itt a kimenetek elég hosszúak, így csak a lényegi részt tartom meg):

0:006> !dumpdomain
...
Assembly: 001b0380 [C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll]
ClassLoader: 001b2af8
SecurityDescriptor: 001aa828
  Module Name
6d7e1000 C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll

A kékkel jelzett cím kell majd nekünk, az a System.Management.Instrumentation modul:

0:006> !dumpmodule -mt 6d7e1000
Name: C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll
...
Types defined in this module

      MT    TypeDef Name
------------------------------------------------------------------------------
6d823ad0 0x02000007 System.Management.Instrumentation.ManagedCommonProvider
6d823ba0 0x0200000b WmiNative.WbemProvider
...

A kékkel jelzett címen van a WbemProvider metódus táblája:

0:006> !DumpMT -MD 6d823ba0
EEClass: 6d7e426c
Module: 6d7e1000
Name: WmiNative.WbemProvider
mdToken: 0200000b  (C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll)
BaseSize: 0x20
ComponentSize: 0x0
Number of IFaces in IFaceMap: 4
Slots in VTable: 42
--------------------------------------
MethodDesc Table
   Entry MethodDesc      JIT Name
79286aa0   79104924   PreJIT System.Object.ToString()
6d81bb20   6d7e7728   PreJIT WmiNative.WbemProvider.WmiNative.IWbemServices.GetObject(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemClassObject ByRef, IntPtr)
6d81bb40   6d7e7730   PreJIT WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)
...

Megvan a metódus leírója, ez kell a breakpointhoz:

0:006> !bpmd -md 6d7e7730
MethodDesc = 6d7e7730
Setting breakpoint: bp 6D81BB40 [WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)]

Akkor most már csak meg kell hívnunk a metódusunkat, és nézni, hogy merre megy majd tovább a hívás. Ebben segít pl. a pc parancs, ami a következő call utasításra ugrik (sajnos ez nem a .NET-es hívás, hanem az alatta levő assembly call, úgyhogy több van belőle). Ugorhatunk a GetProvider hívásra, majd ha onnan visszatérünk, akkor ki kell deríteni, hogy milyen típusú providert kapunk vissza. Ehhez jól jön pl. a !vars kiterjesztés, ami a sosex.dll-ben van benne:

0:003> !vars
Frame 0 (WmiNative.WbemProvider.WmiNative.IWbemServices.GetObjectAsync(System.String, Int32, WmiNative.IWbemContext, WmiNative.IWbemObjectSink)):
Arguments:
[0]:this:0x13021a4 (WmiNative.WbemProvider)
[1]:objectPath:<?>
[2]:flags:<?>
[3]:wbemContext:<?>
[4]:wbemSink:0x131d2e0 (WmiNative.IWbemObjectSink)
Locals:
[0]:Failed to retrieve method desc from IP.  Error = 0x80070057
0x0 (System.Int32)
[1]:Failed to retrieve method desc from IP.  Error = 0x80070057<?>
[2]:Failed to retrieve method desc from IP.  Error = 0x80070057
0x131d2f0 (System.Management.Instrumentation.CallContext)
[3]:Failed to retrieve method desc from IP.  Error = 0x80070057
0x1303bc0 (WMIClassCacheEntry)
[4]:Failed to retrieve method desc from IP.  Error = 0x80070057
<?>
[5]:Failed to retrieve method desc from IP.  Error = 0x80070057
0x13026fc (System.Management.Instrumentation.ProviderMetadata)

Ott van, amit keresünk, nézzük meg az objektumot:

0:003> !dumpobject 0x013026fc
Name: System.Management.Instrumentation.ProviderMetadata
MethodTable: 6d823e80
EEClass: 6d7e44c4
Size: 28(0x1c) bytes
 (C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
7933061c  400004e        4        System.Object  0 instance 01302718 m_oProvider
79333054  400004f        8 ...ections.Hashtable  0 instance 013027a4 m_htSupportedClasses
793044cc  4000050       14       System.Boolean  1 instance        1 m_bIsDecoupled
79330a00  4000051        c        System.String  0 instance 01300dac m_sNamespace
79330a00  4000052       10        System.String  0 instance 01301ef8 m_sProviderName

Látszik, hogy van egy m_oProvider nevű tagváltozónk, annak az értékére vagyunk kíváncsiak:

0:003> !do 01302718
Name: UMPProvider
MethodTable: 6d823fbc
EEClass: 6d7e4654
Size: 12(0xc) bytes
 (C:\WINDOWS\assembly\GAC_MSIL\System.Management.Instrumentation\3.5.0.0__b77a5c561934e089\System.Management.Instrumentation.dll)
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79333054  4000089       24 ...ections.Hashtable  0   static 01300934 TypeToAutomationType
7933061c  400008a       28        System.Object  0   static 013009fc m_ProviderLock

Ott a keresett érték! A RegisterType az UMPProvider-t hívja. Ha megnézzük pl. Reflectorral, akkor annak a Get metódusa teljesen máshogy néz ki, ott azért nem jön elő ez a hiba.

No kicsit hosszúra nyúlt ez a vizsgálódás, lássuk mit is láttunk belőle:

Tanulságok

  • Az eredeti problémát viszonylag hamar megoldottuk, RegisterType-ot kell használni és nem Publisht. Ez nem tudom szándékos-e így, vagy esetleg valami bug, feltettem a kérdést az MSDN fórumon, remélem lesz majd rá válasz.
  • Ha a mélyére akarunk nézni, hogy mi történik egy ilyen komplexebb hívás esetén (aszinkron módon visszahív egy COM-os C++ komponens a .NET-es kódunkba), akkor arra is vannak eszközeink.
  • A Visual Studio debuggere kényelmes, gyorsan meg lehet találni benne a keresett értékeket. Ha ez valamiért nem jó, akkor lehet használni WinDbg-ot, a SOS kiegészítéssel .NET-es kódot is tud kezelni. Visual Studio után nem kényelmes a váltás:), de nagyon hatékony tud lenni, ha kiismeri az ember.
  • Egy ilyen apró hibával is el lehet egy fél napot tölteni:-)

Remélem hasznos lesz másnak is ez a bejegyzés, és sokat tanultam közben:)

Egyszerű .NET WMI Provider futtatása

Ha a saját alkalmazásunkat ki szeretnénk egészíteni, hogy konfigurációs és monitorozási információkat szolgáltasson Windows platformon, akkor erre az ajánlott módszer egy WMI Provider készítése. A 3.5-ös .NET Frameworkben már egész jó támogatás van arra, hogy mindezt menedzselt kódban írjuk meg:

WMI Provider Extensions: itt található némi technológia áttekintő, példakódok, és néhány tanács, hogy mihez kezdjünk hiba esetén.

Saját provider készítése esetén az első kérdés, amit el kell döntenünk, hogy milyen úgynevezett “hosting modelt” használunk:

  • In-process: A providert egy DLL-ben implementáljuk, amit a Global Assembly Cache-be (GAC) kell rakni. A provider a WMI folyamatában fut.
  • Decoupled: A provider kódja az alkalmazás kódjába kerül, így az csak akkor érhető el, ha az alkalmazásunk fut.

Egyszerű provider példa futtatása

Nézzük meg, hogy hogyan lehet egy egyszerű decoupled providert lefordítani és futtatni! Az MSDN-en lévő példakódot fogjuk használni: How To: Create a Basic Decoupled Provider A példa szövegében jól le vannak írva, hogy mik a fontosabb .NET attribútumok, amikkel meg kell jelölnünk a kódunkat. A példa egy Singleton WMI osztályt implementál, így annak csak egy darab WMI-beli példánya lesz majd. A példát a következő módon tudjuk kipróbálni (a parancssoros módszert írom, Visual Studioból hasonló módon lehetne futtatni):

1. Előkövetelmény: 3.5-ös .NET Frameworknek kell telepítve lenni a gépen. Mentsük el a példa kódját egy ConsoleDecoupled.cs fájlba.

2. Fordítás: fordítsuk le a C# fordítóval, a fordításhoz kell a System.Management.Instrumentation szerelvényre hivatkoznunk:

c:\WINDOWS\Microsoft.NET\Framework\v3.5\csc.exe ConsoleDecoupled.cs /reference:"c:\Program Files\Reference Assemblies\Microsoft\Framework\v3.5\System.Management.Instrumentation.dll"

3. Telepítés: a WMI alrendszernek meg kell mondanunk, hogy van egy új provider a rendszerben, ennek a MOF leíró fájlját meg kell adnunk. Ezt szerencsére az installutil elvégzi, ha a kódban volt egy DefaultManagementInstaller-ból számazó osztályunk. Futtassuk le rendszergazdaként az installutilt:

c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\installutil.exe ConsoleDecoupled.exe

Ha minden jól megy, a végén a következőt kell látnunk: “The transacted install has completed.”

4. Futtatás: Indítsuk el az alkalmazásunkat, egyelőre rendszergazdaként:

ConsoleDecoupled.exe

5. Tesztelés: egy PowerShell ablakból le tudjuk ellenőrizni, hogy tényleg szolgáltat-e információt az alkalmazásunk magáról WMI-on keresztül:

PS C:\> Get-WmiObject -namespace root\consoledecoupled -class runtimeconfigsettings

(RunTimeconfigSettings volt annak az osztálynak a neve, amit elláttunk a ManagementEntity attribútummal.) Ha minden működik, a következőhöz hasonló kimenetet kell látnunk:

__GENUS          : 2
__CLASS          : RuntimeConfigSettings
__SUPERCLASS     :
__DYNASTY        : RuntimeConfigSettings
__RELPATH        : RuntimeConfigSettings=@
__PROPERTY_COUNT : 1
__DERIVATION     : {}
__SERVER         : MZ-DEV
__NAMESPACE      : root\consoledecoupled
__PATH           : \\MZ-DEV\root\consoledecoupled:RuntimeConfigSettings=@
ReadMe           : StartValue

Látszik, hogy a WMI példányunknak egy tulajdonsága van, a ReadMe, aminek az értéke a StartValue. Tehát működik rendesen a WMI provider.

Futtatás nem rendszergazdaként

Most már csak azt kéne megoldani, hogy ne kelljen rendszergazdaként futtatni az alkalmazást. Ugyanis, ha sima felhasználóval indítjuk a ConsoleDecoupled.exe-t, akkor a következőt kapjuk:

Unhandled Exception: System.Management.Instrumentation.WmiProviderInstallationException: Exception of type 'System.Management.Instrumentation.WMIInfraException' was thrown.
at System.Management.Instrumentation.InstrumentationManager.RegisterType(TypemanagementType) at ConsoleDecoupled.Program.Main(String[] args)

Egy lehetséges megoldás, ha megadjuk a provider SecurityDescriptorában, hogy ki az még, aki futtathatja. Indítsunk  rendszergazdaként egy wbemtest.exe-t!

  1. Connect: root\ConsoleDecoupled
  2. Enum Classes: a superclass nevéhez nem kell beírni semmit, és állítsuk át Recursive-ra, majd nyomjuk OK-ot. Erre kilistázza az összes WMI osztályt, ami ebben a névtérben van.
  3. Keressük ki a WMI_extension-t, és kattintsuk duplán rá!
  4. Az Instances gombbal kérjük le a példányait, és kattintsunk duplán a ConsoleDecoupled nevűre!
  5. Így most megkaptuk a providerünket reprezentáló példányt.
  6. Keressük meg a SecurityDescriptor tulajdonságát, és írjuk át az értékét a O:%G:%D:(A;;0x1;;;%) stringre (az összes % helyére helyettesítsük be annak a felhasználónak vagy csoportnak a SID-jét, akinek jogot akarunk adni).
  7. Ne felejtsük el megnyomni a Save Object gombot!

Teszteljük a ConsoleDecoupled futtatását sima felhasználóként.

Hibák

Sajnos a WMI Providerek által visszaadott hibakódokról kevés dokumentáció van.

  • Troubleshooting WMI Provider Extensions: itt találhatunk néhány jó tanácsot.
  • Állítólag az Application eseménynaplóba is loggol hiba esetén, én ilyesmivel még nem találkoztam.
  • Miután telepítettük az installutil segítségével a saját providerünket, wbemtest segítségével érdemes kikeresni az osztályt, és a Show MOF gombbal megnézni, hogy tényleg átkerült-e a MOF állományba az összes tulajdonság és metódus, amit a kódban definiáltunk.
  • Ha a WmiConfigurationAttribute-ot hiányolja a fordító, akkor a System.Core-ra kell referenciát hozzáadni (a WmiConfigurationAttribute MSDN-es leírásában az Assembly résznél szerepel az, hogy melyik szerelvény tartalmazza).
  • “Provider is not capable of the attempted operation” hiba (kód: 0x80041024): nincs megadva [ManagementBind] attribútum egyik metódusra sem, vagy az nem publikus, vagy nem konstruktor vagy statikus metódus, így a WMI nem tud mit meghívni, amikor el szeretné érni a providert.
  • A WMIInfraException jogosultság gond esetén szokott például előjönni, ellenőrizzük, hogy rendszergazdaként futtatva is ez a hiba-e.

HTTP fejléc megnézése PowerShell segítségével

Egy apró trükk: Az egyik weboldalunkon meg akartam gyorsan nézni, hogy egy adott lap milyen HTTP fejlécet (HTTP header) ad vissza. Mivel helyileg néztem, ezért a Wireshark ilyenkor nem segít, hisz a kérés a loopback interfészen megy keresztül, azt meg a hálózatmonitorozó programok nem látják Windowson alapból. Fiddlert nem akartam most telepíteni, mert ez egy éles szerver (ez egyébként nagyon hasznos kis eszköz, fejlesztői gépen jól jön!). Szerencsére egy rövid PowerShell script megoldja a problémát, itt találtam egy példát rá:

$url = "http://www.cnn.com"
$xHTTP = new-object -com msxml2.xmlhttp;
$xHTTP.open("GET",$url,$false);
$xHTTP.send();
#$xHTTP.ResponseText; # returns the html doc like downloadstring
$xHTTP.status # returns the status code$xHTTP.getAllResponseHeaders()

A lefuttatáskor pedig szépen visszaadja a kívánt fejlécet:

PS C:\> .\http-header.ps1
200
Cache-Control: no-cache
Content-Length: 1
Content-Type: text/html
Last-Modified: Tue, 10 Mar 2009 07:56:54 GMT
Accept-Ranges: bytes
ETag: "931b6dc755a1c91:16bf"
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Date: Tue, 10 Mar 2009 08:08:35 GMT

Helyi csoporttagság lekérdezése PowerShellből

Még a felhasználókezelés házi feladat kitalálása kapcsán került elő a probléma, hogy hogyan lehet lekérdezni egy helyi felhasználóról, hogy milyen csoportoknak a tagja. Elsőre azt hittem, hogy ez alapfeladat, aztán néhány óra múlva kiderült, hogy annyira nem is triviális a kérdés:)

Részletek itt: Helyi csoporttagság lekérdezése PowerShellből

Végül .NET + Reflectiont használó megoldást találtam csak, valaki tud egyszerűbbet?

Openwsman telepítése CentOS-re

Az IRF tárgy keretében a konfigurációkezelés volt a téma, én ebből a WMI és WS-Management részt vittem. A WS-Managementet először a WinRM segítségével teszteltem, de ugye egy platformfüggetlen szabvány igazi próbája ha tényleg két különböző platformmal használjuk:-) Így aztán nekiálltam beállítani a tesztrendszer egyik Linux szerveren az openwsman nyílt forrású WS-Management implementációt. [Megjegyzés: a Linuxhoz sokkal kevésbé értek, mint a Windowshoz, így lehet, hogy jó pár dolgot lehetne egyszerűbben is csinálni].

Az első kihívást rögtön az jelentette, hogy igazából kevés disztribúcióhoz van stabil csomag belőle. RPM Search-ön Suse-ra van, RPMFind-on Fedora-hoz van csomag, Ubuntu-ban is van most már valami (bár csak a szerver része, a wsmancli parancssori eszközt rakja fel, és 2.0, nem pedig a 2.1-es), viszont CentOS-re nem találtam semmit.  (Lehet, hogy a fedoras jó lenne hozzá, de ezt csak később találtam meg. Meg egyébként is, ez alapján remélhetőleg másfajta rendszerekre is felmegy.) Így aztán lehet forrásból telepíteni:)

A wsmancli parancssoros eszközt külön lehet letölteni, azonban szükséges a fordításához az openwsman, így először azt kell telepíteni.

1. Forrás letöltése

A SourceForge-ról letölthető a forrás, jelenleg a 2.1-es az aktuális. Ezekről a linkekről tölthetőek le:

# wget http://fastbull.dl.sourceforge.net/sourceforge/openwsman/wsmancli-2.1.0.tar.bz2
# wget http://freefr.dl.sourceforge.net/sourceforge/openwsman/openwsman-2.1.0.tar.bz2

Utána kitömörítjük:

# tar xvf openwsman-2.1.0.tar.bz2
# tar xvf wsmancli-2.1.0.tar.bz2

2. openwsman lefordítása és telepítése

Olvassuk el a README-t, ebben leírják (jobbára:) a telepítés és konfigurálás menetét. A telepítés a szokásos ./configure, make, make install hármassal végezhető el. A ./configure-nak egy fontos paramétere van, ez pedig a –sysconfdir. Ezzel lehet megadni, hogy a /etc alá rakja a konfigurációs fájlt, és ne az alapértelmezett /usr/local/etc-be. Én most ezt nem használom, hanem az alapértelmezett beállításokkal futtatom.

# ./configure
# make
# make install

Ahogy azt a README-ben is írják, jó néhány egyéb csomagra szükség van az openwsman-hoz. Idemásolom, hogy a ./configure mire panaszkodik, és ehhez milyen csomagok kellenek:

configure: error: no acceptable C compiler found in $PATH --> gcc
checking for XML... configure: error: Package requirements (libxml-2.0) were not met --> libxml2-devel
configure: error: the curl-config script installed by libcurl could not be found.  libcurl is required for tests. --> curl-devel

A fordítás során még majd panaszkodik majd, hogy a g++ parancsot nem találja, ehhez a gcc-c++ csomagra van szükségünk. Úgyhogy ezeket telepíteni kell a yum segítségével:

# yum install gcc
# yum install gcc-c++
# yum install libxml2-devel
# yum install curl-devel

A fordítás ezután eltart egy darabig, de ha minden jól megy, akkor nincsen hiba, és lehet a make install segítségével telepíteni. A telepítés során kiír egy fontos dolgot (amit én elsőre nem is vettem észre, pedig megspórolt volna némi keresgélést utána:):

Libraries have been installed in:
   /usr/local/lib/openwsman/

If you ever happen to want to link against installed libraries
in a given directory, LIBDIR, you must either use libtool, and
specify the full pathname of the library, or use the `-LLIBDIR'
flag during linking and do at least one of the following:
   - add LIBDIR to the `LD_LIBRARY_PATH' environment variable
     during execution
   - add LIBDIR to the `LD_RUN_PATH' environment variable
     during linking
   - use the `-Wl,--rpath -Wl,LIBDIR' linker flag
   - have your system administrator add LIBDIR to `/etc/ld.so.conf'

Igen, később a wsman pl. nem fogja megtalálni az openwsman megosztott könyvtárait (shared library). Ezen vagy az LD_LIBRARY_PATH beállításával segítünk minden egyes alkalommal/felhasználóra, vagy egyszer és mindenkorra megoldjuk az /etc/ld.so.conf szerkesztésével:

# echo "/usr/local/lib" >> /etc/ld.so.conf
# ldconfig

3. wsmancli lefordítása és telepítése

Az előzőek után a wsmancli telepítése könnyen megy. Nekem egy buktató volt, a pkg-config nem találta meg az openwsman-t:

No package 'openwsman' found

Consider adjusting the PKG_CONFIG_PATH environment variable if you
installed software in a non-standard prefix.

Ezt a javasolt megoldással lehet orvosolni (az elérési utat értelemszerűen módosítsuk, ha máshova raktuk az openswman forrását)

# export PKG_CONFIG_PATH=/root/openwsman/openwsman-2.1.0

Ha települt, wsman –help segítségével meg lehet nézni, hogy elindul-e. (Megjegyzés: kicsit furcsa az elnevezés. A letöltött fájl neve wsmancli, a parancs neve wsman, míg a honlapon openwsman CLI néven hivatkoznak rá:)

4. openwsmand konfigurálása

Ahhoz, hogy le tudjuk tesztelni helyi gépen belül, hogy működik-e mind az openwsmand, mind a wsman, először be kell konfigurálnunk az openwsmandt. Ha ugyanis elindítjuk most az opendwsman -d paranccsal (a -d hatására nem a háttérben indul, és debug üzeneteket ír ki a konzolra), akkor a következő hibát kapjuk:

Cannot parse file [/usr/local/etc/openwsman/openwsman.conf]

A /usr/local/etc/openwsman könyvtárban tényleg nincs konfig fájl, ott csak egy owsmangencert.sh shell script van. A README-ben benne van egy minta, hogy hogyan kéne kinéznie egy ilyen konfig fájlnak, azonban egyszerűbb, ha átmásoljuk a forrásban lévő mintát, és azt módosítjuk:

# cp /root/openwsman/openwsman-2.1.0/etc/openwsman.conf  /usr/local/etc/openwsman/

Ahhoz, hogy működjön, legalább egy hitelesítési módszert be kell állítani. Ez Digest vagy Basic, érdemes kezdésből a Basic-et beállítani. Ehhez a felhasználókat tudja egy fájlból olvasni (basic_password_file), vagy PAM modul segítségével használni a rendszer felhasználóit. Ha bekapcsoljuk a PAM használatát, akkor a következőre panaszkodik:

Could not dlopen /usr/local/lib/openwsman/authenticators/libwsman_pam_auth.so

És tényleg nincs ott, csak a libwsman_file_auth.so van abban a könyvtárban. Ha megnézzük a forrást, akkor az src/authenticators/pam alatt van ez a modul, de ez nem is fordult le. Amikor megpróbáltam lefordítani, egy csomó hibát dobott:) Az src/authenticators könyvtárban lévő Makefile.am-ben látszik, hogy a pam tartalmát csak akkor fordítaná le, ha megadtuk volna az USE_PAM opciót (gondolom ezt a configure-nak kellett volna átadni). Így aztán maradtam a basic_password_file megoldásnál, ilyenkor htpasswd segítségével kell egy fájlba eltároltani a felhasználókat és jelszavukat. Hozzuk létre ezt a fájlt, és adjuk is hozzá a micskeiz felhasználót:

# htpasswd -c simple_auth.passwd micskeiz
New password:
Re-type new password:
Adding password for user micskeiz

Most már csak át kell írni a konfig fájlban, hogy a simple_auth.passwd-t ne a /etc/openwsman könyvtárban, hanem a /usr/local/etc/openwsman-ban keresse, és ezután elindul az openwsmand:

openwsmand-started

5. SSL beállítás az openwsmand-hez

Mivel majd távolról is szeretnénk elérni az openmanwsd-t, jó lenne, ha mindez titkosított csatornán menne. Ehhez először is le kell generálni egy tanúsítványt a szervernek, majd beállítani a konfig fájlban, hogy használja ezt. A /usr/local/etc/openwsman könyvtárban van egy owsmangencrt.sh nevű script, ami elvégzi a tanúsítvány elkészítését, azonban ehhez kell neki egy ssleay.conf, ami megadja az alapbeállításokat. Ezt valamiért nem másolja ide a telepítő, úgyhogy rakjuk ide mellé:

# cp /root/openwsman/openwsman-2.1.0/etc/ssleay.cnf /usr/local/etc/openwsman/

Ha megnézzük a scriptet, van egy ilyen rész benne: CERTFILE=${prefix}/etc/openwsman/servercert.pem. A ${prefix} változót a telepítő nem állította jól be, így ezt még nekünk kell megtenni, különben értelemszerűen nem találja majd ezt a könyvtárat:

# export prefix=/usr/local

Ezen kívül még futtathatóvá kell tenni a scriptet:

# chmod u+x owsmangencert.sh

Ha lefuttatjuk a scriptet, akkor megkérdezi, hogy milyen adatok szerepeljenek a tanusítványon. Ebből igazából tesztrendszer esetén csak a szervernév a lényeg.

openwsman-cert-genHa megnézzük ls-sel, akkor szépen legenerálta a kulcs és a tanúsítvány fájlt is.

Ezután már csak ki kell szedni a #-t a configban az SSL port elöl, át kell írni az ssl_cert_file és ssl_key_file paraméterekben az elérési utat, és a -S paraméterrel indítani az openwsmand-t:

# openwsmand -d -S

Hát, ez sajnos nem jött össze:-), a következő hibaüzenetet dobja:

openwsman-ssl-errorAzt mondja, hogy nem tudja olvasni a /usr/local/etc/openwsman/servercert.pem fájlt, holott elvileg ott van, és van is mindenkinek olvasási joga:( Ebben a fórumbejegyzésben jelentettek hasonló hibát:

openwsman 2.0.0, 2.1.0 does not run with ssl

Ennek megfelelően javítottak a kódban is, ha megnézzük a config.c fájlt az openwsman projekt Browse source részénél, akkor a legutóbbi változtatás (Changeset 3052) pont ezt a bugot javítja:

openwsman Changeset 3052

Megpróbáltam módosítani ennek megfelelően a /root/openwsman/openwsman-2.1.0/src/server/shttpd/config.c fájlt (Jó is az olyan telepítés, amikor a kódba kell beleírni;-), utána újrafordítani az openwsmand-t (a server könyvtárban make clean, majd make), utána felülírni az új verzióval a régit (cp openwsmand /usr/local/sbin/), de ugyanazt a hibaüzenetet adta. Lehet, hogy más is változott a szeptemberi release óta, és az egész kódhoz a legfrissebb forrást kellett volna leszedni.

Úgyhogy egyelőre az SSL nem működik:)

6. Lokális teszt a wsman segítségével

Zárásként ellenőrizzük, hogy helyileg tudunk-e kapcsolódni az openwsmand-hez, miután elindítottuk:

#  wsman identify -h chicago

Szépen működik is, válaszol az identify kérésre megfelelő hitelesítés után:

openwsman-successHát, ennyi lett volna. Olyan érzésem volt, mintha a telepítő részét nem sikerült még utánahúzni a kódnak, egy csomó alapbeállítás még régi értékeket tükröz (pl. /etc/openwsman-ra mutat minden, holott alapból a /usr/local/etc-be rakja). De az SSL-es bugot leszámítva egyelőre működött minden.

WinRM teszt két Vista között

Az IRF tárgy keretében nézegettem a Common Information Modelt (CIM) és a Windows Management Instrumentationt (WMI). Egész jó dolgokat lehet lekérdezni belőlük, a gép gyári számától kezdve a feltelepített kodekekig szinte majdnem mindent:). A beépített wmic parancssori eszköz kicsit nehézkes, de a PowerShell Get-WmiObjectjével már könnyen lehet egy-egy lekérdezést megejteni (nagyon jó WMI leírás van a PowerShell könyvben! Ez pedig egy hasznos grafikus lekérdező felület: PowerShell WMI Explorer). Ha már a WMI előkerült, akkor az ahhoz kapcsolódó WS-Management szabványt is megnéztük, amivel CIM alapú rendszereket lehet lekérdezni és menedzselni szabványos, tűzfalbarát módon. És ezzel már el is érkeztünk a Windows Remote Managementhez (WinRM), ami a WSMAN protokollt használja WMI adatok lekérdezéséhez vagy távoli shellhez. [Megjegyzés: azért nem rossz, hogy ahhoz, hogy elmondjuk egy mondatban, hogy mi a WinRM, legalább két másik rövidítést kellett előbb feloldani;-]

Tehát megnéztem a WinRM leírását, és gondoltam kipróbálom gyorsan a laptopom Vistája és egy virtuális gépben futó Vista között. Az MSDN-en lévő leírásban a telepítés elég rövid, főleg tartományi környezetre fókuszál, és valahogy a hitelesítésről szóló részben se találtam meg mindent (pl. a tanúsítvány hitelességéről szóló kapcsoló a TechNeten lévő leírásban van csak benne.). Meglepően részletes viszont a winrm parancs helpje, az winrm help paranccsal elég sok általános leírást is kapunk (remoting, auth…) Pár lépésben viszont azért be lehet állítani, hogy két munkacsoportban lévő gép között is működjön a WinRM

1. WinRM beállítása a célgépen:

winrm quickconfig

Erre elvileg el kéne indítania a szolgáltatást, meg létrehozni egy HTTP figyelőt, azonban nálam nem történt semmi. Nem sikerült rájönnöm, hogy miért, de itt szerencsére megtaláltam, hogy mivel lehet elvégezni ezeket. (Azóta több gépen is csináltam quickconfig-ot, és máshol sehol sem volt ez a gond.)

sc config "WinRM" start= auto
net start WinRM
winrm create winrm/config/listener?Address=*+Transport=HTTP
netsh firewall add portopening TCP 80 "Windows Remote Management"

2. Kapcsolódási kísérlet (identify)

Az id (identify) művelet megnézi, hogy figyel-e a célgép:

winrm id -remote:192.168.127.140

Ez volt az eredmény:

WSManFault
    Message = The WinRM client cannot process the request. If the authentication scheme is different
 from Kerberos, or if the client computer is not joined to a domain, then HTTPS transport must be us
ed or the destination machine must be added to the TrustedHosts configuration setting. Use winrm.cmd
 to configure TrustedHosts. You can get more information about that by running the following command
: winrm help config.

Error number:  -2144108316 0x803380E4

A WinRM tartományi környezetben Kerberost használ, munkacsoportos környezetben pedig Negotiate hitelesítési módszerrel próbál áthitelesíteni a bejelentkezett felhasználó  vagy a -u és -p kapcsolókkal megadott felhasználó adataival. Ezen kívül meg lehet adni a -r: kapcsolónál, hogy http vagy https szállítási csatornát használjon. A HTTPS listener létrehozásához előbb a célgépnek tanúsítványt kellett volna kiállítani, ezért a TrustedHosts-os megoldás mellett döntöttem:

3. A kliensen a célgép hozzáadása a megbízható gépek listájához

Egy adminisztrátori jogokkal futó parancssorból adjuk ki a következő utasítást:

winrm set winrm/config/client @{TrustedHosts="192.168.127.140"}

4. Újabb kapcsolódási kísérlet

winrm id -remote:192.168.127.140 -u:user
Enter the password for 'user' to connect to '192.168.127.140':
WSManFault
    Message = Access is denied.

Error number:  -2147024891 0x80070005
Access is denied.

Pedig a user felhasználó rendszergazda a távoli gépen. A User Account Control (UAC) miatt még egy lépést meg kellett tenni a célgépen.

5. LocalAccountTokenFilterPolicy megadása

A következő bekezdés segít a probléma megoldásában:

Starting with Windows Vista, User Account Control (UAC) affects access to the WinRM service. When negotiate authentication is used in a workgroup, only the built-in Administrator account can access the service. To allow all accounts in the Administrators group to access the service, set the following registry key to 1 : HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\system\LocalAccountTokenFilterPolicy.

Tehát mivel nem az Administrator felhasználóval akartam csatlakozni, hanem egy másik, de az Administrators csoportban lévő felhasználóval, ezért még ezt engedélyeznem kell (Windows XP-s célgép esetén értelemszerűen erre nincs szükség). Miután beállítottam a megfelelő registry kulcsot, már ment rendesen a kapcsolódás.

winrm-test

A fenti képen például a C: lemez adatait kérdezem le a távoli gép WMI tárából.

A jó hír az, hogy hiába HTTP a szállítási csatorna, a windows integrált hitelesítés (Negotiate) használata esetén a HTTP üzenetek tartalmát titkosítja is az átvitel során:

winrm-wiresharkHa ezzel megvagyunk, akkor ráadásképpen a távoli parancs-végrehajtásra képes WinRS (Windows Remote Shell) is működik.

Összegezve némi állítgatás után azért könnyedén be lehet élesíteni a WinRM-et, és egész hasznos dolgokat lehet lekérdezni a segítségével.

Megagiga VMware

Jónapot kívánok! Veér István vagyok, bár sokan bizonyára jobban ismernek gyerekkori becenevemen.  vérpistike Történt az, hogy lett egy új gépem Core2Duo processzorral, 8 giga rammal és most elvittem egy kis próbakörre. Ennek eredménye látható itt:

Continue reading Megagiga VMware

IAS reason code 262

A bejegyzés a saruman szerverünkhöz kapcsolódik, ami a hallgatóknak nyújt különböző szolgáltatásokat (virtuális gépek tárolása, VPN, WLAN, stb.). Eredetileg ez egy összetákolt desktop PC volt, ami a laborban volt, imgserver névre hallgatott, és a feladata csupán annyi volt, hogy a mérésekhez szükséges virtuális gépeket tárolta. Aztán bekerült a szerverszobába, megkapta az összes laborbeli kliens menedzselésének és alap hálózati szolgáltatások biztosításának a feladatát. Így újra lett telepítve a gép, Linux helyett Windows Server került rá (azt jobban ismerem, így könnyebben tudtam karbantartani, beállítani). Az újratelepítés kapcsán, no meg, hogy bekerül a szerverszobába egy névcserére is megérett a számítógép (mondanom se kell, hogy hardver csere nem járt hozzá, a mai napig maradt a desktop Intel alaplap meg az 1 GB RAM:). A névválasztással nem ment el sok idő, egy linuxos szerver, mely windowsos lesz a saruman nevet kaphatja csak, utalva a később gonosszá váló Fehér Sarumanra:-) A névválasztás tökéletesnek bizonyult, a labor kliensek bekerültek NAT mögé, így saruman elkezdhette építgetni a saját kis birodalmát a többiek szeme elöl rejtve, és kitenyészthette a saját ork fajtáját, a laborkot..

A hallgatóinknak biztosítunk WLAN elérést, a hitelesítést a wireless router RADIUS-on keresztül az IAS segítségével Active Directory-ból végezzük (korábban már írtam róla itt). Legtöbbször nem sikerül beállítaniuk a hálózatot elsőre:), ilyenkor a hiba pontos okát a szerver Eseménynaplójában lehet megnézni. Eddig a legtöbbször a 262-es hibakód szerepelt:

User DOMAIN\user was denied access.
Authentication-Type = PEAP
Reason-Code = 262

A megoldás pl. itt szerepel: 802.1x client authentication fails when you connect to a Windows Server 2003-based computer that is running IAS Bekapcsolva hagyják a kiszolgáló oldali tanúsítvány ellenőrzését, és nem telepítik azt.

(Havonta egyszer előjön ez a hiba, csak addigra mindig elfelejtem a megoldást, ezért született ez a bejegyzés:)

Csoportházirend keresés: 0 találat

Windows Server 2008-ban lehet már keresni a csoportházirend opciók között. Megpróbáltam szűrni a WinRM-es bejegyzésekre, de 0 találatot kaptam. Hát ezek szerint nem került még bele ilyen csoportházirend opció:) De amikor a DNS szóra rákeresve is nulla találatot kaptam, kezdett gyanús lenni a dolog:)

csoporthazirend-0-talalat

Két gomb van a felületen: szűrő feltétel megadása és szűrő bekapcsolása, egyszerűen nem lehet elrontani. Nekem mégis sikerült, nem adott vissza semmit:) Végül szerencsére megtaláltam ezt a KB cikket: The text filter function may not return any results in the Group Policy Management Editor window on a computer that is running Windows Server 2008 or Windows Vista SP1

Át kell állítani angol billentyűzet kiosztásra, és úgy kell beírni a keresési feltételt. Bravó:) Lehet hotfixet igényelni a probléma javítására.

Lord of the Servers

A blog elnevezésének eredete a messzi, ködös homályba vész:-) Az előző rendszergazda kezdte el a szervereket a Gyűrűk Ura karaktereiről elnevezni. Gandalf, Finwe, Feanor, mind-mind szolgáltak az évek során fájlszerverként, webszerverként vagy modem szerverként (igen, volt idő, amikor ilyen is kellett:). A Gyűrűk Ura és a Szilmarilok nevek kimeríthetetlen tárházát jelentették, a legtöbb informatikusnak ismerősek, így ideális jelöltek a szerverek elkeresztelésére.

Később továbbvittük ezt a hagyományt, és az összes azóta érkezett új szerver (vagy mostanában már a virtuális gépek is) innen kapta a nevét. Először a névválasztás pusztán praktikus szempontokat követett (“csak ne egy újabb F betűs szervernevet!”), aztán szép lassan kezdtük a gép fizikai kinézetét vagy jövendő szerepét figyelembe venni (pl. a 8 unit magas storage szerver gimli lett, “Azért lett ez a neve, mert ilyen zömök nehéz cucc.“). Mostanában pedig már komoly megbeszélések előznek meg egy-egy névválasztást, hisz a neveknek hatalmuk van még a gépek felett is:)