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 constituant les 5 à 10 % restants. Cet article se concentre sur ces personnalisations spécifiques à l'appareil, sur la manière d'écrire une stratégie spécifique à l'appareil et sur certains des pièges à éviter en cours de route.

Présentation de l'appareil

Lors de l'écriture d'une stratégie spécifique à l'appareil, procédez comme suit.

Exécuter en mode permissif

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

  • Le mode permissif garantit que la mise en place de la stratégie ne retarde pas les autres tâches de mise en place précoce des appareils.
  • 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, l'ouverture d'un fichier, puis une lecture de fichier. En mode d'application, seul le refus de recherche dans l'annuaire se produirait. Le mode permissif garantit que tous les refus sont vus.

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 est un laps de temps raisonnable pour être en mode permissif global. Après avoir traité la majorité des refus, revenez en mode d'application et corrigez les bogues au fur et à mesure qu'ils surviennent. Les domaines produisant encore des refus ou des 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 application, les refus sont à la fois consigné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 à l'appareil entraîne souvent un produit bogué et une mauvaise expérience utilisateur. Commencez suffisamment tôt pour participer au dogfooding et assurez une couverture complète des tests de fonctionnalité dans le monde réel. Commencer tôt garantit que les préoccupations de sécurité éclairent les décisions de conception. À l'inverse, accorder des autorisations uniquement sur la base des refus observés est une approche dangereuse. Utilisez ce temps pour effectuer un audit de sécurité de l'appareil et signaler les bogues contre les comportements qui ne devraient pas être autorisés.

Supprimer ou supprimer une politique existante

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

Traiter les refus de services de base

Les refus générés par les services de base sont généralement traités 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 où tout ce qui se trouve dans /dev reçoit l'étiquette « périphérique », à moins qu'une étiquette plus spécifique ne soit attribuée. Accepter simplement la sortie de 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 à 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 libellés par défaut est incorrect. Bon nombre de ces autorisations sont refusées 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épondez aux 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 le init. device .rc fichier init. device .rc en tant que :

service foo /system/bin/foo
    class core
  1. Créer 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 pour le 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. Construisez 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 directives, mais ne l'utilisez que pour éclairer la rédaction de 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 en mode application le plus tôt possible et essayez d'y rester.

Erreurs fréquentes

Voici quelques solutions aux erreurs courantes qui se produisent lors de l'écriture de stratégies spécifiques à un appareil.

L'abus de la négation

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

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

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

La règle est défectueuse à plusieurs égards. L'exclusion de untrusted_app est facile à 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 n'autoriser que les domaines qui nécessitent 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 stratégie.

L'alternative la plus simple consiste à n'autoriser la fonction de débogage que 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 de politique de périphérique. 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 dans la plage de 20 % et plus contiennent presque certainement des domaines privilégiés et une politique morte.

Politique inutilement importante :

  • Prend un double impact 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 stratégie d'exécution.

L'exemple suivant montre deux appareils où la stratégie spécifique au fabricant comprenait 50 % et 40 % de la stratégie sur l'appareil. Une réécriture de la politique a entraîné des améliorations substantielles de la sécurité 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 de la politique spécifique à l'appareil après l'audit de sécurité.

Figure 1 . Comparaison de la taille de la stratégie spécifique à l'appareil après audit de sécurité.

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

Octroi de la capacité dac_override

Un refus dac_override signifie que le processus incriminé tente d'accéder à un fichier avec des autorisations d'utilisateur/groupe/monde Unix incorrectes. La bonne solution est presque jamais d'accorder l'autorisation dac_override . Au lieu de cela , modifiez 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.