Implémenter SELinux

SELinux est configuré pour refuser par défaut, ce qui signifie que chaque accès pour lequel il dispose d'un hook dans le noyau doit être explicitement autorisé par une règle. Cela signifie qu'un fichier de règles est composé d'une grande quantité d'informations concernant les règles, les types, les classes, les autorisations, etc. L'examen complet de SELinux dépasse le cadre de ce document, mais il est désormais essentiel de comprendre comment écrire des règles de stratégie lors de la mise en service de nouveaux appareils Android. De nombreuses informations sur SELinux sont déjà disponibles. Consultez la documentation d'aide pour obtenir des ressources suggérées.

Fichiers de clés

Pour activer SELinux, intégrez le dernier noyau Android, puis incorporez les fichiers du répertoire system/sepolicy. Une fois compilés, ces fichiers constituent la stratégie de sécurité du noyau SELinux et couvrent le système d'exploitation Android en amont.

En général, vous ne devez pas modifier directement les fichiers system/sepolicy. À la place, ajoutez ou modifiez vos propres fichiers de règles spécifiques à l'appareil dans le répertoire /device/manufacturer/device-name/sepolicy. Dans Android 8.0 ou version ultérieure, les modifications que vous apportez à ces fichiers ne doivent affecter que les règles de votre répertoire de fournisseurs. Pour en savoir plus sur la séparation de la stratégie SELinux publique dans Android 8.0 et versions ultérieures, consultez Personnaliser la stratégie SELinux dans Android 8.0 et versions ultérieures. Quelle que soit la version d'Android, vous modifiez toujours les fichiers suivants :

Fichiers de règles

Les fichiers se terminant par *.te sont des fichiers sources de règles SELinux, qui définissent les domaines et leurs libellés. Vous devrez peut-être créer des fichiers de règles dans /device/manufacturer/device-name/sepolicy, mais vous devez essayer de mettre à jour les fichiers existants dans la mesure du possible.

Fichiers de contexte

Les fichiers de contexte vous permettent de spécifier des libellés pour vos objets.

  • file_contexts attribue des libellés aux fichiers et est utilisé par différents composants de l'espace utilisateur. Lorsque vous créez des règles, créez ou mettez à jour ce fichier pour attribuer de nouveaux libellés aux fichiers. Pour appliquer de nouveaux file_contexts, reconstruisez l'image du système de fichiers ou exécutez restorecon sur le fichier à réétiqueter. Lors des mises à niveau, les modifications apportées à file_contexts sont automatiquement appliquées aux partitions système et de données utilisateur dans le cadre de la mise à niveau. Les modifications peuvent également être appliquées automatiquement lors de la mise à niveau vers d'autres partitions en ajoutant des appels restorecon_recursive à votre fichier init.board.rc une fois la partition montée en lecture/écriture.
  • genfs_contexts attribue des libellés aux systèmes de fichiers, tels que proc ou vfat, qui ne sont pas compatibles avec les attributs étendus. Cette configuration est chargée dans la stratégie du noyau, mais il est possible que les modifications ne prennent pas effet pour les inodes intégrés. Il peut être nécessaire de redémarrer l'appareil ou de démonter et remonter le système de fichiers pour appliquer pleinement la modification. Des libellés spécifiques peuvent également être attribués à des montages spécifiques, tels que vfat à l'aide de l'option context=mount.
  • property_contexts attribue des libellés aux propriétés du système Android pour contrôler les processus qui peuvent les définir. Cette configuration est lue par le processus init au démarrage.
  • service_contexts attribue des libellés aux services Binder Android pour contrôler les processus qui peuvent ajouter (enregistrer) et trouver (rechercher) une référence Binder pour le service. Cette configuration est lue par le processus servicemanager au démarrage.
  • seapp_contexts attribue des libellés aux processus d'application et aux répertoires /data/data. Cette configuration est lue par le processus zygote à chaque lancement de l'application et par installd au démarrage.
  • mac_permissions.xml attribue un tag seinfo aux applications en fonction de leur signature et, éventuellement, de leur nom de package. La balise seinfo peut ensuite être utilisée comme clé dans le fichier seapp_contexts pour attribuer un libellé spécifique à toutes les applications comportant cette balise seinfo. Cette configuration est lue par system_server au démarrage.
  • keystore2_key_contexts attribue des libellés aux espaces de noms Keystore 2. Ces espaces de noms sont appliqués par le daemon keystore2. Le keystore a toujours fourni des espaces de noms basés sur les UID/AID. Keystore 2 applique également les espaces de noms définis par sepolicy. Pour obtenir une description détaillée du format et des conventions de ce fichier, cliquez ici.

Makefile BoardConfig.mk

Après avoir modifié ou ajouté des fichiers de règles et de contexte, mettez à jour votre fichier make /device/manufacturer/device-name/BoardConfig.mk pour référencer le sous-répertoire sepolicy et chaque nouveau fichier de règles. Pour en savoir plus sur les variables BOARD_SEPOLICY, consultez Fichier system/sepolicy/README.

BOARD_SEPOLICY_DIRS += \
        <root>/device/manufacturer/device-name/sepolicy

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

Une fois la reconstruction terminée, SELinux est activé sur votre appareil. Vous pouvez désormais personnaliser vos règles SELinux pour tenir compte de vos propres ajouts au système d'exploitation Android, comme décrit dans Personnalisation, ou valider votre configuration existante, comme indiqué dans Validation.

