De computer op slot: USB-sleutels pluggen in Linux-PAM

Op Windows zie je steeds vaker configuraties die een elektronische sleutel in de USB-bus vereisen tijdens het inloggen. Terwijl je dat op Linux eigenlijk zelden of nooit ziet. Toen ik onlangs dus een Linux developer's kit met een USB-sleutel kreeg was de tijd dus rijp voor wat geknutsel!

Dit artikel verscheen in een iets andere vorm in Linux Magazine, aflevering 5 van 2003

Van Novaris ontving ik een ePass1000 sleutel om in de USB-bus te prikken. Op de foto zie je dat het er een beetje uitziet als het veel bekendere geheugenstokje, maar een sleutel is een stuk slimmer dan dat. Het bezit een cryptografische processor, waarmee berekeningen gedaan kunnen worden die nuttig zijn voor authenticatie. Het kan daardoor een password verborgen houden, maar er toch mee inloggen.

Het mooie is vooral dat het een los stuk hardware is. Los van die eng-complexe computer met al z'n achtergrondprogramma's, gebruikers en andere zut. Geen systeem zo veilig als eentje waar niemand op werkt immers. Erg handig is dat je zo'n stukje hardware aan je sleutelbos kunt hangen volgens de reclamepraatjes, al twijfel ik of de gemiddelde USB-bus het leuk vindt wanneer je hele sleutelbos er aan komen te hangen. Maar daar is met verlengkabeltjes wel weer wat aan te doen.

Met dit soort sleutels kun je dubbele authenticatie opzetten, bestaande uit iets dat je weet (een password) en iets dat je bezit (de hardware). En alles in de hardware is er op gericht om te voorkomen dat je zijn functionaliteit kunt faken. Daarom komt er cryptografie bij om de hoek kijken, en zoals je in dit verhaal zult zien kun je daar soms Leuke trucs mee uithalen.

De ingewanden van een sleutel

Sleutels, formeel cryptografische tokens genaamd, hebben allemaal zo ongeveer dezelfde interne opbouw. Ze bieden een filesysteem, met lees- en schrijfoperaties, plus speciale bewerkingen die je op de files kunt loslaten. Maar als je dacht dat DOS in de filesysteemwereld gold als een bumpy road, dan staat je nu wel een kennismaking met de achtbaan te wachten.

Files in een token hebben een nummer in plaats van een naam. Vaak hexadecimaal genoteerd, want het is zo handig bij 16-bits getallen (...) Op zich niet zo'n probleem, omdat een toepassing voor een token maar een paar vaste files nodig heeft. Om meerdere toepassingen op één sleutel toe te staan kunnen files gelukkig wel in een directory geplaatst worden, en die kan wel een naam hebben, zodat toepassingen elkaar minimaal in de weg zitten. Verderop maken we bijvoorbeeld een directory OpenFortress_pam_epass, met daarin opgeslagen een sleutel die verdeeld wordt over de files 0x0001 en 0x0002.

De files in een token kunnen een paar types hebben -- in elk geval wordt altijd ruwe data toegestaan (voor certificaten, public keys en voor mijn part je telefoonnummer) en geheimen. Die laatste zijn belangrijk -- het zijn de waarden die intern gehouden worden, en die alleen kunnen dienen als input voor cryptografische bewerkingen. De geheimen zijn dus veilig, is het idee. De behuizing van een token is daar ook op berekend -- de beste zijn `tamper proof' ofwel, de geheimen zijn eerder kapot dan de behuizing zodat je er nooit bij zult kunnen, de ePass1000 classificeert zich als `tamper evident' ofwel je zult het geheid kunnen zien als je token gekraakt is.

Net als onder Linux kennen files ook een vorm van access control. Ruwe data kun je instellen met permissies voor lezen en schrijven, geheimen kun je alleen permissies geven voor gebruik in crypto-routines. De permissies kunnen bij de ePass worden ingesteld als publiek, of alleen voor de gebruiker, of alleen voor de security officer ('root' zeg maar), of onbeschikbaar. De gebruiker is degene die zich aanmeldt met een password (bij tokens heet dat opeens een PIN-code) en de security officer heeft een eigen password (de SO-PIN) en beheersmatige rechten. Overigens eindigt de geldigheid van elke PIN zodra je de sleutel uit de USB-bus verwijdert, daar zorgt de hardware wel voor.

Soorten cryptografische tokens

