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(&current_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