software,

Sandboxing in iOS: usciamo dal recinto

Sabino Maggi Sabino Maggi Segui 22-May-2014 · 14 minuti di lettura
Condividi

Esiste una soluzione semplice al problema del sandboxing delle applicazioni per iOS, che non impatti sulla sicurezza del sistema ma che allo stesso tempo permetta a più applicazioni di aprire e modificare un dato documento, evitando inutili duplicazioni e, quel che è peggio, di dover tenere traccia delle modifiche effettuate da ciascuna applicazione?

Soluzioni fattibili – più o meno semplici e più o meno affidabili o sicure – ce ne sono bizzeffe. Qui ne propongo una anch’io, che ha la caratteristica di sfruttare solo gli strumenti già esistenti in Unix e nei sistemi operativi derivati, fra cui anche iOS.

Sia chiaro che non voglio sostituirmi agli ingegneri e ai programmatori della Apple, che conoscono molto meglio di me il sistema su cui lavorano. Quello che voglio è solo dare un contributo di principio e di dimostrarne la fattibilità pratica.

L’eventuale (eventuale, non dimentichiamolo!) implementazione finale sarà di certo molto più brillante ed efficiente di questa.

Per evitare ambiguità userò nel seguito un linguaggio standard, evitando le definizioni tipiche del mondo Apple: directory al posto di cartella, file invece di documento, soft-link invece che link simbolico, e ogni volta che mi riferirò a Unix intenderò in effetti tutti sistemi operativi che derivano dalla base di Unix, come Linux, i vari BSD e ovviamente OS X e iOS.

Tutto è un file

Prima di proseguire il discorso bisogna ricordare alcuni concetti fondamentali sui file in Unix.

Tutto è un file in Unix. Questo è uno dei primi mantra che si imparano quando ci si accosta a questo sistema operativo. Una directory è un file, persino la comunicazione fra i vari processi o con i dispositivi hardware avviene tramite file.

Ma com’è fatto un file in Unix?

Un file è composto da tre parti diverse e ben distinte fra loro: il nome del file, il contenuto del file e i metadati, una serie di informazioni accessorie sul file che esamineremo meglio dopo.

File in Unix

Il contenuto del file è salvato in uno o più settori del disco rigido, i metadati sono memorizzati negli inode, delle strutture dati create al momento della formattazione del disco rigido e numerate univocamente, e infine il nome del file si trova nella directory (anch’essa un file!) che lo contiene, in una tabella che associa ad ogni nome di file della directory il numero dell’inode in cui sono salvati i metadati corrispondenti.

Struttura di una directory

Ogni inode è una struttura dati (una specie di tabella un po’ più complicata) contenente, fra l’altro, la dimensione del file, i permessi di accesso e di modifica, la data e l’ora in cui in cui il file è stato creato, modificato o semplicemente letto, i puntatori ai settori fisici del disco rigido in cui è salvato il contenuto vero e proprio del file ed infine, quello che ci interessa di più, un numero intero che serve a contare il numero di copie (virtuali) del file stesso create tramite i cosiddetti hard-link.

Struttura di un inode

Se a questo punto non vi gira già la testa potete continuare a leggere…

In Unix è possibile riferirsi in modo indiretto ad un file mediante due diversi tipi di file: gli hard-link e i soft-link.

Dei soft-link ho già scritto in questo blog. Aggiungo che il soft-link crea un riferimento al nome del file, e si usa principalmente per dare un nome diverso ad un file.

Un hard-link crea invece un riferimento al numero dell’inode che contiene i metadati del file di partenza. Il file creato tramite un hard-link appare quindi a tutti gli effetti una copia del file originale, anche se non è esattamente così. Quando si copia un file, il nuovo file ha nome ed inode diverso ed anche il contenuto del file originale viene copiato in un’altra area del disco rigido. Creando un hard-link, il nuovo file avrà solo un nome diverso ma continuerà a riferirsi allo stesso inode, e quindi allo stesso contenuto, sul disco rigido.

Hard-link e soft-link