Er zijn wat verschillende klassen van deze `tokens'. We kennen allemaal wel de kreet `smart card', wat doorgaans slaat op een kaartje met een ingebouwde processor. Dat is vrij eenvoudig spul, en niet erg goed in het veilig houden van de erop opgeslagen informatie; ook is de processor op die dingen niet zo heel krachtig.

Security komt pas echt binnen het bereik van smart cards als ze bepaalde gecompliceerde wiskundige bewerkingen aankunnen die in de cryptografie gebruikelijk zijn; wanneer ze bovendien geheimen kunnen bewaren en alleen gebruiken voor die berekeningen, dan zijn ze opeens heel geschikt voor authenticatie.

Tegenwoordig zie je steeds vaker dat een enkel USB-token als het ware een kaartlezer en een daaraan vastgeklonken smart card bevatten, en dan ontstaat dus een token zoals bijvoorbeeld deze ePass. Handig, omdat de standaard software voor de smart cards dus ook op deze dingen werkt.

De crypto-berekeningen die deze tokens aankunnen vallen in twee soorten uiteen, namelijk symmetrisch of asymmetrisch. De laatste soort is het krachtigst, het is in staat om public-key bewerkingen te doen. Laten we eerst maar eens naar die twee klassen van crypto-algoritmen kijken. En als inleiding daarop ook nog even naar passwords in je normale Linux-systeem.

Passwords op de standaard Unix-manier

