AIRT Lab - Università Politecnica delle Marche
Transcript
AIRT Lab - Università Politecnica delle Marche
Università Politecnica delle Marche FACOLTÀ DI INGEGNERIA Corso di Laurea in Ingegneria Informatica e dell’Automazione SISTEMI OPERATIVI SICURI E PIATTAFORME EMBEDDED: STUDIO DI UNA PARTICOLARE SOLUZIONE. Tesi di Laurea di: Relatore: Lorenzo Pigini Prof. Aldo Franco Dragoni Anno Accademico 2005/2006 a Manuela Ringraziamenti Ai miei genitori, per la fiducia dimostrata e per il sostegno dato A mia sorella, per i suoi innumerevoli aiuti e consigli: a te dedico questa tesi! Ai miei compagni di studio, perché hanno reso piacevoli questi anni trascorsi insieme Ai miei amici, semplicemente, perché sono i miei amici Al Prof. Dragoni, per i preziosi consigli ricevuti durante lo svolgimento della tesi INDICE DEI CAPITOLI Introduzione 1 I Sistemi Embedded Definizione Applicazioni Perché Linux in ambienti embedded 7 9 12 Termini e metodologia per la sicurezza dei computer La necessità della sicurezza Terminologia nell'ambito della sicurezza Definizione di sicurezza dei computer Sicurezza nei sistemi operativi Reference Monitor Trusted Computing Based 17 18 19 23 24 27 ACME Fox Board: linux in soli 66x72 mm. La scheda Caratteristiche tecniche Uno sguardo al software 29 31 35 Architettura di sicurezza SELinux e suo funzionamento Architettura Flask Un po' di storia: evoluzione dell'architettura Flask Componenti e funzionamento dell'architettura Flask SELinux Classi di oggetti e modalità di accesso Controllo dei processi Controllo dei file Controllo dei socket 37 37 38 48 50 52 54 59 “Porting” di SELinux in ambiente embedded CRIS cross-compiler L'ambiente di sviluppo 62 63 INDICE DEI CAPITOLI Impostazioni personali Preparazione del sistema per l'installazione di SELinux BusyBox: il coltellino svizzero di Linux-embedded Configurazione di BusyBox Coreutils Include Libbb Loginutils Procps Sysdeps Toplevel Makefile Accorgimenti finali 67 69 72 72 74 78 81 83 86 89 89 90 Conclusioni 95 Bibliografia i INTRODUZIONE Introduzione. Obiettivo principale di questo lavoro è il “porting” di un'avanzata architettura di controllo degli accessi su una particolare piattaforma embedded. Il termine “sistema embedded” identifica genericamente un sistema elettronico a microcontrollore o microprocessore progettato appositamente per l'applicazione che si vuole sviluppare, spesso con una piattaforma hardware ad hoc. In questa area si collocano sistemi di svariate tipologie e dimensioni, in relazione al tipo di CPU, al sistema operativo ed alla complessità del software, che può variare da poche centinaia di bytes a qualche megabytes di codice. Questi sistemi operano in completa autonomia ma, in alcuni casi, sono connessi ad altri componenti con i quali comunicano formando un’unità logica più complessa. La tecnologia attuale ci ha infatti portato ad essere circondati da miriadi di dispositivi con funzioni sempre più avanzate. Tutto ciò lo dobbiamo allo sviluppo delle potenzialità dei sistemi embedded, in quanto il progresso tecnico ha permesso alle industrie di costruire dispositivi con un 1 INTRODUZIONE altissimo grado di “intelligenza” a costi assai ridotti. Una caratteristica peculiare di questi componenti (che potremmo chiamare anche appliances) è l’alta disponibilità. In altri termini l’alta disponibilità si traduce nella garanzia di funzionamento di un sistema in ogni condizione. La necessità di strumenti efficienti dedicati alla progettazione ingegneristica per applicazioni embedded si sta rivelando sempre più critica via via che le industrie sono spinte a fornire prodotti a basso costo e di elevata qualità, in tempi sempre più brevi. In tale situazione, reperire strumenti che accelerino i cicli di progettazione, supportino le soluzioni più innovative ed evitino errori riveste un’importanza determinante. Costruire embedded systems a livelli garantiti di funzionalità e di qualità è una grande sfida tecnologica. La progettazione centrata sul sistema di componenti embedded deve essere fondata su metodi e standard rigorosi, in grado di far fronte a livelli crescenti di complessità, con un compromesso accettabile tra costo e qualità e con una elevata interoperabilità tra componenti eterogenei. Le embedded technologies offrono rilevanti vantaggi a coloro che sviluppano sistemi e servizi, generando valore aggiunto, migliorando la competitività e creando nuovi mercati. Sono il settore con il più alto tasso di crescita nel mondo delle tecnologie della informazione e della comunicazione. A ciò si aggiunga che gli embedded systems stanno diventando reti di calcolo che debbono essere indifferenti rispetto al dominio della 2 INTRODUZIONE applicazione e alla localizzazione della rete. E' come se dovessimo definire e standardizzare un protocollo di comunicazione universale che sia in grado di far parlare tra loro sistemi eterogenei: l’ambiente dell’auto, quello del telefono mobile, quello domestico; portando diversi oggetti comunicanti a formare una sola funzionalità. In tutti questi ambiti aspetto fondamentale è la sicurezza: il primo problema è quello della vulnerabilità dei singoli apparati, dotati di embedded system, ad attacchi distruttivi in ambienti di reti aperte. Il secondo, di natura diversa, ma non meno importante dal punto di vista economico, è quello della gestione dei diritti di contenuti digitali, anch’essa particolarmente difficile da proteggere in ambiente di rete aperto. Infatti lo studio e l'utilizzo degli strumenti usualmente a disposizione per rendere i nostri sistemi sempre più sicuri (antivirus, firewall, ids, nids, ecc.) si rivela spesso insufficiente, a causa di una ragione di fondo: la sicurezza ottimale è da considerarsi solo teorica e bisogna partire dal presupposto che qualsiasi software può contenere degli errori, delle vulnerabilità che possono essere sfruttate in molteplici maniere e circostanze. Nasce allora l'esigenza di un diverso modo di concepire la sicurezza informatica: non più come difesa del sistema informatico ma come difesa dell'informazione stessa, che dovrà essere dotata di etichette che la qualificano in base alla sua sensibilità e quindi limiti chi non possa 3 INTRODUZIONE accedere ad essa. Tale approccio è stato da tempo affrontato in letteratura e in particolare nel campo del controllo degli accessi. Il controllo degli accessi non va inteso, ovviamente come autenticazione, ma come verifica se l'utente o il processo può accedere a certe risorse o compiere certe operazioni. Soluzioni come il “Discretionary Access Control” dei sistemi Unix, ovvero l'usuale sistema di permessi e privilegi basato sull'utente, non risulta essere più adeguato per certe realtà. Per ovviare questo problema si sta diffondendo attraverso diversi progetti (come SELinux, Systrace, TrustedBSD) un controllo degli accessi di tipo mandatorio, dove l'accesso a risorse di sistema è legato alla sensibilità dell'informazione e alle operazioni che su di essa si compiono e chi e con quale ruolo le effettua. In questo contesto si inserisce questo lavoro di ricerca, in cui ho affrontato lo studio di SELinux: la scheda scelta per il mio progetto è un sistema Linux embedded “ready-to-use”, che ho modificato per poter installare questo modulo di sicurezza. SELinux, infatti, non è un semplice modulo del kernel, poiché per funzionare sfrutta un Framework detto LSM, disponibile dalla versione stabile del kernel 2.6.x. SELinux è stato implementato utilizzando alcuni modelli di controllo degli accessi mandatori ormai ritenuti indispensabili per garantire la sicurezza dei computer. Esso si appoggia su una architettura di sicurezza a livello teorico definita Flask. La potenza di SELinux consiste nel superare alcune limitazioni classiche di 4 INTRODUZIONE Unix/Linux quali la separazione fra il sempre non fidato utente e il sempre fidato amministratore. Consente infatti di suddividere l'accesso all'informazione non in base agli utenti ma in base ai ruoli che essi assumono, in tal modo si può limitare lo strapotere di root e definire una nuova figura, cioè l'amministratore di sicurezza. Il funzionamento di tale modulo può essere descritto facendo riferimento alle astrazioni di Soggetto, Oggetto, Azione, intendendo con il termine Soggetto l'elemento attivo, cioè il processo o l'utente sotto il quale il processo gira, mentre con il termine Oggetto si intende l'entità passiva sulla quale il Soggetto vuole compiere una certa Azione. I concetti principali con cui mi sono dovuto confrontare sono quelli di dominio di un soggetto, tipo di un oggetto, ruolo di un utente, diritto di accesso di una classe di oggetti. Ad ogni processo viene assegnata un' etichetta di sicurezza che consta di tre campi: un' identità, un ruolo ed un dominio. L' identità indica l'utente per il quale il processo è in esecuzione, il ruolo indica la funzione dell'utente nel sistema ed il dominio specifica i privilegi di accesso che il processo ha sugli oggetti aventi determinati tipi. Ad ogni oggetto corrispondente ad un determinato deposito di informazioni ed appartenente ad una data classe, è assegnata un' etichetta che indica il suo creatore, il suo ruolo e il suo tipo. Questo lavoro si basa sul fatto che SELinux consente di realizzare la sicurezza informatica, intesa come capacità di garantire riservatezza ed integrità di dati. 5 INTRODUZIONE La tesi si sviluppa in due parti principali: una prima parte che introduce i “sistemi embedded”, descrivendone le caratteristiche, gli ambiti e gli aspetti fondamentali, per poi passare alla definizione del concetto di sicurezza informatica e sicurezza nei sistemi informativi, cercando di evidenziare l'importanza di avere sistemi operativi sicuri. La seconda parte è, invece, dedicata alla situazione reale che ho creato, ovvero viene descritta la particolare piattaforma embedded su cui ho lavorato e vengono definite le caratteristiche dell'architettura di sicurezza detta Flask in cui SELinux è stato implementato. Al termine del mio lavoro, ho effettuato un'analisi critica di quanto è stato fatto in laboratorio, e qui illustro i risultati conseguiti, mettendo in evidenza le difficoltà e i limiti incontrati. 6 CAPITOLO I – I SISTEMI EMBEDDED Capitolo I I Sistemi Embedded. 1.1 Definizione. Definire sinteticamente cos'è un Sistema Embedded non è impresa facile. Questa difficoltà deriva soprattutto da una mancanza di contorni netti, sia come campo di applicazione che come struttura fisica. Sicuramente si può dire che un sistema embedded è un sistema di elaborazione arbitrariamente complesso contenuto entro un generico apparato, e da esso normalmente non separabile senza perdita di funzionalità; tale sistema di elaborazione risulta usualmente inaccessibile in modo diretto, ed è dedicato esclusivamente al controllo dell'apparato o del sistema che lo ospita. Il dispositivo, che sia indifferentemente preprogrammato o che sia (parzialmente) riprogrammabile, è specificamente dedicato ad un ristretto numero di funzioni: la maggior parte delle volte è pensato e realizzato per una sola. Si tratta di sistemi che forniscono intelligenza ad apparati dei quali sono 1 CAPITOLO I – I SISTEMI EMBEDDED una parte. Un importante requisito per la loro proliferazione è l’integrazione senza soluzione di continuità con l’ambiente in cui debbono operare, nel rispetto di vincoli di affidabilità, disponibilità, robustezza, consumo energetico, dimensione, peso e costo. Gli embedded systems debbono essere: reattivi, dovendo mettere la loro risposta e le loro prestazioni in relazione con l’ambiente in tempo reale; autonomi, in modo da eseguire le loro funzioni senza intervento umano, a volte per lunghi periodi di tempo; invisibili, ovvero il loro utilizzatore non dovrebbe pensare a loro come fossero un computer. Nella vita quotidiana, si possono ritrovare esempi di questi sistemi: un’automobile contiene un numero di chip superiore a quello di un tipico PC, ma, sicuramente, non viene vista come un PC, oppure un forno a microonde in cui un microcontroller preprogrammato (il codice in questione il nome di firmware) controlla i tempi di cottura e comanda l'attivazione del magnetron per l'emissione delle microonde, tiene sotto controllo il sensore termico ed eventualmente quello idrostatico, aziona l'eventuale motorino per la rotazione del piatto a velocità costante o la ventilazione, pilota il display ed accetta le impostazioni dell'utente tramite tasti e manopole, controlla che il sistema non si trovi in una condizione incongruente (sportello aperto durante il funzionamento, impostazione del timer a piatto vuoto...) 2 CAPITOLO I – I SISTEMI EMBEDDED Sintetizzando, sono sistemi embedded consumer la maggior parte degli elettrodomestici moderni, che abbiano una qualsiasi interfaccia (al limite, pulsanti e LED) ed un minimo di programmabilità da parte dell'utente e, soprattutto, che contengano qualche sorta di microprocessore o microcontroller. Ma non finisce qui: anche un PIM (Personal Information Manager) o un palmtop o un tablet PC sono definibili, se consideriamo una definizione a più ampio respiro, dispositivi embedded. Simili apparati consumer, tuttavia, rischiano di diventare "l'embedded" per eccellenza nell'immaginario collettivo. Infatti, la stragrande maggioranza della stampa non di settore, quando parla di "sistemi di sviluppo ed applicazioni embedded", si riferisce soprattutto ad agendine e smartphones, accomunandole così ai sistemi embedded che sono anche realtime e si basano su un principio fondamentale: small is beautiful. 1.2 Applicazioni. Il mercato embedded è, quindi, un mercato fortemente piramidale, alla cui base ci sono prodotti come questi, con un rapidissimo turnover e larghissimo target di utenza indistinta, radicato in modelli di consumo di massa. Tipicamente, questi ultimi sono prodotti acritici, nel senso che se smettono di funzionare, o presentano guasti che ne pregiudichino il 3 CAPITOLO I – I SISTEMI EMBEDDED corretto funzionamento, non esiste o è assai remota la possibilità di danni a cose o persone. Oltre alla sostanziale acriticità, ci sono anche altri motivi per cui questi sistemi hanno avuto un buono sviluppo: tendono a minimizzare i problemi classici relativi all'utilizzo di prodotti consumer, cioè richiedono poca memoria RAM, hanno alimentazione durevole, richiedono poche risorse I/O. Queste problematiche, infatti, sono già state incontrate a partire dai primi anni '80 sui primi elaboratori mainstream, massivamente affrontate ai giorni nostri con soluzioni hardware ad hoc, ed ampiamente trattate in letteratura, con abbondanza di assodate metodologie implementative. A facilitare il lavoro, esistono anche ambienti di sviluppo (cross development tools) in grado di "girare" su PC, con interfacce ben note ed efficienti generatori di codice, in grado di produrre codice binario (o anche Java, in qualche raro caso) adatto ai processori di simili macchine, già pronto per l'upload su tali dispositivi. Questo modo di procedere, a base di cross-compilatori, IDE, crossassemblers o linguaggi simbolici (ladder, grafcet, VHDL...), nonché simulatori, emulatori in-circuit ed altri apparati per il testing ed il debugging remoto, è un paradigma fondamentale nello sviluppo per qualsiasi sistema embedded, quale che sia il target destinato a contenere il "software". La varietà di questi sistemi di sviluppo è enorme e supera di gran lunga in complessità e diversificazione la varietà dei sistemi e linguaggi destinati ai 4 CAPITOLO I – I SISTEMI EMBEDDED PC. Si passa, senza soluzione di continuità, dal più basilare sviluppo con cross-assembly per MCU a 4 bit (le più vendute in assoluto fino a metà degli anni '90 del secolo scorso) a sistemi integrati per simulazione e programmazione di chip personalizzati (ASIC) contenenti quattro o più core a 32 bit, passando per ambienti di programmazione simbolica grafica. Sono apparati embedded anche i router, i gateway, i firewall, e moltissimi altri apparecchi (appliances) necessari in tutte le reti medio-grandi, come i convertitori di protocolli, i bridge e le varie altre black-boxes, più o meno programmabili, utilizzate per la connettività. Anche i sistemi di allarme civili ed industriali possono spesso "fregiarsi" della definizione di sistema embedded. Vale lo stesso per tutti i sistemi che accettano banconote, e, quindi, i distributori di carburante, biglietti, bibite, sigarette e quant'altro, così come i sistemi di controllo di parcheggi ed accessi, i sistemi antitaccheggio, bancomat, POS, etc. Questi apparati, tuttavia, presentano caratteristiche peculiari che li rendono in parte dissimili dai prodotti già esaminati: ad esempio, il blocco di un router non ridondato, o anche un semplice crollo di prestazioni per un bug software, può comportare un grave danno economico, limitatamente al caso di siti e-commerce molto frequentati. La criticità dipende ovviamente dall'applicazione. Lo stesso si applica anche ai sistemi di accettazione di moneta esemplificati: un distributore non funzionante comporta chiaramente un 5 CAPITOLO I – I SISTEMI EMBEDDED danno, sotto forma di mancato introito potenziale per il suo gestore. Questi sistemi, quindi, sono più critici dal punto di vista prestazionale ed economico rispetto ai già visti apparati consumer, e richiedono, di conseguenza, particolari accorgimenti progettuali ed implementativi. La classificazione sulla base della criticità dei sistemi e delle relative applicazioni, la più usata in letteratura, evidenzia una differenza tra le famiglie di apparati embedded, rendendole, di fatto, molto dissimili le une dalle altre. All'interno delle suddivisioni fatte sulla base della criticità, ci sono altri parametri caratteristici che devono essere presi in considerazione: in particolare, ho preferito classificare i sistemi valutando anche la loro complessità e robustezza; ho scelto questi aspetti perché sono legati a questo mio lavoro e sono risultati fondamentali nelle analisi critiche e nelle prove fatte. 1.3 Perché Linux in applicazioni embedded. Dopo aver introdotto il vasto mondo degli apparati embedded, in questo paragrafo voglio fare una panoramica generale sulle opportunità, i vantaggi ed i limiti di utilizzo del sistema operativo GNU/Linux per questo tipo di sistemi. Prenderò in considerazione sia gli aspetti più strettamente tecnici, sia quelli legati genericamente al modello di sviluppo software. 6 CAPITOLO I – I SISTEMI EMBEDDED Il nucleo (kernel) Linux è funzionalmente equivalente ad un kernel Unix, e per questo motivo è molto diverso, nonché molto più complesso, della maggior parte dei tradizionali sistemi operativi per applicazioni specifiche (con esigenze di realtime o meno), nati per CPU con limitate risorse di calcolo e sistemi con piccole quantità di memoria (sia RAM sia ROM). Una prima differenza rilevante riguarda il fatto che il kernel non è compilato insieme all'applicazione, ma vive in maniera indipendente. Il meccanismo di accesso ai servizi del kernel è costituito dalle chiamate di sistema (system call), implementate attraverso interruzioni software. Inoltre, il codice del kernel funziona sempre in modalità "supervisore": ha quindi accesso a tutte le risorse hardware della macchina, gestisce direttamente interruzioni ed eccezioni e si occupa di tutte le funzionalità di basso livello. Al contrario, il codice dell'applicazione viene eseguito in modalità utente, ed è quindi limitato per quanto riguarda l'accesso fisico al sistema, oltre a non poter direttamente gestire interruzioni ed eccezioni. Dal punto di vista della scalabilità, ovvero della portabilità su macchine di dimensioni minori, Linux ha lo svantaggio di richiedere sempre un filesystem per poter funzionare, anche se ciò non implica necessariamente il fatto che il sistema debba essere dotato di un dispositivo di memorizzazione di massa. È, infatti, possibile usare svariati supporti fisici per memorizzare un filesystem: RAM, ROM, memoria FLASH, il filesystem di un'altra macchina con cui si è collegati via rete, e così via. Una delle caratteristiche del kernel Linux, caratteristica fondamentale su cui si basa la sua versatilità, è quella di poter creare un kernel 7 CAPITOLO I – I SISTEMI EMBEDDED personalizzato per il sistema su cui esso deve essere utilizzato: in particolare, si ricorre ad un nucleo che garantisce il funzionamento del sistema nella sua configurazione hardware minima, che viene ampliato ed aggiornato tramite il meccanismo dei moduli ogni qualvolta si aggiunga una periferica. Questa operazione non necessita della ricompilazione del kernel, ed è una peculiarità del sistema operativo Linux: questa caratteristica permette di esportare il kernel su piattaforme con stesso microprocessore ma che si differenziano per le periferiche. I driver di periferica fanno parte del codice del kernel e sono accessibili come voci del filesystem. Lo sviluppo dei driver e, in generale, di porzioni/moduli del kernel è ampiamente facilitato rispetto ai sistemi tradizionali vista la disponibilità totale dei sorgenti del kernel e di un numero notevole di esempi funzionanti sul campo, nonché di un'ampia documentazione. Nel caso di utilizzo di macchine con MMU (Memory Management Unit), sempre più frequente per i costi via via più bassi del silicio, è possibile sfruttare funzioni molto avanzate sia dal punto di vista della protezione della memoria, e, quindi, dell'affidabilità del sistema, sia da quello delle funzionalità. Nonostante il kernel Linux non sia stato scritto pensando a hard realtime, ossia ad applicazioni in cui il mancato rispetto dei tempi di esecuzione specificati porti ad un malfunzionamento grave, è stato portato su un grande numero di architetture differenti ed anche su macchine non dotate di MMU. Per quanto riguarda gli applicativi, sono disponibili 8 CAPITOLO I – I SISTEMI EMBEDDED distribuzioni Linux libere che coprono diverse architetture e dispongono di pacchetti per svariate piattaforme comunemente impiegate su macchine per uso specifico (x86, ARM, PowerPC, SH ed altre). Inoltre, esistono progetti liberi scritti in linguaggio C, cioè il linguaggio in cui è scritto il kernel Linux, nati per dare origine a librerie particolarmente poco esigenti dal punto di vista delle risorse richieste. Dal punto di vista tecnico bisogna tenere presente, però, che Linux non è un sistema operativo che può essere utilizzato su sistemi con risorse limitatissime. L'aspetto più delicato è costituito dalla quantità di memoria a disposizione: di norma si prende come limite inferiore quello dei 2MByte di memoria RAM + 2MByte di memoria ROM (o FLASH), anche se il limite effettivo varia a seconda della piattaforma. La potenza di calcolo richiesta alla CPU dal kernel stesso non rappresenta, invece, un fattore particolarmente critico. Infine, Linux fornisce strumenti validi anche per ciò che concerne l'editing (ad esempio emacs), il controllo della configurazione dei moduli (cvs), la stesura della documentazione (texinfo, sgmtools), ed, in generale, tutto ciò che concerne lo sviluppo di software. Il modello di sviluppo da adottare quando si utilizza software libero deve essere diverso rispetto a quello che si impiega tradizionalmente. Salvo, infatti, che non ci si affidi ad una distribuzione commerciale o si stipuli un contratto di consulenza, il sistema GNU/Linux è distribuito senza garanzia di funzionamento. Questo viene spesso considerato da 9 CAPITOLO I – I SISTEMI EMBEDDED molti utenti, o meglio dai loro manager, come un difetto. In realtà, non si riesce ad afferrare che il software libero fornisce anche dei diritti e di conseguenza delle opportunità in più rispetto al software proprietario: la disponibilità del codice sorgente per la lettura, lo studio e le modifiche consente di correggere malfunzionamenti, effettuare personalizzazioni, nonché imparare dal codice e dagli errori altrui e in qualche caso contribuire attivamente allo sviluppo. 10 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER Capitolo II Termini e metodologia per la sicurezza dei computer. In questo capitolo, viene definita la terminologia usata in seguito così da chiarire a cosa mi riferisco con il termine di sicurezza della tecnologia informatica e sicurezza dei computer. Dopo aver introdotto alcuni aspetti correlati strettamente ai temi già citati, definisco perché non sia possibile, allo stato attuale, garantire sicurezza senza un sistema operativo dotato di talune caratteristiche fondamentali che evidenzierò nel corso di questa trattazione. Concludo descrivendo un modello teorico usato per fornire sicurezza in un sistema operativo. 2.1 La necessità della sicurezza. Il tema della sicurezza dei sistemi per il trattamento automatico dell'informazione (Information Technology system o sistemi IT) è oggi di grande interesse e attualità, non solo per gli addetti ai lavori, ma anche per gli utenti della crescente varietà di applicazioni, rese possibili dai 1 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER progressi tecnologici. Nel passato, invece, chi si occupava di sicurezza veniva visto con diffidenza e considerato una specie di menagramo che, per contrastare minacce spesso percepite come inesistenti o assai improbabili, proponeva misure di sicurezza che degradavano le prestazioni delle macchine e rendevano difficile la vita dell'utilizzatore del sistema IT, imponendogli di ricordare password, compilare chiavi o numeri segreti, seguire noiose procedure di gestione. Oggi tale epoca è finita e l'iniziale diffidenza ha lasciato il posto alla consapevolezza che le enormi potenzialità dei sistemi IT non potrebbero essere sfruttate appieno senza un approccio serio e sistematico alla soluzione dei problemi di sicurezza. 2.2 Terminologia nell'ambito della sicurezza. Il termine sicurezza, nel contesto della tecnologia informatica, [1] [2], sta ad indicare la capacità di salvaguardare riservatezza, integrità e disponibilità dell'informazione (elaborata su computer, memorizzata su supporti di varia natura o trasmessa lungo canali di comunicazione), contrastando efficacemente ogni minaccia sia di tipo accidentale, sia di tipo intenzionale. Più in dettaglio: salvaguardare la riservatezza dell'informazione significa ridurre a livelli accettabili il rischio che un'entità possa, volontariamente o involontariamente, accedere all'informazione stessa senza esserne 2 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER autorizzata; salvaguardare l'integrità dell'informazione significa ridurre a livelli accettabili il rischio che possano avvenire cancellazioni o modifiche di informazioni a seguito di interventi di entità non autorizzate o del verificarsi di fenomeni non controllabili (come il deteriorarsi dei supporti di memorizzazione, la degradazione dei dati trasmessi su canali rumorosi, i guasti degli apparati, etc.) e prevedere adeguate procedure di recupero delle informazioni (piani di backup, etc.); salvaguardare la disponibilità dell'informazione significa evitare che venga impedito alle entità autorizzate l'accesso alle informazioni a seguito di interventi di altre entità non autorizzate o del verificarsi di fenomeni non controllabili del tipo già citato sopra. 2.3 Definizione di sicurezza dei computer. I tre requisiti che devono essere salvaguardati, riservatezza, integrità e disponibilità, differiscono molto tra loro. Possiamo operare una divisione sulla base delle entità cui si riferiscono. Per comprendere meglio questo concetto, si può dire che questi tre attributi possono essere applicati tutti all'informazione, mentre, per esempio, solo la disponibilità può essere correlata anche alle risorse. La divisione dei tre requisiti in due classi di proprietà distinte non è solamente formale; esiste, infatti, una grande differenza tra le proprietà di 3 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER riservatezza ed integrità ed altre proprietà, fra le quali la disponibilità: le prime due possono essere definite precisamente, in un modo globale e persistente tale che sia possibile stabilire, senza ombra di dubbio, se un dato sistema ne goda. In altre parole, utilizzando la terminologia propria della Teoria della Calcolabilità, la verifica delle proprietà di riservatezza ed integrità delle informazioni costituisce un problema calcolabile. Questa affermazione significa, essenzialmente, che è possibile specificare un algoritmo da utilizzare per automatizzare il modo di determinare il risultato di un problema decisionale. Il problema di determinare se i sistemi godano di altre proprietà, come, ad esempio, la disponibilità, l'affidabilità e la SAFETY, è, invece, fondamentalmente un problema non calcolabile. Per quanto detto finora, la riservatezza e l'integrità sono le due componenti della sicurezza della tecnologia dell'informazione che possiamo considerare come costituenti la sicurezza dei computer. Quindi, la sicurezza dei computer è un sottoinsieme della sicurezza della tecnologia dell'informazione, che riguarda la salvaguardia delle informazioni in un computer rispetto alle minacce relative alla confidenzialità e all'integrità, ma non rispetto a quelle relative alla disponibilità. Le basi per fornire i requisiti di confidenzialità e integrità e, quindi, per quanto sopra, della sicurezza dei computer possono essere garantite solo mediante un adeguato meccanismo di controllo degli accessi [3]. Userò qui e in seguito il termine controllo degli accessi per indicare la capacità di garantire che una entità possa espletare le sole operazioni di 4 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER propria competenza e che sia possibile controllare il flusso delle informazioni all'interno del sistema informativo. Il problema della sicurezza acquista sempre maggiore complessità via via che si presenta la necessità di precisarne i vari aspetti. Infatti, non è facile capire, nelle varie situazioni che si presentano, che cosa significa salvaguardare riservatezza, integrità e disponibilità dell'informazione e capire quali minacce devono essere contrastate con opportune contromisure. Inoltre, in molti casi, è ancora più difficile capire a quale livello è opportuno ridurre i rischi suddetti, tenuto conto che in pratica questi non possono mai essere ridotti a zero e che è quasi sempre necessario accettare un compromesso tra costi e prestazioni. Un sistema di elaborazione può considerarsi sicuro solamente rispetto alla sua capacità di soddisfare, nel contesto in cui opera, una serie di specifiche di sicurezza. Tali specifiche sono costruite a partire da una politica di sicurezza di sistema e dall'analisi delle minacce al sistema nel suo ambiente operativo. Una politica di sicurezza intesa come un insieme di leggi, regole e pratiche che precisano come l'informazione e le altre risorse sono gestite, protette e distribuite all'interno del sistema, deve essere abbastanza generale affinché sia utile: tipicamente, non specifica meramente i nomi di chi può o non può avere accesso a certe informazioni, definisce, invece, quali gruppi di utenti abbiano l'autorità per ottenere l'accesso alle risorse ed alle informazioni precedentemente stabilite. Per quanto concerne le minacce, spesso ed erroneamente, è diffusa la 5 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER percezione che il potenziale rischio per il sistema informatico sia rappresentato unicamente dal mondo dei “cracker”, cioè coloro che intenzionalmente e fraudolentemente visionano, modificano o rubano dati ed informazioni [4]. Tale realtà, pur esistente, non è l'unica minaccia da contenere per il sistema informativo. Possono costituire un potenziale rischio per il sistema informativo: un utente “esterno” che consapevolmente tenta di avere accesso ad un sistema e il conseguente arrogarsi di diritti che non gli spettano; un utente “interno” che fraudolentemente mina la sicurezza dei computer; un utente “interno” che erroneamente mette in atto azioni che possono compromettere le risorse e le informazioni del sistema informativo. Il concetto di sicurezza dei computer, non può più essere inteso come una qualità del sistema informativo hardware o software che contiene le risorse di interesse, ma deve in qualche modo essere collegato all'informazione stessa. In tal senso, diventa evidente l'esigenza di definire un qualche metodo che consenta di descrivere la sensibilità dell'informazione che viene manipolata. 6 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER 2.4 Sicurezza dei sistemi operativi. Allo stato attuale, gli sforzi per fornire sistemi sicuri sono destinati all'insuccesso [5]. La spiegazione risiede nell'assunzione erronea che sia possibile fornire un adeguato livello di sicurezza con gli attuali e più diffusi sistemi operativi, mettendo in atto azioni che però risultano essere essenzialmente difensive. La sicurezza del sistema operativo è fondamentale per la sicurezza di ogni elaboratore perché i sistemi operativi sono un punto critico per il fallimento dell'intero sistema [5]. Sfortunatamente, i tentativi per rendere sicuri i sistemi di elaborazione continuano ad essere basati sull'assunzione errata che la sicurezza adeguata può essere fornita nelle applicazioni con i meccanismi di sicurezza esistenti sui sistemi operativi a più larga diffusione. La realtà è che le applicazioni sicure richiedono sistemi operativi sicuri e, ogni sforzo per fornire un sistema sicuro che ignora questa premessa, è destinato a fallire. I sistemi operativi a larga diffusione, infatti, mancano di alcune caratteristiche architetturali e funzionali ritenute fondamentali già da circa un ventennio ma non implementate se non su sistemi ritenuti sensibili a livello governativo o militare [2]. Oggi, con l'informatizzazione di massa, l'interconnessione su scala mondiale e la possibilità di condividere istantaneamente qualsiasi tipo di risorsa, aumentano esponenzialmente i rischi cui i nostri sistemi informativi sono sottoposti. 7 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER Un esempio, in tal senso, è quello degli O-days; dal momento in cui viene scoperta una vulnerabilità di un qualsiasi sistema, inizia una “corsa” fra coloro che devono creare la patch per il sistema, in genere il venditore, ed il potenziale attaccante. Inutile dire che il tempo gioca a favore del secondo, non dovendo testare la compatibilità del nuovo codice con il precedente ma solo il suo effetto. Un amministratore di sistemi nel periodo che intercorre fra la scoperta della vulnerabilità ed il rilascio della patch, che in gergo si dice appunto O-days (“oh days”), può fare ben poco contro un eventuale attacco, a parte monitorare assiduamente i file di log in quanto non ha alcuno strumento a livello di sistema operativo che gli consenta di limitare l'azione del software stesso, se non rimuovendolo. Quindi, mettere in atto azioni volte a tutelare la sicurezza senza il supporto di sistemi operativi dotati di particolari caratteristiche equivale a “..costruire una fortezza sulla sabbia..” [5]. 2.5 Reference Monitor. La tendenza attuale, quando si parla di sicurezza di computer, è la costruzione di una parte del sistema capace di rendere sicuro tutto il sistema stesso. La teoria della sicurezza di computer si basa sul controllo degli accessi ma si realizza nella costruzione di un componente logico che viene definito reference monitor. Il reference monitor, è un'astrazione che consente ad entità dette soggetti di fare riferimento ad entità passive dette oggetti in base ad un insieme di 8 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER autorizzazioni di accesso [6]. Il reference monitor fa riferimento ad un database di autorizzazioni e comunica con un sistema di auditing che memorizza quali accessi agli oggetti siano stati permessi e/o negati ai soggetti, in funzione delle autorizzazioni correnti (Figura 1). Figura 1. Reference monitor: il reference monitor comunica con il database delle autorizzazioni ed il sistema di audit. Un reference monitor supporta due classi di funzioni [7]: funzioni di riferimento, le quali servono a prendere delle decisioni riguardanti il permesso o il diniego di accesso alle informazioni, in base alle regole memorizzate nel database delle autorizzazioni; funzioni di autorizzazione, che servono a controllare i cambiamenti alle singole regole di controllo degli accessi presenti nel database delle autorizzazioni. Tale database non fa 9 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER logicamente parte del reference monitor, ma tutte le modifiche al suo contenuto avvengono per mezzo delle funzioni di autorizzazione. Un'implementazione completa del concetto di reference monitor in un sistema reale prende il nome di security kernel. Essa deve soddisfare una serie di requisiti, ai quali ci si riferisce con i termini di completezza, isolamento e verificabilità. La prima proprietà prevede che il reference monitor sia invocato, senza eccezioni, per ogni riferimento di un soggetto inteso come entità attiva ad un oggetto inteso come entità passiva; la seconda che il reference monitor e il database delle autorizzazioni siano protetti da alterazioni non autorizzate; l'ultima, che il reference monitor sia piccolo, ben strutturato e semplice, così che possa essere completamente analizzato e testato e sia possibile verificare in maniera formale il suo corretto funzionamento. Risulta evidente che i soggetti saranno le entità attive del sistema che operano sulle informazioni, mentre gli oggetti conterranno le informazioni, saranno dunque file, record, segmenti di memoria e tutti gli altri depositi di informazioni del sistema. Il database delle autorizzazioni specificherà, invece, le circostanze sotto le quali un soggetto può ottenere l'accesso ad un determinato oggetto. 10 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER 2.6 Trusted Computing Based. Si è detto che l'implementazione di un reference monitor all'interno di un sistema è chiamata security kernel. In realtà, quando si pensa ad un sottosistema che funga da reference monitor, spesso si è interessati ad agire nel sistema operativo già esistente, apportando modifiche e miglioramenti: si parla in questi casi di sistemi potenziati dal punto di vista della sicurezza, o sistemi security enhanced. Il sistema SELinux (Security Enhanced Linux), che descriverò, rientra esattamente in questa tipologia. La maggior parte dei sistemi operativi può essere modificata per implementare una porzione più o meno grande del concetto di reference monitor e migliorare da un punto di vista della sicurezza, ad un costo relativamente basso: andare oltre e modificare il sistema per incorporare un reale security kernel è, in generale, tanto costoso quanto implementare il sistema da zero. Esistono, quindi, sostanzialmente, due tipi di approcci che possono essere adottati nel rendere sicuri i sistemi: il semplice potenziamento del sistema e l'incorporamento di un security kernel. Indipendentemente dall'approccio utilizzato, si chiama Trusted Computing Based (TCB) il generico sottoinsieme del sistema operativo che implementa tutti i meccanismi responsabili della realizzazione della politica di sicurezza [2]. Il TCB è una parte relativamente piccola del sistema, che fornisce la totalità dei meccanismi di protezione; ogni software che non faccia parte del TCB può essere software malizioso, 11 CAPITOLO II – TERMINI E METODOLOGIA PER LA SICUREZZA DEI COMPUTER senza per questo rendere il sistema insicuro. L'ampiezza del TCB è variabile da sistema a sistema: spazia da un piccolo numero di potenziamenti del sistema operativo ad un vero e proprio security kernel, fino anche a coincidere con la totalità del sistema stesso. Ovviamente, al crescere della quantità di codice che costituisce il TCB diventa sempre più difficile essere sicuri che esso realizzi i requisiti del reference monitor in ogni circostanza. 12 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Capitolo III ACME Fox Board: linux in soli 66x72mm. La FOX Board è un sistema Linux embedded basato su una singola scheda, completo e pronto all'uso, sviluppata da ACME Systems. Si presenta con un sistema operativo già implementato, che contiene servizi, driver e una serie di utili applicazioni: tutto questo in soli 66x72 mm. In questo capitolo, voglio illustrarne le caratteristiche tecniche e implementative, dedicando una parte anche all'ambiente di sviluppo e al cross-compiler, che consentono di ricompilare tutto il software di sistema ed integrarlo con le nuove applicazioni sviluppate. 3.1 La scheda. La FOX Board è un sistema Linux embedded di ridotte dimensioni e bassi consumi pensato e realizzato per l'integrazione immediata di circuiti elettronici con applicazioni e protocolli informatici. 1 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Figura 3.1. Una foto della Fox Board. La contemporanea presenza di connettori standard (Ethernet e USB) e spazi di alloggiamento per connettori passo 2.54 mm, per il collegamento alle periferiche interne o come linee di ingresso e uscita digitale, consente una grande facilità di interfacciamento con l'hardware specifico della propria applicazione, mentre la presenza a bordo del sistema operativo Linux, completo delle maggiori applicazioni e servizi di rete, semplifica l'interfacciamento e la connettività della scheda con il WEB ed i sistemi informativi, permettendo la facile realizzazione di dispositivi telecontrollabili via rete. La scheda permette una forte riduzione del "time to market" rivolgendosi equamente a due settori dello sviluppo applicativo: quello elettronico, che necessita di una scheda a microprocessore pronta e capace di 2 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM interfacciamento con i sistemi informativi odierni e quello informatico, che ha la necessità di interfacciare direttamente dispositivi fisici senza dover ricorrere ad un PC pur mantenendo la facilità di sviluppo e la grande disponibilità di strumenti software tipici di un sistema Linux completo [brochure]. Il campo applicativo della scheda si estende, dunque, dal più semplice dispositivo di rete autonomo per applicazioni quali micro web server, proxy server, router, etc., al più complesso modulo socket da innestare su una scheda madre per progetti particolari, sfruttando tutta la potenza di Linux e le capacità di networking su rete TCP/IP. 3.2 Caratteristiche tecniche. La Fox Board si presenta con un vero sistema operativo Linux (non uC Linux), su un microprocessore ETRAX 100LX, con architettura RISC a 32 bit e una frequenza di clock di 100Mhz (100MIPS), fabbricato da Axis (http://developer.axis.com). Comprende 16Mbyte di memoria RAM e 4MByte di memoria FLASH. È correlata, inoltre, di una porta Ethernet 10/100Mb, di due porte USB host 1.1, di una porta seriale TTL a 3.3 volt, che permette il controllo da un PC attraverso un terminale, e di due connettori di espansione con segnali per interfacce IDE, SCSI, porte di I/O, I2C bus, porte seriali e parallele. Ai lati della scheda Fox è possibile montare due strip da 20x2 pin passo 2,54mm per poterla utilizzare su una scheda madre o portare i segnali su un'altra scheda tramite cavo flat a 40 pin. 3 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Figura 3.2. Fox Board pinout. Figura 3.3. Socket da 20x2 pin, passo 2.54mm. 4 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Più specificatamente, le due interfacce USB possono essere connesse a memorie USB, hard disk, webcam, modem, dispositivi Wi-Fi e Bluetooth, convertitori USB/seriali, etc. Attraverso l'interfaccia ethernet, invece, è possibile accedere al web server, al server FTP, già attivi on board, ed è possibile accedere direttamente alla scheda tramite SSH e Telnet. Nella figura 3.3, sono mostrati gli strip da 20x2 pin, attraverso i quali può essere connessa un'ampia gamma di periferiche; la scheda supporta, infatti, fino a 2 porte parallele, 4 porta IDE, 2 porte SCSI, 62 linee di I/O e 3 porte seriali. Una caratteristica interessante è rappresentata dal dispositivo di alimentazione, che si presenta come una piccola scheda progettata per fornire una corrente continua di +5 volt necessaria per il corretto funzionamento della Fox e delle periferiche collegate. Figura 3.4. The Fox Power Supply. 5 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Figura 3.5. The Fox Power Supply montato sulla scheda. Ho detto che è una caratteristica interessante per due semplici motivi: il primo è che l'FPS (Fox Power Supply) accetta in entrata sia una corrente continua sia una corrente alternata senza una prestabilita polarità del cavo di input, in quanto ha al suo interno un diodo che serve a correggere il voltaggio, per evitare che si creino problemi dovuti ad un errato cablaggio. Il secondo motivo è che questo FPS viene fornito insieme ad un cavo di alimentazione uguale a quello che viene usato per alimentare i floppy drive all'interno dei computer: la scheda, cioè, può essere collegata direttamente all'alimentazione del computer senza utilizzare l'FPS appena descritto. Sul sito della ACME SYSTEMS (http://www.acmesystems.it) si può anche acquistare un simpatico case a forma di pinguino (Figura 3.6), che può ospitare al suo interno la Fox Board e le altre schede e che rappresenta fedelmente Tux, la mascotte del kernel Linux. 6 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Figura 3.6 Tux Case. Figura 3.7 Tux Case e Fox Board all'interno. 3.3 Uno sguardo al software. L' ambiente di sviluppo o SDK (Software Development Kit) è una collezione di programmi, utility e librerie fornite da Axis (http://developer.axis.com), per sviluppare le applicazioni, compilare e installare i driver, ricompilare il kernel, generare l'immagine di boot e le nuove applicazioni che verranno installate nella memoria FLASH della scheda. L'SDK è disponibile soltanto per sistemi operativi Linux, ma gli utenti 7 CAPITOLO III – ACME FOX BOARD: LINUX IN SOLI 66X72MM Windows potranno utilizzare l'on demand Web Compiler. Si tratta di una interfaccia web ad un compilatore C messo a disposizione sul server di ACME Systems, che è in grado di compilare via web un codice sorgente C e di restituire il codice eseguibile da copiare nella Fox Board. In alternativa, è possibile installare l'ambiente di sviluppo su una macchina virtuale, tramite programmi appositi, quale può essere VMWare. L'SDK permette di selezionare quale kernel usare (2.4 o 2.6), quale libreria C (le librerie standard glibc o le librerie per sistemi embedded uClibc), quali programmi includere e molto di più. È organizzato in maniera modulare e questo lo rende adattabile per varie configurazioni. Il codice sorgente è organizzato in directory dal nome descrittivo, cioè esplicativo per il loro contenuto: apps per le applicazioni, libs per le librerie ed OS per il kernel di Linux. A loro volta queste directory sono suddivise in subdirectory che contengono i codici sorgenti delle applicazioni o librerie che possono essere compilate. Per compilare gli applicativi o le librerie si può ricorrere all'uso del Makefile presente tra il codice sorgente; questo o è un file preconfigurato o viene generato tramite uno script di configurazione, fornito nell'ambiente di sviluppo. Quest'ultimo caso si verifica, per esempio, per la compilazione del kernel o per quegli applicativi e librerie che richiedono la conoscenza della configurazione del sistema. Insieme all'ambiente di sviluppo viene fornito il cross-compiler, che è un compilatore che gira sul PC e genera codice per un'altra architettura. In questo caso, l'eseguibile generato è per un sistema RISC, su cui si basa il processore della Fox Board. 8 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Capitolo IV Architettura di sicurezza di SELinux e suo funzionamento. Descrivo in questo capitolo un'architettura di sicurezza chiamata Flask e la sua implementazione in un sistema operativo Linux. Questa permette di realizzare diverse politiche di sicurezza rimanendo neutra rispetto ad esse. Il prodotto risultante prende il nome di Security Enhanced Linux, conosciuto più brevemente come SELinux. 4.1 Architettura Flask . 4.1.1 Un po' di storia: evoluzione dell'architettura di sicurezza Flask. Nel 1992 e 1993, i ricercatori della National Security Agency americana (NSA) e quelli della Secure Computing Corporation (SCC) lavorarono al progetto e all'implementazione di Distributed Trusted Mach (DTMach), un sistema che si basa sui progetti TMach e LOCK. Il progetto DTMach continuò nel progetto Distributed Trusted Operation System (DTOS). Al completamento di DTOS, gli enti governativi NSA, SCC ed il gruppo di 1 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO ricerca Flux dell'Università dello Utah si unirono per trasferire l'architettura di sicurezza di DTOS nel sistema operativo Fluke [8]. Durante la sua integrazione in Fluke, l'architettura venne potenziata e prese il nome di Flask. 4.1.2 Componenti e funzionamento dell'architettura di sicurezza Flask. Flask è un'architettura che viene utilizzata per fornire un supporto flessibile a politiche di controllo di tipo mandatorio. In un sistema con MAC (Mandatory Access Control), ad ogni soggetto ed oggetto è assegnata un'etichetta di sicurezza. Tutti gli accessi da un soggetto ad un oggetto sono autorizzati tramite le politiche basate su tali etichette. In particolare, l'architettura Flask separa in maniera netta la definizione della logica di sicurezza dal meccanismo di protezione [9]. La logica della protezione di sicurezza è incapsulata in una componente separata del sistema operativo, dotata di una interfaccia ben definita per effettuare le decisioni di sicurezza in base alla politica messa in atto. Tale componente si dice security server, e questo è un server che appartiene allo spazio utente ( più propriamente detto user-space) in esecuzione su un microkernel. Le componenti del sistema che realizzano le politiche di sicurezza sono dette, nell'architettura Flask, object manager. Gli object manager corrispondono a tutti i sottosistemi del kernel che svolgono particolari funzioni quali, per esempio, il gestore dei processi, il gestore del 2 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO filesystem, il gestore dei socket. Tali sottosistemi devono essere modificati affinché, interrogando il security server, possano ottenere le decisioni della politica di sicurezza implementata e applicare tali decisioni alle etichette degli oggetti e, quindi, agli oggetti stessi, come illustrato nella figura 4.1. Figura 4.1. Architettura Flask: gli object manager sono i componenti responsabili dell'imposizione (enforcement) della politica di sicurezza; il secutiry server è il componente responsabile delle decisione di sicurezza. L'architettura Flask fornisce due tipi di decisioni: le più importanti sono le decisioni di sicurezza, di tipo booleano (si/no), che specificano se l'accesso richiesto da un'entità etichettata (un soggetto o un oggetto) verso un'altra entità etichettata (un soggetto o un oggetto) può essere permesso. La decisione viene presa interamente sulla base delle etichette applicate. Per ogni decisione presa dal server viene analizzato l'insieme di regole incapsulato nella logica del security server stesso: questo garantisce il controllo sulla propagazione dei diritti di accesso. 3 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Utilizzando la terminologia relativa al concetto di reference monitor, le decisioni corrispondono alle funzioni di riferimento ed il security server ingloba al suo interno il database delle autorizzazioni. Queste decisioni vengono mandate dal server secondo l'insieme di attributi forniti per la politica di sicurezza che compongono le etichette di soggetti ed oggetti. Di fondamentale importanza è mantenere l'associazione tra oggetti ed etichette. La soluzione più semplice sarebbe quella di definire un singolo tipo di dato, indipendente dalla politica, che sia parte degli attributi associati a ciascun oggetto. Tuttavia nessun tipo di dato singolo è adeguato a tutti i differenti modi in cui le etichette sono usate nel sistema. Inoltre, dal punto di vista dell'amministrazione del sistema, la migliore rappresentazione di un'etichetta è sicuramente una stringa altamente descrittiva, mentre dal punto di vista implementativo, per motivi di prestazioni nella ricerca delle regole per fornire le decisioni, è sicuramente conveniente utilizzare etichette che possano essere tradotte velocemente in chiavi di ricerca all'interno di strutture dati efficienti. Flask, per ovviare a queste esigenze contrastanti, utilizza per le operazioni di etichettatura due tipi di dato, indipendenti dalla politica: un security context e un security identifier (SID). Il primo è una stringa di lunghezza variabile che può essere interpretata da un'applicazione (o da un utente) che abbia una conoscenza della logica del security server. Un security context può consistere di differenti attributi per la sicurezza del sistema (un'identità utente, un livello MLS, un ruolo, un dominio TE, o una 4 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO qualsiasi combinazione di concetti che possa essere utilizzata per realizzare un'etichetta), ma ciò non è obbligato dall'architettura, piuttosto compete al modello della politica di sicurezza adottato dall'implementazione. Il security identifier, invece, è un valore di dimensione fissa (in genere, un intero positivo) che può essere interpretato solamente dal security server. In particolare, il security server associa in maniera biunivoca i security context ai security identifier, per cui un SID può essere visto semplicemente come un dettaglio implementativo di Flask. Gli object manager dell'architettura Flask sono responsabili dell'associazione delle etichette di sicurezza agli oggetti che essi gestiscono; risulta così che i SID sono associati agli oggetti attivi del kernel. Scendendo nel particolare, un esempio di questa politica è rappresentato dal fatto che l'object manager del filesystem mantiene un legame persistente tra file e contesti di sicurezza. Gli object manager gestiscono i SID e i security context secondo la modalità nota come black-box; questo fa si che un cambiamento nel formato o nel contenuto delle etichette di sicurezza non richiede cambiamenti per gli object manager. Il secondo tipo di decisioni di cui è responsabile l'architettura è l'insieme delle decisioni di etichettatura, che specificano quale etichetta debba essere assegnata ad un oggetto (tipicamente alla sua creazione), in base alle etichette di soggetti ed oggetti correlati. Questo avviene in Flask in uno dei seguenti tre modi: 5 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO l'etichetta del nuovo oggetto può essere copiata dal soggetto che richiede la creazione o da qualche altro oggetto correlato (ad esempio la directory contenitrice nel caso in cui l'oggetto creato sia un file); l'etichetta del nuovo oggetto può essere specificata dal soggetto come parte della richiesta di creazione; è possibile consultare il security server per ottenere la decisione di etichettatura. In ogni caso, la politica di sicurezza viene sempre esaminata per assicurarsi che al soggetto sia permesso di creare un oggetto con l'etichetta scelta (Figura 4.2). Figura 4.2. Etichettatura di oggetti in Flask: un cliente richiede la creazione di un nuovo oggetto ad un object manager, fornnedo la propria etichetta (SID); l'object manager invia una richiesta per una nuova etichetta al security server inviando il SID del client, quello dell'oggetto correlato ed il tipo di oggetto da creare. Il security server consulta le proprie regole di etichettatura, determina il SID per il nuovo oggetto e lo restituisce, infine l'object manager associa il SID ricevuto all'oggetto. 6 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Gli object manager, inoltre, possono memorizzare le decisioni di sicurezza in un apposito modulo chiamato Access Vector Cache (AVC) per limitare il degrado di prestazioni del sistema dovuto al dispendio di tempo dovuto alla comunicazione con il security server. Si può avere un'unica AVC centralizzata o un'AVC per ogni object manager, a seconda dell'implementazione. Nell'implementazione più semplice, un object manager può effettuare una richiesta al security server ogni volta che sia necessaria una decisione legata alla sicurezza. Tuttavia, Flask mette a disposizione un dispositivo per la memorizzazione di decisioni di sicurezza precedentemente prese, allo scopo di ridurre il potenziale degrado delle prestazioni del sistema, dovuto alla comunicazione con il security server. Quindi, quando un object manager richiede una decisione di sicurezza, interroga, per prima, l'AVC fornendo le etichette dei soggetti e degli oggetti coinvolti e le modalità di acceso richieste; se le decisioni ricercate non vengono trovate nell'AVC, la richiesta viene inoltrata al security server (Figura 4.3). 7 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Figura 4.3. Richiesta e memorizzazione di decisioni di sicurezza in Flask: il client richiede la modifica di un oggetto esistente, l'object manager interroga l'AVC riguardo ad una regola relativa: se non esistono elementi appositi nell'AVC la richiesta viene inoltrata al security server, che consulta le proprie regole di accesso e determina se l'accesso sia consentito o meno, e ritorna le proprie decisioni all'AVC. Per aumentare le prestazioni, il security server può rispondere con decisioni per più modalità di accesso di quante siano state realmente richieste. Più precisamente, esso risponde con lo stato di un insieme di diritti, chiamato vettore di accesso. Un vettore di accesso è un insieme di regole per le modalità di accesso correlate ad una classe di oggetti; ogni classe di oggetti (ad esempio file, processi, socket, ...) ha un vettore di accesso associato, che specifica quali siano le modalità di accesso possibili agli oggetti appartenenti a quella classe. È responsabilità dell'implementazione dell'architettura definire quali siano le classi degli oggetti del sistema e quali i rispettivi vettori di accesso. Quando un soggetto richiede l'accesso ad un oggetto, in una qualche modalità, il security server risponde con un vettore d'accesso per quell'oggetto; il 8 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO vettore contiene la specificazione di quali modalità di accesso siano garantite al soggetto e quali gli siano negate. Tale risposta viene, in seguito, memorizzata all'interno dell'AVC per evitare future richieste relative a decisioni che siano già state fornite. L'AVC di Flask prevede un'interfaccia per gestire la cache quando cambia la politica di sicurezza. Una volta che l'AVC abbia ricevuto la notifica di un cambio di politica, esso aggiorna il suo stato ed invoca una chiamata a funzione, registrata dagli object manager per aggiornare qualsiasi permesso precedentemente registrato. Dopo l'aggiornamento dello stato degli object manager e dello stato dell'AVC, quest'ultimo notifica al security server che la transizione ad una nuova politica è stata completata. L'architettura specifica i requisiti riguardo agli object manager e al security server, assumendo che il sistema sottostante fornisca la separazione fra questi componenti e fornisca anche i meccanismi attraverso i quali essi possano comunicare. Flask fornisce, per ogni object manager, delle interfacce per ottenere le decisioni relative alla sicurezza e alla etichettatura dal security server. L'implementazione, quindi, non è specificata L'interfaccia per ottenere una security label è la seguente: 9 dall'architettura. CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO int security_transition_sid ( security_id_t ssid, security_id_t tsid, security_class_t tclass, security_id_t *out_sid); ret = security_transition_sid ( current > sid, dir > i_sid, SECCLASS_FILE, &sid), Figura 4.4. Interfaccia ed esempio di chiamata per ottenere una security label. I parametri di input sono il soggetto SID, il SID di un oggetto inerente (per esempio la directory padre), e la classe di un nuovo oggetto. Il SID per il nuovo oggetto è restituito come un parametro di output. L'interfaccia, invece, per ottenere una decisione di accesso ha la struttura seguente: int security_compute_av ( security_id_t ssid, security_id_t tsid, security_class_t tclass, access_vector_t requested, access_vector_t *allowed, access_vector_t *decided, __u32 *seqno); Figura 4.5.Interfaccia per ottenere le decisioni di accesso dal security server. I parametri di input sono: una coppia di SID, la classe dell'oggetto e l'insieme dei permessi richiesti. La coppia di SID può essere soggetto a oggetto, soggetto a soggetto, o perfino oggetto a oggetto. I permessi concessi sono ritornati come parametri di output. 10 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO extern inline int avc_has_perm_ref( security_id_t ssid, security_id_t tsid, security_class_t tclass, access_vector_t requested, avc_entry_ref_t *aeref); ret = avc_has_perm_ref( current>sid, sk>sid, sk>sclass, SOCKET__BIND, &sk>avcr); Figura 4.6. Interfaccia AVC e chiamata di esempio percontrollare i permessi. I parametri di input sono gli stessi del security_compute_av, tranne per il parametro addizionale aeref. Al suo primo uso, il parametro aeref è settato per assegnare le entrate AVC usate per il controllo dei permessi, e nei successivi controlli questo riferimento è usato per ottimizzare il “lookup”. Il riferimento è riconvalidato ad ogni suo uso per assicurarne la correttezza. Il security server si occupa, quindi, di fornire le decisioni di sicurezza, di mantenere l'associazione tra security context e SID, di fornire SID per gli oggetti creati (decisioni di etichettatura) e di gestire le AVC degli object manager. Una caratteristica importante dell'architettura Flask è che il security server può essere modificato, addirittura sostituito e, a patto che l'interfaccia resti la stessa, non sono necessarie corrispondenti modifiche a nessuno degli altri componenti dell'architettura. 11 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO 4.2 SELinux. Negli ultimi anni la NSA ha implementato Flask nel sistema operativo Linux, ed il prodotto finale ha preso il nome di Security Enhanced Linux, o, più brevemente, SELinux. Altri contributi al progetto sono derivati da NAI Labs, SCC e MITRE. SELinux è scaricabile all'indirizzo web http://www.nsa.gov/selinux, insieme a tutta la documentazione ufficiale disponibile, al compilatore della politica di sicurezza e ad una sua configurazione di esempio. Linux è stato scelto come base del progetto grazie al suo crescente successo e grazie al fatto che fornisce un ambiente di sviluppo aperto, adatto a dimostrare la fattibilità dell'integrazione di architetture di sicurezza in sistemi operativi esistenti ed ampiamente utilizzati. Oggi, diverse distribuzioni, come Fedora, Gentoo o Debian, sono già abilitate a supportare SELinux, intendendo con questo che forniscono i pacchetti precompilati per utilizzare questo sistema di sicurezza all'interno della propria ditribuzione. È opportuno precisare che le idee generali dell'architettura Flask possono essere estese anche ad altri sistemi operativi “open source”, si veda in tal senso TrustedBSD [http://www.trustedbsd.org]. L'implementazione dell'architettura di sicurezza Flask incorporata da SELinux nel sistema operativo Linux corrisponde ad un TCB. Questo sistema utilizza due tipi di controlli degli accessi, uno MAC (Mandatory Access Control) ed uno DAC (Discretionary Access Control). 12 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO I controlli MAC possono essere configurati dall'amministratore di sicurezza, mentre i controlli DAC sono tipici dei sistemi Unix e subentrano solamente qualora vengano superati con successo i primi. È fondamentale definire i concetti relativi alle etichette di sicurezza utilizzate. Si è già detto che in Flask un'etichetta di sicurezza è chiamata security context; nel seguito userò indistintamente i termini etichetta, etichetta di sicurezza e security context. Come in tutti i modelli di controllo degli accessi mandatori, in SELinux viene assegnata un'etichetta ad ogni entità del sistema, sia essa un soggetto o un oggetto. Il security server di SELinux adotta un modello di controllo degli accessi che si basa, contemporaneamente, su tre differenti sottomodelli di controllo: type enforcement (TE), role based access control (RBAC), e user identity (UI). Di conseguenza, il formato delle etichette specifica tre attributi: un tipo, che rappresenta, utilizzando la terminologia propria del TE, il dominio per un soggetto o il tipo per un oggetto; un ruolo, che indica una funzione che un soggetto può svolgere nel sistema; un'identità selinux (differente da quella di linux), che indica per i soggetti l'identità dell'utente di cui sono surrogati e per gli oggetti quella del loro creatore. Gli insiemi di identità selinux, ruoli e tipi verranno rappresentati, rispettivamente, con U, R e T. In base a questi tre attributi, il meccanismo di autorizzazione per l'accesso ad un oggetto è quello tipico di un reference monitor che realizzi una politica mandatoria: ogni qualvolta un soggetto voglia accedere ad un 13 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO oggetto, si verifica che la modalità richiesta sia autorizzata dalla politica sulla base dei valori dei security context delle entità coinvolte. 4.3 Classi di oggetti e modalità di accesso. Prima di descrivere il sottomodello TE, è necessario precisare il concetto di classe di oggetti sul quale si basa l'attribuzione dei diritti di accesso in SELinux. Il significato intuitivo di diritto o modalità di accesso o permesso è che esso sia una sorta di titolo che garantisca la possibilità di eseguire una qualche operazione ad un certo livello di astrazione. La maggior parte dei sistemi si basa su una serie di astrazioni, come quelle di processo o di filesystem, ognuna delle quali ha un proprio insieme distinto di operazioni, corrispondenti ai servizi forniti dai meccanismi che realizzano l'astrazione. È, quindi, logico definire per ogni astrazione un insieme distinto di modalità di accesso o permessi: le modalità di accesso definite dalle operazioni sul filesystem, quali quelle di lettura, scrittura ed esecuzione, sono generalmente considerate universali e, quindi, applicate a differenti astrazioni. In realtà quanto descritto non è un modo di procedere corretto, perché le modalità di accesso agli oggetti dovrebbero essere definite solamente quando lo sono le corrispondenti operazioni e le operazioni disponibili dipendono strettamente dai servizi offerti, e, dunque, variano da astrazione ad astrazione: a meno di sovraccaricarne il significato, non ha senso considerare le modalità di accesso in lettura e scrittura per un processo. 14 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Infatti, le due astrazioni di processo e file sono molto differenti. Si rende, dunque, necessario partizionare gli oggetti del sistema in classi, per le quali siano definiti insiemi, solitamente distinti, di permessi. L' appartenenza di un oggetto ad una data classe definisce quali sono le operazioni effettuabili sull'oggetto e quali sono i diritti di accesso che possono essere garantiti o negati sull'oggetto in questione. Le modalità di accesso in SELinux sono definite rispetto a classi di oggetti. Esistono classi, gestite da differenti sottosistemi, relative ai processi, ai file, al networking, etc. Modalità di accesso di differenti classi di oggetti possono avere lo stesso nome; si pensi ai diritti definibili sulle classi file e directory: molti di questi, fra i quali quello di lettura e scrittura, saranno gli stessi per entrambe le classi e saranno definiti in entrambe con lo stesso nome. Il diritto di attraversare una directory sarà, però, definito solo sulla classe directory, così come quello di esecuzione solo sulla classe file. Ad ogni modo, non si verificano mai conflitti di nomi, visto che le modalità di accesso vengono sempre specificate rispetto ad una classe. Quindi, per ogni classe è definito un insieme di modalità di accesso peculiari per la stessa, che prende il nome di vettore di accesso. Un simile approccio ha notevoli vantaggi, perché è sempre ben definita la semantica dei singoli diritti di accesso. Inoltre, la classificazione degli oggetti si rende particolarmente utile nella configurazione del sottomodello TE, nel quale entrano in gioco security context che possono essere applicati ad oggetti di classi differenti: il controllo degli accessi 15 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO viene effettuato, infatti, sulla base delle etichette di soggetto e oggetto ma anche sulla base della classe di appartenena dell'oggetto, permettendo di distinguere differenti comportamenti in funzione della tipologia di oggetto al quale si vuole accedere. 4.3.1 Controllo dei processi. PERMISSION(S) DESCRIPTION execute Execute transition Change label entrypoint Enter via program sigkill sigstop sigchld signal Signal fork Fork ptrace Trace getsched Get schedule info setsched Set schedule info getsession Geet sssion getpgid Get group process setpgid Set group process getcap Get capabilities setcap Set capabilities Figura 4.a. Permessi per la classe di appartenenza di oggetti di tipo processo. La tabella in figura 4.a mostra i permessi definiti per la gestione dei processi. 16 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO Il permesso execute è controllato insieme all'etichetta del processo e all'etichetta dell'eseguibile ad ogni esecuzione di un programma. Questo tipo di accesso viene controllato ogni volta che è mandato in esecuzione un interprete per gli script o quando un file è mappato in memoria con un accesso execute (per esempio una libreria condivisa). Il permesso execute di un processo è diverso dal permesso execute di un file, in quanto è usato per controllare la capacità di un processo di avviare l'esecuzione di un programma. Il permesso transition è usato per controllare la capacità di un processo di transire da un SID ad un altro. Il permesso entrypoint è usato per controllare quali programmi possono essere usati come punto di entrata per il SID di un processo. Questo permesso è simile al permesso execute, con l'eccezione che è controllato solo quando un processo transita ad un nuovo SID. Permessi separati sono stati definiti per i segnali SIGKILL e SIGSTOP (segnali per la terminazione di un processo), e sono stati rispettivamente chiamati sigkill e sigstop. Un altro permesso separato, sigchld, è stato definito per controllare il segnale SIGCHLD, segnale che viene inviato dal processo figlio al processo padre per comunicargli la sua terminazione. Un solo permesso, signal, è usato per controllare i rimanenti segnali. Il permesso ptrace è usato per controllare la capacità di un processo di tenere traccia di un altro processo. I permessi getsched, setsched, getsession, getpgid, setpgid, getcap e setcap sono usati per controllare la capacità di un processo di osservare o 17 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO modificare gli attributi corrispondenti di un altro processo. Oltre a questi permessi, SELinux fornisce un permesso equivalente per ognuno dei permessi già esistenti in Linux. 4.3.2 Controllo dei file. PERMISSION(S) DESCRIPTION create Create getattr Get attributes setattr Set attributes inherit Inherit across execve receive Receive via IPC Figura 4.b. Permessi per la classe di appartenenza degli oggetti di tipo open file description. La tabella in figura 4.b elenca i permessi per controllare l'accesso agli oggetti degli open file description. Poiché gli open file description possono essere ereditati attraverso execve o trasferiti tramite socket UNIX IPC, SELinux etichetta e controlla gli open file description. Un open file description è etichettato con il SID del suo processo creante, poiché il suo stato è trattato coma una parte dello stato privato del processo. È importante distinguere tra l'etichetta di un open file description e l'etichetta del file a cui si riferisce. Un'operazione di lettura di un file cambia l'offset del file nell'open file description, così può essere necessario evitare che un processo legga un file usando un open file description ricevuto o ereditato da un altro processo, perfino se al processo è permesso di aprire o leggere direttamente il file. 18 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO La tabella in figura 4.c descrive i permessi per controllare l'accesso ai filesystem. PERMISSION(S) DESCRIPTION mount Mount remount Change options unmount Unmount getattr Get attributes relabelfrom relabelto transition Relabel associate Associate file Figura 4.c. Permessi per la classe di oggetti di tipo filesystem. SELinux etichetta i filesystem e i servizi di controllo che manipolano i filesystem includendo tra questi ultimi le chiamate mount e unmount, la chiamata statfs e le chiamate per la creazione di file (per queste ultime si rimanda alla figura 4.b). SELinux gestisce il mounting dei filesystem tramite diversi controlli dei permessi. Per prima cosa, richiede che il processo abbia il permesso mounton per la directory che si è scelta come punto di montaggio e il permesso mount per il filesystem. Poi, richiede che il permesso mountassociate sia assegnato tra la directory root del filesystem e il punto di montaggio. SELinux lega le etichette ai file e alle directory e controlla gli accessi ad essi. Memorizza una tavola di labeling persistente in ciascun filesystem, che specifica l'etichetta di sicurezza per ciascun file e directory in quel filesystem. Per una memorizzazione efficiente, SELinux assegna un valore 19 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO intero, detto persistent SID (PSID), a ciascuna etichetta usata da un oggetto in un filesystem. La tavola di labeling persistente è partizionata in un mapping tra ogni PSID e la sua etichetta di sicurezza, e un mapping tra ogni oggetto e il suo PSID. Il mapping tra ogni PSID e la sua etichetta di sicurezza è implementato usando file regolari in una fissata subdirectory della root directory di ogni filesystem. Questo mapping è caricato in memoria quando il filesystem è montato, ed è aggiornato sia in memoria che sul disco quando una nuova etichetta viene usata per un oggetto nel filesystem. Il mapping tra ogni oggetto e il suo PSID è implementato memorizzando il PSID in un campo non usato dell'inode. SELinux, attualmente, implementa l'assegnazione di etichette ai file per i filesystem ext2, ext3 e xfs. Nel filesystem nfs è usata una singola etichetta per tutti i file montati da un dato server NFS. SELinux implementa anche l'assegnazione di etichette di file per i filesystem speciali come procfs e devpts, basato sulle etichette del processo associato, ma questi tipi particolari di filesystem non richiedono l'uso di mapping di etichette persistenti. Quando un filesystem non etichettato viene montato per la prima volta, viene creata una tavola di etichette persistente, usando un'etichetta di default, per tutti i file, ottenuta dal security server. Successivamente, i file esistenti possono essere etichettati di nuovo usando system call. Un programma chiamato setfiles è usato per settare inizialmente le etichette dei file da un file di configurazione che specifica le etichette basate su espressioni regolari dei pathname. Questo programma e il file di 20 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO configurazione possono essere usati anche per resettare le etichette dei file ad uno stato ben definito. Tuttavia, a meno che il file di configurazione sia aggiornato per riflettere i cambiamenti nelle etichette dei file in fase di runtime, questi cambiamenti saranno persi quando il programma viene eseguito. Cambiamenti in fase di runtime possono verificarsi, esempio, come risultato della creazione di nuovi file. PERMISSION(S) DESCRIPTION read Read write Write or append append Append poll Poll/select ioctl IO control create Create execute Execute access Check accessibility getattr Get attributes setattr Set attributes unlink Remove link hard link Create link hard rename Rename link hard lock Lock or unlock relabelfrom relabelto transition Relabel Figura 4.d. Permessi per le classi di oggetti di tipo pipe e file. 21 per CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO PERMISSION(S) DESCRIPTION add_name Add a name remove_name Remove a name reparent Change parent directory search Search rmdir Remove mounton mountassociate Use as mount point Figura 4.e. Permessi addizionali per la classe di oggetti di tipo directory. La tabella 4.d mostra i permessi definiti per il controlli dell'accesso ai file mentre la tabella 4.e mostra i permessi addizionali definiti per il controllo dell'accesso alle directory. SELinux definisce un permesso distinto per ogni file e directory di servizio. Per esempio, definisce un permesso append per i file in aggiunta al permesso write, e definisce i permessi separati add_name e remove_name per le directory per supportare il solo append di file e directory. SELinux definisce anche il permesso reparent per le directory: qeusto controlla se il link alla directory padre è stato cambiato a causa di un rename. Fornisce, inoltre, un controllo su ciascun oggetto relativo a un file o a una directory di servizio. Per esempio, in aggiunta al controllo di accesso alla directory padre, SELinux definisce i permessi per i controlli dell'accesso al file stesso per operazioni come stat, link, rename, unlink e rmdir. 22 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO 4.3.3 Controllo dei socket. SELinux fornisce il controllo sul socket IPC attraverso un insieme di controlli stratificati sui socket, messaggi, nodi e interfacce di rete. Correntemente, il prototipo di SELinux fornisce solo l'assegnazione di etichette e il controllo per i domini socket INET e UNIX. Al livello del socket layer, SELinux controlla la capacità dei processi di eseguire operazioni sui socket. Al livello del transport layer, SELinux controlla la capacità di spedire messaggi ai nodi e ricevere messaggi dai nodi. Controlla anche la capacità dei processi di configurare le interfacce di rete e di manipolare la tabella di routing del kernel. Dato che l'accesso ai socket avviene tramite file description, le classi degli oggetti socket ereditano i permessi definiti per il controllo dell'accesso alle classi degli oggetti file. PERMISSION(S) DESCRIPTION bind Bind name name_bind Use port or file connect Initiate connection getopt Get socket options setopt Set socket options shutdown Shut connection down recvfrom Receive socket from sendto Send to socket recv_msg Receive message send_msg Send message Figura 4.f. Permessi addizionali per la classe di oggetti di tipo socket. 23 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO La tabella in figura 4.f mostra i permessi addizionali che sono definiti specificatamente per il controllo dell'accesso alle classi degli oggetti socket. Il servizio connection-oriented fornito dallo stream socket richiede ulteriori permessi, mostrati nella tabella qui sotto. PERMISSION(S) DESCRIPTION listen Listen for connections accept Accept a connection newconn Create new socket for connection connectto Connect to server socket acceptfrom Accept connection from client socket Figura 4.g. Permessi addizionali per le classi oggetto TCP e Unix stream socket. Nella tabella in figura 4.h sono mostrati i permessi per le interfacce di rete e per i nodi PERMISSION(S) DESCRIPTION getattr Get attributes setattr Set attributes tcp_recv Receive TCP packet tcp_send Send TCP packet udp_recv Receive UDP packet udp_send Send UDP packet rawip_recv Receive Raw IP packet rawip_send Send Raw IP packet Figura 4.h. Permessi addizionali per le classi oggetto interfaccia di rete e nodo. 24 CAPITOLO IV – ARCHITETTURA DI SICUREZZA DI SELINUX E SUO FUNZIONAMENTO I socket servono come proxy di comunicazione per i processi nel modello di controllo di SELinux. I socket sono etichettati di default con l'etichetta del processo creante. SELinux permette alle politiche di sicurezza di distinguere tra client e server per connessioni stream socket tramite i permessi connectto, acceptfrom. SELinux, inoltre, permette alle politiche di sicurezza di basare le decisioni sul tipo di socket tramite l'uso di classi di oggetti e permette alle politiche di sicurezza di basare le decisioni sul message protocol tramite i permessi del nodo per-protocol e delle interfacce di rete. I messaggi vengono associati sia con l'etichetta del socket trasmittente sia con un'etichetta propria del messaggio. Per default l'etichetta del messaggio è la stessa del socket trasmittente. Un processo può esplicitamente etichettare i messaggi se il protocollo del sublayer supporta messaggi limitati, come i datagram socket. Il supporto per le etichette dei messaggi comunicanti attraverso la rete non è stato ancora implementato. 25 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Capitolo V “Porting” di SELinux in ambiente embedded. In questo capitolo, si congiungono i temi trattati in questa mia tesi. Illustro, infatti, il lavoro svolto in laboratorio, a partire dallo studio iniziale della Fox Board, fino alla descrizione delle modifiche apportate al sistema per l'implementazione di SELinux sulla scheda stessa. 5.1 CRIS cross-compiler. Per prima cosa ho installato il compilatore, scaricabile dal sito http://developer.axis.com/download/compiler/. Per lo sviluppo dei sistemi, come quello da me utilizzato, basati sul chipset ETRAX, sono necessari tre pacchetti: cris-dist-N.M.tar.gz, cris-dist-glibc-N.M.tar.gz e cris-dist- linux-headers-N.M.tar.gz, dove N e M indicano il numero di versione. In particolare, il primo pacchetto contiene il pacchetto base, che è composto da uno script principale, dallle binutils (assembler e linker) e dal gcc (compilatore) per questa particolare architettura; gli altri due pacchetti, 1 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED invece, servono per poter compilare gli altri sorgenti, ad esempio, il kernel. Per poter installare il compilatore, si deve estrarre per primo il pacchetto cris-dist-N.M.tar.gz in una directory a scelta, e, successivamente, gli altri due, che devono essere estratti nella directory scelta. Una volta completata questa fase, si lancia lo script di installazione, digitando il comando ./install-cris-tools dalla shell: con questa procedura, viene configurato, compilato e installato il cross-compiler. In questa fase e, quindi, prima di portare a termine l'operazione di installazione, vengono richiesti i percorsi delle directory in cui si vuole installare l'applicazione, vengono testati i permessi per poter accedere ai file e viene controllata la presenza di versioni precedentemente installate. 5.2 L'ambiente di sviluppo. Anche l'ambiente di sviluppo ci viene fornito da Axis, è, infatti, scaricabile dal sito http://developer.axis.com/download/software.html. Prima di procedere all'installazione, però, ho dovuto verificare che il mio sistema soddisfacesse alcuni requisiti. Infatti, oltre ad avere un sistema operativo Linux con la possibilità di accedere come root ed una interfaccia di rete, il mio sistema avrebbe dovuto avere una serie di programmi e applicazioni senza le quali l'ambiente di sviluppo non può funzionare., quali: GCC C compiler, GNU make, wget, awk, bc, byacc, lex, perl, sed, tar, zlib, md5sum, pmake, curses, bison, which (figura 5.1). 2 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED * OS: Any fairly new Linux distribution should work. * Ethernet network interface. * Ability to become root (some installation stages require root access. * gcc C compiler * GNU make * GNU wget * CRIS cross-compiler * awk (or gawk) * bc * byacc (or yacc if byacc is a link to it) * lex or flex * perl * sed * tar * zlib * md5sum * pmake * curses or ncurses * bison * which Figura 5.1. Elenco dei requisiti che il sistema deve soddisfare per poter installare correttamente l'ambiente di sviluppo. Una volta scaricato ed estratto il pacchetto contenente l'ambiente di sviluppo, devboard-R2_01.tar.gz, ho lanciato lo script di installazione, che, a sua volta, scarica ed installa il codice sorgente necessario a configurare l'SDK. Al termine dell'installazione, mi è stato chiesto quale prodotto avrei utilizzato. 3 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Select wich product to use. This selection will affect which configuration files the SDK will use. Select the product in the list below that closest resemblels your product. Alternatives: 1. devboard_82+ - "Axis Developer Board 82+/83+" 2. devboard_82 - "Axis Developer Board 82/83" 3. devboard_lx - "Axis Developer Board LX" 4. devboard_lx_ide - "Axis Developer Board LX IDE" 5. fox - "FOX BOARD by ACME systems (www.acmesystems.it) with MCM4+16" 6. mcm_2_8 - "Custom designs with MCM2+8, no additional memory" 7. mcm_4_16 - "Custom designs with MCM4+16, no additional memory" * Specify product (default: 1. devboard_82+): Figura 5.2. Selezione del tipo di hardware. Il passo successivo è stata la configurazione dell'ambiente di sviluppo, in cui possiamo decidere quali applicazioni includere. Io scelto di mantenere le applicazioni già selezionate di default, quali ftp client, web server, utility per il test dell'hardware, etc. (figura 5.5), perché mi sembravano sufficienti e tutte ugualmente necessarie per lo sviluppo del mio sistema. Digitando da terminale make menuconfig, si accede al menù di configurazione (Figura 5.3). 4 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Figura 5.3. Axis Product Configuration. Figura 5.4. Hardware Configuration: si può qui selezionare il tipo di processore utilizzato dalla scheda, e modificare lo spazio attribuito alle porzioni dii memoria (ram, flash e rescue memory). 5 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Figura 5.5. General Configuration: qui si può scegliere quale versione del kernel usare, quale tipo di librerie e quali applicazioni includere. L'installazione viene completata lanciando il comando ./configure e successivamente il comando make : con la prima operazione vengono scaricati ed installati tutti i pacchetti necessari, secondo la configurazione appena effettuata, mentre la seconda operazione, crea l'immagine da caricare sulla scheda. 5.3 Impostazioni personali. Nella fase di configurazione dell'SDK, ho selezionato il kernel 2.6.x, dato che include già il Framework per SELinux, ed ho preferito le librerie standard glibc alle nuove uclibc. L'indirizzo IP di default della scheda è 192.168.0.90. Per cambiarlo basta digitare: 6 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED # ifconfig eth0 x.x.x.x sulla shell. x.x.x.x rappresenta il nuovo indirizzo IP. In questo modo, però, il nuovo indirizzo rimane tale fino al successivo riavvio della scheda, quando ritorna al valore di default. Per cambiare in maniera permanente l'indirizzo IP, ho dovuto modificare il file <path di installazione di devboard>/packages/initscripts/net.eth0-static/conf, come mostrato in Figura 5.6. # Uncomment the following line to override the generic hardware # address # (usually the serial number) for this interface only. # MAC="00:40:8C:CD:00:00" # Valid boot protocols are "dhcp" and "none" (anything other than # "dhcp" works). BOOTPROTO="none" DHCP_CLIENT="/sbin/udhcpc -i eth0" # Valid media types are "auto", "10baseT-HD", "10baseT-FD" # "100baseTX-HD", "100baseTX-FD" and "" (nothing). MEDIA="auto" # If you are using DHCP the following variables will not be used. IP="192.168.1.90" NETMASK="255.255.255.0" BROADCAST="192.168.1.255" GATEWAY="192.168.1.1" Figura 5.6. <path di installazione di devboard>/packages/initscripts/net.eth0-static/conf Cambiato l'indirizzo IP ed escludendo il servizio DHCP per l'assegnazione 7 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED automatica dell'IP stesso, ho modificato anche l'indirizzo fisico, o indirizzo MAC. Per far questo è necessario modificare il file <path di installazione di devboard>/packages/initscripts/mac/conf, inserendo un indirizzo fisico del tipo 00:40:8C:xx:xx:xx, dove xx:xx:xx sono le ultime sei cifre del numero di serie della scheda, che è riportato nella serigrafia della scheda. Nel mio caso, l'indirizzo MAC è diventato 00:40:8C:01:20:04. Questa operazione solitamente non è necessaria: nel mio caso si è dovuto procedere a questo solo dopo che era stata cancellata la memoria FLASH e riscritta con il nuovo kernel e filesystem, che non permettevano il riconoscimento automatico della scheda di rete finché non è stato forzatamente riassegnato l'indirizzo MAC. Per rendere effettive queste semplici modifiche ho ricompilato l'immagine e l'ho caricata di nuovo sulla Fox Board. 5.4 Preparazione del sistema per l'installazione di SELinux. Ho già accennato al fatto che il kernel 2.6.x contiene al suo interno il Framework per SELinux: descrivo ora come attivarlo e quali altre impostazioni sono necessarie per il suo corretto funzionamento. Dal menu di configurazione del kernel, che troviamo sotto <path di installazione di devboard>/os/linux-2.6, attiveremo le opzioni mostrate in figura 5.7. 8 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Sotto "Code maturity level options" [*] Prompt for development and/or incomplete code/drivers Sotto "General setup" [*] Auditing support [*] Enable system-call auditing support Sotto "Pseudo filesystems (via "File systems") [ ] /dev file system support (EXPERIMENTAL) [*] /dev/pts Extended Attributes [*] /dev/pts Security Labels [*] Virtual memory file system support (former shm fs) [*] tmpfs Extended Attributes [*] tmpfs Security Labels Sotto "Security options" [*] Enable different security models [*] Socket and Networking Security Hooks <*> Default Linux Capabilities [*] NSA SELinux Support [ ] NSA SELinux boot parameter [ ] NSA SELinux runtime disable [*] NSA SELinux Development Support [*] NSA SELinux AVC Statistics (1) NSA SELinux checkreqprot default value [ ] NSA SELinux enable new secmark network controls by default Figura 5.7. Opzioni necessarie per SELinux. Come si può vedere dalla figura, non è fondamentale la sola attivazione del secutity module, ma si devono attivare anche gli extended attributes, e, in particolare, le security labels per i filesystem, affinché sia supportato il nuovo modello di controllo degli accessi. Nella figura, è mostrata la sola sezione riguardante gli pseudo-filesystem, mentre, in realtà, le security labels vanno attivate per tutti i filesystem usati nel sistema in cui si vuole implementare SELinux: nel mio caso, questo non è stato possibile perché nel sistema embedded da me utilizzato sono usati i filesystem jffs2 e cramfs, per i quali non sono ancora stati implementati gli extended attributes. Questo, come spiegherò in seguito, ha costituito un problema 9 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED non di poco conto nel mio lavoro. Oltre ai filesystem precedentemente citati, aggiungo che SELinux ha bisogno di un ulteriore pseudo-filesystem, il selinuxfs, che deve essere caricato sin dal processo di init (processo responsabile del boot del filesystem). Per questo filesystem, ho creato un punto di montaggio apposito, aggiungendo le seguenti righe al file <path di installazione di devboard>/packages/filesystem/Makefile: in cui, la prima riga crea la cartella /mnt/flash/etc/security/selinux/ nella memoria FLASH, e la seconda riga crea un collegamento in /etc/security/selinux per un più rapido acceso. Per far si che il filesystem venga montato all'avvio ho modificato anche il file fstab, come riportato in Figura 5.8. Figura 5.8. File fstab. 10 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED 5.5 BusyBox: il coltellino svizzero di Linux-embedded. Dopo aver parlato del cross-compiler e dell'SDK, introduco un altro degli strumenti essenziali: BusyBox. È necessario che io ne parli perché è uno strumento fondamentale e sapere in cosa consiste permette di capire il resto del mio lavoro. BusyBox combina diverse applicazioni standard di Unix in un piccolo eseguibile, e fornisce la maggior parte delle utility che si trovano nei sistemi GNU/Linux, come quelle per la manipolazione dei file, shell e testo, che prendono il nome di coreutils (ad esempio dir, mkdir, rm, cat, ...), per la manipolazione dei processi, dette process utilities (ad esempio kill, ps, pidof, ...), per la manipolazione degli utenti, anche dette login/password management utilities (ad esempio su, login, adduser, ...) ed altre ancora. È stato scritto ed ottimizzato per sistemi con risorse limitate, e, grazie alle sue ridotte dimensioni, è diventato uno standard nell'ambiente Linux-embedded. È, inoltre, estremamente modulare: è, cioè, possibile includere o escludere facilmente le varie applicazioni, e questo lo rende perfettamente adattabile al sistema che si vuole implementare. 5.5.1 Configurazione di BusyBox. Nel menù di configurazione di BusyBox è possibile attivare l'opzione per il supporto di SELinux, come mostrato in figura 5.9. 11 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Figura 5.9. BusyBox General Configuration. In realtà, questa opzione abilita il supporto per SELinux che si concretizza, in BusyBox, nei comandi ls, ps e id, ma questa abilitazione va completata con l'aggiunta degli header file che vengono richiesti, in fase di compilazione, dai sorgenti delle applet citate. Per chiarire, BusyBox non viene distribuito con le librerie e i sorgenti che servono ad implementare delle politiche di sicurezza (figura 5.10), ma è già predisposto per SELinux, qualora si voglia utilizzarlo. Figura 5.10. Suppot NSA Security Enhance Linux. Questa è stata la parte più complicata del mio lavoro: ho dovuto compiere svariate prove e innumerevoli tentativi, prima di riuscire ad ottenere 12 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED un'immagine funzionante. I paragrafi che seguono contengono tutte le modifiche apportate a la procedura seguita. Ho scelto di evidenziare le modifiche apportate attraverso l'uso del comando diff, il quale stampa sullo standard output (una sessione del terminale, per esempio) o salva su un file, le differenze di contenuto tra due file di testo ( e i codici in linguaggio C sono file di testo). Per capire le modifiche bisogna conoscere come funziona questo comando: # diff from-file to file Le differenze, come si può intuire dal prototipo del comando, vengono calcolate come differenza del file che viene inserito per primo. Nel mio caso, il from-file è il file sorgente di busybox, mentre il to-file è il file come io l'ho modificato. Inoltre, nel comando diff ho usato l'opzione -c, in modo che il comando generi un output che mostra non solo la riga diversa, ma almeno un'altra riga del contesto (di solito, tre, cioè la riga precedente e la riga successiva a quella modificata). Questo formato è adatto anche per creare patch di applicativi, il requisito è che nel file della patch (un file diff, appunto), ci siano almeno due righe del contesto. 5.5.2 Coreutils. Le coreutils comprendono gli applicativi per la manipolazione di file, testo e shell. I file modificati da me, che fanno parte di questo pacchetto, sono: id.c: mostra le informazioni riguardanti l'utente e il gruppo e, dopo le modifiche da me apportate, accetta anche l'opzione -Z per 13 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED mostrare il security context; ls.c: elenca i file, il loro proprietario ed i permessi, e, dopo le modifiche da me apportate, accetta anche l'opzione -Z per mostrare il security context dei file. <path di installazione di devboard>/apps/busybox/coreutils/id.c @@ -32,8 +32,7 @@ #include <sys/types.h> #ifdef CONFIG_SELINUX -#include <proc_secure.h> -#include <flask_util.h> +#include <selinux/selinux.h> #endif /* for is_selinux_enabled() */ #define PRINT_REAL 1 @@ -61,9 +60,6 @@ extern int id_main(int argc, char **argv gid_t gid; unsigned long flags; short status; -#ifdef CONFIG_SELINUX int is_flask_enabled_flag = is_flask_enabled(); -#endif bb_opt_complementaly = "u~g:g~u"; flags = bb_getopt_ulflags(argc, argv, "rnug"); @@ -109,17 +105,26 @@ extern int id_main(int argc, char **argv putchar(' '); /* my_getgrgid doesn't exit on failure here */ status|=printf_full(gid, my_getgrgid(NULL, gid, 0), 'g'); + #ifdef CONFIG_SELINUX if(is_flask_enabled_flag) { security_id_t mysid = getsecsid(); char context[80]; int len = sizeof(context); context[0] = '\0'; if(security_sid_to_context(mysid, context, &len)) strcpy(context, "unknown"); + if ( is_selinux_enabled() ) { + security_context_t mysid; + char context[80]; + int len = sizeof(context); + + getcon(&mysid); + context[0] = '\0'; + if (mysid) { + len = strlen(mysid)+1; + safe_strncpy(context, mysid, len); + freecon(mysid); 14 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + + + }else{ safe_strncpy(context, "unknown",8); } bb_printf(" context=%s", context); } #endif + putchar('\n'); bb_fflush_stdout_and_exit(status); } <path di installazione di devboard>/apps/busybox/coreutils/ls.c @@ -64,9 +64,7 @@ enum { #include <sys/sysmacros.h> /* major() and minor() */ #include "busybox.h" #ifdef CONFIG_SELINUX -#include <fs_secure.h> -#include <flask_util.h> -#include <ss.h> +#include <selinux/selinux.h> /* for is_selinux_enabled() */ #endif #ifdef CONFIG_FEATURE_LS_TIMESTAMPS @@ -182,7 +180,7 @@ struct dnode { /* the basic node */ char *fullname; /* the dir entry name */ struct stat dstat; /* the file stat info */ #ifdef CONFIG_SELINUX security_id_t sid; + security_context_t sid; #endif struct dnode *next; /* point at the next node */ }; @@ -195,7 +193,7 @@ static int list_single(struct dnode *); static unsigned int all_fmt; #ifdef CONFIG_SELINUX -static int is_flask_enabled_flag; +static int selinux_enabled= 0; #endif #ifdef CONFIG_FEATURE_AUTOWIDTH @@ -213,18 +211,19 @@ static struct dnode *my_stat(char *fulln struct stat dstat; struct dnode *cur; #ifdef CONFIG_SELINUX security_id_t sid; + security_context_t sid=NULL; #endif int rc; #ifdef CONFIG_FEATURE_LS_FOLLOWLINKS if (all_fmt & FOLLOW_LINKS) { #ifdef CONFIG_SELINUX if(is_flask_enabled_flag) rc = stat_secure(fullname, &dstat, &sid); else 15 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + + + + #endif + if (is_selinux_enabled()) { rc=0; /*Set the number which means success before hand.*/ rc = getfilecon(fullname,&sid); } rc = stat(fullname, &dstat); rc = stat(fullname, &dstat); if(rc) { bb_perror_msg("%s", fullname); @@ -235,11 +234,12 @@ static struct dnode *my_stat(char *fulln #endif { #ifdef CONFIG_SELINUX if(is_flask_enabled_flag) rc = lstat_secure(fullname, &dstat, &sid); else + if (is_selinux_enabled()) { + rc=0; /*Set the number which means success before hand.*/ + rc = lgetfilecon(fullname,&sid); + } #endif rc = lstat(fullname, &dstat); + rc = lstat(fullname, &dstat); if(rc) { bb_perror_msg("%s", fullname); @@ -736,12 +736,16 @@ static int list_single(struct dnode *dn) #ifdef CONFIG_SELINUX case LIST_CONTEXT: { char context[64]; int len = sizeof(context); if(security_sid_to_context(dn->sid, context, &len)) { strcpy(context, "unknown"); len = 7; + char context[80]; + int len; + + if (dn->sid) { + /* I assume sid initilized with NULL */ + len = strlen(dn->sid)+1; + safe_strncpy(context, dn->sid, len); + freecon(dn->sid); + }else { + safe_strncpy(context, "unknown",8); } printf("%-32s ", context); column += MAX(33, len); @@ -857,7 +853,7 @@ #endif #ifdef CONFIG_SELINUX -# define LS_STR_SELINUX "K" 16 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED +# define LS_STR_SELINUX "Z" #else # define LS_STR_SELINUX "" #endif @@ -897,9 +893,9 @@ DISP_ROWS, /* x */ DISP_HIDDEN, /* A */ #ifdef CONFIG_SELINUX LIST_CONTEXT, /* k */ + LIST_CONTEXT, /* Z */ #else 0, /* k - ingored */ + 0, /* Z - ingored */ @@ -963,10 +967,6 @@ extern int ls_main(int argc, char **argv char *terminal_width_str = NULL; #endif -#ifdef CONFIG_SELINUX is_flask_enabled_flag = is_flask_enabled(); -#endif all_fmt = LIST_SHORT | DISP_NORMAL | STYLE_AUTO #ifdef CONFIG_FEATURE_LS_TIMESTAMPS | TIME_MOD 5.5.3 Include. Questo pacchetto di file include le librerie generiche di BusyBox, che vengono richiamate, nel codice sorgente degli applet. Le modifiche sono state necessarie affinché fossero caricate le politiche richieste attraverso l'uso di macro. <path di installazione di devboard>/apps/busybox/include/libbb.h @@ -43,7 +43,7 @@ #include "config.h" #ifdef CONFIG_SELINUX -#include <proc_secure.h> +#include <selinux/selinux.h> #endif #include "pwd_.h" @@ -423,11 +423,12 @@ void bb_xasprintf(char **string_ptr, con #define FAIL_DELAY 3 extern void change_identity ( const struct passwd *pw ); extern const char *change_identity_e2str ( const struct passwd *pw ); -extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args +extern void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args); 17 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED #ifdef CONFIG_SELINUX , security_id_t sid -#endif -); +extern void renew_current_security_context(void); +extern void set_current_security_context(security_context_t sid); +#endif + extern int run_parts(char **args, const unsigned char test_mode, char **env); extern int restricted_shell ( const char *shell ); extern void xsetenv ( const char *key, const char *value ); @@ -459,11 +460,7 @@ typedef struct { char short_cmd[16]; } procps_status_t; -extern procps_status_t * procps_scan(int save_user_arg0 -#ifdef CONFIG_SELINUX , int use_selinux, security_id_t *sid -#endif -); +extern procps_status_t * procps_scan(int save_user_arg0); extern unsigned short compare_string_array(const char *string_array[], const char *key); extern int my_query_module(const char *name, int which, void **buf, size_t *bufsize, size_t *ret); <path di installazione di devboard>/apps/busybox/include/usage.h @@ -1048,7 +1048,7 @@ #define id_full_usage \ "Print information for USERNAME or the current user\n\n" \ "Options:\n" \ USAGE_SELINUX("\t-c\tprints only the security context\n") \ + USAGE_SELINUX("\t-Z\tprints only the security context\n") \ "\t-g\tprints only the group ID\n" \ "\t-u\tprints only the user ID\n" \ "\t-n\tprint a name instead of a number\n" \ @@ -1514,7 +1514,7 @@ #endif #define ls_trivial_usage \ "[-1Aa" USAGE_LS_TIMESTAMPS("c") "Cd" USAGE_LS_TIMESTAMPS("e") \ USAGE_LS_FILETYPES("F") "iln" USAGE_LS_FILETYPES("p") USAGE_LS_FOLLOWLINKS("L") USAGE_LS_RECURSIVE("R") USAGE_LS_SORTFILES("rS") "s" USAGE_AUTOWIDTH("T") USAGE_LS_TIMESTAMPS("tu") USAGE_LS_SORTFILES("v") U SAGE_AUTOWIDTH("w") "x" USAGE_LS_SORTFILES("X") USAGE_HUMAN_READABLE("h") USAGE_NOT_HUMAN_READABLE("") "k" USAGE_SELINUX("K") "] [filenames...]" + "[-1Aa" USAGE_LS_TIMESTAMPS("c") "Cd" USAGE_LS_TIMESTAMPS("e") USAGE_LS_FILETYPES("F") "iln" USAGE_LS_FILETYPES("p") USAGE_LS_FOLLOWLINKS("L") USAGE_LS_RECURSIVE("R") USAGE_LS_SORTFILES("rS") "s" USAGE_AUTOWIDTH("T") U SAGE_LS_TIMESTAMPS("tu") USAGE_LS_SORTFILES("v") USAGE_AUTOWIDTH("w") "x" USAGE_LS_SORTFILES("X") U - 18 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED SAGE_HUMAN_READABLE("h") USAGE_NOT_HUMAN_READABLE("") "k" USAGE_SELINUX("Z") "] [filenames...]" #define ls_full_usage \ "List directory contents\n\n" \ "Options:\n" \ @@ -1542,8 +1542,7 @@ USAGE_LS_SORTFILES("\t-X\tsort the listing by extension\n") \ USAGE_HUMAN_READABLE( \ "\t-h\tprint sizes in human readable format (e.g.,1K 243M 2G)\n")\ USAGE_SELINUX("\t-k\tprint security context\n") \ USAGE_SELINUX("\t-K\tprint security context in long format\n") + USAGE_SELINUX("\t-Z\tprint security context\n") #define lsmod_trivial_usage \ "" @@ -1951,12 +1950,20 @@ #define USAGE_NONSELINUX(a) a #endif +#define load_policy_trivial_usage \ + "" +#define load_policy_full_usage \ + "load SELinux policy\n" + +#define load_policy_example_usage \ + "$ load_policy /etc/selinux/strict/policy/policy.17\n" + #define ps_trivial_usage \ "" #define ps_full_usage \ "Report process status\n" \ USAGE_NONSELINUX("\n\tThis version of ps accepts no options.") \ USAGE_SELINUX("\nOptions:\n\t-c\tshow SE Linux context") + USAGE_SELINUX("\nOptions:\n\t-Z\tshow SELinux context") #define ps_example_usage \ "$ ps\n" \ <path di installazione di devboard>/apps/busybox/include/applets.h @@ -331,6 +331,9 @@ #ifdef CONFIG_LN APPLET(ln, ln_main, _BB_DIR_BIN, _BB_SUID_NEVER) #endif +#ifdef CONFIG_LOAD_POLICY + APPLET(load_policy, load_policy_main, _BB_DIR_SBIN, _BB_SUID_NEVER) +#endif #ifdef CONFIG_LOADFONT APPLET(loadfont, loadfont_main, _BB_DIR_USR_BIN, _BB_SUID_NEVER) #endif 19 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED 5.5.4 Libbb. Questo pacchetto di file è costituito dai sorgenti per ottenere la libreria condivisa libbb.so, che amplia e completa le funzionalità di BusyBox per la gestione dei file. In particolare, io ho modificato: find_pid_by_name.c: dal nome permette di risalire all'identificativo del processo; procps.c: elenca i processi in esecuzione e il loro stato; run_shell.c: manda in esecuzione la shell. In questi file, ho fatto in modo che il comportamento fosse identico per il caso di abilitazione e di disabilitazione del supporto per SELinux. <path di installazione di devboard>/apps/busybox/libbb/find_pid_by_name.c @@ -45,11 +45,8 @@ extern long* find_pid_by_name( const cha procps_status_t * p; pidList = xmalloc(sizeof(long)); -#ifdef CONFIG_SELINUX while ((p = procps_scan(0, 0, NULL)) != 0) { -#else while ((p = procps_scan(0)) != 0) { -#endif + while ((p = procps_scan(0)) != 0) + { if (strncmp(p->short_cmd, pidName, COMM_LEN-1) == 0) { pidList=xrealloc( pidList, sizeof(long) * (i+2)); pidList[i++]=p->pid; <path di installazione di devboard>/apps/busybox/libbb/procps.c @@ -16,11 +16,7 @@ #include "libbb.h" -extern procps_status_t * procps_scan(int save_user_arg0 -#ifdef CONFIG_SELINUX , int use_selinux , security_id_t *sid -#endif ) +extern procps_status_t * procps_scan(int save_user_arg0) { static DIR *dir; struct dirent *entry; @@ -60,16 +56,9 @@ extern procps_status_t * procps_scan(int my_getpwuid(curstatus.user, sb.st_uid, 20 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED sizeof(curstatus.user)); sprintf(status, "/proc/%d/stat", pid); + if((fp = fopen(status, "r")) == NULL) continue; -#ifdef CONFIG_SELINUX if(use_selinux) { if(fstat_secure(fileno(fp), &sb, sid)) continue; } else -#endif name = fgets(buf, sizeof(buf), fp); fclose(fp); if(name == NULL) <path di installazione di devboard>/apps/busybox/libbb/run_shell.c @@ -37,7 +37,33 @@ #include <ctype.h> #include "libbb.h" #ifdef CONFIG_SELINUX -#include <proc_secure.h> +#include <selinux/selinux.h> /* for setexeccon */ +#endif + +#ifdef CONFIG_SELINUX +static security_context_t current_sid=NULL; + +void +renew_current_security_context(void) +{ + if (current_sid) + freecon(current_sid); /* Release old context */ + + getcon(¤t_sid); /* update */ + + return; +} +void +set_current_security_context(security_context_t sid) +{ + if (current_sid) + freecon(current_sid); /* Release old context */ + + current_sid=sid; + + return; +} + @@ -45,11 +71,7 @@ /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. If ADDITIONAL_ARGS is nonzero, pass it to the shell as more arguments. */ 21 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED -void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args -#ifdef CONFIG_SELINUX , security_id_t sid -#endif -) +void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args) { const char **args; int argno = 1; @@ -78,10 +100,11 @@ void run_shell ( const char *shell, int } args [argno] = 0; #ifdef CONFIG_SELINUX if(sid) execve_secure(shell, (char **) args, environ, sid); else + if ( (current_sid) && (!setexeccon(current_sid)) ) { + freecon(current_sid); + execve(shell, (char **) args, environ); + } else #endif execv ( shell, (char **) args ); + execv ( shell, (char **) args ); bb_perror_msg_and_die ( "cannot run %s", shell ); } 5.5.5 Loginutils. Le loginutils comprendono gli applicativi per la gestione del login degli utenti. I file modificati da me, che fanno parte di questo pacchetto, sono: login.c: permette di fare il login, gestisce il nome utente; su.c: permette di cambiare utente; sulogin.c: è invocato quando il sistema passa in modalità singolo utente. Questi file sono stati modificati sostituendo gli header file di default con i corrispettivi per SELinux e, conseguentemente, le chiamate alle funzioni i cui prototipi erano inclusi da quei file. <path di installazione di devboard>/apps/busybox/loginutils/login.c @@ -17,10 +17,10 @@ 22 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED #include "busybox.h" #ifdef CONFIG_SELINUX -#include <flask_util.h> -#include <get_sid_list.h> -#include <proc_secure.h> -#include <fs_secure.h> +#include <selinux/selinux.h> /* for is_selinux_enabled() */ +#include <selinux/get_context_list.h> /* for get_default_context() */ +#include <selinux/flask.h> /* for security class definitions */ +#include <errno.h> #endif #ifdef CONFIG_FEATURE_U_W_TMP @@ -79,8 +79,7 @@ extern int login_main(int argc, char **a char *opt_host = 0; int alarmstarted = 0; #ifdef CONFIG_SELINUX int flask_enabled = is_flask_enabled(); security_id_t sid = 0, old_tty_sid, new_tty_sid; + security_context_t stat_sid = NULL, sid = NULL, old_tty_sid=NULL, new_tty_sid=NULL; #endif username[0]=0; @@ -225,41 +224,45 @@ auth_ok: #ifdef CONFIG_FEATURE_U_W_TMP setutmp ( username, tty ); #endif + + if ( *tty != '/' ) + snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); + else + safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); + #ifdef CONFIG_SELINUX if (flask_enabled) + if (is_selinux_enabled()) { struct stat st; + int rc; + + + + - if (get_default_sid(username, 0, &sid)) if (get_default_context(username, NULL, &sid)) { fprintf(stderr, "Unable to get SID for %s\n", username); exit(1); } if (stat_secure(tty, &st, &old_tty_sid)) rc = getfilecon(full_tty,&stat_sid); freecon(stat_sid); if ((rc<0) || (stat(full_tty, &st)<0)) { fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno)); 23 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", full_tty, strerror(errno)); return EXIT_FAILURE; } if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) if (security_compute_relabel (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0) { fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno)); fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", full_tty, strerror(errno)); return EXIT_FAILURE; } if(chsid(tty, new_tty_sid) != 0) if(setfilecon(full_tty, new_tty_sid) != 0) { fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno)); fprintf(stderr, "chsid(%.100s, %s) failed: %.100s\n", full_tty, new_tty_sid, strerror(errno)); return EXIT_FAILURE; } freecon(sid); freecon(old_tty_sid); freecon(new_tty_sid); + + + + + + + } else sid = 0; #endif if ( *tty != '/' ) snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); else safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); if ( !is_my_tty ( full_tty )) syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty ); @@ -279,11 +282,10 @@ auth_ok: if ( pw-> pw_uid == 0 ) syslog ( LOG_INFO, "root login %s\n", fromhost ); run_shell ( tmp, 1, 0, 0 #ifdef CONFIG_SELINUX , sid + set_current_security_context(sid); #endif ); /* exec the shell finally. */ + run_shell ( tmp, 1, 0, 0); /* exec the shell finally. */ return EXIT_FAILURE; } 24 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED <path di installazione di devboard>/apps/busybox/loginutils/su.c @@ -147,11 +147,10 @@ int su_main ( int argc, char **argv ) change_identity ( pw ); setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw); run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args #ifdef CONFIG_SELINUX , 0 + , NULL #endif ); return EXIT_FAILURE; } <path di installazione di devboard>/apps/busybox/loginutils/sulogin.c @@ -61,7 +61,9 @@ #ifdef CONFIG_FEATURE_SHADOWPASSWDS struct spwd *spwd = NULL; #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ +#ifdef CONFIG_SELINUX + security_context_t sid; +#endif openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH); if (argc > 1) { if (strncmp(argv[1], "-t", 2) == 0) { @@ -155,6 +155,12 @@ extern int sulogin_main(int argc, char * puts("Entering System Maintenance Mode\n"); fflush(stdout); syslog(LOG_INFO, "System Maintenance Mode\n"); run_shell(pwent.pw_shell, 1, 0, 0); +#ifdef CONFIG_SELINUX + getcon(&sid); +#endif + run_shell(pwent.pw_shell, 1, 0, 0 +#ifdef CONFIG_SELINUX + , sid +#endif + ); return (0); } 5.5.6 Procps. Questo pachetto comprende gli applicativi per la gestione dei processi. I file modificati da me sono: ps.c: mostra la lista dei processi, dopo le modifiche apportate, accetta l'opzione -Z per visualizzare il security context dei processi 25 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED attivi; top.c: visualizza i processi attivi ordinandoli per utilizzo di CPU e facendo un refresh continuo della situazione. Le modifiche effettuate fanno si che il comportamento di top non si differenzi in caso di abilitazione o disabilitazione di SELinux. <path di installazione di devboard>/apps/busybox/procps/ps.c @@ -31,9 +31,7 @@ #include <sys/ioctl.h> #include "busybox.h" #ifdef CONFIG_SELINUX -#include <fs_secure.h> -#include <ss.h> -#include <flask_util.h> /* for is_flask_enabled() */ +#include <selinux/selinux.h> /* for is_selinux_enabled() */ #endif static const int TERMINAL_WIDTH = 79; /* not 80 in case terminal has linefold bug */ @@ -48,8 +46,8 @@ extern int ps_main(int argc, char **argv #ifdef CONFIG_SELINUX int use_selinux = 0; security_id_t sid; if(is_flask_enabled() && argv[1] && !strcmp(argv[1], "-c") ) + security_context_t sid=NULL; + if(is_selinux_enabled() && argv[1] && !strcmp(argv[1], "-Z") ) use_selinux = 1; #endif @@ -58,34 +56,42 @@ extern int ps_main(int argc, char **argv terminal_width--; #ifdef CONFIG_SELINUX if(use_selinux) printf(" PID Context Stat Command\n"); + if (use_selinux) + printf(" PID Context Stat Command\n"); else #endif printf(" PID Uid VmSize Stat Command\n"); -#ifdef CONFIG_SELINUX while ((p = procps_scan(1, use_selinux, &sid)) != 0) { -#else while ((p = procps_scan(1)) != 0) { -#endif char *namecmd = p->cmd; + printf(" PID Uid VmSize Stat Command\n"); + while ((p = procps_scan(1)) != 0) 26 { CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + char *namecmd = p->cmd; #ifdef CONFIG_SELINUX if(use_selinux) { + if ( use_selinux ) + { char sbuf[128]; len = sizeof(sbuf); if(security_sid_to_context(sid, (security_context_t)&sbuf, &len)) strcpy(sbuf, "unknown"); + + + + + + + + + + + + + + + #endif + + + + if (is_selinux_enabled()) { if (getpidcon(p->pid,&sid)<0) sid=NULL; } if (sid) { /* I assume sid initilized with NULL */ len = strlen(sid)+1; safe_strncpy(sbuf, sid, len); freecon(sid); sid=NULL; }else { safe_strncpy(sbuf, "unknown",7); } len = printf("%5d %-32s %s ", p->pid, sbuf, p->state); } } else if(p->rss == 0) len = printf("%5d %-8s %s ", p->pid, p->user, p->state); else len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state); if(p->rss == 0) len = printf("%5d %-8s %s ", p->pid, p->user, p->state); else len = printf("%5d %-8s %6ld %s ", p->pid, p->user, p->rss, p->state); i = terminal_width-len; if(namecmd != 0 && namecmd[0] != 0) { <path di installazione di devboard>/apps/busybox/procps/top.c @@ -510,11 +510,7 @@ int top_main(int argc, char **argv) /* read process IDs & status for all the processes */ procps_status_t * p; -#ifdef CONFIG_SELINUX while ((p = procps_scan(0, 0, NULL) ) != 0) { -#else 27 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED while ((p = procps_scan(0)) != 0) { -#endif int n = ntop; top = xrealloc(top,(++ntop)*sizeof(procps_status_t)); 5.5.7 Sysdeps. Le modifiche fatte a questi file fanno si che nel menù di configurazione di BusyBox, il supporto per SELinux sia abilitato per default, e blocca l'accesso all'opzione Selinux Utilities nel caso in cui il supporto sia disabilitato. <path di installazione di devboard>/apps/busybox/sysdeps/linux/defconfig @@ -16,7 +16,8 @@ CONFIG_FEATURE_DEVPTS=y # CONFIG_FEATURE_CLEAN_UP is not set # CONFIG_FEATURE_SUID is not set -# CONFIG_SELINUX is not set +CONFIG_SELINUX=y +CONFIG_LOAD_POLICY=y # # Build Options <path di installazione di devboard>/apps/busybox/sysdeps/linux/Config.in @@ -235,6 +235,7 @@ source shell/Config.in source sysklogd/Config.in source util-linux/Config.in +source selinux/Config.in menu 'Debugging Options' 5.5.8 Toplevel Makefile. In questo file ho aggiunto alla lista dei sorgenti da compilare, anche quelli contenuti nella cartella selinux da me creata. Questa cartella contiene il file per il caricamento delle politiche. <path di installazione di devboard>/apps/busybox/Makefile @@ -42,11 +42,10 @@ DIRS:=applets archival archival/libunarchive coreutils console-tools \ 28 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + debianutils editors findutils init miscutils modutils networking \ networking/libiproute networking/udhcp procps loginutils shell \ sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb sysklogd util-linux libpwdgrp coreutils/libcoreutils libbb selinux ifeq ($(strip $(CONFIG_SELINUX)),y) -CFLAGS += -I/usr/include/selinux -LIBRARIES += -lsecure +LIBRARIES += -lselinux endif CONFIG_CONFIG_IN = sysdeps/$(TARGET_OS)/Config.in 5.6 Accorgimenti finali. Dopo aver modificato i precedenti file, ho importato le librerie di SELinux, scaricabili dal sito http://www.nsa.gov/selinux/code/download5.cfm, nella cartella include di BusyBox ed ho creato i seguenti file, che aggiungono un'ulteriore opzione nella configurazione di BusyBox, quella necessaria ad abilitare il supporto e a caricare la policy all'interno del kernel. Questo pacchetto compare nel menù di configurazione con il nome di Selinux Utilities. <path di installazione di devboard>/apps/busybox/selinux/Makefile.in @@ -0,0 +1,32 @@ +# Makefile for busybox +# +# Copyright (C) 2003 by Dan Walsh <[email protected]> +# Copyright (C) 1999-2003 by Erik Andersen <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 29 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED +# + +SELINUX_AR:=selinux.a +ifndef $(SELINUX_DIR) +SELINUX_DIR:=$(TOPDIR)selinux/ +endif + +SELINUX-y:= +SELINUX-$(CONFIG_LOAD_POLICY) += load_policy.o +libraries-y+=$(SELINUX_DIR)$(SELINUX_AR) + +$(SELINUX_DIR)$(SELINUX_AR): $(patsubst %,$(SELINUX_DIR)%, $(SELINUX-y)) + $(AR) -ro $@ $(patsubst %,$(SELINUX_DIR)%, $(SELINUX-y)) + <path di installazione di devboard>/apps/busybox/selinux/load_policy.c @@ -0,0 +1,55 @@ +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#include <selinux/selinux.h> +#include <locale.h> /* for setlocale() */ +#include <libintl.h> /* for gettext() */ +#define _(msgid) gettext (msgid) +#ifndef PACKAGE +#define PACKAGE "policycoreutils" /* the name of this package lang translation */ +#endif + +extern int load_policy_main(int argc, char **argv) +{ + int fd, ret; + struct stat sb; + void *map; + + if (argc != 2) { + fprintf(stderr, _("usage: %s policyfile\n"), argv[0]); + return 1; + } + + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + fprintf(stderr, _("Can't open '%s': %s\n"), + argv[1], strerror(errno)); + return 2; + } + + if (fstat(fd, &sb) < 0) { + fprintf(stderr, _("Can't stat '%s': %s\n"), + argv[1], strerror(errno)); 30 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED + + + + + + + + + + + + + + + + +} return 2; } map = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { fprintf(stderr, _("Can't map '%s': %s\n"), argv[1], strerror(errno)); return 2; } ret = security_load_policy(map, sb.st_size); if (ret < 0) { fprintf(stderr, _("%s: security_load_policy failed\n"), argv[0]); return 3; } return EXIT_SUCCESS; <path di installazione di devboard>/apps/busybox/selinux/Config.in @@ -0,0 +1,16 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Selinux Utilities" + +if CONFIG_SELINUX +config CONFIG_LOAD_POLICY + bool "load_policy" + default n + help + Enable support for loading SE Linux into the kernel. +endif +endmenu + <path di installazione di devboard>/apps/busybox/selinux/Makefile @@ -0,0 +1,30 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2003 by Erik Andersen <[email protected]> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software 31 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +TOPDIR:= ../ +SELINUX_DIR:=./ +include $(TOPDIR).config +include $(TOPDIR)Rules.mak +include Makefile.in +all: $(libraries-y) +-include $(TOPDIR).depend + +clean: + rm -f *.o *.a $(AR_TARGET) + Compilando, però, ho riscontrato un nuovo problema: si viene, cioè, a creare un file load_policy.o, l'eseguibile responsabile del caricamento della policy, di dimensioni troppo grandi, perchè, una volta incluso nella mia immagine, che deve contenere anche il kernel e il filesystem, viene superato il limite di 4MByte, dato dalla dimensione della memoria FLASH. In questa fase mi è venuto in aiuto il compilatore messo a disposizione da Axis: tra gli applicativi che compongono il cross-compiler c'è un tool, cris-strip, che permette di togliere le informazioni di debug dal file. Anche se questo fatto renderà difficile le operazioni di debug sulla mia applicazione (se necessarie), mi ha permesso di creare un'immagine adatta ad essere caricata sulla Fox Board. Una volta avviata la scheda con questa nuova immagine, si può vedere che la directory creata come punto di montaggio per il selinuxfs viene popolata con i file mostrati in figura 5.11. 32 CAPITOLO V – “PORTING” DI SELINUX IN AMBIENTE EMBEDDED Figura 5.11. Directory /etc/security/selinux. 33 CONCLUSIONI Conclusioni. Alla fine di questo mio lavoro, posso dire di avere ottenuto un sistema embedded pronto per l'implementazione di SELinux e di una sua politica di sicurezza. Non ho potuto, però, provare tale politica e testare il degrado del sistema in termini di prestazioni, per alcuni semplici motivi. Innanzitutto, come accennato, il fatto che i filesystem utilizzati nel sistema non supportino gli extended attributes e le security labels, incide sul corretto funzionamento di SELinux. In particolare, il cramfs è un filesystem che permette la sola lettura, non è, dunque, adatto per questo tipo di supporto, mentre, per il jffs2, si potrebbero implementare gli extended attributes, ma è un compito che va al di fuori di questo mio lavoro e delle mie stesse conoscenze. Avrei potuto provare a cambiare il filesystem usato nella scheda, ma ho preferito non fare questa prova perché questo tipo di memorie FLASH possono essere riscritte un numero limitato di volte: usare un filesystem non ottimizzato per questi dispositivi potrebbe saturare questo limite in un breve periodo e comprometterne il funzionamento. Nel futuro, però, si potrà continuare con questa piattaforma e SELinux in 1 CONCLUSIONI quanto il team di sviluppo del jffs2 ha annunciato che, probabilmente, la prossima versione del filesystem, cioè il jffs3, sarà dotata di tali caratteristiche. Un ulteriore ostacolo è stato il fatto che, per una corretta implementazione, la policy va configurata e compilata nel sistema. Questa operazione, però, in un ambiente embedded come quello usato nel mio lavoro, non è possibile, a causa della mancanza di alcuni requisiti, come il comando make, che non è compreso tra gli applicativi di BusyBox. Concludendo, posso affermare che l'ambiente embedded, una volta superati questi limiti evidenziati, rappresenta una nuova frontiera per l'implementazione di sistemi operativi sicuri quali SELinux. SELinux, infatti, potrebbe essere adatto a questi dispositivi con funzionalità specifiche, potendo configurare politiche non molto complesse e perfettamente adattate al sistema, piuttosto che in altri ambienti (come server professionali) nei quali si richiede uno sforzo di configurazione tale per cui, allo stato attuale, i costi supererebbero i benefici. 2 BIBLIOGRAFIA [1] Commission of the European Communities. Information Tecnology Security Evaluation Criteria (ITSEC). Provisional Harmonized Criteria: version 1.2, Office for Official Publications of the European Communities, Luxembourg, June 1991 [2] Trusted Computer System Evaluation Criteria, DoD 5200.28_STD U.S. Department of Defence, Dec 1985 [3] Lampson, B.W., Abadi, M., Burrows, M. and Wobber, E. 1991, Authentication in Distributed Systems: Theory and Practice, Operating System Rev., Vol 25, No 5, Oct, pp 165-182 [4] Lampson, B.W. 1974. Protection. Oper. Syst. Revi., 8, 1, Jan, pp 18-24 [5] The Inevitability of Failure: The Flawed Assumption of Security in Modern Computing Environments. Peter A.Loscocco, Stephen D. Smalley, Patrick A. Muckelbauer, Ruth C. Taylor, S. Jeff Turner, John F. Farrell, National Security Agency, Oct 2002 [6] Anderson, J.P. 1972. Computer Security Technology Planning Study. ESD-TR-73-51, Vol 1, Hanscom AFB, Mass [7] Schell, R.R. 1974. Effectiveness – The Reason for a Security Kernel. In Proceedings of the National Computer Computer Conference. pp 975-976 [8] Ford B., Hibler M., Lepreau J., McGrath R. and Tullmann P., 1999. Interface and execution models in the Fluke Kernel. In Proceedings of the 3rd USENIX Symposium on Operating Sstem Design and Implementaion, Feb, pp 101-116 [9] Smalley S. D., 2002. Configuring the SELinux Policy. Nai Labs Report #02-007, June [10] P. Loscocco and S. Smalley. Integrating Flexible Support for Security Policies into the Linux Operating System. Technical report, NSA and NAI Labs, Oct. 2000 [11] Michael Fox, John Giordano, Lori Stotler, Arunn Thomas. SELinux and grsecurity: A Case Study Comparing Linux Security Kernel Enhancements. University of Virginia, Department of Computer Science, Olsson Hall [12] Bill McCarty, SELINUX NSA's Open Source Security Enhanced i BIBLIOGRAFIA Linux, O'Reilly, Oct 2004 [13] Karim Yaghmour, Building Embedded Linux System, O'Reilly, Apr 2003 [14] Craig Hollabaugh Ph.D. Embedded Linux®: Hardware, Software, and Interfacing. Addison Wesley, Mar 2002 [15] John Lombardo. Embedded Linux®. New Riders [16] [17] [18] [19] [20] [21] [22] [23] [24] [25] http://developer.axis.com http://www.acmesystems.it/ http://www.nsa.gov/selinux/ http://mhonarc.axis.se/dev-etrax/ http://www-128.ibm.com/developerworks/library/s-selinux/ http://www.freego.it/articles/show/58 http://www.technologyreview.it/index.php?p=article&a=255 http://www-128.ibm.com/developerworks/linux/library/l-busybox/ http://forum.ioprogrammo.it/ http://en.wikipedia.org/ ii