giovedì 1 maggio 2014

Quel pasticcio di Heartbleed

L’Antefatto Il giorno primo gennaio 2011 viene dato l’ok per l’inclusione di una porzione di codice relativa ad una feature del protocollo TLS all’interno della libreria OpenSSL. Tale feature era un’estensione del protocollo volta a consentire a due server TLS di comunicare tra loro dei dati e verificare che la connessione tra i due fosse stabile. Tale estensione fu standardizzata nel febbraio 2012, ovvero più di un anno dopo l’inclusione del codice, nel RFC 6520.

In quella porzione di codice però, per un malaugurato errore, non era stato inserito un controllo di congruenza tra la quantità di dati inviata e quella richiesta.

7 aprile 2014
Viene diramato il comunicato che una nuova versione di OpenSSL è stata rilasciata e che gli utenti della libreria sono caldamente invitati ad effettuare l’upgrade per mitigare gli effetti della vulnerabilità indicata dal codice CVE-2014-0160 (Heartbleed).

Dal 8 aprile in poi
Viene aperto il sito ufficiale di Heartbleed [heartbleed.org] e il panico si diffonde nella rete: siti e blog tecnici disquisiscono sulle possibili implicazioni del bug Heartbleed mentre nel resto dei media si diffonde l’allarme sulle possibili fughe di password e altri segreti.

Le conseguenze
Il bug Heartbleed ha avuto un notevole impatto, specialmente emotivo, ma se cerchiamo materiale in merito in lingua italiana troviamo ben poco… Anche la pagina di Wikipedia in merito è decisamente scarna…
Per ovviare un po’ a questa lacuna (e perché lo stesso blog degli GNUrants è ancora molto scarno) vi esporrò (al meglio delle mie capacità) quello che ho appreso sul bug in questione e sul suo impatto dal punto di vista tecnico per poi passare ad esporre alcune considerazioni sul come si è arrivati a tutto questo e su quali passi ritengo si debbano prendere per ridurre la probabilità che si verifichi di nuovo una simile situazione (alcuni di questi passi sono già stati intrapresi, altri richiedono tempi molto più lunghi).

Giochiamo con l’input
Cominciamo col catalogare Heartbleed e col descrivere di che genere di vulnerabilità si tratta…
Per prima cosa dobbiamo studiare un po’ la feature Heartbeat del protocollo TLS e capire cosa fa e in questo ci viene in aiuto Randall di XKCD (immagine omessa per questioni di Copyright): in breve l’Heartbeat è un meccanismo per chiedere ad un server TLS se è ancora vivo in una maniera simile al caro buon vecchio ICMP Echo Request/Echo Response. Il problema è che, nell’implementazione di OpenSSL è possibile forgiare un pacchetto che abbia un messaggio breve ma che richieda una risposta lunga e la libreria, invece di ignorare tale richiesta malformata, provvederà ad allocare abbastanza spazio nella memoria per mandare indietro la risposta.
“E questo è un problema?” E’ un problema nel momento in cui in quella porzione di memoria ci sono informazioni sensibili che non verranno sovrascritte ma inviate a chi ha fatto la richiesta.
“Di che informazioni stiamo parlando?” In generale di qualsiasi informazione che sia presente nello spazio di memoria a disposizione di OpenSSL, il che può voler dire cose come:
  • Traffico già criptato & spazzatura.
  • Traffico non ancora criptato (token di autenticazione, email, comunicazioni VoIP).
Nel primo caso non ci sono informazioni utili subito disponibili all’attaccante, ma il secondo caso è tutta un’altra storia!

Andiamo più in profondità
Ok, è venuto il momento tanto atteso: adesso faremo un salto dentro al codice sorgente e vedremo un po' più in dettaglio cosa ha causato tutto questo pasticcio. Come riferimento userò la patch rilasciata dai maintainer del progetto OpenBSD e la ragione è duplice:
  1. E’ molto leggibile e ben documentata.
  2. Va dritta al punto.