Normaalgesproken log je op Unix-systemen zoals Linux in met een password. Dat controleert het login-programma niet doordat het password opgeslagen is, maar doordat er een éénrichtingsfunctie op losgelaten is; klassiek was dat crypt, maar tegenwoordig zie je steeds meer dat hier zogenaamde `secure hashes' voor worden gebruikt, omdat die heel grondig zijn in de onomkeerbaarheid van de berekening.

Secure hashes kun je zelf ook berekenen, bijvoorbeeld via het hash-algoritme MD5. Probeer deze twee commando's bijvoorbeeld:

echo -n Hiephoi | openssl md5
echo -n HiepHoi | openssl md5

Hoewel de regels nauwelijks verschillen, levert dit totaal verschillende uitkomsten:

21c36407f9ae74c0eb5f1ac6eca82c06
6d4901664e070fdaa10e3878c877c50f

Deze twee waarden zijn 128 bits lang, zoals altijd uit het MD5 algoritme, en dit kleine verschil in de input is al goed voor een verschil van 62 bits, dus op 48,4% van de bits. Hoe veel of weinig je ook verandert, altijd zullen rond de 50% van de bits omklappen -- alleen valt totaal niet te voorspellen welke. Ofwel, de kans dat je uit de uitvoer nog iets van de invoer kunt afleiden is verwaarloosbaar. Het is zo verwaarloosbaar klein dat men het niet gevaarlijk acht.

Tegenwoordig wordt aangeraden zo veel mogelijk SHA1 te gebruiken in plaats van MD5, omdat tegen de laatste wat vergezochte verdenkingen beginnen te rijzen, maar hardware kan daar natuurlijk niet zo snel in meelopen als software, dus tokens die aan symmetrische crypto doen zullen vaak nog op MD5 zijn gebaseerd.

In de file /etc/shadow (of vroeger in /etc/passwd) zie je per account een reeks rare kriebels staan, en zoals je wel zult raden wordt die vergeleken met een hash op het door jou ingetikte password. Dat houdt dus in dat je password niet opgeslagen staat op het systeem, alleen een waarde die daarmee samenhangt maar niet naar je password terug te rekenen is. Soms is wiskunde best nuttig, nietwaar?

Symmetrische authenticatie

In sommige gevallen kun je nog wat verder gaan, en met name over netwerken wordt dat interessant. Stel je voor, een inbreker die netwerkverkeer aftapt in de hoop delen na te kunnen spelen. Bijvoorbeeld tot op het punt dat jij bent ingelogd. Een password is dan erg interessant, maar ook een hash daarvan, want als dat alleen over het netwerk vliegt is dat voor de andere kant blijkbaar afdoende basis voor authenticatie.

Wat je eigenlijk wilt in zo'n toepassing is telkens een andere authenticatie, en wel zo dat de server waarop je inlogt wat in te brengen heeft. Dat kan met een zogenaamd challenge/response systeem. Hierin wordt niet de hash berekend over alleen het password (dat langdurig hetzelfde blijft) maar over het password met daarachteraan een door de server bepaalde random bitreeks, de zogenaamde challenge. Stel je password is 'sekreet' en de challenge van de server is 'abcde', dan kan een response op die challenge bijvoorbeeld berekend worden met:

echo sekreet:abcde | openssl md5

De uitkomst hiervan is de zogenaamde response, en die gaat naar de server die het zelf ook narekent en vergelijkt. Volgende keer stuurt'ie een andere challenge, en dan is de response dus ook anders. Jammer voor die aftapper, die komt nu geen steek verder.

De ePass1000 is in staat tot dit type challenge/response algoritmen, en ook niet meer dan dat. Het gebruikt hiervoor het veelgebruikte HMAC-MD5 algoritme, een standaardmanier voor challenge/response aanpakken. Het password 'sekreet' stuur je eenmalig naar het token, en hieruit worden vervolgens interne geheimen afgeleid die alleen nog via het HMAC-MD5 algoritme aangesproken kunnen worden. Dat algoritme roep je dan bijvoorbeeld aan met 'abcde' en dan komt de response eruit, volgens een berekening die lijkt op wat we hierboven toonden.

Voordat je nou lyrisch wordt, dit is nog steeds een beetje armeluiscrypto. De tokens kosten maar een paar tientjes, maar de toepassingen zijn wat beperkt. Je hebt namelijk het password zelf vaak nodig om de uitkomst te controleren. Gebeurt dat op een goedbeveiligde server dan is er niet zo veel aan de hand, maar als je er het inloggen op je eigen systeem mee wilt beveiligen dan wens je natuurlijk niet dat het password op dat systeem zelf rondslingert! Maar als je doorleest zul je zien dat er wel enige rek in zit.

Public key authenticatie

Het meest ideale mechanisme in de cryptografie is dat van public key authenticatie. De algoritmen die dit doen zijn doorgaans RSA of DSA. Die algoritmen zijn complex, en de hardware is vaak een stuk duurder. Een paar namen van tokens die dit aankunnen zijn het iKey USB-token, de STARCOS smart cards en de ePass2000.

Bij public key authenticatie heb je twee sleutels die elkaars werking inverteren. De ene houd je geheim (liefst op een cryptografisch token) en de andere kondig je publiekelijk aan (vergezeld van je identiteit heet dat een certificaat).

Hardwarematige tokens die dit type bewerkingen bieden zullen aan boord sleutels kunnen genereren, en de private key zal het token nooit verlaten. De public key daarentegen wordt vrijelijk beschikbaar gesteld. Voor wie het echt wil is er doorgaans een bypass waarlangs je een private key uit je tokenloze verleden kunt importeren. En zoals wel duidelijk zal zijn roep je op dit soort tokens niet een HMAC-MD5 algoritme aan, maar de private-key bewerking voor het public key algoritme, bijvoorbeeld RSA.

Als je een challenge stuurt naar dit type token dan wordt aan de hand van de private key een response berekend die tot de originele challenge herleid kan worden aan de hand van de public key. De controle of de geheime sleutel gebruikt is maakt dus geen gebruik van geheime data, alleen van publieke data. En daarin zit de kracht van public key crypto. Dat is er ook de reden van dat het bruikbaar is voor digitale handtekeningen -- die alleen door een enkeling te plaatsen zijn, maar door iedereen te controleren.

Ook hierbij blijkt hardware wat achter te lopen overigens -- wegens constante toename van computerkracht en van kraakkennis moeten sleutels elk jaar een beetje langer worden gekozen. RSA sleutels van 1024 bits zijn voor veel hardware-tokens de langst mogelijke, terwijl de meest sceptische bronnen die lengte sinds 2002 niet meer vertrouwen, en nog met grondige onderbouwing ook. Als troost geldt dat er een flinke bliksemafleider is, te weten VeriSign die nog doodleuk root keys van die lengte en zelfs korter hanteert voor de komende 25 jaar. VeriSign is in crypto-land even populair als Microsoft onder de lezers van dit blad, dus of dat nou wel zo slim is...

Een crypto-truc

Ik wilde het maximale effect bereiken met een simpel symmetrisch token, en met wat creativiteit ben ik daar redelijk in geslaagd. Het laatste dat we willen is een password dat in letterlijke vorm op de harddisk staat van het systeem dat authenticeert. En bovendien willen we dat het geheime password dat op het token staat daar amme-never-nooit-niet meer vanaf komt. En dat kan!

Op een gegeven moment weet je door een goed verlopen challenge/response interactie dat de juiste sleutel in de USB-bus zit, en zolang die er niet uitgetrokken wordt kun je gewoon een volgende challenge naar de sleutel sturen om daar alvast de response bij te bepalen. Als je die challenge en de bijbehorende response op zou slaan tot de volgende keer dan zou je daarmee op een later moment kunnen zekerstellen dat, wederom, de juiste sleutel in de USB-bus zit. En dan bepaal je meteen weer een nieuwe challenge/response voor de keer daarop, enzovoort. Zo werkt het token dus echt als een sleutel, omdat telkens hetzelfde stuk hardware vereist wordt. Tenminste, als je een random bitreeks in de sleutel opslaat als geheim voor de HMAC-MD5 bewerking, en als je die bitreeks verder nergens opslaat.

Het hete hangijzer in deze denkwijze is het opslaan van de response op de harddisk. Immers, een inbreker zou die kunnen aftappen, en daarna via een USB-prutsdingetje aanbieden. Daarmee zou een manier gevonden zijn om de sleutel overbodig te maken voor authenticatie, en dat is ongewenst. De oplossing voor dit probleem is gelukkig erg simpel: sla niet de response op de schijf op, maar een secure hash van de response. Zo kan wel worden gecontroleerd dat de juiste response wordt berekend door het token, maar die waarde kan niet worden herleid uit de opgeslagen informatie, zo die al gelezen of afgetapt mocht worden.

Toch blijft het goed uitkijken geblazen -- het overschrijven van de challenge/responsehash paren op de harde schijf met een ooit afgetapte waarde levert nog steeds kans op replay-aanvallen via de USB-bus, en je moet ook durven aannemen dat het aftappen van de communicatie met de USB-bus onmogelijk is. Maar meer rek zie ik niet in symmetrische tokens, en hoewel er op af te dingen valt, is dit toch een behoorlijk goede authenticatiemethode.

Linux-PAM maakt de toepassing compleet

Bij de ePass1000 devkit zit een halfslachtige poging om PAM-ondersteuning te maken, maar het ziet eruit alsof de programmeur na wat gesteggel ontdekte dat het niet slim was een password op de schijf op te slaan, en dat hij niet zo creatief was als ik hierboven. Negeer die poging dus gewoon, maar installeer mijn code vanaf de download-URL.

We kunnen de ePass module als alternatieve inlog-eis specificeren door bijvoorbeeld aan /etc/pam.d/sudo een regel toe te voegen als

auth sufficient /lib/security/pam_epass_onetimehmac.so realm=admin

Hierin staat admin voor de naam van de realm (of authenticatie-omgeving) waarin je in wilt loggen. Deze module zoekt in /etc/pam_epass/onetimehmac naar een directory met de naam van de realm en dan van de user als wie je inlogt -- voor su is die laatste overigens altijd 'user=root'. In de directory voor deze realm/user combinatie moet je eerst nog wel het geheim opslaan voor die gebruiker, met

welcome_pam_epass_user user=root realm=admin

Dit commando geef je overigens ook om beschadigde toegangsrechten te herstellen; de sleutel moet hiervoor ingeplugd zijn, en vanzelfsprekend kun je deze directories maar beter beschermen tegen misbruik; met welcome_pam_epass_user wordt dat allemaal zo geregeld.

Foutmeldingen die opborrelen tijdens pogingen tot authenticatie vind je terug in je secure log, doorgaans /var/log/secure, die alleen voor root leesbaar hoort te zijn. PAM levert wat minder feedback op de desktop, omwille van de veiligheid. Werkt het authenticatie-alternatief naar wens, dan kun je ook eens met een required regel gaan werken in plaats van sufficient -- ofwel, het gebruik van de sleutel als extra eis. Pas hierbij wel op, als je het fout doet kun je jezelf buitensluiten en moet je vanaf een ander medium booten om binnen te komen -- als je BIOS dat toestaat...

De meest interessante PAM configuraties om mee te spelen zijn vermoedelijk su en sudo om root te worden, alsmede gdm, kdm, xdm en login voor het inloggen. Alleen xscreensaver implementeert PAM onvolledig, wat jammer is omdat die een mogelijkheid biedt om na een time-out of lock-klik het scherm zodanig te locken dat je de sleutel nodig hebt om de controle over het scherm terug te krijgen.

Het resultaat... mag er zijn!

Zoals je hier zag is het mogelijk om zelfs met het meest simpele crypto-token al een krachtig systeem te maken om je te authenticeren bij je Linux-systeem. En dankzij de flexibiliteit van Linux-PAM is het vervolgens een koud kunstje om de sleutel daarna daadwerkelijk als randvoorwaarde voor de authenticatie van allerlei services te configureren; desgewenst als aanvulling op de normale authenticatie of juist als zelfstandig authenticatiemechanisme in plaats van je alledaagse passwords.

Het is erg prettig om te zien dat je oude, vertrouwde systeem je opeens niet meer binnenlaat als je sleutel niet in de USB-bus steekt. En dat je weer als vanouds welkom bent als je de sleutel in het slot prikt. Dat geeft een prettig gevoel van controle over je eigen systeem.

Linux-PAM, een schot in de roos

PAM staat voor Pluggable Authentication Modules, en in dit verhaal lees je over een plugbare module die bijgemaakt is voor de ePass hardware tokens. In /etc/pam.d vind je files per toepassing, bijvoorbeeld voor su of voor login. In die files geven `auth' regels randvoorwaarden aan authenticatie. Bijvoorbeeld om aan te geven dat de root gebruiker een authenticatie mag skippen schrijf je

