Toute discipline de durcissement repose sur une compréhension fine de ce qui se passe déjà, par défaut, dans le noyau. Cette note rassemble les mécanismes fondamentaux — appels système, processus, permissions de fichiers, identités — sur lesquels s’appuient ensuite les mécanismes de restriction plus avancés.

L’appel système, unité de base

Un syscall est un appel à une fonction de l’API du noyau. Il provoque une interruption logicielle, ce qui le rend intrinsèquement coûteux en temps — une contrainte de performance qui explique en partie pourquoi les mécanismes de filtrage modernes (seccomp, LSM) cherchent à intercepter les syscalls sans en multiplier le coût. On peut tracer l’exécution d’un processus avec strace :

$ strace ls
execve("/usr/bin/ls", ["ls"], 0x7ffe0c1f5f80 /* 21 vars */) = 0
brk(NULL)                               = 0x55a12f8e5000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe0f6db1000

Processus

Définition

Un processus est un programme en cours d’exécution dans un contexte spécifique.

Le système d’exploitation exécute plusieurs processus de manière concurrente : même si un seul occupe le processeur à un instant , plusieurs autres se trouvent dans divers états d’exécution. Sous Linux, un processus naît via l’appel système clone (mécanisme copy-on-write).

Quatre appels système structurent la gestion des processus : exit termine le processus courant ; fork clone le processus courant, produisant deux processus initialement identiques (même pile, même tas) ; exec remplace l’image du processus courant par un nouvel exécutable, sans création ni destruction de processus ; waitpid attend qu’un autre processus se termine.

Isolation et ses limites

L’isolation des processus sépare les applications les unes des autres en leur attribuant des ressources distinctes — chaque processus dispose de son propre espace mémoire privé, inaccessible aux autres. Cette isolation garantit à la fois la robustesse (la défaillance d’un processus n’entraîne pas celle du système) et la sécurité (un programme malveillant ne peut interférer directement avec un autre).

Une isolation jamais parfaite

Les processus partagent malgré tout des ressources physiques — cache du processeur, carte graphique, interface réseau. Ce partage crée des canaux auxiliaires (side-channels) susceptibles de faire fuiter de l’information entre processus pourtant censés être isolés.

Threads et processus

Un thread est l’unité de base à laquelle le système d’exploitation alloue du temps processeur. Contrairement aux processus, qui s’exécutent dans des espaces mémoire séparés, les threads d’un même processus partagent une mémoire commune — ce qui rend leur communication plus rapide, mais expose aussi à des conditions de course plus directes.

Le système de fichiers /proc — un pseudo-système de fichiers exposant les structures de données du noyau — donne accès à l’état de chaque processus et de chaque thread actif. /proc/<pid>/maps, par exemple, liste les régions mémoire mappées d’un processus et leurs permissions d’accès.

Permissions de fichiers

Linux applique un contrôle d’accès discrétionnaire (DAC) : c’est le propriétaire du fichier qui décide de ses permissions, réparties en trois droits (lecture, écriture, exécution) pour trois catégories d’utilisateurs (propriétaire, groupe, autres).

Le bit setuid permet à un utilisateur d’exécuter un programme avec les privilèges du propriétaire du fichier — souvent root. La commande passwd en dépend directement : elle doit pouvoir écrire dans /etc/shadow, que seul root peut normalement modifier.

Les listes de contrôle d’accès (ACL) affinent ce modèle en autorisant n’importe quel utilisateur nommément désigné à accéder à une ressource, au-delà du triptyque propriétaire/groupe/autres :

setfacl -m "u:user:permissions" <file/dir>
getfacl <file/dir>

Les identités d’un processus, au pluriel

L’identité d’un processus ne se résume jamais à un seul identifiant : plusieurs UID distincts coexistent, chacun avec un rôle propre.

Le Real User ID (RUID) identifie l’utilisateur qui a réellement lancé le processus — c’est à lui qu’appartient le processus, et à lui que sont adressés les signaux. L’Effective User ID (EUID) est celui que le noyau consulte pour vérifier les permissions lors d’un accès à une ressource. RUID et EUID coïncident la plupart du temps, mais divergent lors de l’exécution d’un binaire setuid.

Le cas d'école : passwd

Lancée par un utilisateur standard (RUID = 1000), la commande passwd doit pourtant modifier /etc/shadow, qui appartient à root. Grâce au bit setuid, son EUID devient 0 le temps de l’exécution, tandis que son RUID reste 1000 — le système sait ainsi qui a initié l’action, tout en lui accordant temporairement le privilège nécessaire pour l’accomplir.

D’autres identifiants gèrent des scénarios plus complexes : le Saved Set-User-ID (SSUID) permet à un processus privilégié de basculer temporairement son EUID vers une valeur non privilégiée, tout en conservant la possibilité de récupérer son identité privilégiée plus tard ; le File System User ID (FSUID), spécifique à Linux, ne sert qu’aux vérifications de droits sur le système de fichiers. Les groupes suivent une logique symétrique (RGID, EGID, Saved GID), complétée par une liste de groupes supplémentaires étendant les droits d’accès d’un processus.

Groupes et vecteurs d’escalade

Les groupes simplifient la gestion des permissions DAC, mais jouent aussi un rôle dans les vecteurs d’attaque. La capacité CAP_SETGID permet à un processus de changer son GID effectif, ouvrant potentiellement la voie à l’usurpation d’un groupe privilégié. Si un utilisateur parvient à intégrer — ou à usurper — un groupe sensible (le groupe docker, par exemple, ou shadow), il accède à des ressources protégées et peut réaliser une escalade de privilèges.

sudo, entre traçabilité et limitation

L’usage de sudo répond à deux exigences complémentaires : assurer une traçabilité des opérations d’administration via auditd et les journaux systèmes, et limiter précisément les actions autorisées via le fichier sudoers. Le principe qui doit guider toute configuration reste constant : un programme requérant des droits root pour une action donnée doit renoncer à ses privilèges une fois cette action accomplie, plutôt que de les conserver par confort.