Ik maak me meer zorgen over een eventueel zero-day-lek in Linux dan over de git-repositories. Objectrepositories zitten bij git namelijk zo in elkaar dat gesjoemel daarmee niet onopgemerkt blijft. Daar is het op ontworpen. Het is wellicht aardig, voor wie het niet al weet, om daar een beeld van te geven. Het is een mooi voorbeeld van een ontwerp waarin vanaf de basis met beveiliging (van de integriteit van de repository, inclusief de audit trail) rekening is gehouden. Het is in de wijze van opslag zelf geïntegreerd, het zijn niet alleen maar toegevoegde controles.
De basis van een git-repository is content addressable storage. Een object dat erin opgeslagen is wordt geïdentificeerd door zijn inhoud, door een SHA1-hash over zijn inhoud om precies te zijn. Voor elk bestand in een source-tree wordt dus een object aangemaakt. Als een bestand gewijzigd wordt dan levert de andere inhoud een andere hash op, en de nieuwe versie wordt onder die naam naast de oude toegevoegd aan de repository bij een commit.
Directory's worden vertegenwoordigd door tree-objecten. Die verwijzen via de hashes naar alle objecten in de directory, en ook hier wordt de hash over de inhoud de naam van het tree-object in de repository. Als ergens in de directory-tree een bestand wijzigt, dan wijzigt zijn hash, en die moet dus ook in het tree-object aangepast worden. Die veranderde inhoud leidt tot een nieuwe hashwaarde voor het tree-object, en dat effect plant zich voort tot aan de hoogste directory die in de repository wordt bijgehouden. Al deze gewijzigde tree-objecten worden bij een commit toegevoegd aan de repository, naast de oude versies.
Elke wijziging leidt dus tot een wijziging in het hoogste tree-object, omdat elke wijziging zich in de vorm van gewijzigde hashes naar boven toe voortplant. De commit-actie waarbij de wijzigingen worden toegevoegd wordt vastgelegd in een commit-object. Die verwijst via hun hashes naar het huidige hoogste tree-object en de voorafgaande commit (of commits, zie verderop). De hash van een commit is daarmee indirect een hash over de volledige huidige staat van de repositiory en de volledige voorgeschiedenis van die staat. Verander één bit, waar dan ook in die voorgeschiedenis, en je moet een kettingreactie van foute hashes herstellen tot aan de meest recente commit.
Commits kunnen van tags worden gezien, die optioneel ook weer als object in de repository worden opgeslagen (ze kunnen ook alleen in de metadata bestaan). Tags in de repository kunnen met GPG ondertekend worden. Aanwezige digitale handtekeningen in tags verwijzen naar de oorspronkelijke objecten, niet naar objecten waar mee gesjoemeld is.
Zoals met elk source code managementsysteem kunnen met git branches en merges worden uitgevoerd. Een branch houdt in dat meerdere commits naar dezelfde parent-commit verwijzen, en merge houdt in dat een commit naar meerdere parent-commits verwijst (plus natuurlijk het in elkaar schuiven van wijzigingen aan de bestanden, maar dat wordt niet door de objectrepository gedaan, die slaat alleen het resultaat op).
Branche-namen en de verwijzingen naar hun 'HEAD' worden niet in de objectrepository zelf maar in metadata bijgehouden.
Een integriteitscheck van de repository houdt (onder meer) in dat van alle objecten de checksums worden gecontroleerd, dat van tree-, commit- en tag-objecten wordt gecontroleerd of de obejcten waar ze naar verwijzen aanwezig zijn, en dat eventuele GPG-handtekeningen in tags worden gecontroleerd.
Kernel.org is niet meer dan een distributiepunt, het is niet de centrale waarheid zoals een gecentraliseerd SCM dat is. Git is een gedistribueerd systeem, waarbij alle ontwikkelaars zelf complete repositories op hun systeem hebben staan, vaak zelfs meerdere. Al die repository's kunnen als basis voor verder klonen dienen en zonodig onderling gesynchroniseerd worden. Die zijn natuurlijk niet identiek als mensen daar zelf hun wijzigingen in aanbrengen, maar git zit wel zo in elkaar dat de volledige onstaansgeschiedenis van de aanwezige branches ook aanwezig is. Dit op zich betekent al dat ze in principe de kernel.org-repository moeten kunnen weggooien en reconstrueren.
Maar wat gebeurt er als iemand voor de inbraak ontdekt was met een gecompromitteerde repository had gesynchroniseerd? Als de inhoud van bestanden is gewijzigd zonder de hashes aan te passen dan komt dat snel bij een integriteitscheck of een andere actie die gewijzigde objecten raakt aan het licht. Als hashes wel zijn gewijzigd dan kloppen de GPG-handtekeningen niet mee. Ook kloppen de hashes niet meer met degene die synchroniseert, en die krijgt dan een foutmelding. Als iemand op dat moment een kloon maakt krijgt hij dit probleem als hij met de herstelde of een andere goede repository wil synchroniseren. Als je het samenvoegen van de twee versies van de repository forceert dan komen de twee varianten als branches naast elkaar te staan, er zijn immers vanaf een bepaald punt commit-objecten met verschillende hashes, die op dat punt wel naar een gezamenlijke parent-hash verwijzen, en dat is hoe git een branch definieert.
Maar een meer voor de hand liggende wijziging is denk ik het toevoegen van een nieuwe commit die iets vervelends introduceert. Die is tussen de recente wijzigingen relatief makkelijk te herkennen, en kan nooit ondertekend zijn met sleutels die niet op de gecompromitteerde machine staan én zelf gecompromitteerd zijn (maar hier doe ik aannames over hoe bij de Linux-kernel wordt gewerkt). Wijzigingen aan de metadata, configuratie-instellingen van de repository, hook-scripts voor diverse acties, dat zijn andere dingen waarmee gesjoemeld kan worden en die moeten natuurlijk gecontroleerd worden.
Hoewel SHA1 voor cryptografische doeleinden geen toppertje meer is is de kans dat je broncode van een programma aan kan passen op een manier die zich laat compileren, compatible is met het origineel, jouw exploit toevoegt, en ook nog dezelfde hash oplevert astronomisch klein. Geen reëel risico, lijkt me. Maar ook dat is te controleren door de objecten met die uit ongecompromitteerde repository's te vergelijken.
Omdat elk detail van de samenhang tussen objecten en commits zo volledig met SHA1-hashes in beton is gegoten, en helemaal als ook de met GPG ondertekende tags worden gebruikt, heb je een ijzersterke audit-trail en een ijzersterke integriteitscontrole. En dat alles op basis van een opzet die zo eenvoudig is dat ik hem uit mijn hoofd kan beschrijven. Dit is precies de reden dat ik enthousiast werd over git en het zelf ging gebruiken. Ik begrijp waarom het goed in elkaar zit.