Gentoo Logo

[ << ] [ < ] [ Sommaire ] [ > ] [ >> ]


1. Présentation de SELinux

Table des matières :

1.a. Les types SELinux

Un type SELinux est un attribut de sécurité assigné à un objet (un fichier, un port réseau, etc.). Le type d'un processus est communément appelé son domaine. Une politique de sécurité SELinux est principalement constituée de règles pour respecter les types, qui décrivent de quelles manières les domaines sont autorisés à interagir avec les objets et avec les autres domaines. Un type est généralement suffixé par « _t », par exemple : sysadm_t. Le type est l'attribut le plus important pour un processus ou un objet, car la plupart des décisions politiques se basent sur les types sources et cibles.

1.b. Les rôles SELinux

La sécurité fournie par SELinux tourne autour des types. Les rôles SELinux sont donc différents de ce que l'on peut trouver dans un système de contrôle d'accès basé sur les rôles (RBAC). Les différentes autorisations ne sont pas attribuées aux rôles. Un rôle décrit l'ensemble des types dont un utilisateur peut se servir. Par exemple, un administrateur qui utilise un système pour exécuter des tâches d'utilisateur classique devrait être dans le rôle staff_r. S'il veut procéder à une tâche d'administration système, il doit alors explicitement basculer dans le rôle sysadm_r. En termes SELinux, la liste des domaines dans lesquels un utilisateur peut se trouver dépend de son rôle. Si un rôle n'est pas autorisé à avoir un certain domaine, une transition vers ce domaine sera rejetée, même si la règle du type permet la transition de domaine. Un rôle est généralement suffixé par un « _r », par exemple : system_r.

1.c. Les identités SELinux

Qu'est-ce qu'une identité SELinux ?

Une identité SELinux est similaire à un nom d'utilisateur Linux. Le changement d'identité devrait être limité à certains cas spécifiques, car le contrôle d'accès basé sur les rôles se repose sur l'identité SELinux. Donc, en général, l'identité SELinux d'un utilisateur ne changera pas tout au long de sa session. L'identifiant utilisateur de Linux peut être changé par set(e)uid, ce n'est donc pas un identifiant approprié pour une identité SELinux. Si on attribue une identité SELinux à un utilisateur, celle-ci doit correspondre au nom d'utilisateur Linux. À chaque identité SELinux on attribue un ensemble de rôles.

Configurer la correspondance des identités SELinux

Une politique SELinux contient plusieurs identités SELinux génériques qui devraient convenir pour tous les utilisateurs. Cette correspondance n'a besoin d'être configurée que lors d'une utilisation de la politique stricte. Vous n'avez pas besoin de la configurer avec la politique ciblée, car l'identité par défaut (user_u) est suffisante dans tous les cas.

Lorsqu'un utilisateur se connecte, l'identité SELinux est déterminée selon cette correspondance.

Identité SELinux Rôles Description
system_u system_r Pour les processus systèmes (non-interactifs). Ne devrait pas être attribuée à un utilisateur.
user_u user_r Pour les utilisateurs sans privilège. La correspondance par défaut.
staff_u staff_r, sysadm_r Pour les administrateurs systèmes qui se connectent également pour exécuter des tâches d'utilisateur normal.
sysadm_u sysadm_r Pour les administrateurs systèmes qui ne font que se connecter pour exécuter des tâches administratives. Il est suggéré de ne pas utiliser cette identité.
root staff_r, sysadm_r Identité spéciale pour root. Les autres utilisateurs devraient plutôt utiliser staff_u à la place.

Pour savoir comment utiliser semanage afin de configurer vos correspondances, lisez notre Guide pratique SELinux.

1.d. Les contextes SELinux

L'utilisation des trois modèles de sécurité précédents ensemble s'appelle un contexte SELinux et se note sous la forme : identité:rôle:type. Le contexte SELinux est la valeur la plus importante dans la décision d'un accès.

Le contexte objet

Un ls -Z typique peut renvoyer un résultat semblable à celui-ci :

Exemple de code 4.1 : Exemple de ls -Z

drwxr-xr-x  root     root     system_u:object_r:bin_t          bin
drwxr-xr-x  root     root     system_u:object_r:boot_t         boot
drwxr-xr-x  root     root     system_u:object_r:device_t       dev
drwxr-xr-x  root     root     system_u:object_r:etc_t          etc

Les trois premières colonnes décrivent les permissions, le nom d'utilisateur et le nom de groupe, valeurs typiques de Linux. La quatrième colonne est le contexte du fichier ou du répertoire. On donne le rôle générique object_r aux objets. Des deux autres champs du contexte, on en déduit que les fichiers ont l'identité système et qu'ils sont de types différents, bin_t, boot_t, device_t, et etc_t.

