Copy Link
Add to Bookmark
Report
BFi numero 13 file 19
================================================================================
---------------------[ BFi13-dev - file 19 - 20/08/2004 ]-----------------------
================================================================================
-[ DiSCLAiMER ]-----------------------------------------------------------------
Tutto il materiale contenuto in BFi ha fini esclusivamente informativi
ed educativi. Gli autori di BFi non si riterranno in alcun modo
responsabili per danni perpetrati a cose o persone causati dall'uso
di codice, programmi, informazioni, tecniche contenuti all'interno
della rivista.
BFi e' libero e autonomo mezzo di espressione; come noi autori siamo
liberi di scrivere BFi, tu sei libero di continuare a leggere oppure
di fermarti qui. Pertanto, se ti ritieni offeso dai temi trattati
e/o dal modo in cui lo sono, * interrompi immediatamente la lettura
e cancella questi file dal tuo computer * . Proseguendo tu, lettore,
ti assumi ogni genere di responsabilita` per l'uso che farai delle
informazioni contenute in BFi.
Si vieta il posting di BFi in newsgroup e la diffusione di *parti*
della rivista: distribuite BFi nella sua forma integrale ed originale.
--------------------------------------------------------------------------------
-[ HACKiNG ]--------------------------------------------------------------------
---[ SCAT0LE Di SABBiA E (DUEMiLA) FiNESTRE ]-----------------------------------
-----[ KJK::Hyperion <noog@libero.it> http://spacebunny.xepher.net ]------------
SCATOLE DI SABBIA E (DUEMILA) FINESTRE
========================================
KJK::Hyperion <noog@libero.it>
1. Tanto tempo fa, in una galassia lontana lontana
1.1. L'autore
1.2. L'articolo
2. Modello di sicurezza di Windows
2.1. Soggetti, oggetti, descrittori di sicurezza
2.2. Infrastruttura di autenticazione
2.3. Novita' in Windows 2000: token filtrati
3. Mettere insieme il tutto: il comando iam
3.1. La teoria
3.2. Il mondo reale
3.3. Studio di casi
3.4. Conclusioni
4. Metterci sopra una facciata carina: Desktop Sandbox
5. Cosa manca, cosa non va
6. Conclusioni, ringraziamenti, saluti
1. TANTO TEMPO FA, IN UNA GALASSIA LONTANA LONTANA
--------------------------------------------------------------------------------
1.1. L'AUTORE
* ... e' un utente Microsoft, fin dalla piu' tenera eta'. Non sapeva niente
della guerra Amiga/Commodore/PC -- lui usava un MSX. Finche' la sua
sorellina non ha estratto a caldo una cartuccia una volta di troppo.
* ... ha comprato il suo primo, unico e attuale PC nel 1999. Ha usato Windows
98 prima edizione fino a delirare: per un periodo di tempo piuttosto lungo
credeva persino che Linux gli piacesse di piu'. Per sua fortuna scopri'
BeOS. Tre mesi prima che la Be fallisse. Per sua fortuna scopri' poi Windows
2000, la sua anima fu salvata e il suo destino gli fu chiaro.
* ... non ha un santino di David Cutler [1] accanto al letto, ma lo vorrebbe.
* ... non usa Linux ne' ci crede molto. Percio' lavora su ReactOS [2]. Anche
in ReactOS crede poco, ma ci spera molto.
* ... ha scoperto il progetto ReactOS <http://www.ReactOS.com/> nel 2001,
e non se ne e' piu' staccato. Finora ha dato contributi di poco conto, ma
non si scoraggia: crede nel Colpo Decisivo e nella Fama Istantanea. Corri
Forrest, corri.
* ... ha conosciuto smaster (e mayhem, e vecna, e koba, e vodka, ecc.)
all'HackIt-04, ma lo conosceva gia'. Lui distribuiva gratuitamente CD di
prova di ReactOS, smaster gli ha proposto un salto di qualita': scrivergli
un articolo su ReactOS per BFi.
1.2. L'ARTICOLO
* ... non riguarda ReactOS.
* ... e' stato redatto in Word 97 (le illustrazioni in CorelDRAW),
ri-formattato a mano per l'edizione elettronica perche' il filtro di
esportazione "testo con layout" di Word e' una sola.
* ... e' stato consegnato in ritardo.
* ... non e' stato riletto.
* ... ESPLORA UNA POSSIBILE TECNICA PER IMPLEMENTARE SANDBOX IN WINDOWS 2000 E
SUCCESSIVI.
* ... non e' eccezionale, l'argomento e' noioso e la tecnica e' banale, non si
reggerebbe in piedi senza il lavoro di Microsoft e non e' comunque esaustiva
(l'autore e' pigro: accontentatevi).
2. MODELLO DI SICUREZZA DI WINDOWS
--------------------------------------------------------------------------------
Un'infarinatura di base sull'alquanto eccentrico modello di sicurezza di
Windows, perche' senno' non ci capite niente. Leggete anche se siete sicuri di
sapere gia' tutto il necessario: potrebbero esservi sfuggite delle sfumature
importanti.
2.1. SOGGETTI, OGGETTI, DESCRITTORI DI SICUREZZA
Il controllo di accesso in Windows si basa su un concetto di fondo molto
semplice: un *soggetto* ottiene un determinato *accesso* ad un *oggetto*
protetto da un *descrittore di sicurezza*. Il controllo di accesso viene
effettuato solo al primo accesso. Tutti gli accessi successivi avvengono
tramite un *handle*, un valore opaco che identifica l'oggetto e l'accesso
consentito la prima volta. Consideriamo l'apertura in lettura di un file:
l'oggetto e' il file, l'accesso e' "lettura", il controllo di accesso viene
effettuato quando il file viene aperto e l'handle verra' usato in funzioni come
ReadFile (WriteFile, ad esempio, si lamenterebbe che l'handle non comprende
l'accesso in scrittura).
2.1.1. Il descrittore di sicurezza: SID e ACL
Il descrittore di sicurezza e' una struttura associata ad ogni oggetto e
contiene un sovrainsieme delle informazioni associate ai file UNIX: l'utente e
il gruppo proprietari dell'oggetto, l'ACL (Access Control List) associata
all'oggetto ed eventualmente una SACL (Security Access Control List), una
struttura simile all'ACL usata per l'auditing, che non trattero' in questo
articolo (forse in futuro, chissa'). L'ACL e' un array di oggetti chiamati ACE
(Access Control Entry), ognuno contenente un id di utente o gruppo, l'accesso
concesso o negato all'utente/gruppo, la propria ereditabilita' (cioe' se si
applica anche agli oggetti-figlio, o solo agli oggetti-figlio, o solo ai
sotto-contenitori, ecc.), e opzioni extra non troppo interessanti.
Utenti e gruppi (e computer di un dominio, e interi domini, ecc.) non sono
identificati da un numero, ma da una struttura di dimensioni variabili chiamata
SID (Security IDentifier) -- che e' una rogna da trattare via codice, peraltro.
La forma di un SID nella sintassi attuale (revisione 1) e':
+------------+-----------+-----------+-----------+- - - -+-----------+
| Autorita' | sotto- | sotto- | sotto- | | sotto- |
| principale | autorita' | autorita' | autorita' | | autorita' |
+------------+-----------+-----------+-----------+- - - -+-----------+
\____ _____/ \___________________ ________________ _ _ / \____ ____/
V V V
prefisso dominio id utente
In realta' non e` proprio cosi`: l'interpretazione dell'ultima sotto-autorita'
come id utente e delle altre come id del dominio e' solo una convenzione. Ma fa
comodo pensarlo cosi'.
La convenzione per esprimere un SID come stringa e' S-R-I-A-A-...-A, dove S e'
la lettera S, R e' la versione della sintassi (l'unica versione definita e' 1),
I e' l'autorita' principale e A le sotto-autorita'.
L'autorita' principale e' un numero curiosamente da 48 bit e curiosamente
big-endian, e identifica la sotto-sintassi, cioe' come l'array di
sotto-autorita' vada interpretato. Le piu' importanti sono 1 (World) e 5 (NT
Authority). L'autorita' 1 contiene un solo SID, S-1-1-0, che e' il gruppo
"Everyone" (essendo "mondo" un gruppo vero e proprio e non un concetto astratto
come in UNIX, volendo, si potrebbe avere un utente che non ne e' membro. Ma ci
faremmo solo del male). L'autorita' 5 contiene la stragrande maggioranza dei
SID che mai incontreremo: tutti gli account utente, gli account dei computer,
del dominio, ecc. sono SID nell'autorita' 5. Il sistema locale e' S-1-5-18, il
gruppo Administrators locale e' S-1-5-32-544, i domini hanno SID del tipo
S-1-5-21-<numeraccio random>-<numeraccio random>-<numeraccio random>, ecc.
Le sotto-autorita', dette anche RID (Relative IDentifier), sono numeri a 32 bit
del tutto arbitrari. In realta', tutti i componenti di un SID sono arbitrari:
il kernel non ci bada (quasi) mai, non e' un suo problema (vedremo poi di chi
e') che un soggetto si identifichi come "Everyone" (che non e' un utente),
appartenente al gruppo "Administrator" (che non e' un gruppo). Per quanto
balzana, e' un'identita' valida: se il soggetto puo' vantarla e' perche' ha
avuto abbastanza privilegi o credenziali per crearsela (vedremo poi ecc. ecc.).
E questo ci porta, se Dio vuole, alla parte piu' interessante nonche'
pertinente: il soggetto.
2.1.2. Il soggetto: oggetti token
In Windows, i soggetti sono rappresentati da... oggetti. Sembra un controsenso,
ma permette cose utili, come proteggere un soggetto con un descrittore di
sicurezza. Questo tipo di oggetto si chiama *token*, contiene tutte le
informazioni di identificazione pertinenti al controllo di accesso e ogni
processo ne ha uno associato (*token primario*. Esistono anche token associati
temporaneamente a singoli thread, i token di impersonazione, ma si usano in
ambiti client-server che esulano dagli scopi di questo articolo), inizialmente
copiato da quello del processo genitore (il primo token e' quello del processo
System ed e' creato dal nulla in fase di avvio, con un set di attributi fisso.
Tutti i processi di sistema ne ereditano una copia). Il contenuto di un token
puo' essere riassunto come segue:
* Id dell'utente (ovviamente un SID). Strettamente di sola lettura.
* Lista di gruppi e relativi attributi. I gruppi sono strettamente di sola
lettura, per gli attributi dipende. Gli attributi possibili sono:
* Abilitato: il gruppo viene usato per il controllo di accesso. Attributo
di lettura-scrittura (o sola lettura se e' anche presente l'attributo
"obbligatorio").
* Logon id: il gruppo identifica la sessione di logon interattivo a cui il
token e' associato. Essenzialmente, e' il gruppo dei soggetti abilitati
ad accedere al display (cioe' l'ACL del display contiene un'ACE che
consente l'accesso a questo gruppo). Generalmente e' un gruppo volatile,
cioe' non e' nel database dei gruppi ma e' generato dinamicamente, ed e'
nella forma S-1-5-5-X-Y, dove (X, Y) e' un LUID (Locally Unique
IDentifier, identificatore a 64 bit). Attributo di sola lettura.
* Obbligatorio: il gruppo non puo' essere disabilitato. Tutti i gruppi
aggiunti dal normale sistema di autenticazione hanno questo attributo
(disattivare un gruppo non sempre significa limitare il soggetto, perche'
permette anche di evadere le ACE di accesso negato: poco intuitivo, e gli
amministratori che non leggono la documentazione tutte le sere come una
Bibbia farebbero solo casino -- quindi e' meglio cosi'). Ovviamente di
sola lettura.
* Owner: il gruppo puo' essere selezionato come proprietario predefinito
degli oggetti creati dal soggetto. Dato che l'owner degli oggetti e'
praticamente l'unico modo in cui si puo' effettuare l'accounting delle
risorse (esempio: quote disco per i file) e' importante che non possa
essere impostato arbitrariamente. Sola lettura.
* Lista di privilegi e relativi attributi. I privilegi sono identificati da
dei LUID e sono capacita' speciali del soggetto, generalmente riguardanti
un'intera classe di oggetti (ad esempio il privilegio Debug permette accesso
illimitato a tutti gli oggetti-processo e oggetti-thread). Strettamente di
sola lettura, ma alcuni attributi possono essere modificati. Gli attributi
possibili sono:
* Abilitato: il token possiede questo privilegio. Se un privilegio e'
disattivato (e lo e' di default), e' come se non ci fosse, ma puo' essere
sempre abilitato quando necessario (ed e' l'uso previsto che se ne
dovrebbe fare).
* Eliminato: il token (o un antenato da cui e' stato copiato) possedeva
inizialmente questo privilegio, ma ora non piu'. Come fosse disabilitato,
ma non puo' essere piu' riabilitato.
* Identificatore della sessione di logon. In breve, e' un LUID in cambio del
quale il servizio di autenticazione restituisce delle credenziali (nome
utente e password, ticket Kerberos, certificato, ecc.). E' il modo in cui i
driver dei filesystem di rete fanno trasparentemente login sui file server
con le stesse credenziali fornite dall'utente (proprio cosi', componenti
kernel-mode che interrogano un servizio user-mode. Sfumature di microkernel,
non trovate?), o in cui il driver di NTFS riceve i certificati per la
crittografia dei file. I processi di sistema e i servizi hanno un id
associato alle credenziali anonime (mai sentito parlare di "null session"?
Lei).
* Owner e gruppo primario predefiniti degli oggetti creati da questo soggetto.
Modificabili. L'owner deve sempre puntare all'id utente o ad un gruppo con
l'attributo "owner" (per impedire al soggetto di evadere dall'accounting
delle risorse), e il gruppo primario deve essere uno dei gruppi del token.
* ACL predefinita degli oggetti creati da questo soggetto. Interamente
modificabile, formato libero.
* Varie ed eventuali, tra cui la data di scadenza (attualmente inutilizzata),
un identificatore arbitrario del servizio di autenticazione che ha creato il
token, un LUID che identifica il token e un numero che cambia ad ogni
modifica apportata (suonano molto come requisiti C2/Orange Book. Devo
informarmi).
2.1.3. Mettere insieme il tutto e girare la manovella: il controllo di accesso
Per la gioia di grandi e piccini, il modello di sicurezza di Windows non e'
solo chiacchere: tutto cio' di cui ho parlato, alla fine, confluisce in una
manciata di funzioni del kernel che implementano la semantica del controllo di
accesso. Una di queste funzioni e' pubblica e documentata (ce ne sono numerose
variazioni, per il supporto dell'auditing e/o delle ACL object-oriented usate
in Active Directory, ma non cambiano molto), e posso mostrarvene il prototipo:
/* NOTA: IN, OUT e OPTIONAL sono macro vuote che servono solo a rendere piu'
leggibile il codice */
BOOLEAN SeAccessCheck
(
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
IN BOOLEAN SubjectContextLocked,
IN ACCESS_MASK DesiredAccess,
IN ACCESS_MASK PreviouslyGrantedAccess,
OUT PPRIVILEGE_SET * Privileges OPTIONAL,
IN PGENERIC_MAPPING GenericMapping,
IN KPROCESSOR_MODE AccessMode,
OUT PACCESS_MASK GrantedAccess,
OUT PNTSTATUS AccessStatus
);
Dovrebbe piu' o meno spiegarsi da se', ma credo che qualche chiarimento non
guasti:
* Il valore di ritorno puo' essere TRUE o FALSE, e indica se il soggetto ha
accesso all'oggetto o no.
* SecurityDescriptor e' il descrittore di sicurezza dell'oggetto.
L'identificatore dell'oggetto non e' usato nel controllo d'accesso, quindi
qui compare solo il descrittore di sicurezza (ma e' usato nell'auditing, e
compare appunto come parametro nelle funzioni di controllo di accesso con
supporto per l'auditing).
* SubjectSecurityContext e' il soggetto. Punta al token corrente, ed
eventualmente anche a quello primario se quello corrente e' un token di
impersonazione.
* SubjectContextLocked e' una flag. Se TRUE, SeAccessCheck usa
SubjectSecurityContext direttamente. Altrimenti, prima di usarlo, lo blocca
per sincronizzarsi con le altre funzioni che trattano soggetti.
* DesiredAccess e' l'accesso che il soggetto ha chiesto di ottenere
sull'oggetto. Una ACCESS_MASK e' una mappa di 32 bit con questa forma:
+--------------------+---+
| GENERIC_READ | g | 31
| ---------------- | e |
| GENERIC_WRITE | n | 30
| ---------------- | e |
| GENERIC_EXECUTE | r | 29
| ---------------- | i |
| GENERIC_ALL | c | 28
+--------------------+---+
|-- --|
| |
|-- --|
+------------------------+
| MAXIMUM_ALLOWED | 25
+------------------------+
| ACCESS_SYSTEM_SECURITY | 24
+--------------------+---+
|-- --| |
| | |
|-- --| |
| | |
|-- --| s |
| ---------------- | t |
| SYNCHRONIZE | a | 20
| ---------------- | n |
| WRITE_OWNER | d | 19
| ---------------- | a |
| WRITE_DAC | r | 18
| ---------------- | d |
| READ_CONTROL | | 17
| ---------------- | |
| DELETE | | 16
+--------------------+---+
| | s |
| | p |
| | e |
. . c .
. . i .
. . f .
| | i |
| | c |
| | | 0
+--------------------+---+
* Accessi specifici: il significato di questi bit varia in base al tipo di
oggetto.
* Accessi standard: accessi validi per tutti i tipi di oggetto.
* DELETE: eliminazione (il significato di "eliminazione" varia in base
al tipo di oggetto).
* READ_CONTROL: lettura del descrittore di sicurezza.
* WRITE_DAC: modifica dell'ACL.
* WRITE_OWNER: modifica del proprietario e del gruppo principale.
* SYNCHRONIZE: attesa dello stato segnalato dell'oggetto (richiesto per
l'I/O sincrono su file e per l'uso di oggetti di sincronizzazione
condivisi)
* ACCESS_SYSTEM_SECURITY: lettura e modifica della SACL. Puo' essere solo
richiesto o inserito in una SACL, mai inserito in un'ACL -- l'accesso e'
concesso implicitamente se il privilegio Security e' presente e attivato.
* MAXIMUM_ALLOWED: accesso speciale che puo' essere solo richiesto e che
indica di aprire l'oggetto sempre e comunque, con il massimo accesso
consentito al chiamante.
* Accessi generici: validi per tutti i tipi di oggetto, ma con significato
diverso. Prima che un'ACL contenente accessi generici possa essere usata,
gli accessi generici devono essere convertiti in accessi specifici e/o
standard.
* PreviouslyGrantedAccess e' l'accesso che il soggetto ha gia', ad esempio per
un privilegio.
* Privileges e' dove SeAccessCheck restituisce i privilegi che ha usato
durante il controllo di accesso. I privilegi attualmente usati sono
Security, che controlla le richieste dell'accesso ACCESS_SYSTEM_SECURITY
(che non puo' essere specificato in un'ACL), e TakeOwnership, che, se
attivato, consente accesso WRITE_OWNER a qualsiasi oggetto. Tutti gli altri
privilegi devono essere controllati dal chiamante, e, se concedono a priori
un qualche accesso all'oggetto, questo accesso va passato in
PreviouslyGrantedAccess.
* GenericMapping e' la mappa che converte gli accessi generici in accessi
specifici. Ha questa forma:
typedef struct _GENERIC_MAPPING
{
ACCESS_MASK GenericRead;
ACCESS_MASK GenericWrite;
ACCESS_MASK GenericExecute;
ACCESS_MASK GenericAll;
}
GENERIC_MAPPING, * PGENERIC_MAPPING;
Non servono ulteriori spiegazioni, credo.
* AccessMode puo' essere UserMode o KernelMode. Se e' KernelMode,
SeAccessCheck ritorna TRUE direttamente e senza passare dal via.
Altrimenti... vedrete.
* GrantedAccess e' dove SeAccessCheck ritorna l'accesso consentito al soggetto
sull'oggetto. Essenzialmente, e' utile quando il soggetto richiede l'accesso
MAXIMUM_ALLOWED.
* AccessStatus e' dove SeAccessCheck ritorna la ragione del fallimento in caso
di fallimento (non sempre si trattera' di STATUS_ACCESS_DENIED, quindi e'
importante che l'informazione non vada perduta).
L'algoritmo e' un lungo calvario di loop annidati, piuttosto intuitivo:
1. Le access mask vengono normalizzate, se necessario, convertendo i permessi
generici in permessi specifici. La normalizzazione e' importante: permette
di accelerare il controllo di accesso trasformandolo in un loop di semplici
operazioni bit-a-bit di AND/OR e confronto.
2. L'accesso concesso viene inizializzato con il valore normalizzato
dell'accesso gia' concesso. Se l'accesso cosi' ottenuto e' uguale
all'accesso richiesto, la funzione ritorna con successo.
3. Se l'ACL del descrittore di sicurezza e' nulla, tutto l'accesso richiesto e'
concesso implicitamente. La funzione ritorna con successo. Se l'accesso
richiesto era MAXIMUM_ALLOWED, viene aggiunto anche l'accesso GENERIC_ALL.
4. Se e' richiesto l'accesso WRITE_DAC e il soggetto e' il proprietario
dell'oggetto, WRITE_DAC viene aggiunto all'accesso concesso (cioe' il
proprietario di un oggetto puo' sempre cambiarne i permessi. Dite che sulle
condivisioni di rete non e' sempre cosi'? Ottimo spirito di osservazione, ma
continuate a leggere). Se l'accesso cosi' ottenuto e' uguale all'accesso
richiesto, la funzione ritorna con successo.
5. Se l'accesso richiesto implicitamente richiede (o potrebbe essere concesso
da) dei privilegi, come nel caso di ACCESS_SYSTEM_SECURITY e WRITE_OWNER,
viene fatto un controllo sui privilegi detenuti dal soggetto. La maschera
dell'accesso concesso viene aggiornata con l'accesso concesso in questo
modo. Se l'accesso cosi' ottenuto e' uguale all'accesso richiesto, ecc. ecc.
6. Per ogni ACE nell'ACL, se il gruppo specificato nell'ACE e' l'user id o un
gruppo abilitato contenuto nel token:
* La maschera di accesso dell'ACE viene normalizzata.
* Se l'ACE e' di accesso negato e la rispettiva maschera di accesso si
interseca di anche solo un bit con l'accesso richiesto, l'accesso e'
negato e la funzione fallisce (a meno che non sia richiesto l'accesso
MAXIMUM_ALLOWED, nel qual caso la maschera di accesso richiesto viene
aggiornata spegnendo i bit accesi nella maschera dell'ACE, e il loop
continua).
* Se l'ACE e' di accesso consentito, l'accesso consentito viene aggiornato
mettendolo in OR con l'intersezione (AND) dell'accesso richiesto e della
maschera dell'ACE. Se l'accesso cosi' ottenuto e' uguale all'accesso
richiesto, la funzione ritorna con successo. A meno che non sia richiesto
l'accesso MAXIMUM_ALLOWED: in tal caso la maschera viene solo aggiornata,
e il loop continua.
7. L'accesso viene negato implicitamente, e la funzione fallisce (a meno che
non sia richiesto l'accesso MAXIMUM_ALLOWED). Un'ACL vuota, percio', non
consente nessun accesso.
Tutto chiaro? Spero di si'.
2.2. INFRASTRUTTURA DI AUTENTICAZIONE
Starete, immagino, morendo dalla curiosita' di sapere da dove nascono questi
famosi token. Vedete, a volte, quando due servizi di sistema si vogliono bene,
molto bene...
User +-----------------+
| | runas (1) |
| | |
v +--------|--------+
+---------------------+ |
| +-----------------+ | |
| | GINA (1) | | v
| | | | +-----------------+
| | | | | Secondary |
| | | | | logon |
| +-----------------+ | | |
| | | |
| Winlogon | | |
| | | |
+---------|-----------+ +-------|---------+
| ^ | ^
(2) | | (5) (2) | | (5)
| | | |
+---------------------|-|----------------------|-|----------------+ (4)
| LSASS | | | | | _______
| v | v | | / \
| +--------------------|------------------------|--------------+ | |-_______-|
| | LSA | | | |
| | ---->| Policy |
| | | | |database |
| +---|----------------------------------------------------|---+ | | |
| | | | \ _______ /
| (2) | (2) | |
| v | |
| - - - - - - - - - - - - - - - - - - - - - - - - - - - - | |
| | APs | | |
| | |
| | +-------------+ +-------------+ +-------------+ | | |
| | MSv1_0 | | Negotiate | | Kerberos | | |
| | | |<--| |-->| | | | |
| | | | | | | | |
| | +---|-----|---+ +-------------+ +------|------+ | | |
| - - - -|- - -|- - - - - - - - - - - - - - - - | - - - - | |
| | +--------------------+ +-----+ v |
| v - - - - - -|- - - - - - - - -|- - - - - | (3)
| +-------------+ | SSPs | | | | _______
| | SAM | v v | / \
| | | | +-------------+ +-------------+ | | |-_______-|
| | | | NetLogon | | Active | | | |
| +------|------+ | | | | Directory --|--->|Directory|
| | | | | | | | |
| (3) | | +------|------+ +------|------+ | | | |
| | - - - - - -|- - - - - - - - -|- - - - - | \ _______ /
+---------|--------------------------|-----------------|----------+
| | |
___v___ (3) | (3) |
/ \ | |
|-_______-| v v
| | Domain controller
| SAM |
|database |
| |
\ _______ /
Perdonate il casino che faro' spiegandolo, ma la documentazione non e' molta, e
quella che c'e' manca poco riporti indicazioni tipo "hic sunt leones".
Fortunatamente, non e' vitale capire come funzioni l'autenticazione, per gli
scopi di questo articolo (anzi, ignoreremo completamente l'autenticazione), ma
mi pare brutto lasciarvi cosi', "a piedi". Pazientate, ancora poco e sara'
tutto finito.
Prima di tutto, esaminiamo le componenti riportate nell'illustrazione:
* *LSA* (Local Security Authority) e' il cuore dell'autenticazione in Windows,
nonche' dell'intera infrastruttura del modello di sicurezza stesso. Il
compito di LSA e' duplice: mediare le chiamate tra i vari componenti
dell'infrastruttura, e gestire il database delle policy.
Quest'ultimo aspetto comprende l'assegnazione dei privilegi, quella dei
diritti (simili ai privilegi, ma si applicano _prima_ dell'autenticazione,
e rappresentano capacita' richieste _a priori_. Esempi di diritti sono
NetworkLogon, InteractiveLogon, ecc.), delle quote di memoria e CPU (tuttora
vergognosamente inutilizzate), dei criteri di auditing, dei trust tra domini
(di cui non so un accidente) e dei dati cifrati associati agli utenti (ad
esempio, il dialer salva qui le password delle connessioni a Internet). Se
vi suona tutto stranamente familiare, forse state pensando alla finestra
"Impostazioni protezione locale" (comando secpol.msc). Precisamente.
Il database delle policy e' un hive del registro,
HKEY_LOCAL_MACHINE\SECURITY. Potete guardarci dentro, con un po' di impegno,
ma non troverete niente di immediatamente interessante (il cracking del
database delle policy e' un argomento a se', ma io in crittografia sono una
mezza sega). Inoltre e' interamente criptato -- non si nota perche', di
default, la chiave e' salvata in chiaro nel registro, ma potete scegliere di
tenerla su un floppy o proteggerla con una password (provate il comando
syskey).
* Il *SAM* (Security Account Manager) e' il servizio che gestisce il database
degli utenti e dei gruppi. Contiene i nomi di tutti gli utenti e i gruppi, i
rispettivi SID, i membri di ogni gruppo e gli hash delle password degli
utenti, generate dagli SSP (vedi piu' avanti). Sicuramente molto altro, ma
francamente lo ignoro. Se vi interessa, credo Samba sia una fonte di
informazioni piu' completa.
Anche il database del SAM e' un hive del registro, HKEY_LOCAL_MACHINE\SAM,
anch'esso crittato.
* *NetLogon* e *Active Directory* sono degli *SSP* (Security Service
Provider). Essenzialmente, forniscono l'accesso a database utenti remoti e
permettono di serializzare/deserializzare le credenziali. Supportano l'uno i
domini vecchio stile (fino a Windows NT 4), l'altro i nuovi domini
gerarchici basati su Internet introdotti in Windows 2000. NetLogon, in
particolare, non e' che un ponte RPC verso un SAM remoto.
* Gli *AP* (Authentication Package), infine, sono i componenti che ricevono le
richieste di autenticazione e le validano sulla base delle informazioni
raccolte dagli SSP e dal SAM. Windows, al momento, ne ha tre: Microsoft 1.0
(anche noto come MSV1_0), che gestisce i logon locali e quelli remoti su
domini vecchio stile; Kerberos, che gestisce i logon remoti tramite Active
Directory; e Negotiate, che sceglie l'AP adatto in base al nome utente.
Questi benedetti token, allora, da dove escono? Cosa trasforma un nome utente e
una password in un token? La procedura esatta varia da un percorso di
autenticazione all'altro, ma, schematicamente, si puo' dividere in fasi:
1. *Raccolta delle credenziali*. Qualche esempio:
* Il servizio di logon primario (Winlogon) le ottiene tramite un'interfaccia
grafica personalizzata (GINA, Graphic Identification aNd Authorization), che
chiede all'utente un nome utente e una password.
* Il servizio di logon secondario (RunAs) le ottiene tramite il comando
testuale runas o la finestra "Esegui come...".
* I server di rete che usano il database utenti del sistema ottengono o
nome utente e password piu' o meno in chiaro, o un blocco di credenziali
"pre-digerite" da un sistema di autenticazione remoto (la famosa
"autenticazione NTLM", o, piu' propriamente "autenticazione SSPI").
2. *Connessione ad un AP* (o ad un SSP se abbiamo gia' pronte le credenziali e
dobbiamo solo validarle). Il servizio di logon si connette all'LSA e chiede
di parlare con un AP/SSP, per risolvere le credenziali e/o validarle, ed
ottenere in cambio il token corrispondente. Alcuni servizi di autenticazione
suggeriscono anche dei gruppi da aggiungere al token. L'esempio piu'
importante e' Winlogon, che genera un logon id (S-1-5-5-X-Y) e ne chiede
l'inserimento all'interno del token, perche' e' l'unico gruppo (a parte
LocalSystem, S-1-5-18) con accesso completo al display -- senza appartenere
a questo gruppo, la shell dell'utente non si avvierebbe nemmeno, men che
meno riuscirebbe a creare finestre.
L'API per dialogare con l'LSA, curiosamente, e' la stessa per user mode e
kernel mode (un tema ricorrente nel design di Windows NT), e i server di
rete kernel-mode che si servono dell'LSA sono tutt'altro che rari (pensate
al file server CIFS). Molto piu' rari gli AP/SSP kernel-mode (mai visto
uno), anche se del tutto possibili.
3. *Verifica delle credenziali*. L'AP/SSP si connette ai database utenti di cui
fa uso e valida le credenziali provenienti dal servizio di autenticazione, o
comunque usa qualunque sistema di verifica su cui il suo protocollo e'
basato. In risposta ottiene o un codice di errore, o un set di user id,
gruppi, privilegi, ACL predefinita, ecc. che l'LSA usera' per costruire il
famoso token. L'AP/SSP, inoltre, associa le credenziali al token creando una
sessione di logon, che l'LSA assegnera' al token se e quando lo creera'.
4. *Applicazione delle policy*. L'LSA, a questo punto, fa valere la voce
dell'amministratore di sistema, che ha l'ultima parola su chi ha quali
diritti. Se la verifica delle credenziali e' fallita per un utente
registrato nel SAM locale, l'account dell'utente potrebbe essere bloccato.
Se ha avuto successo, l'utente deve avere il diritto per il tipo di login
richiesto: ad esempio, se l'utente ha cercato di fare login accedendo ad una
condivisione di rete, ma non ha il diritto NetworkLogon, il login fallira'.
Infine, l'LSA puo' cancellare privilegi e gruppi, o aggiungerne altri,
eventualmente registrare il login dell'utente nel registro eventi, tutto in
base alle direttive contenute nel database delle policy. Per i login remoti,
le policy locali seguono e si sovrappongono a quelle, eventuali, remote.
5. Se Dio vuole, ci siamo: *creazione del token*. L'LSA crea il token e lo
ritorna al servizio di logon, che ne fara' un po' quel che gli pare
(impersonarlo, usarlo per lanciare la shell dell'utente, ballarci la rhumba,
ecc.).
Ora sapete tutto quello che _non_ e' la creazione di una sandbox (non e' stata
solo una gita di piacere. Quando piu' avanti parlero' di accesso al display,
sessioni di logon e credenziali SSPI, saprete per che verso girano le ruote).
Non servono password, servizi di sistema o server remoti. La creazione di una
sandbox nasce sulle spalle di questo intricato traliccio infrastrutturale, e
consiste nello _scendere_, rinunciando man mano ai permessi.
Quel che da' noia e' che... non si puo' fare. La creazione di qualsiasi
identita' deve passare obbligatoriamente per questa trafila, pure che si
tratti di un'identita'-copia che fornisce un ovvio sotto-insieme di permessi:
si deve passare nuovamente per l'LSA (o, avendo il privilegio CreateToken, si
puo' saltare l'LSA e creare il token dal nulla. Ma non solo resta il problema
di non poter creare sessioni di logon senza cooperare con l'LSA -- che il
kernel contatta direttamente ogni volta che viene creato un token, proprio per
verificare che la sessione di logon sia valida -- costringendoci a copiare una
sessione esistente o usare la sessione anonima, c'e' anche che, in Windows
Server 2003, Microsoft ha deciso di soffocare la "creativita'" di alcuni
sviluppatori, applicando l'attributo "eliminato" al privilegio CreateToken in
tutti i token generati dall'LSA: ora, solo l'LSA puo' creare token -- o
programmi _molto_ creativi. Ed e' un bene, perche' l'LSA logga tutto, applica
le policy e crea token con un contenuto consistente e prevedibile).
Si _doveva_. Molto e' cambiato in Windows 2000 -- e, come sempre quando
Microsoft introduce dei miglioramenti reali, piuttosto in silenzio.
2.3. NOVITA' IN WINDOWS 2000: TOKEN FILTRATI
2.3.1. Digressione
Arrivato a questo punto, e cioe' all'argomento decisivo, mi trovo in un grande
imbarazzo. Mi mancano improvvisamente le parole. Quindi faccio una piccola
pausa, vi racconto un po' di sciocchezze e poi riprendiamo le cose serie, anche
perche' quattro pagine e piu' di dettagli intimi sul modello di sicurezza di
Windows sono troppo persino per me.
L'autore di questo articolo ha una lunga storia di interventi fallimentari in
comunita' sulla sicurezza informatica. Il piu' celebre e' il messaggio su
Bugtraq (cercate "KJK::Hyperion executable attachments bugtraq" con Google) che
ha iniziato una flame war niente male. L'autore, come sempre, aveva ragione [3]
e tutti gli altri puzzavano, ma commise un paio di imprudenze (leggete il
messaggio, giudicate voi) -- ora che lo sa, quando lo fa, lo fa apposta (se
dopo aver letto la breve sezione autobiografica di questo articolo volete
scrivermi "Tu! pezzo d'asino! come _OSI_ nominare Linux!", lasciate perdere:
non avete colto. O fatelo almeno con un po' di ironia).
Alcune figuracce sono passate in ombra. Male: tutto il mondo deve sapere.
Tizio chiede sulla mailing list di Sikurezza: "Come impedisco agli utenti di
cambiare i permessi dei loro file?". L'imbecille (io) risponde: "togli
l'accesso completo dall'ACL". Tizio risponde: "Il proprietario di un file puo'
sempre cambiare i permessi" (ricordate, no?). L'imbecille dice "Ah, e' vero! ci
penso io!", e butta due settimane su un progetto assurdo di un wrapper per la
GINA predefinita di Windows che cambi l'owner predefinito del token della shell
con un SID che non sia l'user id (impossibile, ricordate? l'user id e' sempre
un owner valido). Si arrende per manifesta imbecillita', ma il problema
continua a tormentarlo.
Il mondo decide di giocare un po' con il suo cervello, e un giorno gli fa notare
che sulla rete della scuola, nonostante sia il proprietario dei suoi file, non
puo' cambiarne i permessi. "Ma che cazzo?", pensa l'imbecille, e scrive un
complicatissimo programma che legge il descrittore di sicurezza di un file e
controlla "a mano" l'accesso consentito. Inconcludente: il programma dice sempre
"accesso WRITE_DAC consentito", ma i tentativi di aprire file con quell'accesso
falliscono sempre. Ancora piu' strano: i file locali si comportano come atteso,
solo i file di rete hanno quella limitazione.
L'imbecille si arrende definitivamente, si deprime e va a casa a soffocare i
suoi dispiaceri nel lievito maltato in polvere.
Il mondo prova pieta' per lui, e un giorno gli fa sentire l'amministratore
della rete della sua scuola spiegare che i permessi sui file di rete vengono
filtrati dai permessi sulla condivisione attraverso la quale vi si accede.
L'imbecille si batte una mano sulla fronte e si affoga nel WC.
Se ci pensate, e' dolorosamente ovvio. Non so il vostro, ma il mio computer si
chiama ALDEBARAN e Windows e' installato in D:\WINNT (e D: e' l'unica lettera
di unita' -- si', non ho C: -- nonostante abbia una decina di partizioni, ma
questa e' un'altra storia). Ovviamente, ho accesso (in sola lettura) a D:\WINNT.
Perche', quindi, non posso accedere ad \\ALDEBARAN\D$\WINNT? Ovvio: perche' non
sono amministratore, e non ho nessun accesso alla condivisione automatica di D:.
Non ci voleva molto, nevvero?
(La risposta per Tizio e': sposta i profili utente su un'unita' di rete e revoca
-- revoca soltanto, non negare -- il permesso WRITE_DAC sulla condivisione per
il gruppo Users. Usa eventualmente una policy per disattivare l'editor di ACL
sui client).
2.3.2. I token filtrati
Il fatto che il proprietario di un file possa sempre cambiare i permessi (e
quindi ottenere tutto l'accesso necessario) e' (era) una delle fregature del
modello di sicurezza di Windows, ed uno dei motivi per cui abbiamo (avevamo)
davvero bisogno di antivirus: non esiste (ecc.) un modo semplice, alla portata
di tutti e non solo dei super-hacker in grado di scrivere un AP, di avviare
programmi con un token limitato.
Microsoft deve aver ricevuto parecchie lamentele e averci pensato un po' su (o
forse no. Forse il design era gia' pronto e aspettavano solo che i clienti ne
mostrassero la necessita' -- vedremo poi cosa me lo fa pensare), e il risultato
e' molto interessante: i token filtrati. I token filtrati funzionano
esattamente come i token normali, con alcune importanti differenze:
* Chiunque puo' creare token filtrati. Creare token normali richiede il
privilegio CreateToken, da cui il bisogno di un complesso sistema di
autenticazione per impedire l'elevazione di privilegi. Cio' e' possibile e
sicuro perche' i token filtrati possono solo essere creati a partire da un
token esistente e possono _al massimo_ avere gli _stessi_ privilegi del
token di partenza: mai di piu', spesso molti di meno.
* Chiunque puo' assegnare un token filtrato ad un processo, se il token e'
stato creato filtrando il token principale del processo chiamante. Assegnare
token, normalmente, richiederebbe il privilegio AssignPrimaryToken -- i
servizi di logon che lanciano una shell devono sempre avere questo
privilegio (il comando runas fa lanciare i propri processi dal servizio di
logon secondario). Dato che un token filtrato copiato dal proprio token non
puo' elevare i privilegi in nessun modo, assegnarlo ad un processo e'
un'operazione non privilegiata.
* I token filtrati riescono a limitare i privilegi di un processo, senza
rompere i meccanismi di auditing e accounting, presenti e futuri, e in
generale integrandosi perfettamente con il modello di sicurezza esistente (e
creando tutta una nuova famiglia di problemi...). Il filtraggio di un token
puo' consistere in una o piu' di queste operazioni:
* *Eliminazione di privilegi*. Esattamente quello che sembra: il token
filtrato puo' avere meno privilegi. Si possono eliminare privilegi
specifici, o tutti i privilegi. Di default, il privilegio ChangeNotify
non e' considerato uno dei "tutti", perche' e' piuttosto innocuo (in
breve: permette di accedere a C:\blah\doh anche senza avere accesso
FILE_TRAVERSE -- sottoinsieme di FILE_GENERIC_EXECUTE, se capite cosa
intendo -- a C:\blah) e viene solo disabilitato per compatibilita' con
sistemi POSIX (ad esempio se installate Interix), perche' il suo effetto
va direttamente contro lo standard POSIX.
* *Disabilitazione parziale di gruppi*. L'eliminazione dei gruppi non e'
sicura come quella dei privilegi: futuri meccanismi ausiliari di
accounting potrebbero essere basati non solo sull'user id o sul
proprietario, ma anche sui gruppi (avrebbe senso, no?) -- magari qualche
applicazione server lo fa gia'. Eliminare un gruppo da un token
nasconderebbe un'informazione importante: il fatto che quel particolare
soggetto era membro di quel gruppo. E ricorderete dalla spiegazione sui
token che la disabilitazione dei gruppi non e' normalmente concessa,
perche' ha la conseguenza non ovvia di ignorare le ACE di accesso
esplicitamente negato nelle ACL.
La soluzione usata nel filtraggio dei token e' il proverbiale uovo di
Colombo: anziche' eliminare o disabilitare i gruppi, li rendiamo validi
solo per i controlli di accesso negato, con il nuovo attributo "deny
only". Otteniamo lo stesso effetto (limitare i privilegi di un processo),
ma senza dover cambiare le ACL pre-esistenti (e la documentazione, e il
software che genera ACL, ecc.).
* *Aggiunta di gruppi filtranti*. Avete presente il filtraggio operato dal
file server CIFS? Cioe' l'ACL della condivisione che viene sovrapposta a
quella dei file a cui permette di accedere? I gruppi filtranti permettono
di implementarlo in modo consistente e per tutti gli oggetti: file
locali, chiavi di registro, ecc.. L'algoritmo di controllo di accesso, in
pratica, viene modificato in modo che, qualora l'accesso fosse
consentito, venga effettuato un secondo controllo di accesso, questa
volta usando i gruppi filtranti anziche' quelli principali. Se, ad
esempio, inserissimo in un token Everyone come gruppo filtrante, quel
token consentirebbe al massimo l'accesso concesso ad Everyone.
Come forse avrete intuito, i gruppi filtranti sono, da soli, la chiave di
volta nella creazione di una sandbox, nonche' il motivo per cui io non
valgo una cicca -- e' praticamente gia' tutto pronto, per la mia bella
faccia.
* *Aggiunta della flag SANDBOX_INERT*. Anch'io mi son chiesto "e quindi?".
Pare che non abbia nessun effetto, e non capisco che significato utile si
potrebbe assegnare alla flag (che non e' altro che una lucetta che si
puo' accendere o spegnere). Sappiate che e' possibile. Se trovate un uso,
fatemi sapere.
2.3.3. Token filtrati e sandbox
L'uso dei token filtrati per il sandboxing non e', ovviamente, una mia
invenzione. Microsoft stessa, in Windows XP, ha aggiunto un'opzione alla
finestra "Esegui come...", che non richiede un nome utente e una password, e
altro non e' che un modo per lanciare un processo con un token filtrato.
L'opzione presente in Windows XP, tuttavia, e' abbastanza limitata. Diciamo che
soddisfa un bisogno estemporaneo: ho un programma di dubbia provenienza e
voglio provarlo in tutta sicurezza. Non prevede, ad esempio, che il programma
di dubbia provenienza non sia un virus, ma magari un programma che ci serve
usare quotidianamente. O che certi programmi abbiano necessariamente bisogno di
accedere in scrittura a certe risorse, per funzionare. Insomma, e' utile, ma
solo in casi specifici.
Questo pero' io non lo sapevo, e tuttavia ho deciso sin dal primo momento di
sviluppare un tool molto piu' configurabile, che permettesse di implementare
almeno una serie di scenari utili, e magari anche tutti quelli perfettamente
inutili (faro' poi un confronto tra quello che ho prodotto io e l'opzione di
Windows XP) -- preferisco non escludere nulla a priori.
Le limitazioni principali che vorremo imporre con una sandbox si possono
riassumere in pochi punti:
* *Accesso in sola lettura ai file e alle impostazioni dell'utente*, perche'
vogliamo che il programma giri nell'ambiente a cui siamo abituati, e che
tuttavia non possa danneggiarlo. Perche' e' vero che l'uso di un account
utente limitato impedisce di far danni al sistema, ma il nostro profilo
utente, i nostri importantissimi documenti, che non possiamo semplicemente
reinstallare, sono ancora alla merce' di qualsiasi programma.
Per ottenere cio', e' sufficiente aggiungere un gruppo filtrante al token e
dare al gruppo accesso in sola lettura al profilo utente.
* *Negazione della lettura di file privati*. Se sappiamo che il programma e'
interessato al furto di informazioni personali, avremo bisogno di negargli
completamente l'accesso a certe parti del profilo utente. Come sopra:
aggiungere un gruppo filtrante e negargli qualsiasi accesso alle zone
riservate.
Nota bene: i token filtrati _mantengono la stessa sessione di logon del
token originale_. Questo significa che le credenziali SSPI rimangono
invariate: i sistemi remoti continueranno a poterci identificare, ma
soprattutto _il token filtrato avra' accesso agli stessi file criptati con
EFS_. Non possiamo nascondere i file criptati alla sandbox, perche' la
sandbox ha la nostra stessa identita', credenziali incluse.
* *Accesso in scrittura ai file necessari per il funzionamento del programma*.
Per quanto ci piacerebbe limitare completamente un programma, non e' sempre
possibile: il programma potrebbe essere qualcosa che ci e' utile, ma di cui
non ci fidiamo del tutto, quindi vogliamo che sia comunque in grado di
scrivere il proprio output, sia pure non in grado di fare altro. E'
sufficiente creare un gruppo per ogni "gruppo" di programmi con requisiti
comuni, e aggiungerli come gruppi filtranti quando necessario.
Ad esempio, due programmi A e B necessitano di creare file nella directory
C:\downloads, e B anche in C:\partials. Creeremo due gruppi,
"restrict-downloads" e "restrict-partials", permettendo al primo di creare
file in C:\downloads e al secondo in C:\partials. "restrict-downloads"
verra' aggiunto come gruppo filtrante di A e B, e "restrict-partials" solo
come gruppo filtrante di B.
Solo una raccomandazione: *NON AGGIUNGETE MEMBRI AI GRUPPI CHE INTENDETE
USARE COME GRUPPI FILTRANTI*. Nelle ACL non c'e' nessuna flag che dice
"questa ACE e' solo un filtro": la natura filtrante di un'ACE dipende
interamente dal relativo gruppo. Se il vostro "filtro" dice "accesso
completo a restrict-downloads", un utente _membro_ di restrict-downloads
avra' accesso completo ai file di C:\downloads, di chiunque siano. E'
importante che l'unico significato di questi gruppi nelle ACL sia di filtro.
Il mio tool rende tutto questo possibile (possibile, non comodo -- vi esporro'
poi un paio di idee che ho sul suo possibile successore).
3. METTERE INSIEME IL TUTTO: IL COMANDO IAM
--------------------------------------------------------------------------------
A fine maggio 2003, l'autore, che allora studiava il modello di sicurezza di
Windows per diletto, decise che non poteva piu' vivere solo di supposizioni, e
inizio' a progettare e scrivere un programma che avrebbe permesso di creare
qualsiasi token possibile, e lanciare comandi con quel token (l'autore
abbandono' presto l'idea e riprogetto' il tool per creare solo token filtrati,
comunque permettendo di alterare un token esistente).
Il tool si sarebbe chiamato "I Am" (perche'? Provate il comando "iam --help", e
leggete bene. Potreste arrivarci), e sarebbe stato un comando testuale. Sarebbe
stato _il_ tool definitivo di sandboxing (anche se non era ancora chiaro che
sarebbe stato questo il suo scopo principale).
3.1. LA TEORIA
Inizialmente, l'help interno di I Am era questo (piu' o meno -- in realta' l'ho
messo sotto CVS solo di recente, quindi tutte le revisioni precedenti sono
andate perdute):
C:\>iam --help
I Am: sanboxing tool for Windows
Usage: IAM [OPTION]... [--] COMMAND [PARAMETERS]...
Runs COMMAND with PARAMETERS in a sandbox constructed according to the
OPTIONs
OPTION DESCRIPTION CRITICAL
Privileges:
-priv=PRIVILEGE Disable PRIVILEGE in the token no
-priv=* Disable all privileges in the token --
+priv=PRIVILEGE Enable PRIVILEGE in the token no
+priv=* Enable all privileges in the token --
-!priv=PRIVILEGE Delete PRIVILEGE from the token [1] no
-!priv=* Delete all privileges from the token [1] --
+!priv=PRIVILEGE Undelete PRIVILEGE from the token [1] --
+!priv=* Undelete all privileges from the token [1] --
Groups:
-group=GROUP Disable GROUP in the token [2]
-group=* Disable all groups in the token yes
+group=GROUP Enable GROUP in the token no
+group=* Enable all groups in the token no
-!group=GROUP Set GROUP in the token as deny-only [1] no
-!group=* Set all groups in the token as deny-only [1] --
+!group=GROUP Unset GROUP in the token as deny-only [1] --
+!group=* Unset all groups in the token as deny-only [1] --
Restricting groups: [1]
-rgroup=GROUP Don't add GROUP to the restricting groups of the no
token
-rgroup=* Add no group to the restricting groups of the --
token
+rgroup=GROUP Add GROUP to the restricting groups of the token --
Miscellaneous:
-help Show this help and exit --
-about Show version and copyright information and exit --
Notes:
[1] Requires system support for filtered tokens (Windows 2000 and later)
[2] Non-critical if GROUP is not found in the token, critical if GROUP is
mandatory and can't be disabled
Remarks:
[...]
Ero ingenuo, il mondo era rosa e gli uccelli cinguettavano. Mi accorsi quasi
subito di due problemi: la flag DISABLE_MAX_PRIVILEGE per la creazione di token
filtrati non eliminava mai ChangeNotify (non documentato!), e non aggiungere
nemmeno un gruppo filtrante di default aveva una scarsa utilita', perche' il
processo non sarebbe stato in grado di accedere a nulla, rendendo l'uso di I Am
piuttosto frustrante (la scelta ricadde su Everyone -- cioe', di default, la
sandbox sarebbe stata "nessuno", potendo solo avere lo stesso accesso concesso
a tutti). A seguito di queste considerazioni, aggiunsi qualche opzione e
modificai alcune opzioni esistenti:
-priv=* Disable all privileges in the token --
-priv=** ... including ChangeNotify --
-!priv=* Delete all privileges from the token --
-!priv=** ... including ChangeNotify --
-group=* Disable all groups in the token yes
-group=** ... including Everyone yes
-!group=* Set all groups in the token as deny-only --
-!group=** ... including Everyone --
-rgroup=* Add no group to the restricting groups of the --
token
+rgroup=* Add Everyone to the restricting groups of the --
token
In pratica, qualche default utile (mica tanto. Tutta la famiglia di opzioni
-group e' inutile, dato che tutti i gruppi che un token potra' mai contenere
saranno obbligatori e quindi non disabilitabili, ma l'ho aggiunta cosi',
perche' non si sa mai), e qualche opzione per simmetria con le nuove opzioni
(-priv=** contro -!priv=**, ad esempio). Notate che manca un'opzione -!rgroup
(e non c'e' tuttora, nella versione attuale del programma): i gruppi filtranti
non possono essere eliminati o disabilitati, solo aggiunti a quelli
eventualmente esistenti.
Idealmente, l'uso tipico sarebbe consistito in:
C:\>iam -#priv=* -#group=* +rgroup=* -- !COMSPEC!
(La sintassi !variabile! e' equivalente a %variabile%, ma viene espansa in una
fase diversa dell'interpretazione della riga di comando, ed e' piu' sicura
perche' meno ambigua -- ed e' la ragione per cui le -!opzioni adesso sono
-#opzioni. Magari un giorno scrivero' un articolo sul prompt dei comandi di
Windows e sul suo curioso parser).
In pratica, le cose andarono molto diversamente quando, un anno dopo (l'autore
e' pigro), I Am comincio' a funzionare davvero.
3.2. IL MONDO REALE
Non appena fu possibile avviare realmente qualcosa con I Am, emersero delle
complicazioni non previste. Il nostro eroe, coraggiosamente, le affronto' e le
sconfisse tutte (tranne quelle troppo difficili).
3.2.1. Il display
Lo ammetto, questa era una stupidaggine e avrei dovuto aspettarmelo: Everyone
come unico gruppo filtrante non basta per niente. Il display non permette
l'accesso a DogsAndPigs (leggi: Everyone), ma solo al suo proprietario, ai
processi di sistema e alla sessione di logon che gira al suo interno. La scelta
piu' ovvia era di aggiungere il SID di logon come gruppo filtrante, e in genere
di trattarlo come prima trattavo solo Everyone. Modificai cosi' le opzioni:
-group=** ... including Everyone and the logon id yes
-#group=** ... including Everyone and the logon id --
+rgroup=* Add Everyone and the logon id to the restricting --
groups of the token
Tanto per la cronaca, quello che succede quando si avvia un programma senza
accesso al display che carica implicitamente user32.dll (e quindi accede al
display) e' una bella finestra di errore con il messaggio "Applicazione non
correttamente inizializzata (0xc0000142). Fare clic su OK per chiudere
l'applicazione." (lo so perche' ho provato, apposta per questo articolo, a
togliere i permessi al display e lanciare poi dei programmi grafici).
Scopriro' in seguito che questa soluzione e' sub-ottimale, ma ci arriveremo un
po' alla volta.
3.2.2. Il profilo utente
Sapevo che questo avrebbe rappresentato un problema, ma non avevo ancora capito
bene il funzionamento dei gruppi filtranti, quindi mi ci volle un po' per
arrivare a cio' che ormai mi sembra ovvio: avrei creato un gruppo senza membri
(chiamato "IAM") e gli avrei concesso accesso in sola lettura al profilo utente.
Il gruppo IAM avrebbe rappresentato, in pratica, il filtro predefinito per tutti
i processi avviati da I Am. Le opzioni vennero modificate come segue:
-group=** ... including Everyone, the logon id and IAM yes
-#group=** ... including Everyone, the logon id and IAM --
+rgroup=* Add Everyone, the logon id and IAM to the --
restricting groups of the token
Il profilo utente e' composto da due parti principali: la directory del profilo
e l'hive HKEY_CURRENT_USER dell'utente. Entrambe devono essere rese di sola
lettura al gruppo IAM. Facendo questo, scoprii qualcosa che fece scattare nel
mio cervello un vecchio meccanismo rimasto inceppato. L'HKEY_CURRENT_USER del
mio utente aveva nell'ACL il misterioso gruppo "RESTRIZIONI" che avevo gia'
incontrato, ma che non compariva da nessun'altra parte che in alcune ACL
predefinite. RESTRIZIONI, dietro al quale si nasconde il SID S-1-5-12, altro
non e' che l'equivalente, standard e presente almeno da Windows NT 4 in poi (ma
allora da quanto tempo e' che il sandboxing e' stato almeno ipotizzato? Spero
di sbagliarmi), del gruppo IAM inventato da me. Improvvisamente molte cose
furono chiare.
Ebbi la tentazione di sostituire subito IAM con S-1-5-12, ma c'era un problema:
S-1-5-12 non puo' essere aggiunto tramite il normale editor di ACL, perche' il
nome "RESTRIZIONI" e' a senso unico (S-1-5-12 viene tradotto in "RESTRIZIONI",
ma "RESTRIZIONI" non e' considerato nemmeno un nome valido) e non compare
nemmeno nella lista. Grossa fregatura. Decisi di tenermi IAM, che almeno e'
sotto il mio controllo.
3.2.3. La directory dei programmi
A questo punto mi sentivo ancora soddisfatto: il design del programma era
rimasto pulito. A parte la questione IAM/S-1-5-12, non c'era niente di
eccezionalmente brutto, e cominciavo a credere che non ce ne sarebbe stato
bisogno. Inutile dirlo, mi sbagliavo. Molte ACL predefinite non includono
Everyone, se riuscite a crederci.
Fui costretto ad aggiungere Users alla lista dei gruppi-filtro di base, perche'
praticamente nessun programma riusciva ad accedere nemmeno in lettura alla
propria directory (non so la vostra, ma l'ACL della mia D:\programmi contiene
ACE solo per Administrators, Power Users, SYSTEM e Users). Ora so che non e'
una cosa tanto terribile, perche' ho potuto vedere che Users e' diventato una
specie di alias di Everyone -- tutti i token che abbia mai visto contengono
entrambi, anche quelli di utenti che non sono esplicitamente membri di Users.
3.2.4. La directory temporanea
Cose che la documentazione non dice (e chi potrebbe immaginare, del resto?), e
che rendono l'esperienza diretta insostituibile: la directory temporanea si
presume esistere ed essere scrivibile. Sempre. Non e' una richiesta
inaccettabile, ma non ci avevo pensato: i processi dentro la sandbox non
possono usare la directory temporanea usata da tutti gli altri, perche' si trova
nel profilo utente e loro, dalla sanbox, vi hanno accesso in sola lettura.
Questa e' particolarmente rognosa, e ho scelto di non occuparmene in I Am. Le
soluzioni possibili sono tante, vi espongo le piu' utili:
1. Concedete accesso in scrittura al gruppo IAM sulla directory temporanea. La
soluzione piu' semplice, in piu' di un senso, ma considerate che permette ai
programmi della sandbox di fare cio' che vogliono con i file temporanei di
qualsiasi applicazione, perche' detti file sono non solo di proprieta'
vostra, ma anche ad accesso pieno per IAM. Togliere l'accesso a IAM o
limitarlo alla creazione di file e directory non funziona: ricordate che IAM
e' un gruppo filtrante, e l'accesso in scrittura sara' consentito solo se
consentito _sia_ all'utente, _sia_ a IAM.
2. Create una directory temporanea solo per le sandbox, che conceda l'accesso
in lettura e scrittura all'utente e a IAM, e impostate le variabili TEMP e
TMP per puntare ad essa prima di avviare I Am (I Am non crea file
temporanei, e, se li creasse, ora so che e' piu' sicuro renderli
inaccessibili e fare in modo che si eliminino da se' alla chiusura del
processo). Puo' essere anche globale (la mia, D:\tmp, lo e'), permettendo ad
Everyone l'accesso _non ereditabile_ in lettura e la creazione di file, e a
CREATOR OWNER l'accesso completo _solo ereditato_: chiunque potra' creare
file nella directory, e, una volta creati, il loro proprietario vi avra'
accesso completo (comunque filtrato dal gruppo IAM).
3.2.5. Il profilo utente 2 -- la vendetta
Questa fu bella. Mi accorsi di un problema: nonostante tutti gli accorgimenti
presi, i processi nella sandbox non riuscivano ad accedere al proprio
HKEY_CURRENT_USER. Ancora piu' strano: se nella sandbox avviavo regedit,
riuscivo ad accedere alla chiave HKEY_USERS\<SID> normalmente. Che diavolo
stava succedendo?
Disassemblai persino la funzione che apre la chiave HKEY_CURRENT_USER,
partendo dal codice di inizializzazione di advapi32.dll e finendo in ntdll.dll,
tanto lavoro per scoprire che... semplicemente, i processi nella sandbox non
sapevano nemmeno _chi_ fossero. Ricordate che anche i token sono oggetti? Bene,
l'ACL del token che avevo creato non concedeva nessun accesso a IAM: i processi
della sandbox erano cosi' limitati che non riuscivano nemmeno a leggere il
proprio user id, e quindi non riuscivano a riempire il SID nella stringa
"HKEY_USERS\<SID>", e al posto della chiave giusta aprivano
HKEY_USERS\.Default. Wow (leggasi "vov").
Qui nacquero l'opzione -tokacl e famiglia:
-tokacl=ACCESS Revoke ACCESS to the token --
-tokacl=* Set the token ACL to an empty ACL --
+tokacl=ACCESS Grant ACCESS to the token --
+tokacl=* Set the token ACL to a null ACL. NOT RECOMMENDED --
-#tokacl=ACCESS Deny ACCESS to the token --
-#tokacl=* Same as -tokacl=* --
+#tokacl=ACCESS Set ACCESS to the token --
Dove "grant", "revoke", "set" e "deny" hanno lo stesso significato che hanno
per il comando di Windows cacls (anzi, penso che usiamo proprio la stessa
funzione per implementarlo), e le ACL vuote e nulle hanno gli effetti di cui vi
ho gia' parlato.
Gia' che c'ero, ho formalizzato la posizione di IAM come gruppo filtrante
principale, e ho aggiunto ancora un po' di opzioni ad un comando con una
schermata di aiuto che sembrava gia' "Guerra e pace":
-mrgroup=GROUP Unset GROUP as a main restricting group --
-mrgroup=* Set no group as a main restricting group --
-mrgroup=** ... including IAM --
+mrgroup=GROUP Set GROUP as a main restricting group. Implies: --
- +rgroup=GROUP
- +tokacl=GROUP,F
DO NOT SPECIFY A GROUP WITH ANY MEMBERS
-rtoken Use a restricting token. Requires system support --
for restricted tokens (Windows 2000 and later).
Initially implies +mrgroup=IAM
Chiaro, no? iam -rtoken abbrevia le opzioni praticamente obbligatorie per
ottenere dalla sandbox un comportamento intuitivo e marginalmente utile.
3.2.6. Le directory di output comuni e il paradosso del proprietario
impotente
Restava un piccolo, odioso problema: i processi nella sandbox creavano file...
e, una volta che li chiudevano e cercavano di riaprirli, non ci riuscivano.
Questo succede nelle directory comuni, dove chiunque puo' creare file, e ha
accesso completo sui file che ha creato. Come abbiamo gia' abbondantemente
visto, la sola proprieta' di un file non e' abbastanza perche' un processo
dentro sandbox possa accedervi.
A volte e' persino peggio: in Windows 2000 (non in Windows XP, ad esempio), i
file creati dagli amministratori hanno BUILTIN\Administrators come
proprietario, nemmeno l'user id. Aggiungere Administrators come gruppo
filtrante non avrebbe senso: non solo Administrators ha dei membri, ma ad
Administrators e' consentito accesso molto ampio a praticamente qualsiasi
oggetto -- non sarebbe "filtrante" proprio per niente. Per questo motivo (e
altri, principalmente per completezza) ho aggiunto un'opzione -owner, per
impostare il proprietario predefinito. -owner=*, in particolare, imposta il
proprietario all'user id del token.
Una possibile soluzione sarebbe di aggiungere IAM all'ACL della directory
comune, ma non puo' funzionare a lungo -- non abbiamo mica il controllo su
tutte le directory di questo tipo che potrebbero venir create. La soluzione
che ho scelto io e' di far creare ai processi della sandbox delle ACL che
concedono sempre pieno accesso a IAM, e -- ma che novita' -- questo ha
richiesto delle nuove opzioni e modifiche a quelle esistenti:
+mrgroup=GROUP Set GROUP as a main restricting group. Implies: --
- +rgroup=GROUP
- +tokacl=GROUP,F
- +acl=GROUP,F
DO NOT SPECIFY A GROUP WITH ANY MEMBERS
-acl=ACCESS Revoke ACCESS in the default ACL --
-acl=* Set the default ACL to an empty ACL --
+acl=ACCESS Grant ACCESS in the default ACL --
+acl=* Set the default ACL to a NULL ACL --
-#acl=ACCESS Deny ACCESS in the default ACL --
-#acl=* Same as -acl=* --
+#acl=ACCESS Set ACCESS in the default ACL --
+owner=GROUP Set GROUP as the default owner. GROUP must be a no
valid owner group contained in the current
token
+owner=* Set the user id as the default owner --
+pgroup=GROUP Set GROUP as the default primary group. GROUP no
must be any group contained in the current token
Vuoi per semplicita', vuoi perche' aveva senso, vuoi per preservare la mia
salute mentale, ho deciso di considerare il primo processo e thread della
sandbox come creati da dentro la sandbox, e quindi con il descrittore di
sicurezza predefinito costruito in questo modo.
Dilemma interessante: cosa fare degli eventuali accessi specifici nell'ACL
attuale? E' una pessima idea avere accessi specifici nell'ACL predefinita,
perche' verranno applicati a oggetti di qualsiasi tipo, tant'e' vero che non se
ne troveranno praticamente mai. Metti che se ne trovino: che farne? Ho scelto
di mapparli ad accessi generici come se fossero accessi specifici per file, e,
se alla fine avanzano dei bit, errore (ma e' sempre possibile svuotare l'ACL
con -acl=* e ricostruirla da zero). Soluzione molto brutta, ma ad un problema
che non dovrebbe mai verificarsi: sento che verro' perdonato.
3.2.7. S-1-5-12: difficile farne a meno
Questo maledetto gruppo e' ovunque: nell'ACL predefinita del display (ragione
per cui +rgroup=* non aggiunge piu' il logon id -- non ne vedevo il motivo),
quelle di alcune periferiche, quella della directory del namespace degli
oggetti \BaseNamedObjects\Restricted, che suppongo sostituisca
\BaseNamedObjects quando il processo chiamante e' in una sandbox. Scrivere un
programma di sandboxing che non usi S-1-5-12 odora di guai: quanto potra'
durare, man mano che tutte le ACL predefinite inizieranno ad includerlo?
Quindi mi sono arreso, e ho modificato un altro po' di opzioni:
-mrgroup=** ... including IAM and S-1-5-12 --
+mrgroup=GROUP Set GROUP as a main restricting group. Implies: --
- +rgroup=GROUP
- +tokacl=GROUP,F
- +acl=GROUP,F
+mrgroup=* Equivalent to +mrgroup=IAM +mrgroup=S-1-5-12 --
-rtoken Use a restricting token. Requires system support --
for restricted tokens (Windows 2000 and later).
Initially implies +mrgroup=*
Spero di completare presto gli altri tool della famiglia, cosi' potro'
liberarmi di IAM e usare solo S-1-5-12 (che, ricordo, l'editor di ACL di
Windows non permette di usare).
3.2.8. Il profilo utente 3 -- il ritorno
Sapevo che i token filtrati avrebbero creato tutta una nuova famiglia di
problemi, ma proprio non arrivavo ad immaginare fino a che punto. Sentite che
ve ne pare di questa: le sandbox permettono di evadere dalle group policy. I
programmi non possono leggere le chiavi di registro create dalle policy (la
loro ACL nemmeno contiene S-1-5-12), quindi niente policy per loro: i
ragazzacci si comporteranno come se fossero a casa propria.
Chissenefrega.
C'e' un limite a quello che un comando generico dovrebbe fare, e questo lo
oltrepassa. Forse un giorno ci daro' un'occhiata, cerchero' di trovare una
soluzione o una mezza soluzione e la aggiungero' alla documentazione di I Am.
Per adesso, sappiatelo: I Am nasconde le group policy.
3.3. STUDIO DI CASI
Tutto molto bello, ma, se I Am non facesse quello per cui e' stato creato, cioe'
lanciare processi con privilegi limitati, allora ci sarebbe qualcosa che non va
(vi rovino il finale: lo fa). Seguono le prove di un paio di programmi che sono
riuscito a far girare in una scatoletta di sabbia, e indicazioni sulle
considerazioni da fare a priori per scegliere le opzioni per I Am e i permessi
per i file (e la descrizione di altre opzioni ancora).
3.3.1. Il caso: eMule
eMule e' un client per la rete peer-to-peer eDonkey. Un'inquietante
caratteristica di eMule e' che tutti i suoi aggiornamenti sono descritti come
"importante aggiornamento di sicurezza, installare immediatamente". Metterlo
dietro firewall non si puo': continuerebbe a ricevere connessioni, grazie al
protocollo buca-firewall di eDonkey, e, se anche modificassi il programma per
non accettare connessioni, la rete mi "voterebbe" come imbroglione egoista e
nessuno mi lascerebbe scaricare piu' niente. Fortunatamente, preparare una
sandbox per chiuderci eMule e' molto facile.
Prima di tutto, identifichiamo i requisiti: dove ha la necessita' di leggere e
scrivere? Come ho anticipato, e' facile. eMule deve scrivere nella propria
directory dei download e i propri file di configurazione e, teoricamente,
leggere solo dalle directory dei file condivisi.
eMule e' uno di quei programmi stupidi che pretendono di scrivere nella
directory in cui sono stati installati. Per fare le cose fatte bene, dovrei
creare una sotto-directory di D:\programmi per ogni utente (o una
sotto-directory "programmi" in ogni profilo utente), installare eMule nella
directory "giusta" e crearne un'"ombra" nella directory dell'utente tramite
hard link ai file di sola lettura (una union in stile FreeBSD sarebbe molto
meglio, e risolverebbe questo e molti altri problemi -- progetto futuro
ennesimo di infiniti). Dato che il problema e' minore e che non ne ho realmente
bisogno (ho praticamente un solo utente), ho fatto che installare eMule nel mio
profilo utente e al diavolo. Il mio profilo utente, ovviamente, e' in sola
lettura al gruppo IAM (sia i file sia il registro).
Per la directory di eMule ho creato un gruppo, IAM-eMule, e gli ho dato accesso
completo alla directory in cui eMule e' installato (creare gruppi non dovrebbe
essere necessario -- qualsiasi SID va bene come gruppo filtrante, anzi, se non
sono veri gruppi e' ancora meglio -- ma al momento sono costretto ad usare
l'editor di ACL di Windows), in modo che possa salvare il suo stato, i log e la
configurazione (uno spunto: potrei definire due sandbox, una in cui posso solo
configurare e un'altra solo usare il programma, ma mi sembra eccessivo), e alla
chiave di registro HKEY_CURRENT_USER\Software\eMule (che sembra usi solo per
salvare la lingua dell'interfacccia grafica). Lo stesso vale per la directory
dei download e quella dei download parziali, perche' ovviamente eMule deve
poterci scrivere. Altrettanto ovviamente, non basta che IAM-eMule abbia accesso
a queste directory: anche il mio utente deve avervi accesso, perche' la
presenza di IAM-eMule nell'ACL puo' solo _limitare_ il mio accesso.
Qui ho scelto di non usare il piu' sofisticato meccanismo dei "ruoli" (cioe'
usare un gruppo filtrante per ogni risorsa o gruppo di risorse -- e, per
piacere, non crocifiggetemi se "ruolo" significa gia' qualcos'altro), ma di
creare un solo gruppo (IAM-eMule) che riassumesse tutte le risorse di cui il
programma ha bisogno.
Quasi dimenticavo: per la directory temporanea, ho fatto che creare una
sotto-directory %TEMP%\eMule, con permessi di scrittura per IAM-eMule, e lancio
eMule con un file batch che imposta le variabili TEMP e TMP prima di lanciare I
Am. A proposito, la riga di comando e' questa:
C:\>iam --rtoken +rgroup=* --#priv=* -#group=* +rgroup=IAM-eMule -- emule.exe
Cioe' rinuncio all'accesso consentito a tutti i miei gruppi (-#group=*) --
tranne quelli limitati, come Users, Everyone e il logon id -- a tutti i
privilegi (-#priv=*) -- tranne ChangeNotify -- e filtro il token (-rtoken) in
modo da non avere piu' accesso di quanto ne potrebbero avere IAM, S-1-5-12
(-mrgroup=IAM e -mrgroup=S-1-5-12 impliciti in -rtoken), Everyone, Users
(+rgroup=*) e IAM-eMule (+rgroup=IAM-eMule). Gia' che c'ero, ho (indovina
indovinello?) aggiunto un po' di nuove opzioni:
+rgroup=** Add all the existing groups of the token to the --
restricting groups, except BUILTIN\Administrators
and BUILTIN\Power Users, plus IAM and S-1-5-12
-nowait Don't wait for the COMMAND to terminate --
-typical Select typical options for a sandbox. Equivalent --
to -rtoken +rgroup=* -#priv=* -#group=* +owner=*
-wincompat Select options compatible with the native Windows --
sandbox. Equivalent to -rtoken +rgroup=**
-#priv=* +owner=* -#group=BUILTIN\Administrators
"-#group=BUILTIN\Power Users"
L'opzione -nowait e' semplice da spiegare: e' brutto avere una finestra di
console che resta aperta per tutto il tempo, ed eMule e' un programma
interattivo, quindi del suo codice di uscita non mi interessa granche'.
L'opzione -typical dovrebbe spiegarsi da se'.
Oramai siete degli esperti, e dalla descrizione dell'opzione -wincompat
dovreste aver capito come funziona la sandbox implementata da Windows XP (piu'
o meno. In realta' non so se funzioni cosi' anche in un dominio). Come ho fatto
a scoprirlo? Se avete il Platform SDK, cercate il tool pview, e provatelo. Ha
un bellissimo visualizzatore di token.
Che dire della sandbox creata con I Am? Funziona molto bene. eMule riesce a
connettersi, a dialogare, a scaricare file e tutto quanto. Un solo problema,
stranissimo. eMule ha una simpatica finestrella pop-up con la mascotte di eMule
(ovviamente un mulo) che mi segnala ogni nuova riga nel log. Per qualche
ragione, da dentro la sandbox la finestrella con il mulo non compare. Boh?
Forse un giorno prendero' i sorgenti di eMule e cerchero' di scoprire che
cavolo succede.
Cosa non sono riuscito ad ottenere: non ho trovato un modo abbastanza solido
per impedire ad eMule di leggere altro che le proprie directory condivise
permettendogli comunque di funzionare (cioe', ad esempio, di accedere a tutte
le DLL necessarie). E' ancora possibile, in altre parole, sfruttare un
potenziale buco di eMule per rubarmi file o andare a spasso per il disco. Da un
lato, Windows e' estremamente complicato, cresciuto disordinatamente e senza
una forte autorita' centrale per quanto riguarda la posizione di certi file:
non ritengo pensabile, in Windows, qualcosa come le sandbox UNIX basate su
chroot. D'altro canto, per caricare una DLL non serve l'accesso in lettura, ma
solo quello in esecuzione, e l'accesso in esecuzione non permette di caricare
altro che le DLL (e di attraversare le directory, anche se abbiamo visto come
il privilegio ChangeNotify permette di ignorarlo), quindi, se avessi tempo e
voglia, sono sicuro si potrebbe trovare una soluzione basata interamente su I
Am. Per ora mi accontento di vivere in un terrore un po' meno forte (saro'
paranoico?).
3.3.2. Il caso: Miranda IM
Miranda IM (gia' Miranda ICQ) e' un programma di chat multi-protocollo basato
su un concetto che a me piace moltissimo: il programma e' solo una "matrice"
in cui inserire dei plug-in che fanno il lavoro vero e proprio. Miranda, di
suo, crea solo una finestra con un menu, una barra di stato e una lista di
contatti, e mantiene il database dell'utente. Praticamente tutto il resto e' un
plug-in, inclusi tutti i protocolli.
Permettetemi una divagazione: lo uso da quando l'ho scoperto, abbandonando
immediatamente ICQ 99 in suo favore -- era gia' migliore. Migliore anche di ICQ
2000, che all'epoca era gia' uscito da mesi, ed era un orrore pieno di banner
pubblicitari. Miranda (che si chiamava ancora Miranda ICQ) importo' senza
problemi il mio database con anni di log e contatti (lo stesso database che uso
ancora oggi, e che a differenza di quello di ICQ non ho dovuto riparare ogni
due settimane), e un plug-in alla volta costruii un clone quasi perfetto di ICQ
99. Molta strada e' passata, da allora. Adesso Miranda e' un client
multi-protocollo (io uso ICQ, Jabber e IRC), e' alla versione 0.3.3 e si puo'
scaricare, sorgenti inclusi, da <http://www.miranda-im.org/>. E perdonate
lo spam, ma e' un programma che merita davvero.
Detto questo, che interesse ho a rinchiudere Miranda in un recinto? Be', avere
un sacco di plug-in non e' solo una cosa positiva. Lo potete vedere tutti i
giorni in Windows: driver obsoleti che mandano in crash il sistema, plug-in
scritti alla pene di segugio che mandano in crash il browser (anatema, Adobe!
Anatema!), estensioni della shell scritte da gente che non riesce nemmeno a
respirare senza ingoiarsi la lingua, figurarsi programmare in C++, che
inchiodano Esplora Risorse quando aprite un drive di rete su una connessione
lenta... insomma, avrete presente. Le famigerate Terze Parti, i mostri piu'
spaventosi, secondi solo all'Utente Finale.
Insomma, un "rich client" composto da un collage di frammenti di terze parti,
per quanto rispetto e ammirazione nutra per i suoi sviluppatori, tocca il mio
tasto "paranoia". Fortunatamente, anche Miranda e' di una facilita' disarmante
da mettere in una sandbox (e voi direte: perche' non scegli qualche programma
davvero impegnativo? Internet Explorer, magari? Perche' sono pigro _e_ in
ritardo. Siate al MOCA <http://www.olografix.org/> di quest'anno: se avro'
tempo, voglia e il computer in prestito che mi e' stato promesso faro' una
dimostrazione dal vivo anche di questo -- oltre che di ReactOS, s'intende. E
portero' CD di prova gratuiti di ReactOS, personalizzati per il MOCA. Se ne
avete ricevuto uno all'HackIt-04, conservatelo, voglio fare una specie di serie
da collezione: chi li ha tutti... boh, non ho ancora deciso).
Ha solo bisogno di scrivere nel (o nei) database (in cui conserva anche tutte
le impostazioni, oltre che i log) e di creare sotto-directory e file nella
directory dei file ricevuti. Tutto qui. Creiamo un gruppo IAM-Miranda, diamogli
l'accesso necessario a questi oggetti (io ho fatto che creare una directory
D:\home\Hyperion\settings\Miranda nel mio profilo, fargli mettere tutto la'
dentro e far puntare mirandaboot.ini a questa directory --
ProfileDir=%APPDATA%\Miranda), creiamogli una directory temporanea perche' non
si sa mai, e lanciamolo:
C:\>iam --typical +rgroup=IAM-Miranda -- miranda32.exe
Cosi' facile che mi vergogno quasi.
3.4. CONCLUSIONI
Noterete che non ho incluso neanche un pezzo del codice di I Am. E' che,
sinceramente, non ne vedevo la necessita'. I Am, per quanto bello il concetto,
e' un programma noiosissimo. Non fa altro che costruire una dozzina di array da
passare alle funzioni che costruiscono o alterano i token e le ACL. L'unico
valore aggiunto da I Am e' rendere tutto questo intuitivo, e lo fa con
complicatissime macchine a stati, in modo che se passate, per esempio,
-#priv=** +#priv=Shutdown +priv=Shutdown, l'effetto sara' di eliminare tutti i
privilegi tranne Shutdown, che verra' anche attivato (avrei voluto dare un
significato globale alle opzioni, piuttosto che sequenziale -- cioe' ordinare
automaticamente le opzioni dando la priorita' a quelle piu' specifiche, e, a
parita' di specificita', alle ultime specificate -- ma trovo che
l'interpretazione sequenziale sia piu' intuitiva, e meno ambigua).
Mi accorgo di essermi dimenticato di una cosa importante: non vi ho detto da
dove si scarica I Am. Faccio in un attimo:
<http://spacebunny.xepher.net/hack/iam>. Gia' che ci siete, guardatevi pure
attorno, vedete se c'e' qualcos'altro che vi piace (dubito, I Am e' l'unica
cosa allo stesso tempo utile ed _esistente_ che troverete. Beh, forse anche
il port di ElectricFence potrebbe interessarvi).
Ma soprattutto, cosa penso che manchi in I Am? Cosa ci riserva il futuro?
3.4.1. Digressione: un'altra novita' di Windows 2000
La vita e' breve, il tempo stringe, smaster cammina nervosamente su e giu' per
la stanza aspettando che gli consegni un articolo che avevo promesso di
consegnare cinque giorni fa... e io faccio un'altra digressione. L'ultima,
promesso.
Gli odiatori di Microsoft hanno urlato, _tuonato_ contro Microsoft per il
comando runas di Windows 2000. "Ma come! Ma ci voleva tanto! Ma! Ma!". Si', ci
voleva tanto. C'e' voluto un discreto cambiamento architetturale, infatti.
Il Resource Kit di Windows NT 4 aveva un tool chiamato su, che funzionava
esattamente come il comando omonimo di UNIX: avviava comandi come un altro
utente. Non funzionava troppo bene: non solo il profilo utente del comando
avviato con su rimaneva "appeso", ma a volte, facendo logout, il comando
rimaneva attivo, e l'utente successivo se lo ritrovava sul desktop. Molto
brutto. Il problema era proprio nel sistema operativo, una carenza addirittura
a livello di kernel.
Cosa succede quando si fa logout? L'esatta procedura e' piuttosto lunga e non
troppo interessante, ma si puo' riassumere in tre fasi: tutti i processi utente
della sessione vengono avvertiti, terminati a forza se necessario; tutti i
servizi vengono avvertiti, casomai avessero delle risorse dedicate a quella
sessione utente; e il servizio di logon torna nello stato iniziale (non prima
di aver avvertito tutti i provider di servizi di rete, che cosi' sanno di dover
chiudere le sessioni dell'utente verso server remoti, e tutti i vari servizi
che impongono policy agli utenti, come il servizio delle group policy e il
Windows Installer).
Riuscite a vedere il problema? Presto detto: i processi lanciati da su venivano
visti come di proprieta' del servizio di su, quindi era il servizio ad essere
tenuto a terminarli. Il problema e' che il servizio non aveva modo di terminare
non solo il comando, ma anche tutti i processi che il comando aveva lanciato.
Poteva solo terminare il comando e sperare che gli altri processi facessero i
bravi e terminassero quando il sistema glielo chiedeva -- il sistema non avrebbe
forzato la terminazione, perche' ai servizi e ai loro processi figli i messaggi
di logout vengono inviati solo per loro informazione: sappiano che stiamo
facendo logout, ma non si sentano tenuti a farci niente. In pratica, il servizio
voleva trattare il comando e i suoi discendenti come gruppo di processi, e non
poteva, perche' in Windows mancava completamente il concetto di gruppo di
processi.
Indovinate un po'? Windows 2000 ha i gruppi di processi. Si chiamano "job". Il
servizio di logon secondario (quello su cui si basa runas) lancia i processi in
un job, cosi' allo stesso tempo sa quando va scaricato il profilo utente (il
job invia un messaggio quando tutti i processi al suo interno terminano), e
puo' terminare in blocco tutti i processi lanciati dal comando iniziale. Fateci
caso: quando fate logout, i processi lanciati con runas sono sempre gli ultimi
a terminare, specialmente se non hanno finestre. Non e' un caso: i processi con
finestre generalmente terminano subito perche' il messaggio broadcast di logout
arriva a tutte le finestre del display attuale, indipendentemente dal processo,
ma gli altri non ricevono nessun messaggio, perche' sono visti come proprieta'
di un servizio e non hanno nessun "ricevitore" di messaggi. Sono gli ultimi a
terminare perche' e' seclogon a terminarli, non appena viene avvertito del
logout (cosa che avviene solo dopo che tutti i processi della sessione utente
sono stati terminati), e li termina terminando il job in blocco.
Alla Microsoft potevano fare come al loro solito: trovare una soluzione ad hoc,
tirare il fiato sperando che Longhorn -- che gli permettera' di fare un po'
quel che accidenti pare a loro -- sia completato in tempo utile e sopportare
gli insulti nel frattempo (e ho in mente: temi grafici, assembly side-by-side,
Attachment Execution Services, ecc.). Del tutto inaspettatamente, invece, i job
sono una genuina figata, e uniti ai token filtrati sono una bomba. Vi riassumo
per punti quello che possono fare:
* Imporre *limiti al tempo CPU user-mode*, cumulativi o sui singoli processi.
* Imporre *limiti alle dimensioni del working set* (cioe' le pagine di memoria
che vengono caricate sempre in memoria fisica quando un processo diventa
quello attivo), cumulativi o sui singoli processi.
* Imporre *limiti alla dimensione della memoria virtuale*, cumulativi o sui
singoli processi.
* *Scegliere le CPU che i processi del job possono usare*.
* *Forzare una priorita' di scheduling* su tutti i processi e i thread del job.
* *Limitare l'accesso al display*. E' possibile impedire l'accesso agli
appunti, alle impostazioni globali (come i colori di sistema), alla modalita'
video, persino impedire ai processi nel job di accedere a finestre create al
di fuori del job (ma si puo' anche aggiungere al job una whitelist di
finestre esterne).
* *Limitare i token dei processi nel job*. Si puo' impedire a qualsiasi
processo di usare un token che contenga il gruppo degli amministratori,
costringere i processi ad usare solo token filtrati, o addirittura solo copie
di un token specifico, o ancora forzare un filtro su tutti i token (cioe' un
set obbligatorio di gruppi da disabilitare, privilegi da eliminare e gruppi
filtranti da aggiungere a tutti i token creati nel job).
Sto scrivendo con una mano sola! Semplicemente fantastico. Trovo che manchino
solo due cose alla perfezione: una funzione per sospendere tutti i thread di
tutti i processi di un job, e supporto per i job annidati (ma mi pare di aver
intravisto entrambe le cose in Windows XP...).
3.4.2. Il futuro di I Am
Ovviamente, il prima possibile vorrei aggiungere supporto per i job: una
garanzia che i processi non possano uscire dalla sandbox. Fatto cio', dubito
rimarra' molto di aggiungibile. Gia' cosi' e' un sacco di roba: l'help interno,
anche senza il supporto per i job, ha superato le due schermate di console.
Ogni tanto me lo leggo prima di andare a dormire, che' dimentico sempre come
va a finire (e' stato il maggiordomo).
Una cosa che faro' il prima possibile, poco ma sicuro, sara' di aggiungere un
file di configurazione che contenga degli pseudo-gruppi (con SID sotto
un'autorita' principale inesistente, tipo S-1-1000-X) da usare come gruppi
filtranti, che I Am supporti come gruppi validi nella creazione di sandbox, e
magari un pannello di controllo per gestirli.
Un altro tool che renderebbe l'uso di I Am molto piu' comodo potrebbe essere un
editor di ACL specializzato, che supporti solo S-1-5-12 e i gruppi di cui
sopra, e permetta di impostare facilmente i permessi di accesso concessi alle
sandbox: potrei sbarazzarmi del gruppo IAM, una buona volta, e renderei un gran
servizio a tutti.
4. METTERCI SOPRA UNA FACCIATA CARINA: DESKTOP SANDBOX
--------------------------------------------------------------------------------
I Am e' solo l'inizio. Il mio vero obiettivo e' ottenere un programma di
sandboxing che mio padre possa usare. Letteralmente: e' molto attento con
quello che riceve via e-mail e possiede i rudimenti di base della sicurezza
informatica, ma se ho paura di essere fregato io, non oso immaginare cosa
potrebbe succedere a lui. Un antivirus non voglio metterglielo: rallenta
oscenamente il sistema, va tenuto aggiornato e quasi tutti i programmi una
volta ritenuti "a rischio", come Word, adesso se la cavano molto bene in quanto
a sicurezza. Senza considerare che usa un utente non amministratore e tutte le
connessioni a Internet hanno il firewall attivato. Si', ci son passato io, si
nota tanto?
Capita piu' di una volta che gli arrivino file di dubbia provenienza, o via
e-mail o tramite floppy (si'! virus su floppy! succede ancora!), e devo sempre
intervenire io a dirgli se e' sicuro aprirli o meno. Vorrei renderlo un po'
piu' indipendente, e gia' che ci siamo anche scrivere un programma che
assecondi la mia pigrizia (non ho _tutta_ questa voglia di scrivere un file
batch per ogni sandbox, visto anche che quasi tutte usano le stesse, identiche
opzioni).
Fermo restando che sto parlando di programmi che non esistono ancora, butto li'
qualche idea:
* Nelle proprieta' di un collegamento ad un programma, impostare le directory
e le chiavi di registro a cui puo' accedere (questo sarebbe bello, ma non so
se si puo' fare).
* Un programma che avvii una nuova istanza di Esplora Risorse in un desktop a
parte, girando in una sandbox, magari rinchiuso in un job: tutte le comodita'
di un desktop, tutta la sicurezza di una sandbox.
* Un pannello di controllo per creare e gestire sandbox e modelli di sandbox.
* Una voce di menu contestuale simile ad "Esegui come...", ma che permetta di
scegliere in quale delle sandbox configurate far girare il programma.
* Un'estensione che aggiunga un'icona nella barra del titolo di tutte le
finestre di una sandbox, indicando che si trova in una sandbox, ed
eventualmente in quale.
Sara' intuitivo come spero? Sara' utile? Funzionera'? Migliorera' la vita mia,
di mio padre o comunque di chi lo usera'? Si vedra'.
5. COSA MANCA, COSA NON VA
--------------------------------------------------------------------------------
Conoscendo BFi, immagino che praticamente tutti i lettori abbiano una qualche
esperienza con sistemi UNIX. Alcuni, immagino, mangiano, bevono e respirano
UNIX. Starete pensando "ma questa non e' una sandbox! chroot e jail sono
sandbox, questa e'... e'...". Per quei due o tre lettori che non sanno di che
si tratta, lo spiego in un attimo: chroot e' una funzione UNIX che permette di
nascondere ad un processo tutte le directory che non siano sotto-directory di
una certa directory, detta "root" ("radice"). Dato che in UNIX quasi tutte le
risorse di sistema sono accessibili come file, chroot quasi sempre basta, da
sola, a creare una sandbox piuttosto efficace, che anziche' _impedire_
l'accesso a certe risorse (o consentire l'accesso solo ad alcune), come fa I Am,
_nasconde_ tutte le risorse da proteggere (o mostra solo quelle necessarie).
jail, in breve, equivale a chroot ma, in piu', protegge molte risorse _non_
accessibili come file.
Che dire? Avere qualcosa come chroot sarebbe molto bello, ma non solo richiede
modifiche a parti di Windows su cui non ho nessun controllo (si vedra' in
ReactOS...), e' anche molto difficile da implementare. Non solo Windows ha
_almeno tre_ modi, isolati tra di loro e incompatibili, per accedere alle
risorse (come file, come chiavi di registro e come oggetti), ma bisogna avere a
che fare con le famigerate Terze Parti: chi puo' sapere di che oggetti, file e
chiavi di registro ha bisogno un determinato programma (o un componente di
sistema, se e' per questo)? Come rendere accessibili le periferiche nelle
sandbox? Quali periferiche, e con che nome?
Insomma, sarebbe un lavoraccio. Richiederebbe modifiche molto complesse al
kernel. Personalmente, ci ho anche pensato su, e credo che il modo migliore sia
di creare un nuovo oggetto del kernel, la "Partition", che rappresenti una
"fetta" di sistema (il nome "Subsystem" e' gia' stato preso), con la propria
radice del namespace dei nomi, e quindi il proprio registro e i propri file (il
registro e' un oggetto, chiamato \Registry, e i file sono quasi tutti
sotto-oggetti di una periferica, e le periferiche sono oggetti normalmente
creati in \Device). O potrei estendere i job, ma magari un programma vorrebbe
poter usare job per conto suo, e far girare tutto in un job potrebbe
interferire. Resta da vedere se ai driver piacera' una cosa del genere, se
sara' anche solo _fattibile_, e quanto lavoro richiedera'. Ma e' qualcosa che
sarebbe interessante avere in ReactOS.
Per adesso, accontentatevi. Le sandbox basate sul controllo di accesso non sono
_cosi'_ male. Sono molto facili da tirare su, configurare e farci realmente
funzionare dei programmi, danno comunque una certa sicurezza e possono essere
configurate con i tool gia' esistenti.
E, si', secondo me sono delle sandbox vere e proprie. Non trovo inerente al
concetto di "sandbox" il nascondere il resto del sistema o la sandbox stessa:
l'importante, nonche' quello che fa funzionare davvero la protezione, e' che i
programmi non riescano ad uscirne (il che non e' necessariamente vero! Un
programma potrebbe eseguire comandi inviando messaggi ad una finestra gia'
aperta, ad esempio. Ma, beh, e' anche per questo che esistono i job).
6. CONCLUSIONI, RINGRAZIAMENTI, SALUTI
--------------------------------------------------------------------------------
In questo articolo abbiamo fatto una rapida carrellata sul modello di sicurezza
di Windows, con particolare enfasi su autorizzazione ed autenticazione, abbiamo
visto una delle novita' in questo modello introdotte in Windows 2000, i token
filtrati, e abbiamo visto come permettono di implementare un tipo di sandbox
basato interamente sul controllo di accesso. Abbiamo visto un tool, I Am,
scritto dall'autore per mettere in pratica l'uso di token filtrati, e un'altra
novita' di Windows 2000, i job (gruppi di processi), che rendono molto piu'
solida una sandbox (e verranno supportati, prima o poi, da I Am), e hanno molti
altri usi interessanti. Abbiamo concluso con alcune ipotesi di programmi
costruibili con lo stesso motore di I Am, piu' intuitivi, piu' comodi e a
prova di genitore.
L'autore, al momento, non ringrazia nessuno, avendo svolto tutto questo lavoro
in completa solitudine, al buio del suo angolo scrivania e senza nemmeno una
connessione a Internet (puttana Wind, puttana). Ringraziera' a tempo debito chi
riuscira' a leggere questo noiosissimo articolo fino alla fine, chi gli dara'
suggerimenti o insulti, chi provera' il suo programma, ma soprattutto smaster,
per la pazienza dimostrata.
L'autore vi saluta, vi invita a incontrarlo al MOCA (ma chi voglio prendere in
giro? Questo articolo verra' pubblicato _al_ MOCA. Semmai, voi che ci siete
gia', venitemi a trovare. Sono nella tenda con su scritto "ReactOS". Vi do' un
CD di ReactOS, vi offro un caffe', vi spiego come conciliare l'amore per
Windows con l'open source, in generale rispondo ai vostri dubbi esistenziali),
al LinuxWorld di fine ottobre, a Francoforte (cercate lo stand di ReactOS,
ovviamente), ed eventualmente alla sua prossima collaborazione con BFi --
senza garanzie di trovarcelo.
Hola,
KJK:: HYPE
RION
NOTE:
--------------------------------------------------------------------------------
[1] Ha progettato e sviluppato tre sistemi operativi, uno piu' strano
dell'altro (RSX-11, VMS e Windows NT). Attualmente lavora in Microsoft: ha
diretto lo sviluppo di Windows 2000 e il port a 64 bit di Windows.
[2] Clone open source di Windows NT.
[3] Non proprio. Una delle domande auto-postesi dall'autore nel messaggio --
"questo per gli eseguibili, ma come fare per gli script?" -- rimase senza
risposta. Dato che l'autore e' un genio, ha poi trovato la soluzione per
conto proprio. Un giorno potrebbe addirittura benedire questo squallido
mondo con un'implementazione.
================================================================================
------------------------------------[ EOF ]-------------------------------------
================================================================================