Vad är ett kapabilitetsbaserat operativsystem
Moderna operativsystem upprätthåller säkerhetsgränser mellan processer, filer, enheter och användare. Hur dessa gränser implementeras varierar dock avsevärt mellan olika systemdesigner.
De flesta vanliga operativsystem förlitar sig på identitetsbaserad åtkomstkontroll och globala namnrymder. Kapabilitetsbaserade operativsystem använder ett fundamentalt annorlunda angreppssätt: de representerar auktoritet explicit och gör den till ett förstaklassigt begrepp.
Detta inlägg introducerar kapabilitetssystem, förklarar hur de skiljer sig från traditionella modeller och beskriver varför de är centrala för EriX design.
Problemet: omgivande auktoritet⌗
I traditionella system har processer ofta åtkomst till resurser genom omgivande auktoritet.
Omgivande auktoritet innebär att ett program kan komma åt en resurs enbart för att den existerar i en delad namnrymd och systemet avgör att programmet har tillstånd att använda den.
Till exempel:
- En process kan öppna
/etc/passwdom den har tillräckliga rättigheter. - Ett program kan ansluta till en nätverkssocket om operativsystemet tillåter det.
- En tjänst kan komma åt filer baserat på användaridentitet eller gruppmedlemskap.
I alla dessa fall är auktoriteten att komma åt resursen implicit.
Processen har ingen direkt, explicit referens till resursen. I stället förlitar den sig på:
- globala namn (filsökvägar, portar, enhetsidentifierare)
- åtkomstkontroller (användar-ID:n, rättigheter, ACL:er)
Denna modell skapar flera problem:
1. Confused Deputy-problemet⌗
Ett program kan av misstag missbruka sin auktoritet på uppdrag av ett annat program.
Till exempel kan en privilegierad tjänst läsa en fil på begäran av en opålitlig klient, även om klienten själv inte borde ha åtkomst till den.
2. Alltför bred auktoritet⌗
Program kör ofta med fler rättigheter än de faktiskt behöver. Det ökar konsekvenserna av buggar eller komprometteringar.
3. Svårt att resonera om⌗
Det är svårt att avgöra vad en process får göra utan att analysera globalt tillstånd, användaridentiteter och åtkomstkontrollpolicyer.
Kärnidén: kapabiliteter⌗
En kapabilitet är en oförfalskbar token som ger åtkomst till ett specifikt objekt med specifika rättigheter.
I stället för att fråga:
“Har den här processen tillstånd att komma åt denna resurs?”
frågar ett kapabilitetsbaserat system:
“Innehar den här processen en kapabilitet som auktoriserar denna operation?”
Om svaret är nej kan operationen inte fortsätta.
Egenskaper hos kapabiliteter⌗
Kapabiliteter har flera definierande egenskaper:
1. Oförfalskbarhet⌗
En process kan inte skapa en giltig kapabilitet ur tomma luften.
Kapabiliteter skapas och hanteras av kärnan, vilket säkerställer att de inte kan förfalskas eller gissas fram.
2. Explicit auktoritet⌗
All auktoritet representeras explicit genom kapabiliteter.
Om en process kan utföra en operation måste den ha en kapabilitet som tillåter det. Det finns ingen implicit åtkomst via globala namnrymder.
3. Finkorniga rättigheter⌗
Kapabiliteter kan koda specifika rättigheter, till exempel:
- skrivskyddad åtkomst
- skrivrättigheter
- exekveringsrättigheter
- begränsade delmängder av operationer
Detta möjliggör exakt kontroll över vad varje process får göra.
4. Överförbarhet⌗
Kapabiliteter kan överföras mellan processer, vanligtvis via interprocesskommunikation (IPC).
Detta möjliggör kontrollerad delegering av auktoritet.
Objekt och kapabiliteter⌗
I ett kapabilitetsbaserat system modelleras allt som ett objekt:
- minnesregioner
- filer
- enheter
- IPC-ändpunkter
- processer
En kapabilitet är en referens till ett objekt kombinerad med en uppsättning rättigheter.
En process interagerar med systemet genom att anropa operationer på objekt via sina kapabiliteter.
Det finns inget behov av globala namn som filsökvägar eller enhetsidentifierare inne i kärnan. All åtkomst medieras genom kapabiliteter.
Hur kapabilitetssystem fungerar⌗
På en övergripande nivå fungerar ett kapabilitetsbaserat system så här:
- Kärnan skapar objekt och kapabiliteter.
- Varje process har ett kapabilitetsutrymme (ofta kallat CSpace), där dess kapabiliteter lagras.
- En process kan bara operera på objekt som den har kapabiliteter för.
- Kapabiliteter kan överföras mellan processer via IPC.
- Kärnan upprätthåller alla kapabilitetskontroller.
Resultatet är ett system där auktoritet är:
- explicit
- lokaliserad
- överförbar
- lätt att resonera om
Exempel: att öppna en fil⌗
Traditionell modell⌗
I ett traditionellt system:
- En process anropar
open("/etc/config") - Kärnan kontrollerar rättigheter:
- användar-ID
- gruppmedlemskap
- filens lägesbitar
- Om åtkomst tillåts returneras en filbeskrivare
Auktoriteten kommer från globalt tillstånd och identitet.
Kapabilitetsbaserad modell⌗
I ett kapabilitetssystem:
- En process måste redan inneha en filkapabilitet
- Den använder denna kapabilitet för att utföra läs- eller skrivoperationer
Om processen inte har kapabiliteten kan den inte komma åt filen, oavsett vilket namn den använder.
Det finns inget globalt uppslagssteg inne i kärnan.
Delegering och minsta privilegium⌗
En av de mest kraftfulla aspekterna av kapabilitetssystem är delegering.
En process kan ge en annan process en delmängd av sin auktoritet genom att överföra en kapabilitet.
Till exempel:
- En filserver ger en klient skrivskyddad åtkomst till en fil
- En minneshanterare ger åtkomst till en specifik minnesregion
- En process ger åtkomst till en IPC-ändpunkt
Detta möjliggör principen om minsta privilegium:
Varje komponent får bara den auktoritet den faktiskt behöver.
Att eliminera omgivande auktoritet⌗
Kapabilitetssystem eliminerar omgivande auktoritet helt.
Det finns:
- inga globala namnrymder inne i kärnan
- ingen implicit åtkomst baserad på identitet
- inga dolda privilegier
All auktoritet måste överföras explicit.
Det gör det mycket enklare att analysera:
- vad en process kan göra
- hur auktoritet flödar genom systemet
- var potentiella säkerhetsproblem kan uppstå
Återkallelse (ett svårt problem)⌗
En utmaning i kapabilitetssystem är återkallelse.
När en kapabilitet väl har getts till en process, hur kan den tas tillbaka?
Olika system implementerar återkallelse på olika sätt:
- indirektionslager
- referensspårning
- kapabilitetsträd
- versionsmekanismer
Återkallelse är ett viktigt forskningsområde och kommer att utforskas i senare skeden av EriX.
Kapabilitetssystem i praktiken⌗
Kapabilitetsbaserade idéer är inte nya. Flera system har implementerat dem:
- KeyKOS / EROS
- seL4 (en formellt verifierad mikrokärna)
- CHERI (maskinvarustödda kapabiliteter)
- Capsicum (kapabilitetsutökningar för FreeBSD)
Dessa system visar att kapabilitetsbaserade designer är både praktiska och kraftfulla.
Hur EriX använder kapabiliteter⌗
EriX är från grunden utformat som ett kapabilitetsbaserat system.
I EriX:
- representeras all auktoritet genom kapabiliteter
- är kapabiliteter starkt typade
- är kapabiliteter immutabla när de väl har skapats
- överförs kapabiliteter via IPC
- upprätthåller kärnan oförfalskbarhet och säkerhetsinvarianter
Det finns inga globala namnrymder inne i kärnan. All resursåtkomst medieras genom kapabiliteter.
Detta ligger i linje med det bredare målet att göra auktoritet:
- explicit
- minimal
- lätt att resonera om
Varför detta spelar roll⌗
Kapabilitetsbaserade system ger en grund för att bygga:
- säkrare system
- mer modulära arkitekturer
- system som är lättare att analysera och verifiera
Genom att ta bort implicit auktoritet och göra all åtkomst explicit eliminerar de hela klasser av sårbarheter som är vanliga i traditionella system.
Framåt⌗
Kapabiliteter är ett grundläggande begrepp i EriX. I framtida inlägg kommer vi att utforska hur de implementeras i praktiken, inklusive:
- kapabilitetsutrymmen (CSpace)
- interprocesskommunikation (IPC)
- minneshantering med hjälp av otypade kapabiliteter
- delegering och auktoritetsgrafer
Att förstå kapabiliteter är det första steget mot att förstå resten av systemet.