Qualunque operazione effettuata sull’hard-link – modifica del contenuto del file, dei permessi, dell’ora di accesso al file – appare come se fosse effettuata anche sul file originale e su tutti i suoi hard-link: un hard-link è a tutti gli effetti lo stesso file, ma con un nome diverso.

A differenza del soft-link, però, che perde il riferimento al file originale e non serve più a nulla se quest’ultimo viene cancellato dal disco rigido, un hard-link continua ad essere valido anche se si cancella il file a cui fa riferimento.

Un hard-link ad un file viene creato con il comando,

$ ln file hfile

dove file è il file originale e hfile è l’hard-link (ovviamente i due file possono anche essere in directory diverse). Per creare il soft-link sfile basta aggiungere l’opzione -s al comando precedente.

$ ln -s file sfile

Ogni volta che si crea un nuovo file, il contatore delle copie del file contenuto nell’inode viene settato ad 1. Aggiungendo un hard-link a quel file, il sistema operativo incrementa di 1 il contatore, cancellando l’hard-link il contatore viene decrementato di 1. In effetti, tramite il contatore, il sistema operativo può sapere quanti hard-link sono presenti sul disco rigido oltre al file originale.

Arrivati a questo punto, chi vuole può saltare la sezione seguente e andare direttamente all’ultima parte del post.

Un po’ di pratica…

Usiamo ora il Terminale di OS X per mettere in pratica quanto abbiamo appena visto.

Il comando touch crea un nuovo file, a cui aggiungiamo una riga di testo qualunque con echo (un metodo quasi da hacker, si potrebbe benissimo farlo con un editor di testo).

$ touch file.txt
$ echo "Ho appena creato un file di testo." >> file.txt

Tramite ls -l mostriamo il file appena creato e una parte dei metadati associati.

$ ls -l
-rw-r--r--  1 maggi  staff  35 May 19 22:53 file.txt

Ci interessano qui due numeri in particolare. Quello contenuto nella seconda colonna è il contatore delle copie del file e vale 1 poiché il file è stato appena creato. Il numero nella quinta colonna, 35, è invece la dimensione in byte del file, uguale al numero di lettere che compongono il testo aggiunto con echo più il carattere (invisibile) di a capo.

Creiamo ora un hard-link e un soft-link che puntano entrambi al file di testo file.txt.

$ ln file.txt hfile.txt
$ ln -s file.txt sfile.doc

Eseguendo di nuovo ls -l,

$ ls -l
-rw-r--r--  2 maggi  staff  35 May 19 22:53 file.txt
-rw-r--r--  2 maggi  staff  35 May 19 22:53 hfile.txt
lrwxr-xr-x  1 maggi  staff   8 May 19 22:54 sfile.doc -> file.txt

si notano alcune cose fondamentali: (1) l’hard-link appare come un file del tutto identico al file originale, dimensioni, permessi, data di creazione (o di accesso), (2) il soft-link ha invece date, dimensioni e permessi differenti, (3) ed è anche mostrato in modo diverso nel Terminale, con una freccia che punta la file originale e con una l al posto del - nel primo carattere della riga, (4) avendo creato un hard-link il contatore delle copie del file contenuto nell’inode viene incrementato di 1 e, poiché i due file si riferiscono allo stesso inode (e quindi allo stesso contatore), il valore del contatore è uguale per file.txt e hfile.txt.

L’opzione -i del comando ls permette di stampare il numero dell’inode associato ad ogni file. Nel nostro esempio, si vede chiaramente che l’inode del file originale e quello dell’hard-link associato sono uguali.

$ ls -li
219115720 -rw-r--r--  2 maggi  staff  35 May 19 22:53 file.txt
219115720 -rw-r--r--  2 maggi  staff  35 May 19 22:53 hfile.txt
219115795 lrwxr-xr-x  1 maggi  staff   8 May 19 22:54 sfile.doc -> file.txt

Creando un hard-link si incrementa il contatore del numero di file contenuto nell’inode,