Le contexte processus

Un ps ax -Z typique peut renvoyer ce genre de résultat :

Exemple de code 4.2 : Exemple de ps ax -Z

  PID CONTEXT                                  COMMAND
    1 system_u:system_r:init_t                 [init]
    2 system_u:system_r:kernel_t               [keventd]
    3 system_u:system_r:kernel_t               [ksoftirqd_CPU0]
    4 system_u:system_r:kernel_t               [kswapd]
    5 system_u:system_r:kernel_t               [bdflush]
    6 system_u:system_r:kernel_t               [kupdated]
  706 system_u:system_r:syslogd_t              [syslog-ng]
  712 system_u:system_r:httpd_t                [apache]
  791 system_u:system_r:sshd_t                 [sshd]
  814 system_u:system_r:crond_t                [cron]
  826 system_u:system_r:getty_t                [agetty]
  827 system_u:system_r:getty_t                [agetty]
  828 system_u:system_r:getty_t                [agetty]
  829 system_u:system_r:getty_t                [agetty]
  830 system_u:system_r:getty_t                [agetty]
  831 system_u:system_r:httpd_t                [apache]
  832 system_u:system_r:httpd_t                [apache]
  833 system_u:system_r:httpd_t                [apache]
23093 system_u:system_r:sshd_t                 [sshd]
23095 user_u:user_r:user_t                     [bash]
23124 system_u:system_r:sshd_t                 [sshd]
23126 user_u:user_r:user_t                     [bash]
23198 system_u:system_r:sshd_t                 [sshd]
23204 user_u:user_r:user_t                     [bash]
23274 system_u:system_r:sshd_t                 [sshd]
23275 pebenito:staff_r:staff_t                 [bash]
23290 pebenito:staff_r:staff_t                 ps ax -Z

Dans cet exemple, le contexte de chaque processus est affiché en plus des informations typiques. Si on examine cette liste, on remarque que tous les processus du noyau et les démons tournent sous l'identité system_u et avec le rôle system_r. Les domaines individuels dépendent du programme. On note plusieurs utilisateurs connectés en SSH, sous l'identité générique user_u. Enfin, un utilisateur, portant l'identité pebenito, est connecté avec le rôle staff_r et se trouve dans le domaine staff_t.

1.e. Les fichiers de politiques de SELinux

Les fichiers sources des politiques de SELinux ne sont plus installés sur le système. Dans les répertoires /usr/share/selinux/{strict,targeted}, vous trouverez une série de paquets de politiques et d'en-têtes afin de construire des modules locaux. Les fichiers de politiques sont traités par m4, puis le compilateur de politique checkmodule vérifie qu'il n'y ait pas d'erreur de syntaxe avant de créer un module de politique. Ensuite, un paquet de politique est créé avec la commande semodule_package en utilisant le module de politique et les contextes de fichiers du module. La politique, une fois empaquetée, peut alors être chargée dans un noyau SELinux en l'insérant dans le magasin de modules.

*.pp

Les paquets de politiques pour cette politique. Ils doivent être insérés dans le magasin de modules afin d'être chargés dans la politique. Dans le paquet, se trouve un module de politique chargeable et éventuellement un fichier de contexte de fichier.

include/

Les en-têtes de politiques pour cette politique.

1.f. Les versions de politiques binaires

Lors d'une compilation de politique, le fichier binaire résultant contient un numéro de version. La première version qui a été introduite dans le noyau Linux a été la version 15. Le numéro de version n'est en général incrémenté que lorsqu'une nouvelle fonctionnalité ajoutée nécessite des changements de structure de la politique compilée binaire. Par exemple, dans Linux 2.6.5, des extensions concernant les politiques conditionnelles ont été ajoutées. Le numéro de version des politiques résultantes a été incrémenté à 16.

Quelle est la version de politique de mon noyau ?

La version de politique d'un noyau peut se déterminer à l'aide des outils sestatus ou policyvers. Les noyaux actuels peuvent charger une politique d'une version antécédente, par exemple un noyau en version 17 peut aussi charger les politiques en version 16. Cependant, cette compatibilité pourra être supprimée à l'avenir.

Note : L'infrastructure de gestion des politiques (libsemanage) créera et utilisera automatiquement la bonne version de la politique. Aucune étape supplémentaire n'est requise.

Versions de politiques

La table suivante indique l'historique des versions de politiques selon la version de Linux 2.6.

