================================================================================ ---------------------[ 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 http://spacebunny.xepher.net ]------------ SCATOLE DI SABBIA E (DUEMILA) FINESTRE ======================================== KJK::Hyperion 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 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---, 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\ 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\", 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 . 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 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: . 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 ]------------------------------------- ================================================================================