$ ln file.txt h2file.dat
$
$ ls -li
219115720 -rw-r--r--  3 maggi  staff  35 May 19 22:53 file.txt
219115720 -rw-r--r--  3 maggi  staff  35 May 19 22:53 h2file.dat
219115720 -rw-r--r--  3 maggi  staff  35 May 19 22:53 hfile.txt
219115795 lrwxr-xr-x  1 maggi  staff   8 May 19 22:54 sfile.doc -> file.txt

mentre se si cancella un hard-link il contatore viene decrementato di 1.

$ rm hfile.txt
$
$ ls -li
219115720 -rw-r--r--  2 maggi  staff  35 May 19 22:53 file.txt
219115720 -rw-r--r--  2 maggi  staff  35 May 19 22:53 h2file.dat
219115795 lrwxr-xr-x  1 maggi  staff   8 May 19 22:54 sfile.doc -> file.txt

Usciamo dal recinto

Per permettere a più applicazioni iOS di accedere alla stesso file, evitando allo stesso tempo la moltiplicazione dei file in diverso stato di “lavorazione”, serve un meccanismo che permetta di inserire tutti i documenti in un’area comune, riuscendo però a fare in modo che ciascuna applicazione continui ad accedere solo ai documenti che è autorizzata a gestire, cioè quelli contenuti nel suo sandbox.

Ricordo che il sandbox è un meccanismo che limita l’accesso di una applicazione ai file, alle preferenze, alle risorse di rete, all’hardware e così via, incapsulando ciascuna applicazione con i suoi documenti in una directory protetta.

Proprio gli hard-link possono essere sfruttati per implementare questo meccanismo.

In iOS (ma anche in OS X) le applicazioni ci appaiono come delle semplici icone. In realtà ogni applicazione è costituita da una directory che contiene al suo interno tutto quello che le serve per funzionare: l’eseguibile, le librerie, i file di supporto e di configurazione, le immagini e le icone e i documenti creati con l’applicazione stessa.

Immaginiamo ora di avere due applicazioni installate nel nostro dispositivo iOS. Se guardiamo al loro interno, osserveremo una struttura delle directory simile a questa.

$ tree
.
├── appA
│   ├── appA.app
│   ├── documents
│   ├── library
│   └── tmp
└── appB
	├── appB.app
	├── documents
	├── library
	└── tmp

La directory con estensione .app contiene il file eseguibile dell’applicazione, le altre non necessitano di spiegazioni. Si noti che il comando tree usato per mostrare la struttura delle directory delle due applicazioni non esiste di default in OS X, ma può essere installato sul Mac tramite Homebrew.

Aggiungiamo qualche file in ciascuna delle due applicazioni.

$ tree
.
├── appA
│   ├── appA.app
│   ├── documents
│   │   ├── file-AB.txt
│   │   └── file_A1.pdf
│   ├── library
│   └── tmp
└── appB
	├── appB.app
	├── documents
	│   ├── file-AB.txt
	│   ├── file_B1.pdf
	│   └── file_B2.txt
	├── library
	└── tmp

Come abbiamo già visto, ognuna delle due applicazioni può gestire solo i documenti contenuti al suo interno. Le due applicazioni possono al massimo scambiarsi i file contenuti nella directory documents di ciascuna, copiandoli integralmente da una applicazione all’altra. Nell’esempio precedente il file comune è file-AB.txt.

Una volta completata la copia, le modifiche effettuata da appA su file-AB.txt sono del tutto indipendenti da quelle effettuate da appB sul file omonimo contenuto nel suo sandbox. L’unico modo per mantenere il file sincronizzato fra le due applicazioni è quello di copiarlo dopo ogni modifica dal sandbox di una applicazione a quello dell’altra. Una cosa poco pratica e facilmente soggetta ad errori.

Immaginiamo invece di creare una directory pool, esterna alle due applicazioni, che agisca come spazio comune per tutti i documenti.

$ tree
.
├── appA
│   ├── appA.app
│   ├── documents
│   ├── library
│   └── tmp
├── appB
│   ├── appB.app
│   ├── documents
│   ├── library
│   └── tmp
└── pool
	├── file-AB.txt
	├── file_A1.pdf
	├── file_B1.pdf
	└── file_B2.txt