auth sufficient /lib/security/pam_rootok.so 

en om aan te geven dat de traditionele Unix-authenticatie vereist is schrijf je

auth requried /lib/security/pam_unix.so

Je mag zulke eisen stapelen zoals je wilt. Alles wat required is moet dan gezamenlijk slagen, of een enkele sufficient moet gunstig uitpakken.

PAM is flexibel genoeg om extra informatie op te vragen, zoals de PIN van het token. Op RedHat 8 werkte dit in elk geval prima met gdm, su, sudo, en login. Alleen xscreensaver liep uit de pas doordat het deze standaard PAM-verzoeken niet aankon.

PAM is origineel op Sun en daarna op Linux ontwikkeld, maar omdat het zo krachtig en algemeen is heeft het flink om zich heen gegrepen. Het is behalve voor Linux and SunOS ook beschikbaar voor FreeBSD, Mac OS X, HP-UX, en IBM systemen. In de meeste gevallen gaat het daarbij om de Linux-implementatie, die Linux-PAM heet. We kunnen wel zeggen dat dit Linux-project een schot in de roos is!

Goedkoper dan Windows... what's new?

Zoals gezegd is het meest ideale systeem dat van public key crypto, maar het is ook de duurste vorm. De berekingen zijn complex, en vergen meer van de hardware, die dus duurder is.

