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/passwd s’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 :

  1. Le noyau crée les objets et les capacités.
  2. Chaque processus possède un espace de capacités (souvent appelé CSpace), qui stocke ses capacités.
  3. Un processus ne peut agir que sur les objets pour lesquels il détient des capacités.
  4. Les capacités peuvent être transférées entre processus via IPC.
  5. 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 :

  1. Un processus appelle open("/etc/config")
  2. Le noyau vérifie les permissions :
    • identifiant utilisateur
    • appartenance à un groupe
    • bits de mode du fichier
  3. 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 :

  1. Un processus doit déjà posséder une capacité de fichier
  2. 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.