Fragen? Antworten! Siehe auch: Alternativlos
Kurzfassung: GnuPG benutzt als Kryptolibrary aus religiösen, äh, Lizenzgründen nicht OpenSSL oder etwas anderes existierendes, sondern "libgcrypt", eine eigene Krypto-Library, die im Allgemeinen langsamer als andere ist und deren API noch übler ist als die von OpenSSL. Ich kenne kein Argument dafür, die gegenüber einer Alternative zu bevorzugen.
Da ist kürzlich eine neue Version released worden, 1.9, in der Assembler-Optimierungen für einige Funktionen neu dazu kamen. Unter anderem für Hashing-Verfahren. Hashing ist eine der wichtigsten Funktionen in einer Krypto-Library und spielt z.B. eine Rolle beim Validieren von Signaturen in GnuPG.
Wenn man Daten hasht, gibt es normalerweise drei Schritte. Man hat einen "Kontext", d.h. eine Datenstruktur mit dem internen Status, die initialisiert man (Funktion 1), dann ruft man so oft man es braucht "hash mal auch diese Daten hier" auf (Funktion 2), und am Ende holt man sich den Hashwert über alle bisherigen Daten (Funktion 3). Je nach Hashfunktion, für die man das macht, beinhaltet das Finalisieren noch ein mehr oder weniger abstoßendes Padding-Verfahren, denn die Hashes arbeiten normalerweise auf Blöcken von Bytes, nicht auf einzelnen Bytes.
Bei libgcrypt heißt der letzte Schritt "finalize". Der Assembler-Code nahm an, dass niemand so blöd sein würde, nach einem finalize nochmal weitere Daten hashen zu wollen, weil das nie gültige Ergebnisse liefern kann, denn da ist ja schon Padding reingeflossen.
Stellt sich raus: GnuPG macht genau das. Das sollte wohl ein halbherziger Versuch sein, einen Timing-Seitenkanal zu schließen.
Da hätte wohl der Autor von GnuPG mal mit dem Autor von libgcrypt reden sollen, denkt ihr euch jetzt vielleicht? Das ist dieselbe Person. Werner Koch.
Was heißt das? Anscheinend reicht das Öffnen einer verschlüsselten Mail mit GnuPG, um Speicherkorruption herbeizuführen und damit eventuell Remote Code Execution.
Ich habe den Bug nicht analysiert, aber wenn das stimmt, ist das auf der Krassheitsskala ungefähr so weit oben wie Heartbleed bei OpenSSL war, denn GnuPG hat keine Privilege Separation und kein Sandboxing. Die gute Nachricht ist: Das betrifft euch nur, wenn euer GnuPG so neu ist, dass er schon gegen die neue libgcrypt linkt, die vor ner Woche oder so erst rauskam.
Kann man diese Art von Bug verhindern?
Ja, kann man.
Diese Art von Bug wäre aufgefallen, wenn GnuPG eine Testsuite hätte, die z.B. mit valgrind oder dem Address Sanitizer automatisch durchläuft, bevor er eine Version released. Noch beunruhigender als dass das offensichtlich nicht stattfindet, finde ich Werner Kochs Reaktion, als Hanno Böck genau das vorschlägt. Er schließt den Bug als "invalid". Das ist selbst für Werners Verhältnisse ausgesprochen unprofessionell, aber leider nicht überraschend.
Hey, Werner, wenn du keinen Bock auf die Verantwortung hast, die der Maintainer von GnuPG tragen muss, dann übergib das Projekt doch am besten der Apache Software Foundation oder so. Jetzt wäre jedenfalls der richtige Zeitpunkt für zerknirscht Besserung geloben gewesen, nicht für close as invalid.
Ich muss dann wohl doch mal meinen OpenPGP-Code fertig machen. So geht das jedenfalls nicht weiter.
Update: Ich hatte meinen OpenPGP-Code prokrastiniert, weil ich dachte, fürs reine Signatur-Prüfen ist mein Ansatz mit der Prozessisolation vielleicht nicht nötig. Wer verkackt denn schon eine Hashfunktion, dachte ich mir.
Update: Leserbrief von Hanno Böck:
libgcrypt hat eine testsuite, und die hat den bug nicht gefunden. Müsste man jetzt genauer analysieren warum und ob das ein mangel bei der testabdeckung ist und sie den bug hätte finden sollen.
Der Punkt den ich aber kritisiert hab: Es gibt einen anderen bug in libgcrypt 1.9.0 in der testsuite, der durch asan gefunden wird. Es ist "nur" eine selbsttestfunktion, insofern ist der bug nicht so relevant, aber daraus kann man halt schließen: libgcrypt wird offenbar nicht regelmäßig vor einem release (oder via CI)mit asan getestet, sonst hätte das ja merken müssen.
Und achso, noch was: Valgrind hätte diesen Bug (also den in der testsuite) nicht gefunden. Das ist ein buffer overread in einem predefinierten array, sowas kann valgrind nicht. Siehe beispiel im Anhang.
Ich predige seit jahren dass die leute asan nutzen sollen und nicht glauben valgrind sei ein vergleichbar mächtiges tool. Ist es nicht.