Ma le applicazioni presuppongono che i documenti siano all’interno del loro sandbox. È possibile fare in modo che le applicazioni riescano comunque ad accedere ai file in pool?

Facile: basta creare degli hard-link che dalla directory documents di ogni applicazione puntino ai file contenuti in pool. Ricreando tramite hard-link il contenuto originale delle directory documents delle due applicazioni si ottiene,

$ tree
.
├── appA
│   ├── appA.app
│   ├── documents
│   │   ├── hfile-AB.txt
│   │   └── hfile_A1.pdf
│   ├── library
│   └── tmp
├── appB
│   ├── appB.app
│   ├── documents
│   │   ├── hfile-AB.txt
│   │   ├── hfile_B1.pdf
│   │   └── hfile_B2.txt
│   ├── library
│   └── tmp
└── pool
	├── file-AB.txt
	├── file_A1.pdf
	├── file_B1.pdf
	└── file_B2.txt

dove, per chiarezza, gli hard-link sono indicati dal prefisso h aggiunto al nome del file.

Il vantaggio principale di questo meccanismo è che se una delle due applicazioni modifica il file hfile-AB.txt contenuto nel suo sandbox (quindi all’interno della sua directory documents), tutte le modifiche fatte dall’applicazione valgono anche per il file originale e per l’hard-link corrispondente contenuto nel sandbox dell’altra applicazione.

Ovviamente quanto detto finora è facilmente generalizzabile a un numero qualunque di applicazioni e di file.

Un altro vantaggio, che dovrebbe essere evidente da quanto detto in questo lunghissimo articolo, è che un hard-link non occupa altro spazio sul disco rispetto a quello occupato dal file originale. Si evita quindi di sprecare inutilmente spazio sul disco dell’iPhone e dell’iPad a causa della duplicazione dei file trasferiti da una applicazione all’altra. E si sa bene che lo spazio su qualunque computer non basta mai.

Ma come fa l’applicazione a inserire un nuovo documento in pool e a creare l’hard-link corrispondente nella sua directory documents? La risposta è semplice: non serve che lo faccia l’applicazione, il meccanismo immaginato qui può essere gestito dal solo sistema operativo. In altre parole, il meccanismo può essere implementato a livello del sistema operativo in modo del tutto trasparente alle applicazioni, che quindi non hanno bisogno essere modificate per funzionare correttamente.

Quando l’applicazione appA apre per la prima volta file-AB.txt, è il sistema operativo che si occupa di inserire il file in pool e di creare l’hard-link nel sandbox di appA. Il tutto è assolutamente trasparente ad appA, che non si accorge nemmeno di quello che è successo dietro le quinte. Se poi a un certo punto ho bisogno di inviare file-AB.txt da appA ad appB, posso limitarmi a cliccare come succede già oggi sull’icona apposita in appA, e sarà poi il sistema operativo ad intercettare la richiesta di copia del file, creando in realtà un nuovo hard-link in appB che punta anch’esso a file-AB.txt.

Infine, se si disinstalla una applicazione, supponiamo appB, anche i suoi documenti vengono cancellati dal disco, compreso hfile-AB.txt condiviso con appA. Ma il documento originale e l’hard-link in appA rimangono sul disco e non vengono persi inavvertitamente. Solo se si cancella anche appA, il sistema operativo attraverso il contatore nell’inode, si accorge che non ci sono più hard-link a file-AB.txt contenuto in pool e può decidere di cancellare anche il file originale, magari chiedendo conferma esplicita al proprietario o dopo aver effettuato un backup di sicurezza sul Mac.

E anche la cancellazione dei file può essere affidata senza problemi al sistema operativo e non deve essere gestita dalla singola applicazione.

Detto questo, aspettiamo pazientemente la WWDC 2014 per sapere che diavolerie inventerà veramente la Apple, in questo settore. Ammesso, naturalmente, che ne inventi qualcuna.

Sabino Maggi
Pubblicato da Sabino Maggi Segui
Commenti

Aggiungi un commento