Une fois les nouveaux fichiers de règles et les mises à jour BoardConfig.mk en place, les nouveaux paramètres de règles sont automatiquement intégrés au fichier de règles du noyau final. Pour en savoir plus sur la façon dont sepolicy est créé sur l'appareil, consultez Créer sepolicy.

Implémentation

Pour commencer avec SELinux :

  1. Activez SELinux dans le noyau : CONFIG_SECURITY_SELINUX=y
  2. Modifiez le paramètre kernel_cmdline ou bootconfig de sorte que :
    BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive
    ou
    BOARD_BOOTCONFIG := androidboot.selinux=permissive
    Cette option ne sert qu'au développement initial de la règle pour l'appareil. Une fois que vous disposez d'une règle d'amorçage initiale, supprimez ce paramètre pour que votre appareil applique la règle ou échoue au CTS.
  3. Démarrez le système en mode permissif et identifiez les refus rencontrés au démarrage :
    Sur Ubuntu 14.04 ou version ultérieure :
    adb shell su -c dmesg | grep denied | audit2allow -p out/target/product/BOARD/root/sepolicy
    
    Sous Ubuntu 12.04 :
    adb pull /sys/fs/selinux/policy
    adb logcat -b all | audit2allow -p policy
    
  4. Évaluez la sortie pour détecter les avertissements qui ressemblent à init: Warning! Service name needs a SELinux domain defined; please fix! Consultez la section Validation pour obtenir des instructions et des outils.
  5. Identifier les appareils et les autres nouveaux fichiers qui doivent être étiquetés.
  6. Utilisez des libellés existants ou nouveaux pour vos objets. Consultez les fichiers *_contexts pour voir comment les éléments étaient étiquetés auparavant et utilisez vos connaissances sur la signification des libellés pour en attribuer un nouveau. Dans l'idéal, il s'agit d'un libellé existant qui correspond à la règle, mais il est parfois nécessaire de créer un libellé et des règles d'accès. Ajoutez vos libellés aux fichiers de contexte appropriés.
  7. Identifiez les domaines/processus qui doivent avoir leurs propres domaines de sécurité. Vous devrez probablement écrire une toute nouvelle règle pour chacun d'eux. Par exemple, tous les services générés à partir de init doivent avoir leur propre fichier. Les commandes suivantes permettent d'identifier les services qui continuent de s'exécuter (mais TOUS les services doivent être traités de la sorte) :
    adb shell su -c ps -Z | grep init
    
    adb shell su -c dmesg | grep 'avc: '
    
  8. Consultez init.device.rc pour identifier les domaines qui n'ont pas de type de domaine. Attribuez-leur un domaine tôt dans votre processus de développement pour éviter d'ajouter des règles à init ou de confondre les accès init avec ceux de leur propre règle.
  9. Configurez BOARD_CONFIG.mk pour utiliser les variables BOARD_SEPOLICY_*. Pour savoir comment configurer cela, consultez le fichier README dans system/sepolicy.
  10. Examinez les fichiers init.device.rc et fstab.device et assurez-vous que chaque utilisation de mount correspond à un système de fichiers correctement libellé ou qu'une option context= mount est spécifiée.
  11. Passez en revue chaque refus et créez une règle SELinux pour les gérer correctement. Consultez les exemples dans Personnalisation.

Vous devez commencer par les règles de l'AOSP, puis les développer pour vos propres personnalisations. Pour en savoir plus sur la stratégie de règles et pour examiner de plus près certaines de ces étapes, consultez Écrire une règle SELinux.

Cas d'utilisation

Voici des exemples spécifiques d'exploits à prendre en compte lorsque vous créez votre propre logiciel et les règles SELinux associées :

Liens symboliques : comme les liens symboliques apparaissent sous forme de fichiers, ils sont souvent lus comme des fichiers, ce qui peut entraîner des failles de sécurité. Par exemple, certains composants privilégiés, tels que init, modifient les autorisations de certains fichiers, parfois de manière excessive.

Les pirates informatiques peuvent ensuite remplacer ces fichiers par des liens symboliques vers du code qu'ils contrôlent, ce qui leur permet d'écraser des fichiers arbitraires. Toutefois, si vous savez que votre application ne traverse jamais de lien symbolique, vous pouvez l'empêcher de le faire avec SELinux.

Fichiers système : tenez compte de la classe de fichiers système qui ne doit être modifiée que par le serveur système. Toutefois, comme netd, init et vold s'exécutent en tant que root, ils peuvent accéder à ces fichiers système. Par conséquent, si netd est compromis, cela peut compromettre ces fichiers et potentiellement le serveur système lui-même.

Avec SELinux, vous pouvez identifier ces fichiers comme des fichiers de données de serveur système. Par conséquent, le seul domaine qui dispose d'un accès en lecture/écriture à ces fichiers est le serveur système. Même si netd était compromis, il ne pourrait pas passer au domaine du serveur système ni accéder à ces fichiers système, même s'il s'exécute en tant que root.

Données d'application : un autre exemple est la classe de fonctions qui doivent s'exécuter en tant que root, mais qui ne doivent pas avoir accès aux données d'application. C'est extrêmement utile, car il est possible de faire des assertions très variées, par exemple en interdisant à certains domaines sans rapport avec les données de l'application d'accéder à Internet.

setattr : pour les commandes telles que chmod et chown, vous pouvez identifier l'ensemble des fichiers dans lesquels le domaine associé peut effectuer setattr. Tout ce qui ne relève pas de cette catégorie peut être interdit par ces modifications, même par la racine. Une application peut donc exécuter chmod et chown sur les éléments libellés app_data_files, mais pas sur ceux libellés shell_data_files ou system_data_files.