Rédaction de la politique SELinux

Le projet Android Open Source (AOSP) fournit une politique de base solide pour les applications et les services communs à tous les appareils Android. Les contributeurs à l’AOSP affinent régulièrement cette politique. La politique de base devrait représenter environ 90 à 95 % de la politique finale sur l'appareil, les personnalisations spécifiques à l'appareil représentant les 5 à 10 % restants. Cet article se concentre sur ces personnalisations spécifiques aux appareils, sur la façon d’écrire une stratégie spécifique à l’appareil et sur certains des pièges à éviter en cours de route.

Affichage de l'appareil

Lors de la rédaction d’une stratégie spécifique à l’appareil, suivez ces étapes.

Exécuter en mode permissif

Lorsqu'un appareil est en mode permissif , les refus sont enregistrés mais ne sont pas appliqués. Le mode permissif est important pour deux raisons :

  • Le mode permissif garantit que l’affichage de la stratégie ne retarde pas les autres tâches d’affichage précoce du périphérique.
  • Un refus forcé peut masquer d’autres refus. Par exemple, l'accès aux fichiers implique généralement une recherche dans un répertoire, une ouverture de fichier, puis une lecture de fichier. En mode d'application, seul le refus de la recherche dans l'annuaire se produirait. Le mode permissif garantit que tous les refus sont visibles.

Le moyen le plus simple de mettre un périphérique en mode permissif consiste à utiliser la ligne de commande du noyau . Cela peut être ajouté au fichier BoardConfig.mk de l'appareil : platform/device/<vendor>/<target>/BoardConfig.mk . Après avoir modifié la ligne de commande, effectuez make clean , puis make bootimage et flashez la nouvelle image de démarrage.

Après cela, confirmez le mode permissif avec :

adb shell getenforce

Deux semaines constituent un délai raisonnable pour être en mode permissif global. Après avoir résolu la majorité des refus, revenez en mode d'application et corrigez les bugs au fur et à mesure qu'ils surviennent. Les domaines produisant encore des refus ou les services encore en développement intensif peuvent être temporairement mis en mode permissif, mais remettez-les en mode d'application dès que possible.

Appliquer tôt

En mode d'application, les refus sont à la fois enregistrés et appliqués. Il est recommandé de mettre votre appareil en mode d'application le plus tôt possible. Attendre pour créer et appliquer une politique spécifique à un appareil entraîne souvent un produit bogué et une mauvaise expérience utilisateur. Commencez suffisamment tôt pour participer au dogfooding et assurez-vous d’une couverture complète des tests de fonctionnalités dans le monde réel. Commencer tôt garantit que les préoccupations en matière de sécurité éclairent les décisions de conception. À l’inverse, accorder des autorisations sur la seule base des refus observés est une approche dangereuse. Utilisez ce temps pour effectuer un audit de sécurité de l'appareil et signaler des bogues concernant des comportements qui ne devraient pas être autorisés.

Supprimer ou supprimer la stratégie existante

Il existe un certain nombre de bonnes raisons de créer une stratégie spécifique à un appareil à partir de zéro sur un nouvel appareil, notamment :

Répondre aux refus de services de base

Les refus générés par les services de base sont généralement résolus par l'étiquetage des fichiers. Par exemple:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

est complètement résolu en étiquetant correctement /dev/kgsl-3d0 . Dans cet exemple, tcontext est device . Cela représente un contexte par défaut dans lequel tout ce qui se trouve dans /dev reçoit l'étiquette « dev » à moins qu'une étiquette plus spécifique ne soit attribuée. Accepter simplement le résultat d' audit2allow ici entraînerait une règle incorrecte et trop permissive.

Pour résoudre ce genre de problème, donnez au fichier une étiquette plus spécifique, qui dans ce cas est gpu_device . Aucune autre autorisation n'est nécessaire car le serveur multimédia dispose déjà des autorisations nécessaires dans la politique principale pour accéder au gpu_device.

Autres fichiers spécifiques à l'appareil qui doivent être étiquetés avec des types prédéfinis dans la stratégie principale :

En général, accorder des autorisations aux étiquettes par défaut est une erreur. Beaucoup de ces autorisations sont interdites par les règles Neverallow , mais même lorsqu'elles ne sont pas explicitement refusées, la meilleure pratique consiste à fournir une étiquette spécifique.

Étiquetez les nouveaux services et résolvez les refus

Les services lancés par Init doivent s'exécuter dans leurs propres domaines SELinux. L'exemple suivant place le service « foo » dans son propre domaine SELinux et lui accorde des autorisations.

Le service est lancé dans init. device .rc fichier init. device .rc comme :