Version Description Versions de Linux
12 SELinux « Vieille API » (obsolète)
15 SELinux « nouvelle API » intégrée dans 2.6 2.6.0 - 2.6.4
16 Ajout d'extensions permettant les politiques conditionnelles 2.6.5
17 Ajout du support IPv6 2.6.6 - 2.6.7
18 Amélioration du support des sockets Netlink 2.6.8 - 2.6.11
19 Amélioration de la sécurité multi-niveaux 2.6.12 - 2.6.13
20 Optimisation de la taille des tables de vecteurs d'accès 2.6.14 - 2.6.18
21 Classes d'objets 2.6.19 - 2.6.24
22 Fonctionnalités des politiques Depuis 2.6.25

1.g. Les extensions de politique conditionnelle

Les extensions de politique conditionnelle permettent l'activation et la désactivation de règles à la volée, sans besoin de recharger une politique modifiée. En utilisant les booléens et les expressions de politique, les règles peuvent être appliquées conditionnellement.

Afficher les valeurs des booléens

L'état des booléens de la politique courante peut être déterminé de deux façons. Premièrement, en utilisant sestatus :

Exemple de code 7.1 : Exemple de résultat de sestatus

# sestatus
SELinux status:         enabled
SELinuxfs mount:        /selinux
Current mode:           enforcing
Policy version:         17
 
Policy booleans:
user_ping               inactive

La deuxième méthode est l'utilisation de getsebool, un outil simple qui affiche l'état courant et l'état en attente (pending) des booléens.

Exemple de code 7.2 : Exemple de résultat de getsebool

# getsebool -a
user_ping --> active: 0 pending: 0

Modifier un booléen

La valeur d'un booléen peut être inversée en utilisant la commande togglesebool. On peut spécifier plusieurs booléens sur la ligne de commande. La nouvelle valeur des booléens sera affichée en retour.

Exemple de code 7.3 : Exemple d'utilisation de togglesebool

# togglesebool user_ping
user_ping: active

La valeur d'un booléen peut également être définie explicitement avec setsebool.

Exemple de code 7.4 : Exemple d'utilisation de setsebool

# setsebool user_ping 0

Pour définir la valeur d'un booléen et en faire sa valeur par défaut, utilisez l'option -P.

Exemple de code 7.5 : Changer la valeur par défaut

# setsebool -P user_ping 1

1.h. Les messages du noyau concernant la politique

Lorsque le système tourne, un programme ou un utilisateur peut tenter de faire quelque chose qui viole la politique de sécurité. Si le système respecte la politique (mode enforcing), l'accès sera refusé et un message sera envoyé dans le journal du noyau. Si le système est en mode permissive, l'accès sera autorisé mais il y aura tout de même un message envoyé au journal du noyau.

Les messages de l'AVC

La plupart des messages du noyau provenant de SELinux viennent du cache de vecteurs d'accès (AVC pour Access Vector Cache). Comprendre les refus d'accès est important pour déterminer si une attaque est en cours ou bien si le programme demande un accès non prévu. Voici un exemple de refus d'accès :

Exemple de code 8.1 : Exemple de message de l'AVC

avc:  denied  { read write } for  pid=3392 exe=/bin/mount dev=03:03 ino=65554
scontext=pebenito:sysadm_r:mount_t tcontext=system_u:object_r:tmp_t tclass=file

Bien que la plupart des messages de l'AVC soient des refus d'accès, il peut aussi de temps en temps y avoir des messages d'audit à propos d'accès autorisés :

Exemple de code 8.2 : Un autre exemple de message de l'AVC

avc:  granted  { load_policy } for  pid=3385 exe=/usr/sbin/load_policy
scontext=pebenito:sysadm_r:load_policy_t tcontext=system_u:object_r:security_t tclass=security

Dans ce cas-ci, la demande de charger la politique a été autorisée. C'est un évènement de sécurité critique et est donc toujours audité. Un autre évènement toujours audité est le passage du mode enforcing vers permissive et inversement.

SELinux n'enregistrera pas les refus d'accès dans le journal s'il y en a trop à la fois. Cela ne veut pas forcément dire qu'une attaque est en cours. Un programme peut par exemple être en train d'exécuter un stat() sur les fichiers présents dans /dev/. SELinux limite le nombre de messages afin de minimiser les risques de saturation du journal :

Exemple de code 8.3 : Un dernier exemple de message de l'AVC

AVC: 12 messages suppressed.

Si le comportement du programme est normal et que ces accès doivent bien être refusés, la politique doit alors être modifiée afin de ne pas auditer ces accès.

Autres messages du noyau

Exemple de code 8.4 : inode_doinit_with_dentry

inode_doinit_with_dentry:  context_to_sid(system_u:object_r:bar_t) returned 22 for dev=hda3 ino=517610