La parte iniziale è un commento su cosa si va a correggere e su come applicare la patch, segue la patch vera e propria in formato diff. Per chi non fosse familiare con i diff: le righe che cominciano con un “-” sono righe eliminate, quelle che cominciano con un “+” sono righe aggiunte e infine quelle che non hanno simboli all’inizio sono invariate. Noi ci concentreremo prima sulle righe con il “-”.
E adesso, finalmente, vediamo il codice C:

    /* Read type and payload length first */
    hbtype = *p++;
    n2s(p, payload);
    pl = p;

Ok, questa è una porzione di libreria che è stata tolta, il commento ci dà un indizio: “Read type and payload length first”.
Non abbiamo tutto il codice sorgente sotto gli occhi, ma è ragionevole supporre che p sia un puntatore ai dati del pacchetto TLS Heartbeat appena ricevuto e siccome il tipo è indicato nel RFC come intero a 8 bit (ma può assumere come valori solo 1 o 2) il nostro programmatore ha deciso di fare in fretta e fare due cose con un'unica istruzione: leggere il valore (salvandolo in hbtype) e portarsi al campo successivo con l'uso dell'operatore di post-incremento (il “++” dopo “p”) il “*” davanti a “p” è necessario perché altrimenti invece del valore leggeremmo l'indirizzo di memoria in cui questo si trova (e non ce ne faremmo nulla). Fin qui nulla di strano, si tratta di una pratica standard nella programmazione C anche se è malvista perché riduce la leggibilità del codice.
La linea di codice successiva è una chiamata alla funzione n2s: anche qui non abbiamo tutto il codice, ma possiamo ricavare dal contesto e dai parametri che gli vengono passati che quella funzione non faccia altro che leggere la lunghezza del messaggio di Heartbeat dal pacchetto e salvare tale lunghezza in payload. Notate bene: questa è la lunghezza dichiarata, non necessariamente la lunghezza reale del messaggio. Tenete bene a mente che non abbiamo alcuna garanzia che quello che ci viene detto corrisponda a verità e che il diavolo sta nei dettagli.
L'ultima riga serve a salvare un puntatore al messaggio vero e proprio in “pl”.

Segue una porzione che è rimasta invariata e che si occupa di controllare se è stata registrata una callback e di chiamare tale callback: le ragioni per cui si vuole poter chiamare una funzione esterna quando si comincia a processare un pacchetto possono essere le più varie, ma di solito lo si fa per avere un log per questioni di debug.