service foo /system/bin/foo
    class core
  1. Créez un nouveau domaine "foo"

    Créez le fichier device/ manufacturer / device-name /sepolicy/foo.te avec le contenu suivant :

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    Il s'agit du modèle initial du domaine foo SELinux, auquel vous pouvez ajouter des règles basées sur les opérations spécifiques effectuées par cet exécutable.

  2. Étiquette /system/bin/foo

    Ajoutez ce qui suit à device/ manufacturer / device-name /sepolicy/file_contexts :

    /system/bin/foo   u:object_r:foo_exec:s0
    

    Cela garantit que l'exécutable est correctement étiqueté afin que SELinux exécute le service dans le domaine approprié.

  3. Créez et flashez les images de démarrage et système.
  4. Affinez les règles SELinux pour le domaine.

    Utilisez les refus pour déterminer les autorisations requises. L'outil audit2allow fournit de bonnes lignes directrices, mais ne l'utilise que pour éclairer la rédaction des politiques. Ne vous contentez pas de copier la sortie.

Revenir au mode d'application

C'est bien de dépanner en mode permissif, mais revenez au mode d'application le plus tôt possible et essayez d'y rester.

Erreurs courantes

Voici quelques solutions aux erreurs courantes qui se produisent lors de la rédaction de stratégies spécifiques aux appareils.

Utilisation excessive de la négation

L'exemple de règle suivant revient à verrouiller la porte d'entrée tout en laissant les fenêtres ouvertes :

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

L'intention est claire : tout le monde, à l'exception des applications tierces, peut avoir accès au périphérique de débogage.

La règle est erronée à plusieurs égards. L'exclusion de untrusted_app est simple à contourner car toutes les applications peuvent éventuellement exécuter des services dans le domaine isolated_app . De même, si de nouveaux domaines pour des applications tierces sont ajoutés à AOSP, ils auront également accès à scary_debug_device . La règle est trop permissive. La plupart des domaines ne bénéficieront pas de l'accès à cet outil de débogage. La règle aurait dû être écrite pour autoriser uniquement les domaines nécessitant un accès.

Fonctionnalités de débogage en production

Les fonctionnalités de débogage ne doivent pas être présentes sur les versions de production, pas plus que leur politique.

L'alternative la plus simple consiste à autoriser la fonctionnalité de débogage uniquement lorsque SELinux est désactivé sur les versions eng/userdebug, telles que adb root et adb shell setenforce 0 .

Une autre alternative sûre consiste à inclure les autorisations de débogage dans une instruction userdebug_or_eng .

Explosion de la taille des politiques

Caractériser les politiques SEAndroid dans la nature décrit une tendance inquiétante dans la croissance des personnalisations des politiques relatives aux appareils. La stratégie spécifique à l’appareil doit représenter 5 à 10 % de la stratégie globale exécutée sur un appareil. Les personnalisations de l'ordre de 20 % et plus contiennent presque certainement des domaines trop privilégiés et des politiques mortes.

Politique inutilement importante :

  • Prend un double coup sur la mémoire car la politique se trouve dans le disque virtuel et est également chargée dans la mémoire du noyau.
  • Gaspille de l'espace disque en nécessitant une image de démarrage plus grande.
  • Affecte les temps de recherche de la stratégie d’exécution.

L'exemple suivant montre deux appareils sur lesquels la stratégie spécifique au fabricant représentait 50 % et 40 % de la stratégie sur l'appareil. Une réécriture de la politique a apporté des améliorations de sécurité substantielles sans perte de fonctionnalité, comme indiqué ci-dessous. (Les appareils AOSP Shamu et Flounder sont inclus à titre de comparaison.)

Figure 1 : Comparaison de la taille des politiques spécifiques aux appareils après un audit de sécurité.

Figure 1 . Comparaison de la taille des politiques spécifiques à l'appareil après un audit de sécurité.

Dans les deux cas, la politique a été considérablement réduite, tant en taille qu’en nombre d’autorisations. La diminution de la taille de la stratégie est presque entièrement due à la suppression des autorisations inutiles, dont beaucoup étaient probablement des règles générées par audit2allow et ajoutées sans discernement à la stratégie. Les domaines morts constituaient également un problème pour les deux appareils.

Accorder la capacité dac_override

Un refus dac_override signifie que le processus incriminé tente d'accéder à un fichier avec les autorisations utilisateur/groupe/monde Unix incorrectes. La bonne solution consiste presque jamais à accorder l’autorisation dac_override . Modifiez plutôt les autorisations Unix sur le fichier ou le processus . Quelques domaines tels que init , vold et installd ont réellement besoin de pouvoir remplacer les autorisations de fichiers Unix pour accéder aux fichiers d'autres processus. Voir le blog de Dan Walsh pour une explication plus approfondie.