Cela signifie que le fichier se trouvant sur /dev/hda3 et ayant le numéro d'inode 517610 a le contexte system_u:object_r:bar_t, qui est invalide. Les objets qui ont un contexte invalide sont traités comme s'ils avaient le contexte system_u:object_r:unlabeled_t.

1.i. Autopsie d'un refus d'accès

Les refus d'accès contiennent une quantité variable d'information, selon le type d'accès.

Exemple de code 9.1 : Exemples de refus d'accès

avc:  denied  { lock } for  pid=28341 exe=/sbin/agetty path=/var/log/wtmp dev=03:03 ino=475406
scontext=system_u:system_r:getty_t tcontext=system_u:object_r:var_log_t tclass=file

avc:  denied  { create } for  pid=20909 exe=/bin/ls scontext=pebenito:sysadm_r:mkinitrd_t
tcontext=pebenito:sysadm_r:mkinitrd_t tclass=unix_stream_socket

avc:  denied  { setuid } for  pid=3170 exe=/usr/bin/ntpd capability=7
scontext=system_u:system_r:ntpd_t tcontext=system_u:system_r:ntpd_t tclass=capability

Les refus d'accès concernent généralement l'accès à un fichier. Pour mieux comprendre, décortiquons ici le premier message :

Extrait Explication
avc: denied SELinux a refusé l'accès.
{ lock } La tentative d'accès est de type « lock » (verrou).
pid=28341 L'identifiant du processus est 28341.
exec=/sbin/agetty Le chemin complet du processus exécutable est /sbin/agetty.
path=/var/log/wtmp Le chemin complet de l'objet cible est /var/log/wtmp. Notez que le chemin complet n'est pas toujours disponible.
dev=03:03 L'objet cible réside sur le périphérique 03:03 (numéros majeur:mineur). Le noyau 2.6 peut procéder à une résolution de nom, hda3 ici.
ino=475406 Le numéro d'inode de la cible est 475406.
scontext=system_u:system_r:getty_t Le contexte du programme est system_u:system_r:getty_t.
tcontext=system_u:object_r:var_log_t Le contexte de l'objet cible est system_u:object_r:var_log_t.
tclass=file L'objet cible est un fichier normal.

Tous les messages de l'AVC n'ont pas forcément tous ces champs, comme notamment les deux exemples suivants. Les champs présents varient en fonction de la classe de l'objet cible. Cependant, les champs les plus importants (le type d'accès, les contextes source et cible et l'objet cible) seront toujours affichés dans un message de l'AVC.

Comprendre le refus d'accès

Les messages de refus d'accès sont parfois trompeurs car ils peuvent être envoyés pour plusieurs raisons. Pour comprendre ce qu'il se passe, il faut connaitre le comportement du programme et savoir correctement interpréter les messages de refus d'accès. La cible n'est pas forcément un fichier, mais peut aussi être une socket réseau, une communication interprocessus, etc.

Dans l'exemple précédent, agetty se voit refuser le droit de poser un verrou sur un fichier. Le type du fichier est var_log_t, ce qui implique que le fichier cible se trouve dans /var/log/. Grâce aux informations supplémentaires fournies par le champ path= du message de refus d'accès, on confirme que le fichier est /var/log/wtmp. Si l'information du chemin complet n'avait pas été disponible, on aurait pu trouver le fichier avec le numéro d'inode. wtmp est un fichier qui contient des informations à propos des utilisateurs actuellement connectés au système et agetty gère justement les connexions depuis un tty. On peut donc en conclure que c'est un comportement normal de la part d'agetty de vouloir mettre à jour wtmp. Cependant, pourquoi cet accès est-il refusé ? Y aurait-il une anomalie dans la politique qui empêcherait agetty de mettre à jour wtmp ? En réalité, c'est wtmp qui a un mauvais contexte. Cela devrait être system_u:object_r:wtmp_t et non pas system_u:object_r:var_log_t.

Si cette demande d'accès n'avait pas été bien comprise, l'administrateur aurait pu faire l'erreur d'autoriser les accès en lecture/écriture de getty_t vers les fichiers var_log_t, ce qui aurait été incorrect, puisqu'agetty n'a besoin que de modifier /var/log/wtmp. Cet exemple souligne à quel point il est important de garder les contextes des fichiers dans un état cohérent.

1.j. Références

Le site de SELinux et le fichier README de SELinux.


[ << ] [ < ] [ Sommaire ] [ > ] [ >> ]


Imprimer

Voir tout

Dernière mise à jour le 16 mai 2008

Résumé : Ce chapitre explique les concepts importants de SELinux et les politiques de sécurité.

Chris PeBenito
Author

Camille Huot
Traducteur

Donate to support our development efforts.

Copyright 2001-2014 Gentoo Foundation, Inc. Questions, Comments? Contact us.