Dopo una sezione di codice aggiunto (che ignoreremo) abbiamo le righe seguenti:

    if (hbtype == TLS1_HB_REQUEST)
        {
        unsigned char *buffer, *bp;
        int r;
        /* Allocate memory for the response, size is 1 bytes
         * message type, plus 2 bytes payload length, plus
         * payload, plus padding
         */
        buffer = OPENSSL_malloc(1 + 2 + payload + padding);
        bp = buffer;
        /* Enter response type, length and copy payload */
        /*...Omississ...*/
        /* Random padding */
        RAND_pseudo_bytes(bp, padding);

        r = dtls1_write_bytes(s, TLS1_RT_HEARTBEAT, buffer, 3 + payload +
                        padding);
        if (r >= 0 && s->msg_callback)
            s->msg_callback(1, s->version, TLS1_RT_HEARTBEAT,
               buffer, 3 + payload + padding,
               s, s->msg_callback_arg);

        OPENSSL_free(buffer);


Allora, vediamo un po' cosa abbiamo qui… Questa riga qui è la radice del Male:

buffer = OPENSSL_malloc(1 + 2 + payload + padding);

OpenSSL è una libreria multipiattaforma, il che significa che deve girare su una gran varietà di Sistemi Operativi diversi. Alcuni di questi offrono meccanismi di protezione della memoria, altri no. Per avere una base comune gli sviluppatori di OpenSSL hanno creato una loro implementazione delle chiamate malloc e free: la prima riserva della memoria mentre la seconda la libera. Il problema di questo approccio è che, se non fai le cose per bene, puoi bypassare completamente i meccanismi che il Sistema Operativo adotta per proteggere la memoria e ridurre l’impatto che possono avere certi errori di programmazione.
La chiamata malloc fa parte della libreria standard del C e si appoggia a chiamate simili del sistema operativo per allocare (riservare) una porzione di memoria che un programma in esecuzione può utilizzare come meglio crede (anche condividendola con librerie e/o altri programmi in esecuzione). malloc può riservare aree di memoria precedentemente non utilizzate da altri programmi (bene) oppure aree di memoria già utilizzate da altri processi e da questi rese libere per il riutilizzo (male perché potrebbero ancora contenere dati) mentre OPENSSL_malloc riutilizza memoria pre-allocata dalla libreria stessa (molto male) e mantiene una sua lista delle allocazioni per poter far funzionare OPENSSL_free (la funzione che si occupa di liberare la memoria per il riutilizzo). Facendo così OpenSSL bypassa i meccanismi di ASLR (Address Space Layout Randomization) messi in atto dal kernel del Sistema Operativo per ridurre l'impatto di certi errori di programmazione.
Una discussione completa sui meccanismi di protezione della memoria esula dallo scopo di questo articolo, ma invito il lettore a dare un'occhiata alle slide che Theo DeRaadt ha proposto al ruBSD 2013 e, per i più curiosi, all'articolo in merito ad ASLR su Wikipedia.
Quello che succede in quella porzione di codice è che il programmatore si è fidato della lunghezza dichiarata dal pacchetto e ha riservato una porzione di memoria pari a quella lunghezza. Come già detto OpenSSL gestisce per conto suo la memoria e, siccome la memoria non è infinita, riutilizza la memoria. Nella porzione di codice precedente quella memoria non viene “pulita” prima di essere utilizzata e quindi potrebbe contenere qualsiasi cosa. Inoltre non vengono fatti dei controlli che il messaggio sia lungo quanto dichiarato.
Se il messaggio è più breve del valore dichiarato quello che succede è che viene occupata solo parte della memoria allocata e il resto viene spedito così com'è al richiedente.
C'è una piccola consolazione: il campo lunghezza consente di avere un payload che può essere lungo al massimo 65536 byte e occorre indicare almeno un byte per il messaggio portando la quantità di dati leggibili a dall'attaccante a 65535 byte (64 kilobyte). La probabilità di trovare dati utili in una finestra così stretta si abbassa molto ed occorre anche essere in grado di distinguere i dati utili dalla spazzatura (e gli algoritmi di generazione delle chiavi crittografiche fanno di tutto per far apparire le chiavi stesse come dati casuali apparentemente senza capo nè coda), ma è già stato dimostrato che avendo abbastanza pazienza (si parla di milioni di tentativi) si può leggere anche la chiave privata usata da un server web per decrittare TUTTE le comunicazioni criptate.

Conclusioni
Il bug in questione ha tutta l'aria di essere finito lì a causa di una svista: la feature era nuovissima (ancora in fase di standardizzazione) e in seguito è stata poco utilizzata (pochi hanno sentito il bisogno di usare il TLS Hearthbeat quando ci sono decine di altre tecniche di High Availability disponibili) per cui pochi occhi si sono concentrati su quel codice.
Sicuramente è significativo che anche nel mondo dell'Open Source ci siano casi di feature “aggiunte e dimenticate” che ricevono poca o nessuna manutenzione. Ed è significativo che il presupposto principale dello sviluppo a sorgente aperto (molti occhi che guardano il codice si accorgono prima di certi errori) sia venuto meno nel caso di una delle librerie più utilizzate per la comunicazione sicura di pagine web, posta elettronica e una moltitudine di altri servizi.
Ci si è fidati della buona volontà e delle capacità di chi ha scritto OpenSSL (persone che meritano la stima e il rispetto di tutti quanti noi per il lavoro svolto) senza ricontrollare e così un errore fatto in buona fede è finito per avere un impatto clamoroso su tutta l'infrastruttura su cui si basa il web 2.0. Non basta che il codice sia visibile a tutti: occorre che qualcun altro oltre agli sviluppatori ci dia un'occhiata ogni tanto.
Ricette magiche non ce ne sono, ma questo pasticcio ha senz'altro portato all'attenzione di tutti le falle presenti in OpenSSL e la necessità di rimettere a posto quel codice nel suo complesso (e non solo la parte relativa al bug Heartbleed).
Moltissime aziende utilizzano OpenSSL nei loro prodotti (grazie anche alla licenza molto liberale) e alcuni dei player più grossi si sono resi conto che forse è il caso di dare qualcosa indietro a quei quattro gatti che lavorano su quel codice così importante. La mia speranza è che (oltre a beccarsi enormi quantità di trolling) lo sforzo degli sviluppatori venga premiato e che altra gente cominci a pensare che non basta sviluppare un daemon DHCP nell'init system ma che c'è anche bisogno di mantenere e controllare quello che già c'è.

martedì 22 aprile 2014

A proposito di systemd

Qualche tempo fa nel covo segreto degli Illuminati...

Gianfranco Gallizia
Non me n'ero accorto ma abbiamo una prima richiesta: un rant su systemd!
Chi di voi giovini vuole scriverlo? Io sono poco pratico di queste cose moderne! XD

Diego Pi
Iniziamo dal fondo: un server DHCP nell'init e log scritti in un formato binario non documentato che di fatto li rende closed.

Gianfranco Gallizia
Il fatto che systemd spezzi parecchie delle convenzioni di UNIX/POSIX? Non è un singolo tool che si integra col resto del sistema, ma un nuovo sistema che si appoggia al kernel Linux. Può essere un bene o un male, ma io sono un fan di POSIX perché ha passato la prova del tempo. systemd è un pischello.
Un'altra mia obiezione a systemd è la convinzione dei suoi sviluppatori di essere nel giusto. Leggevo l'altro giorno di un rant di Linus Torvalds in merito al fatto che hanno dovuto patchare il kernel in modo da celare in /proc/cmdline la stringa "debug" perché altrimenti systemd spara talmente tanta merda di logging da bloccarsi all'avvio e impedire il boot!
Se non è un "WTF!" questo...

Federico Di Pierro
Diego: ricordiamo che si può disabilitare tranquillissimamente, nessuno è forzato a usarlo.
Gianfranco: su Torvalds, dell'altro giorno, ho letto un po' e ti do ragione, ma errare è umano.
Systemd è un insieme di tool, tant'è vero che non è solo un init system, ma un "System and Service Manager", ha molte più funzionalità del vecchio init system standard. Ma funziona meglio, e questo è innegabile. In più in fase di compilazione si può togliere parecchia roba (ma non mi sono mai documentato su quanto si possa effettivamente non compilare). Ovviamente io stesso ho qualche dubbio sul fatto che quest'accentramento sia corretto...
A volte sembra di utilizzare GNU/linux/systemd ormai.
Ma provate a vederla come un utente: hai un sistema che boota in 5 secondi, hai dei servizi che sono altamente personalizzabili e facili da creare, hai un avvio di sistema gestibile molto semplicemente (systemctl enable/start...lo capirebbe chiunque)...
Capite anche voi che rompe tante convenzioni, ma l'altra faccia della medaglia è che regala tante innovazioni!

Fanfurlio Farolfi
Federico Di Pierro a me piace systemd, ho solo alcune cose da recriminargli...
Il fatto dei log scritti in formato binario ad esempio, lo trovo fastidioso, ma non del tutto inutile.
Se fosse documentato il formato, mi piacerebbe.

Gianfranco Gallizia
Federico d’accordo, ma capisci che cose come la questione del "debug" sono uno show-stopper per chi deve far funzionare dei servizi con il 99.98% di uptime? Non esiste che un flag di avvio del kernel mi schianti l'init system perchè quest'ultimo fa troppi log!

Diego Pi
Federico systemd mi piace... Sono le idiosincrasie che si porta dietro a tirarmi fuori WTF.
Peccato io veda poco sforzo per sistemare le poche scemenze che ha e tante energie spese a buttare dentro qualsiasi tipo di funzionalità.
Il bug tirato fuori da Gianfranco è patognomonico di ciò che dico. Non puoi fixare un bug di un tuo software proponendo una patch per il kernel.

Fanfurlio Farolfi
Aggiungerei "inutile" a "funzionalità", a che serve un server DHCP all'interno del sistema di init? Non bastava far partire prima quello installato?

Federico Di Pierro
Infatti lato kernel T. gli ha aperto il culo.
Però per me è normale sbagliare...poi Lennart ha sempre dovuto combattere tra insulti (ahimè anche personali) e altre cazzate. Ovviamente si è creata una "cerchia" PRO systemd e una CONTRO. Anche se a breve resterà l'unico init system ormai.
Diciamo che effettivamente, ed è ovvio, su alcune cose errano e spero correggano il tiro. Però per ora secondo me, come tecnologia, vale la candela. Qualche bug, qualche cazzata, ma era impensabile fino a qualche anno fa un init system del genere.

Gianfranco Gallizia
Poi c'è un altro potenziale problema che potrebbe manifestarsi con l'attuale politica di integrazione sfrenata di servizi attuata dagli sviluppatori di systemd. Mettiamo il caso che in uno dei vari sottosistemi di systemd ci sia un heap overflow che consenta di eseguire codice arbitrario in kernel space: ci sarebbe un'ecatombe.
Il vecchio init, per quanto brutto e lento, è troppo semplice per consentire un simile approccio. systemd dal'tro canto ha già costretto a riscrivere parti del kernel Linux per adattarsi alle sue esigenze e chi mi dice che in tutte le migliaia di righe di codice che stanno scrivendo per aggiungere server DHCP, connessioni di rete up in 500 nanosecondi e PoetteringSaCosa non ci sia un bug nascosto che si verifica solo in determinate condizioni ma che possa essere sfruttato per fini oscuri?
Dunque: qual è il verdetto della giuria?

Federico Di Pierro
Systemd ha solamente bisogno di tempo per maturare per bene. Credo che voi abbiate ragione quando parlate insistentemente di bugfixing al posto di continuare a buttarci dentro roba (a volte anche “inutile” per un init system)...mi ricorda un po’ Gnome (e vai di flame!!) anche se qua almeno per fixare i bug non rimuovono le feature! :D
In compenso, come già detto e sottolineato, è talmente più avanzato dei vecchi init che mi vien da pensare: “a me cazzo me ne frega a me, c’ho il diesel!”.

Dissolvenza… Seguono rumori di una violenta collutazione...

venerdì 4 aprile 2014

Chi sono gli GNUrants?

Salve a tutti e quattro i nostri lettori! Scrivo queste poche righe a nome e per conto degli GNUrants per dare due o tre informazioni in merito a questo piccolo angolo della vasta, sconfinata e sorvegliata internet (ciao NSA!).

GNUrants deriva da un gioco di parole tra "GNU rants" (ovvero lamentazioni/invettive/discorsi enfatici relativi a GNU) e l'espressione dialettale "'gnurant" (ignorante). Gli GNUrants sono quindi invettive prodotte dalle menti di ignoranti autoproclamatisi tali in merito a GNU/Linux, Software Libero, Informatica e in generale qualsiasi altra cosa che ottenga l'approvazione della maggioranza degli Illuminati... ehm... degli autori e degli editor del Blog.

Un'ultima nota: non vi aspettate cose come articoli con cadenza periodica scritti con un vocabolario che rispetti l'Etichetta e il Protocollo di Buckingham Palace: in fin dei conti siamo o no degli GNUrants? ;-)

P.S.: FOTTETEVI STRONZI!

martedì 1 aprile 2014


Gli GNUrants stanno arrivando.

No, non è un pesce, è uno GNU.