Disclaimer: Měl jsem moooc článků na svém dřívějším blogu, hostovaném na placeném www.pipni.cz. Akorát mi ho bez varování zrušili. Teďka makám na obnově článků, speciálně na to používám www.archive.org, kde se dají přecejenom alespoň nějaké zbytky ještě najít. Takže tu je jeden obnovený článeček. Upozorňuju, že odkazy nejspíš nebudou fungovat a obrázky tam také nejsou.
6. June 2007, 09:01 by Ondřej Ševeček
Je obvyklé, že potřebujete v Active Directory něco vyhledat. Každý nejspíš zná onen vyhledávací dialog z konzole Active Directory Users and Computers. Nejspíš není také probém vyhledat účet podle jeho jména, nebo loginu. My se ale podíváme na některé pokročilé techniky vyhledávání v Active Directory.
Využijete je jistě v oné konzoli. Mnohdy se vám však budou hodit při skriptování. Například ve skriptu pro PowerShell. A nebo, když třeba vytváříte filtr pro address list do Microsoft Exchange.
Co tedy budeme vyhledávat? Vyhledat jde prakticky cokoliv, až byste se divili:
- zakázané účty
- účty, které si nemusí měnit heslo
- účty, které si již nějakou dobu heslo nezměnily
- členství uživatele ve skupinách
- emailové adresy
- zamčené účty
- bezpečnostní a distribuční skupiny
- nově vytvořené účty
- uživatele a skupiny podle SID
- uživatele, kteří se již nějakou dobu nepřihlásili
- a cokoliv dalšího vás napadne
Co budeme hledat
Při hledání budeme potřebovat znát jména atributů, které obsahují námi hledané hodnoty. Atribut je vlastně pojmenovaná položka u objektu v Active Directory. Pokud si stáhnete program ADSIEDIT.MSC
(je to součást Support Tools od Microsoftu), můžete se na všechny přesně podívat. Některé, pro nás důležité atributy jsou v následující tabulce:
Libovolný objekt |
objectCategory |
Obsahuje právě jméno typu objektu, kterým daný objek je. Například tedy user , computer , nebo group |
whenChanged |
Datum a čas poslední změny jakéhokoliv parametru příslušného objektu |
User |
givenName |
Křestní jméno |
sn |
Příjmení (surname) |
displayName |
Zobrazované jméno |
email |
Primární email adresa, kterou najdete hned na první záložce uživatelského účtu |
proxyAddresses |
Všechny emailové adresy, na kterých Exchange přijímá poštu pro daný účet |
department |
Oddělení. Mnohdy se hodí pro hledání v adres listech |
sAMAccountName |
Starý login uživatele |
userPrincipalName |
Nový login uživatele. Ten se objeví třeba v certifikátech |
userAccountControl |
Parametry účtu jako je Uživatel si musí změnit heslo, nebo Účet je zakázán |
memberOf |
Seznam skupin, jichž je uživatel členem. Tedy přímo. Zřetězené členství se zjišťuje jinak |
objectSid |
Uživatelův SID |
pwdLastSet |
Datum a čas poslední změny hesla |
lastLogon |
Datum a čas posledního přihlášení uživatele |
Group |
sAMAccountName |
Jméno skupiny |
groupType |
Typ skupiny (jako je Domain Local, Global, Universal) |
member |
Seznam členů dané skupiny. Opět pouze přímé členství, zřetězené se zjistí jinak (viz. dále) |
memberOf |
Seznam skupin, jichž je tato skupina členem |
Computer |
dNSHostName |
DNS jméno počítače |
lastLogon |
Datum a čas posledního spuštění (v doméně) počítače |
operatingSystem |
Jméno operačního systému, například Windows Server 2003 |
operatingSystemVersion |
Verze operačního systému, například 5.2 pro Windows Server 2003 |
operatingSystemServicePack |
Verze záplaty Service Pack daného systému |
Jak to budeme vyhledávat
Nebo lépe se zeptat kde? Tedy pomocí konzole Uživatelé služby Active Directory (anglicky Active Directory Users and Computer). Pomocí okénka Hledat. Musíte si ale zvolit Vlastní vyhledávání (anglicky Custom search). Tak jak je na obrázku:
LDAP vyhledávací řetězce
První věc, kterou si musíme říct je, jak vlastně Active Directory vyhledává. Používá se k tomu vždy takzvaný LDAP vyhledávácí řetězec (anglicky LDAP search string). Ten je konstruován za pomoci logických operací AND (a současně), OR (nebo), NOT (negace). Logickými operacemi se spojují jednotlivé vyhledávací podmínky typu rovnost, nerovnost a podobnost pomocí hvězdičkové konence. Příkladem by mohlo být třeba:
(givenName=o*) AND (sn=s*)
(givenName=o*) OR (givenName=*a)
(NOT givenName=o*)
Tedy snaha o vyhledání všech objektů, jejichž křestní jméno začíná na o
a přijmení na s
(viz. ona horní tabulka). Nebo ve druhém případě objektů s křestním jménem začínajícím na o
nebo končící na k
. Třetí případ chce hledat všechny účty, jejichž křestní jméno nezačíná na o
. Takto to ale zadat nelze! Musíte to přepsat do formy srozumitelné pro LDAP:
(& (givenName=o*) (sn=s*) )
(| (givenName=o*) (givenName=*a) )
(!givenName=o*)
Myslím, že onen zápis LDAP řetězce by mohl být docela srozumitelný, pokud ho porovnáte s předchozím uvažovaným zadáním, ne? Následující tabulka operátory pěkně shrnuje:
LDAP Operátor |
Vysvětlení |
& (amprsand) |
Logický operátor součinu, tedy AND |
pipe |
Logický operátor nebo, tedy OR |
! (vykřičník) |
Logická negace, tedy NOT |
= (rovnítko) |
Porovnání na rovnost |
>= |
Větší nebo rovno |
<= |
Menší nebo rovno |
Obvykle budeme ještě k vyhledávání přidávat specifikaci typu hledaných položek pomocí atributu objectCategory
. Také nebudu vynechávat mezery, protože by to mohlo dělat potíže. Takže předchozí příklady by měly správně vypadat asi takto:
(&(objectCategory=user)(givenName=o*)(sn=s*))
(&(objectCategory=user)(|(givenName=o*)(givenName=*a)))
(&(objectCategory=user)(!givenName=o*))
Takže tohle si již můžete konečně zadat do vašeho vyhledávacího okna a vyzkoušet, jestli vám to něco v adresáři najde.
Hledáme příjemce pošty Microsoft Exchagne
První příklad vyhledá všechny uživatele, kteří mají emailovou adresu začínající písmenem O
. Druhý příklad vyhledá úplně všechny objekty, které jsou Exchange příjemci (tedy Exchange recepient jako jsou uživatelé, kontakty a skupiny). Třetí příklad vyhledá jen skupiny s emailem.
(&(objectCategory=user)(proxyAddresses=smtp:o*))
(proxyAddresses=*)
(&(objectCategory=group)(proxyAddresses=*))
Někdy naopak chcete všechny uživatele, kteří email nemají vůbec (všimněte si onoho vykřičníku, tedy negace):
(&(objectCategory=user)(!proxyAddresses=*))
Vyhledání zakázaných účtů
Hledat zakázané účty (ang. disabled accounts) není tak jednoduché. Chce to vědět další detaily. Tak zaprvé, který atribut nese informaci, že účet je zakázaný? Je to userAccountControl
. Problém je v tom, že se jedná o bitovou masku s více informacemi dohromady. Tento atribut může mít hodnotu například 66048, 512, nebo 262656. Ani jedna z těhto hodnot neznamená zakázaný účet. Naopak 262658, nebo 514 znamená zakázaný účet.
Jak to tedy je? Přeložte si ona čísla pomocí kalkulačky na jejich binární zápis. Zjistíte, že jednotlivé bity znamenají různé vlastnosti účtu:
Jednotlivé bity v atributu userAccountControl
znamenají to, co najdete v následující tabulce (pro ostatní googlujte ADS_USER_FLAG_ENUM
):
Zakázaný účet |
2 |
00000000 00000000 00000010 |
Účet zamknut |
16 |
00000000 00000000 00010000 |
Nesmí si měnit heslo |
64 |
00000000 00000000 01000000 |
Nemusí si měnit heslo |
65536 |
00000001 00000000 00000000 |
Vyžaduje se čipová karta |
262144 |
00000100 00000000 00000000 |
Musí změnit heslo při přihlášení |
8388608 |
10000000 00000000 00000000 |
Jednoduchým příkladem může být třeba zakázaný účet, který si nemusí měnit heslo, který je současně zamčen kvůli jeho špatnému zadávání. Binární hodnota bude 10000000000010010, což se převede na desítkovou hodnotu 65554.
Nemůžete vyhledávat konkrétní hodnotu, protože v ní mohou být obsaženy různé bity. Musíte vyhledat konkrétní bit. A na to je LDAP speciálně zařízen. Musíte použít speciální číslo (je to OID), určující, že se má hledat jenom jeden bit a nikoliv konkrétní hodnota. Tedy takto vyhledáte zakázané účty:
(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=2))
A takto účty, které si nemusí pravidelně měnit heslo:
(&(objectCategory=user)(userAccountControl:1.2.840.113556.1.4.803:=65536))
Uživatelské skupiny
U skupin se jedná o stejný případ. Zde je atribut groupType
, který obsahuje bity podle následující tabulky (pro ostatní googlujte ADS_GROUP_TYPE_ENUM
):
Nastavení |
Hodnota desítkově |
Bezpečnostní (angl. security group) |
2147483648 |
Doménová lokální (angl. domain local group) |
4 |
Globální (angl. global group) |
2 |
Universální (angl. universal group) |
8 |
Chceteli potom vyhledat nějaké zkupiny, zadejte následující. První příklad hledá bezpečnostní skupiny, druhý distribuční skupiny (všiměte si vykřičníku). Třetí hledá universální skupiny. Ve čtvrtém případě hledáme všechny bezpečnostní globální skupiny (hodnoty musíte sečíst).
(&(objectCategory=group)
(groupType:1.2.840.113556.1.4.803:=2147483648))
(&(objectCategory=group)
(!groupType:1.2.840.113556.1.4.803:=2147483648))
(&(objectCategory=group)
(groupType:1.2.840.113556.1.4.803:=2147483648))
(&(objectCategory=group)
(groupType:1.2.840.113556.1.4.803:=2147483650))
Od Windows Server 2003 SP2 máte skvělou možnost vyhledat všechny skupiny, ve kterých je nějaký uživatel členem! A tím se myslí i nepřímo, pokud jsou skupiny vnořené. Použije se jiný OID a distinguishedName
uživatele:
(&(objectCategory=group)
(member:1.2.840.113556.1.4.1941:=CN=ondra,OU=Uzivatele,DC=podnik,DC=cz))
Hledání podle času
Časové údaje jsou kódovány několika způsoby. Vždy je potřeba vědět jak. Ukážeme si tři nejběžnější příklady. První z nich vyhledá všechny uživatele, kteří byli vytvořeni před nějakým datem v minulosti (konkrétně před 5:45 hod. dne 17.6.2005 UTC):
(&(objectCategory=user)(whenCreated<=20050617054500.0Z)
Myslím, že formát data by měl být jasný. Takže jen pro jistotu. Formát je následující. Dokonce můžete specifikovat i posun vůči UTC, neboli greenwichskému času (v našich končinách +1 hod.):
RRRRMMDDhhmmss.0Z
RRRRMMDDhhmmss.0+0100
Některé časy jsou ale v jiném formátu. Je to konkrétně počet 100ns (sto nanosekundových) intervalů od 1.1.1601. To je hodně komplikovaný výpočet. Pro převod takových údajů jsem si udělal skriptík v PowerShellu.
Chcete třeba najít uživatele, kteří si už nějakou dobu nezměnili heslo? Řekněme od 1.5.2007? Pomocí onoho skriptu si převedete to datum na hodnotu:
Get-100NS-Since-1601( ( new-object DateTime(2007,5,1) ) )
>> 128224512000000000
A vyhledáte všechny objekty, kde je hodnota pwdLastSet
menší než zjistěné megačíslo. Tedy všechny objekty, u kterých se heslo naposledy změnilo před oním datem:
(&(objectCategory=user)(pwdLastSet<=128224512000000000))
Stejně tak funguje atribut lastLogon
. Jeho jméno už samo napovídá, že nás zajímají čas vůbec posledního přihlášení. Použijeme datum z předchozího příkladu. A vyhledáme uživatele, kteří se už od 1.5.2007 nepřihlásili. Na co tam takové účty vůbec jsou?
(&(objectCategory=user)(lastLogon<=128224512000000000))
Hledání podle SID
Chcete hledat uživatele podle jejich SID? No, né uplně často, ale někdy přece. Takže zkusme. Najdeme si uživatele, jehož SID je následující. Druhá hodnota je jeho šetnáctkový zápis (hexadecimální). Můžete zase použít kalkulačku:
S-1-5-21-911941294-13226589-850011906-1697
S-01-05-15-365B1EAE-C9D25D-32AA2702-000006A1
Potom vyhledáte uživatelský účet s tímto SIDem následovně. Je to trošku komplikovanější pochopit. Prostě to z části zapíšete odzadu:
(objectSID=\01\05\00\00\00\00\00\05\15\00\00\00\
AE\1E\5B\36\5D\D2\C9\00\02\27\AA\32\A1\06\00\00)
A to je vše. Dobře se bavte!