virli/tutorial/3/capabilities.md

6.2 KiB

\newpage

Les capabilities

Présentation

Historiquement, dans la tradition UNIX, on distinguait deux catégories de processus :

  • les processus privilégiés : dont l'identifiant de son utilisateur est 0 ;
  • les processus non-privilégiés : dont l'identifiant de son utilisateur n'est pas 0.

Lors des différents tests de permission fait par le noyau, les processus privilégiés outrepassaient ces tests, tandis que les autres devaient passer les tests de l'effective UID, effective GID, et autres groupes supplémentaires...

Depuis Linux 2.2 (en 1998), les processus privilégiés peuvent activer ou désactiver des capabilities, chacune donnant accès à un groupe d'actions privilégiées au sein du noyau.

On trouve par exemple :

  • CAP_CHOWN : permet de modifier le propriétaire d'un fichier de manière arbitraire ;
  • CAP_KILL : permet de tuer n'importe quel processus ;
  • CAP_SYS_BOOT : permet d'arrêter ou de redémarrer la machine ;
  • CAP_SYS_MODULE : permet de charger et décharger des modules ;
  • et beaucoup d'autres, il y en a environ 39 en tout (ça dépend de la version du noyau) !

ping

Pour émettre un ping, il est nécessaire d'envoyer des paquets ICMP. À la différence des datagrammes UDP ou des segments TCP, il n'existe pas d'interface exposée par le noyau aux utilisateurs pour envoyer des paquets ICMP. Pour le faire, il est nécessaire de pouvoir écrire directement sur l'interface ; ça, seul le super-utilisateur peut le faire.

Pour permettre à tous les utilisateurs de pouvoir envoyer des ping, le programme est donc généralement Setuid root. Cela permet à n'importe quel utilisateur de prendre les droits du super-utilisateur, le temps de l'exécution du programme.

Les problèmes surviennent lorsque l'on découvre des vulnérabilités dans les programmes Setuid root. En effet, s'il devient possible pour un utilisateur d'exécuter du code arbitraire, ce code sera exécuté avec les privilèges de l'utilisateur root ! Dans le cas de ping, on se retrouverait alors à pouvoir lire l'intégralité de la mémoire, alors que l'on avait juste besoin d'écrire sur une interface réseau.

C'est donc à ce moment que les capabilities entrent en jeu : un processus (ou même un thread) privilégié peut décider, généralement à son lancement, de réduire ses capabilities, pour ne garder que celles dont il a réellement besoin. Ainsi, ping pourrait se contenter de CAP_NET_RAW.

Les attributs de fichier étendus

Une grosse majorité des systèmes de fichiers (ext[234], XFS, btrfs, ...) permet d'enregistrer, pour chaque fichier, des attributs (dits attributs étendus, par opposition aux attributs réguliers qui sont réservés à l'usage du système de fichiers).

Sous Linux, les attributs sont regroupés dans des espaces de noms :

  • security : espace utilisé par les modules de sécurité du noyau, tel que SELinux, ... ;
  • system : espace utilisé par le noyau pour stocker des objets système, tels que les ACL POSIX ;
  • trusted: espace dont la lecture et l'écriture est limité au super-utilisateur ;
  • user : modifiable sans restriction, à partir du moment où l'on est le propriétaire du fichier.

Par exemple, on peut définir un attribut sur un fichier comme cela :

```shell 42sh$ echo 'Hello World!' > toto 42sh$ setfattr -n user.foo -v bar toto 42sh$ getfattr -d toto # file: toto user.foo="bar" ```

Encore plus fort, vous pouvez utiliser les ACL POSIX :

```shell 42sh$ sudo chown root:root toto && sudo chmod o-r toto 42sh$ cat toto cat: toto: Permission denied 42sh$ sudo setfattr -m u:$USER:r toto 42sh$ cat toto Hello World! ```

Bien que les droits UNIX traditionnels ne vous donnent pas accès au fichier, les ACL POSIX vous autorisent à le lire.

Vous pouvez voir ces attributs avec la commande :

```shell 42sh$ getfattr -d -m "^system" toto # file: toto system.posix_acl_access=0sgAAEAD/////AgAEOgDAEAA/////xAABAD////8= ```

ping

De la même manière que l'on peut définir de façon plus fine les droits d'accès par utilisateur, un attribut de l'espace de nom security peut être défini pour accroître les capabilities d'un processus lorsqu'il est lancé par un utilisateur non-privilégié. On peut alors voir le Setuid root comme l'utilisation de cet attribut auquel on accroîtrait l'ensemble des capabilities.

Si votre distribution profite de ces attributs étendus, vous devriez obtenir :

```shell 42sh$ getfattr -d -m "^security" $(which ping) # file: bin/ping security.capability=0sAQAAAgAgAAAAAAAAAAAAAAAAAAA= ```

Ou, dans sa version plus lisible :

```shell 42sh$ getcap $(which ping) /bin/ping = cap_net_raw+ep ```

Exercice : visualisateur de capabilities d'un processus

Écrivons maintenant un programme permettant de voir les capabilities d'un processus :

```shell 42sh$ ./view_caps 1 cap_user_header_t ----------------- Version: 20080522 PID: 1

cap_user_data_t

effective: 0xffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE CAP_BLOCK_SUSPEND CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH permitted: 0xffffffff CAP_AUDIT_CONTROL CAP_AUDIT_READ CAP_AUDIT_WRITE CAP_BLOCK_SUSPEND CAP_CHOWN CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH inheritable: 0x0

</div>

Astuces : `capget(2)`, X-macros, ...


## Pour aller plus loin

Je vous recommande la lecture des *man* suivants :

* `capabilities(7)` : énumérant tous les capabilities, leur utilisation, etc. ;
* `xattrs(7)` : à propos des attributs étendus.

Et de ces quelques articles :

* [Secure Your Containers with this One Weird Trick](https://rhelblog.redhat.com/2016/10/17/secure-your-containers-with-this-one-weird-trick/)
* [Guidelines for extended attributes](https://www.freedesktop.org/wiki/CommonExtendedAttributes/)
* [File-based capabilities](https://lwn.net/Articles/211883/)
* [A bid to resurrect Linux capabilities](https://lwn.net/Articles/199004/)

Pour revenir à Docker, par défaut, un certain nombre de *capabilities* sont
désactivées par défaut ; vous pouvez en ajouter et en retirer via les arguments
`--cap-add` et `--cap-drop` du `docker container run`.