Qu’est-ce qu’un système d’exploitation à capacités
Les systèmes d’exploitation modernes imposent des frontières de sécurité entre processus, fichiers, périphériques et utilisateurs. Cependant, la manière dont ces frontières sont mises en oeuvre varie considérablement selon la conception du système.
La plupart des systèmes d’exploitation classiques reposent sur le contrôle d’accès fondé sur l’identité et sur des espaces de noms globaux. Les systèmes d’exploitation à capacités adoptent une approche fondamentalement différente : ils représentent l’autorité explicitement et en font un concept de premier ordre.
Cet article présente les systèmes à capacités, explique en quoi ils diffèrent des conceptions traditionnelles, et montre pourquoi ils sont centraux dans la conception d’EriX.
Le problème : l’autorité ambiante⌗
Dans les systèmes traditionnels, les processus accèdent souvent aux ressources par l’autorité ambiante.
L’autorité ambiante signifie qu’un programme peut accéder à une ressource simplement parce qu’elle existe dans un espace de noms partagé et que le système détermine que le programme a le droit de l’utiliser.
Par exemple :
- Un processus peut ouvrir
/etc/passwds’il dispose de droits suffisants. - Un programme peut se connecter à une socket réseau si le système d’exploitation l’y autorise.
- Un service peut accéder à des fichiers en fonction de l’identité de l’utilisateur ou de l’appartenance à un groupe.
Dans tous ces cas, l’autorité permettant d’accéder à la ressource est implicite.
Le processus ne détient pas de référence directe et explicite vers la ressource. Il s’appuie plutôt sur :
- des noms globaux (chemins de fichiers, ports, identifiants de périphériques)
- des contrôles d’accès (identifiants utilisateur, permissions, ACL)
Ce modèle crée plusieurs problèmes :
1. Le problème du délégué confus⌗
Un programme peut accidentellement mal utiliser son autorité au nom d’un autre programme.
Par exemple, un service privilégié peut lire un fichier demandé par un client non fiable, alors même que ce client ne devrait pas y avoir accès.
2. Une autorité trop large⌗
Les programmes s’exécutent souvent avec plus de permissions qu’ils n’en ont réellement besoin. Cela augmente l’impact des bogues ou des compromissions.
3. Un raisonnement difficile⌗
Il est difficile de déterminer ce qu’un processus est autorisé à faire sans analyser l’état global, les identités utilisateur et les politiques de contrôle d’accès.
L’idée centrale : les capacités⌗
Une capacité est un jeton infalsifiable qui accorde l’accès à un objet spécifique avec des droits spécifiques.
Au lieu de demander :
“Ce processus a-t-il la permission d’accéder à cette ressource ?”
un système à capacités demande :
“Ce processus possède-t-il une capacité qui autorise cette opération ?”
Si la réponse est non, l’opération ne peut pas se poursuivre.
Propriétés des capacités⌗
Les capacités ont plusieurs propriétés déterminantes :
1. Infalsifiabilité⌗
Un processus ne peut pas créer une capacité valide à partir de rien.
Les capacités sont créées et gérées par le noyau, ce qui garantit qu’elles ne peuvent ni être falsifiées ni être devinées.
2. Autorité explicite⌗
Toute autorité est représentée explicitement par des capacités.
Si un processus peut effectuer une opération, il doit posséder une capacité qui l’y autorise. Il n’existe aucun accès implicite via des espaces de noms globaux.
3. Droits à grain fin⌗
Les capacités peuvent encoder des permissions précises, telles que :
- un accès en lecture seule
- un accès en écriture
- des permissions d’exécution
- des sous-ensembles limités d’opérations
Cela permet un contrôle précis de ce que chaque processus peut faire.
4. Transférabilité⌗
Les capacités peuvent être transférées entre processus, généralement via la communication inter-processus (IPC).
Cela permet une délégation contrôlée de l’autorité.
Objets et capacités⌗
Dans un système à capacités, tout est modélisé comme un objet :
- des régions mémoire
- des fichiers
- des périphériques
- des points de terminaison IPC
- des processus
Une capacité est une référence à un objet, combinée à un ensemble de droits.
Un processus interagit avec le système en invoquant des opérations sur des objets au moyen de ses capacités.
Il n’y a pas besoin de noms globaux comme des chemins de fichiers ou des identifiants de périphériques dans le noyau. Tout accès est médié par des capacités.
Comment fonctionnent les systèmes à capacités⌗
À haut niveau, un système à capacités fonctionne comme suit :
- Le noyau crée les objets et les capacités.
- Chaque processus possède un espace de capacités (souvent appelé CSpace), qui stocke ses capacités.
- Un processus ne peut agir que sur les objets pour lesquels il détient des capacités.
- Les capacités peuvent être transférées entre processus via IPC.
- Le noyau applique toutes les vérifications de capacités.
Il en résulte un système où l’autorité est :
- explicite
- localisée
- transférable
- facile à raisonner
Exemple : ouvrir un fichier⌗
Modèle traditionnel⌗
Dans un système traditionnel :
- Un processus appelle
open("/etc/config") - Le noyau vérifie les permissions :
- identifiant utilisateur
- appartenance à un groupe
- bits de mode du fichier
- Si l’accès est autorisé, un descripteur de fichier est renvoyé
L’autorité provient de l’état global et de l’identité.
Modèle à capacités⌗
Dans un système à capacités :
- Un processus doit déjà posséder une capacité de fichier
- Il utilise cette capacité pour effectuer des opérations de lecture ou d’écriture
Si le processus ne possède pas la capacité, il ne peut pas accéder au fichier, quel que soit le nom qu’il utilise.
Il n’existe aucune étape de recherche globale dans le noyau.
Délégation et moindre privilège⌗
L’un des aspects les plus puissants des systèmes à capacités est la délégation.
Un processus peut donner à un autre processus un sous-ensemble de son autorité en lui transférant une capacité.
Par exemple :
- Un serveur de fichiers donne à un client un accès en lecture seule à un fichier
- Un gestionnaire de mémoire accorde l’accès à une région mémoire spécifique
- Un processus accorde l’accès à un point de terminaison IPC
Cela permet le principe du moindre privilège :
Chaque composant ne reçoit que l’autorité dont il a réellement besoin.
Éliminer l’autorité ambiante⌗
Les systèmes à capacités éliminent entièrement l’autorité ambiante.
Il n’y a :
- aucun espace de noms global dans le noyau
- aucun accès implicite fondé sur l’identité
- aucun privilège caché
Toute autorité doit être transmise explicitement.
Cela rend beaucoup plus facile l’analyse :
- de ce qu’un processus peut faire
- de la manière dont l’autorité circule dans le système
- des endroits où des problèmes de sécurité peuvent apparaître
Révocation (un problème difficile)⌗
L’un des défis des systèmes à capacités est la révocation.
Une fois qu’une capacité a été donnée à un processus, comment peut-on la retirer ?
Les différents systèmes implémentent la révocation de manières différentes :
- des couches d’indirection
- le suivi des références
- des arbres de capacités
- des mécanismes de versionnement
La révocation constitue un domaine de recherche important et sera explorée dans les étapes ultérieures d’EriX.
Les systèmes à capacités dans la pratique⌗
Les idées basées sur les capacités ne sont pas nouvelles. Plusieurs systèmes les ont mises en oeuvre :
- KeyKOS / EROS
- seL4 (un micro-noyau vérifié formellement)
- CHERI (capacités assistées par le matériel)
- Capsicum (extensions à capacités pour FreeBSD)
Ces systèmes démontrent que les conceptions à capacités sont à la fois pratiques et puissantes.
Comment EriX utilise les capacités⌗
EriX est conçu dès le départ comme un système à capacités.
Dans EriX :
- toute autorité est représentée par des capacités
- les capacités sont fortement typées
- les capacités sont immuables une fois créées
- les capacités sont transférées via IPC
- le noyau applique l’infalsifiabilité et les invariants de sécurité
Il n’existe aucun espace de noms global dans le noyau. Tout accès aux ressources est médié par des capacités.
Cela s’aligne avec l’objectif plus large de rendre l’autorité :
- explicite
- minimale
- facile à raisonner
Pourquoi cela compte⌗
Les systèmes à capacités fournissent une base pour construire :
- des systèmes plus sûrs
- des architectures plus modulaires
- des systèmes plus faciles à analyser et à vérifier
En supprimant l’autorité implicite et en rendant tout accès explicite, ils éliminent des classes entières de vulnérabilités courantes dans les systèmes traditionnels.
La suite⌗
Les capacités sont un concept fondamental dans EriX. Dans de futurs articles, nous explorerons leur mise en pratique, notamment :
- les espaces de capacités (CSpace)
- la communication inter-processus (IPC)
- la gestion mémoire à l’aide de capacités non typées
- la délégation et les graphes d’autorité
Comprendre les capacités est la première étape vers la compréhension du reste du système.