De ePass1000 maakt het nogal bont met deze authenticatievorm -- er wordt een Windows-library meegeleverd die public key functionaliteit levert, maar dat is wat te suggestief. Als je de berekeningen in software uitvoert moet je de geheime sleutel uit de ePass onttrekken en dan vervalt het hele voordeel van een sleutel in hardware. Niet gebruiken dus.

Wil je met een token inloggen op Windows, dan ben je wel gedwongen om public key tokens gebruiken, en ben je dus duurder uit. Wat flink in de papieren kan lopen voor een bedrijf dat al haar werknemers een token wil geven. Deze techniek voor Linux is goedkoper omdat het een simpeler token op het juiste niveau inzet. En dat kon alleen maar omdat Linux de openheid biedt om dat aan te kunnen!

Killer app? Omzeil root passwords

Distro's zoals RedHat vragen je doorgaans om het root password voordat je aan de systeemconfiguratie mag tornen. Dit werkt ook allemaal via een configuratiefile in /etc/pam.d, en dus kun je het configureren. Handig is als je dan een afzonderlijke realm gebruikt, bijvoorbeeld `syscfg', waarin je alleen die gebruikers aanmeldt met welcome_pam_epass_user die het systeem mogen configureren.

Vervolgens edit je alle PAM files voor configuratietools, en daarin neem je vooraan deze regel op:

auth sufficient /lib/security/pam_epass_onetimehmac.so realm=syscfg

De verwelkomde gebruikers hoeven nu geen root password meer in te geven om het systeem te beheren -- de aanwezigheid van de ePass volstaat. En dankzij PAM kun je heel gedecideerd aangeven wat wel of niet mag. Je kunt zelfs een speciale (rode?) ePass gebruiken voor systeembeheerstaken als je zou willen!

Links


 
   ------ 8< ---------- 8< ----------- 8< ------ | OpenFortress*