In questo articolo vi introdurrò all'uso di LDAP, un protocollo di accesso alla directory, scoprirete come sia possibile interrogare e manipolare gli oggetti e le informazioni contenute nel database di Active Directory al fine di automatizzare determinate procedure amministrative o semplicemente estrapolare da esso utili informazioni.
Lo Schema di Active Directory
Gli oggetti con i quali lavoriamo quotidianamente in Active Directory vengono creati seguendo le regole dettate da uno schema, banalizzando potremmo affermare che esso costituisce la struttura del database della directory.
Per fare un esempio molto banale provate a pensare che un utente sia un oggetto al quale vengono associate una serie di proprietà: il suo nome, il suo cognome, il suo indirizzo email, il suo username e così via, queste proprietà vengono dette attributi e lo Schema di Active Directory si occupa di definirli stabilendo anche quali di essi possano essere associati ad un oggetto piuttosto che ad un altro; un gruppo utenti o una stampante avranno delle proprietà sicuramente differenti da un semplice utente.
Lo Schema inoltre è modificabile, in particolari condizioni potrebbe essere necessario creare nuovi attributi al fine di garantire una perfetta integrazione tra gli oggetti di Active Directory e le applicazioni esterne ad esso. Un esempio di quanto ho appena affermato è dato dalla procedura di installazione di Microsoft Exchange Server, un prodotto che fornisce servizi legati alla posta elettronica ed ideato per il Groupware. Per far sì che i servizi di Exchange Server possano integrarsi nella directory occorrerà, in fase di installazione, eseguire alcuni comandi che si occuperanno di estendere lo Schema al fine di aggiungere ad esso una nuova serie di attributi (perché estendere lo schema? basti pensare alla necessità di collegare agli oggetti utente tutti i dettagli relativi alla casella email assegnatagli).
Per visualizzare lo schema su di un qualsiasi Domain Controller dobbiamo registrare la snap-in amministrativa Schema Management, questa fa parte del pacchetto amministrativo di Windows Server ed è già installata nel sistema ma per motivi di sicurezza non viene registrata automaticamente.
N.B.: E' necessario prestare particolare attenzione alle azioni che si compiono utilizzando lo Schema Management, modificare in maniera errata anche soltanto una singola voce all'interno di esso può portare a conseguenze disastrose.
Dal menu Start->Run (o da un prompt dei comandi) digitate il seguente comando
regsvr32.exe schmmgmt.dll
Se tutto procede correttamente dovreste ottenere un messaggio che vi avvisa dell'avvenuta registrazione del componente e dovrebbe ora essere possibile utilizzare la snap-in di gestione dello Schema.
Per chi non sapesse come caricare una snap-in amministrativa, ecco cosa dovete fare
- Dal menu Start->Run (o da un prompt dei comandi) digitate il comando mmc.exe, questo farà in modo che venga aperta l'interfaccia del motore Microsoft Management Console (MMC).
- Attivate il menu File->Add/Remove Snap-in, questa azione farà apparire una finestra che vi permetterà di caricare una alla volta tutte le snap-in delle quali avrete bisogno; noi ne attiveremo una soltanto, quella chiamata Active Directory Schema.
- Premete il pulsante Add, trovate e selezionate la voce Active Directory Schema, premete il tasto Add di questa nuova finestra e poi Close.
- Noterete che ora la snap-in "Active Directory Schema" risulta in lista e perciò caricata. Premete OK per confermare chiudendo così tutte le finestre di dialogo.
Qualche screenshot che illustra i passaggi sopra descritti...
Espandere ed esplorare le voci contenute nell'interfaccia amministrativa che avete appena caricato vi renderà subito l'idea di come è organizzato questo schema, in realtà non è particolarmente complesso: come vi ho accennato in precedenza esistono diverse classi di oggetti che possono essere creati all'interno del database e ad ognuno di essi vengono associati degli attributi.
Prendiamo come esempio la classe user, se fate scorrere rapidamente la lista degli attributi ad essa associati noterete l'esistenza di voci che potrebbero risultarvi familiari come: mail, HomeDirectory, HomeDrive, displayName, info, userPrincipalName o le più interessanti memberOf, LastLogon, LastLogoff.
Sull'MSDN potete trovare una lista completa [7] di tutte le classi e gli attributi che trovate in questo Schema.
Alla luce di tutto ciò viene naturale chiedersi come sia possibile interrogare e manipolare a piacere tutte queste informazioni, ed è proprio ciò che andremo a vedere tra poco.
Quello che sappiamo fino ad ora è che Active Directory è organizzato secondo uno schema che stabilisce le tipologie di oggetti che possiamo creare e gli attributi ad essi associati, ma dove vengono posizionati poi questi dati? Come facciamo a raggiungerli? Per dare una risposta a queste due domande occorre fermarsi un attimo e parlare di protocolli di accesso, in particolare parleremo di LDAP.
LDAP
LDAP (Lightweight Directory Access Protocol) è il protocollo che permette di interfacciarsi con Active Directory, ogni Domain Controller infatti esegue un Server LDAP interrogabile tramite sintassi LDAP.
Non vi proporrò ora la storia di LDAP, in questo articolo ci concentreremo soltanto sull'aspetto tecnico-pratico dell'argomento, un buon approfondimento [8] lo potete trovare su Wikipedia.
Innanzitutto, al fine di seguire la mia spiegazione, vi consiglio di installare i Support Tools di Windows Server, per la versione 2003 li potete trovare qui per Service Pack 1 [9] e qui per Service Pack 2 [10], vi ricordo che è possibile installarli anche su Windows XP.
All'interno dei Support Tools sono infatti inclusi alcuni strumenti grafici che permettono di lavorare con LDAP, in particolare ADSI Edit ci permetterà di esplorare la struttura di un Domain Controller ed ldp.exe di connetterci ad un server al fine di eseguire ricerche all'interno di esso.
Iniziamo quindi da ADSI Edit, grazie a questo strumento potrete facilmente capire come vengono identificati gli oggetti tramite la sintassi LDAP, se avete installato i Support Tools lo troverete nel percorso C:\Program Files\Support Tools\adsiedit.msc
All'apertura il programma visualizzerà la seguente schermata
Per fare un minimo di chiarezza occorre innanzitutto dire che le voci Domain, Configuration e Schema rappresentano le tre partizioni che compongono il database di Active Directory, in particolare le partizioni di configurazione e di schema sono comuni per tutta la foresta mentre la partizione di dominio è specifica e ne esiste una per ogni dominio che compone la foresta.
Nel caso in cui la vostra foresta sia composta da più di un unico dominio e che stiate eseguendo lo strumento ADSI Edit connettendovi ad un server che svolge la funzione di Global Catalog è possibile che vengano visualizzate anche altre partizioni; quanto detto succede perché, a differenza di un normale Domain Controller il quale ospita nel proprio database soltanto gli oggetti appartenenti al dominio di cui esso fa parte, il server Global Catalog ha il compito di memorizzare anche ulteriori informazioni che appartengono ai vari domini della foresta.
Ciò che però interessa maggiormente a noi in questo momento è esplorare la sola partizione di Dominio, provate ad espanderne i vari livelli ed osservatene la struttura...
Avete notato come i nomi dei vari oggetti siano preceduti da acronimi? DN, DC, CN, OU... essi sono i componenti di un nome.
Secondo la sintassi LDAP ogni oggetto che potete trovare all'interno dell'albero della directory viene identificato per mezzo di un Distinguished Name (DN), un nome univoco. Il DN viene costruito mettendo in sequenza una serie di componenti specifiche separate per mezzo di una virgola, il compito di tale sequenza è quello di descrivere il percorso assoluto dell'oggetto preso in esame, o meglio la sua posizione, all'interno della struttura di directory.
Le componenti di un DN sono le seguenti
- L'acronimo DC (Domain Component) indica la componente di dominio, il dominio testdomain.lan in LDAP viene rappresentato come DC=testdomain, DC=lan così come il percorso america.azienda.inet verrebbe rappresentato con DC=america, DC=azienda, DC=inet; possiamo pertanto affermare che in LDAP un nome di dominio viene spezzettato in più Domain Component separate da una virgola, nello specifico una DC per ogni livello del suo nome DNS.
- L'acronimo OU identifica invece una Organizational Unit, se nel vostro Active Directory avete organizzato gli oggetti per mezzo di esse dovrete ricostruirne la struttura anche nel percorso LDAP.
- CN invece sta per Common Name ed indica il nome comune di un oggetto, il nome comune dell'oggetto di classe utente Mirko Iodice sarà appunto "CN=Mirko Iodice".
Percui, in base a quanto mostrato dal mio precedente screenshot, il Distinguished Name per il suddetto utente sarà
CN=Mirko Iodice, OU=Domain Users, DC=testdomain, DC=lan
Non è complicato... è sufficiente ricostruire l'intero percorso in senso contrario a come viene sfogliato nella struttura ad albero proposta dagli strumenti amministrativi di Active Directory.
Voglio fare soltanto una precisazione: non dovete confondere il CN (Common Name) di cui abbiamo appena parlato con il Canonical Name che viene invece visualizzato all'interno dello strumento Active Directory Users and Computers, quest'ultimo è come un Distinguished Name privato però di informazioni come DC, OU, CN. Di seguito il Canonical Name dell'oggetto appena preso in esame.
Dalla teoria alla pratica - l'uso di ldp.exe
Chiudete pure ADSI Edit ed aprite ora lo strumento ldp.exe che trovate sempre sotto a C:\Program Files\Support Tools.
ldp.exe ci permetterà di sperimentare quanto abbiamo detto fin'ora, per mezzo di esso eseguiremo infatti una connessione al server LDAP ed una query, il nostro fine sarà quello di ottenere come risultato una lista dei gruppi di cui fanno parte gli oggetti di classe utente contenuti in una specifica Unità Organizzativa (Domain Users). Credetemi... si fa più fatica a descriverlo che a farlo.
Dopo aver aperto ldp.exe Andate sul menu Connection e selezionate la voce Connect, vi verrà ora richiesto di specificare alcuni dati, in condizioni normali occorre soltanto inserire l'indirizzo di un Domain Controller. Nel mio caso inserirò localhost poiché sto lavorando sul Domain Controller stesso.
Appariranno a video alcuni messaggi di log e vi renderete subito conto se la connessione ha avuto esito positivo.
La sola connessione al server LDAP per fortuna non basta per ottenere informazioni, sarà necessario ora stabilire un binding completo utilizzando un account utente di dominio autorizzato. Dal menu Connection selezionate la voce Bind, inserite i dati richiesti e poi premete il tasto OK.
Di nuovo alcuni messaggi di log confermeranno l'esito positivo dell'operazione
Una volta eseguita l'operazione di binding avrete la possibilità di utilizzare tutti i comandi disponibili sotto al menu Browse, per questo esempio utilizzeremo soltanto la funzione Search... selezionatela.
Compiliamo i dati richiesti dalla finestra di dialogo Search, in particolare
- Base Dn deve corrispondere alla posizione di base dalla quale vorrete far partire la vostra ricerca, nel mio caso non voglio eseguirla all'interno dell'intero dominio ma soltanto tra gli oggetti contenuti nella unità organizzativa Domain Users, per questo motivo specificherò come Base dn soltanto il percorso LDAP di questa OU.
- Filter è un filtro di ricerca, praticamente costituisce la nostra chiave di ricerca vera e propria; il carattere asterisco funziona da wildcard (non è però supportato per tutti i tipi di attributo) ed utilizzato da solo permette di non specificare nessun filtro. Come filtro è possibile utilizzare sia valori riferiti a classi di oggetti che attributi (vi ricordate la struttura dello Schema di AD spiegata poco fa?). Per il momento mi limiterò a ricercare i soli oggetti di classe user perché la mia intenzione è quella di ottenere la lista dei gruppi di cui fa parte ciascun utente contenuto nella suddetta organizzativa.
- Il parametro Scope permette di specificare la profondità della ricerca, cioè il numero di sottolivelli a partire dal Base Dn nei quali verrà effettuata la ricerca; vogliamo forse che essa si limiti ad analizzare i soli oggetti contenuti in questa specifica unità organizzativa? Oppure ci farebbe comodo analizzare anche il livello immediatamente successivo o addirittura l'intero albero? Nel mio caso la scelta non farà nessuna differenza poiché non ci sono altre unità organizzative all'interno del Base Dn "Domain Users", lascerò quindi selezionata la voce subtree.
Non ho però ancora espresso in nessun modo la volontà di visualizzare i gruppi di cui fanno parte gli eventuali utenti contenuti nel Base Dn, per questa operazione utilizzerò il pulsante Options che vedete evidenziato nella precedente immagine.
La stringa Attributes permette di specificare una lista di tutti gli attributi che verranno restituiti in output qualora vengano trovati oggetti che soddisfino il filtro di ricerca prestabilito. Procedo quindi aggiungendo memberOf infondo alla lista degli attributi ed apponendo un carattere punto e virgola per terminare la stringa. A quanto punto è sufficiente premere il pulsante OK per accettare le modifiche e poi Run per eseguire la query. Un piccolo consiglio: in un secondo momento, a scopo puramente didattico, provate a visualizzare anche l'attributo "pwdLastSet" e godetevi i risultati della ricerca... vi verrà visualizzata la data esatta dell'ultimo cambio password eseguito dall'utente.
L'output generato dall'operazione di ricerca non risulta certamente facile da leggere, in ogni caso si nota che sono stati trovati due utenti all'interno del Base Dn preso in esame e che soltanto uno di essi fa parte di qualche gruppo, per la precisione l'utente Mirko Iodice fa parte di due gruppi: Italy Admins ed IT Department.
Getting 2 entries: >> Dn: CN=Mirko Iodice,OU=Domain Users,DC=testdomain,DC=lan 4> objectClass: top; person; organizationalPerson; user; 1> cn: Mirko Iodice; 1> distinguishedName: CN=Mirko Iodice,OU=Domain Users,DC=testdomain,DC=lan; 2> memberOf: CN=Italy Admins,OU=Domain Groups,DC=testdomain,DC=lan; CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan; 1> name: Mirko Iodice; 1> canonicalName: testdomain.lan/Domain Users/Mirko Iodice; >> Dn: CN=Mario Rossi,OU=Domain Users,DC=testdomain,DC=lan 4> objectClass: top; person; organizationalPerson; user; 1> cn: Mario Rossi; 1> distinguishedName: CN=Mario Rossi,OU=Domain Users,DC=testdomain,DC=lan; 1> name: Mario Rossi; 1> canonicalName: testdomain.lan/Domain Users/Mario Rossi;
Se ritoccassi le opzioni di ricerca eliminando la visualizzazione di tutti gli attributi eccetto memberOf potrei migliorare sensibilmente la leggibilità di quanto sopra riportato ottenendo il seguente risultato
Getting 2 entries: >> Dn: CN=Mirko Iodice,OU=Domain Users,DC=testdomain,DC=lan 2> memberOf: CN=Italy Admins,OU=Domain Groups,DC=testdomain,DC=lan; CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan; >> Dn: CN=Mario Rossi,OU=Domain Users,DC=testdomain,DC=lan
Vi voglio far notare che anche i gruppi di cui il mio utente fa parte vengono identificati per mezzo di un Distinguished Name e che il loro nome è anch'esso un CN.
AdFind
Purtroppo l'output offerto dallo strumento ldp.exe non risulta particolarmente ordinato e riutilizzabile. Per venire incontro alle esigenze di tutti vi segnalo l'esistenza di AdFind [22] scaricabile da qui [23]. Si tratta di un programma molto versatile ma ad un primo impatto risulterà altrettanto complicato da utilizzare, in questa pagina [24] potete trovare il suo manuale completo.
Innanzitutto occorre dire che con AdFind avrei potuto digitare la seguente stringa per avere lo stesso risultato ottenuto con ldp.exe
AdFind.exe -h localhost -p 389 -u TESTDOMAIN\Administrator -up P@ssw0rd -s Subtree -b "OU=Domain Users,DC=testdomain,DC=lan" -f "objectclass=user" memberOf
AdFind mette però a disposizione alcune funzionalità avanzate che permettono di reindirizzare l'output in un file di testo o csv, nonché di migliorare, ordinare e filtrare i risultati. Il comando di cui sopra potrebbe in realtà essere nettamente semplificato: poiché il computer dal quale l'ho eseguito fa parte di un dominio non dovrebbe essere necessario specificare i dati di connessione al server LDAP, quest'ultimo verrà selezionato automaticamente sfruttando le funzionalità di Active Directory e per il binding verranno utilizzate le credenziali fornite da una autenticazione Windows integrata.
Osservate il risultato, nettamente più pulito del precedente, che ottengo utilizzando la seguente stringa di ricerca semplificata
AdFind.exe -s Subtree -b "OU=Domain Users,DC=testdomain,DC=lan" -f -nodn -noctl -list "objectclass=user" cn memberOf
Mirko Iodice CN=Italy Admins,OU=Domain Groups,DC=testdomain,DC=lan CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan Mario Rossi
I filtri di ricerca nel dettaglio
Abbiamo visto che sia ldp.exe che AdFind permettono di estrapolare informazioni mediante l'applicazione di un filtro di ricerca (mi riferisco al campo Filter di ldp.exe o allo switch -f di Adfind), precedentemente non sono sceso nel dettaglio e mi sono limitato ad utilizzare un semplice (objectclass=user) per ricercare tutti gli oggetti di classe utente, ora però voglio mostrarvi qualche altra possibilità...
(memberOf=CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan)
Mi permetterebbe di visualizzare gli attributi di tutti gli oggetti che fanno parte del gruppo IT Department. memberOf è un attributo di tipo Distinguished Name quindi quando viene utilizzato come filtro non supporta wildcard e va obbligatoriamente specificato per intero.
(&(objectcategory=user)(memberOf=CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan))
Un esempio di filtro multiplo, visualizzerà gli attributi dei soli oggetti di classe user che fanno parte del gruppo IT Department
(&(objectcategory=user)(!(memberOf=CN=IT Department,OU=Domain Groups,DC=testdomain,DC=lan)))
Quest'altro filtro invece è di esclusione e visualizzerà gli attributi di tutti gli oggetti di classe user che NON fanno parte del gruppo IT Department
Qualche altra idea? ...
(physicalDeliveryOfficeName=*Verona*)
(badPwdCount=1)
LDAP nella vita quotidiana di un amministratore
Oltre all'attività di ricerca informazioni all'interno del database della directory voglio darvi almeno altri due motivi per i quali una conoscenza basilare di LDAP può tornarvi utile in qualsiasi momento: WSH Scripting [27] con ADSI [28] ed Active Directory Authoritative Restore [29].
Gli script "Find And Move Specified Users To A New OU [30]" e "List All Computers And Users Within An OU And SubOUs [31]" che potete trovare nella mia sezione scripts [32] sono soltanto un piccolo assaggio di come sia possibile creare procedure amministrative per Active Directory, un esempio ancora più interessante è costituito dal seguente script che è in grado di resettare la password (rendendone obbligatoria la modifica al prossimo logon) di tutti gli utenti contenuti in una specifica OU.
' StudentPwd.vbs
' Source http://www.computerperformance.co.uk/ezine/ezine83.htm
' Example VBScript to change a user's password
' Version 2.0 - August 2005
' ---------------------------------------------------------'
Option Explicit
Dim objOU, objUser, objRootDSE
Dim strContainer, strDNSDomain, strPassword
Dim intCounter, intAccValue, intPwdValue
' --------------------------------------------------------'
' Note: Please change OU=nowhere, to reflect your domain
' --------------------------------------------------------'
strContainer = "OU=nowhere, "
strPassword = "H0l1d@y$"
intAccValue = 544
intPwdValue = 0
intCounter = 0
' -------------------------------------------------------'
' Makes the user change H0l1d@y$ password at first logon
' -------------------------------------------------------'
Set objRootDSE = GetObject("LDAP://RootDSE")
strDNSDomain = objRootDSE.Get("DefaultNamingContext")
strContainer = strContainer & strDNSDomain
set objOU =GetObject("LDAP://" & strContainer )
For each objUser in objOU
If objUser.class="user" then
objUser.SetPassword strPassword
objUser.SetInfo
objUser.Put "pwdLastSet", intPwdValue
objUser.SetInfo
objUser.Put "userAccountControl", intAccValue
objUser.SetInfo
intCounter = intCounter +1
End if
next
WScript.Echo strPassword & " is Password. UserAccountValue = " _
& intAccValue & vbCr & intCounter & " accounts changed"
WScript.Quit
' End of change password example VBScript
Una interessante introduzione all'utilizzo di LDAP in VBScript la potete leggere nell'articolo Walking the AD Tree [33] riportato da computerperfomance.co.uk [34].
Autore
Mirko Iodice
mirko -at- notageek (.dot) it