Validation de SELinux

Restez organisé à l'aide des collections Enregistrez et classez les contenus selon vos préférences.

Android encourage fortement les OEM à tester minutieusement leurs implémentations SELinux. Au fur et à mesure que les fabricants implémentent SELinux, ils doivent d'abord appliquer la nouvelle politique à un pool de tests d'appareils.

Après avoir appliqué une nouvelle politique, assurez-vous que SELinux s'exécute dans le bon mode sur l'appareil en exécutant la commande getenforce .

Cela imprime le mode SELinux global : soit Enforcing, soit Permissive. Pour déterminer le mode SELinux pour chaque domaine, vous devez examiner les fichiers correspondants ou exécuter la dernière version de sepolicy-analyze avec l'indicateur ( -p ) approprié, présent dans /platform/system/sepolicy/tools/ .

Refus de lecture

Vérifiez les erreurs, qui sont acheminées sous forme de journaux d'événements vers dmesg et logcat et sont visibles localement sur l'appareil. Les fabricants doivent examiner la sortie SELinux vers dmesg sur ces appareils et affiner les paramètres avant la diffusion publique en mode permissif et le passage éventuel en mode application. Les messages de journal SELinux contiennent avc: et peuvent donc être facilement trouvés avec grep . Il est possible de capturer les journaux de refus en cours en exécutant cat /proc/kmsg ou de capturer les journaux de refus du démarrage précédent en exécutant cat /sys/fs/pstore/console-ramoops .

Avec cette sortie, les fabricants peuvent facilement identifier quand les utilisateurs ou les composants du système enfreignent la politique SELinux. Les fabricants peuvent alors réparer ce mauvais comportement, soit en modifiant le logiciel, la politique SELinux, ou les deux.

Plus précisément, ces messages de journal indiquent quels processus échoueraient en mode d'application et pourquoi. Voici un exemple:

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

Interprétez cette sortie comme suit :

  • Le { connectto } ci-dessus représente l'action en cours. Avec le tclass à la fin ( unix_stream_socket ), il vous indique approximativement ce qui a été fait à quoi. Dans ce cas, quelque chose essayait de se connecter à un socket de flux unix.
  • Le scontext (u:r:shell:s0) vous indique quel contexte a initié l'action. Dans ce cas, il s'agit de quelque chose qui s'exécute en tant que shell.
  • Le tcontext (u:r:netd:s0) vous indique le contexte de la cible de l'action. Dans ce cas, il s'agit d'un unix_stream_socket appartenant à netd .
  • Le comm="ping" en haut vous donne un indice supplémentaire sur ce qui était en cours d'exécution au moment où le refus a été généré. Dans ce cas, c'est un très bon indice.

Un autre exemple:

adb shell su root dmesg | grep 'avc: '

Sortir:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

Voici les éléments clés de ce refus :

  • Action - l'action tentée est mise en surbrillance entre parenthèses, read write ou setenforce .
  • Acteur - L' scontext (contexte source) représente l'acteur, dans ce cas le démon rmt_storage .
  • Objet - L' tcontext (contexte cible) représente l'objet sur lequel on agit, dans ce cas kmem.
  • Résultat - L' tclass (classe cible) indique le type d'objet sur lequel on agit, dans ce cas un chr_file (périphérique de caractère).

Vidage des piles utilisateur et noyau

Dans certains cas, les informations contenues dans le journal des événements ne sont pas suffisantes pour identifier l'origine du refus. Il est souvent utile de rassembler la chaîne d'appel, y compris le noyau et l'espace utilisateur, pour mieux comprendre pourquoi le refus s'est produit.

Les noyaux récents définissent un point de trace nommé avc:selinux_audited . Utilisez Android simpleperf pour activer ce point de trace et capturer la chaîne d'appels.

Configuration prise en charge

  • Le noyau Linux >= 5.10, en particulier les branches Android Common Kernel mainline et android12-5.10 sont pris en charge. La branche android12-5.4 est également prise en charge. Vous pouvez utiliser simpleperf pour déterminer si le point de trace est défini sur votre appareil : adb root && adb shell simpleperf list | grep avc:selinux_audited . Pour les autres versions du noyau, vous pouvez sélectionner les commits dd81662 et 30969bc .
  • Il devrait être possible de reproduire l'événement que vous déboguez. Les événements de démarrage ne sont pas pris en charge avec simpleperf ; cependant, vous pouvez toujours redémarrer le service pour déclencher l'événement.

Capturer la chaîne d'appel

La première étape consiste à enregistrer l'événement à l'aide simpleperf record :

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

Ensuite, l'événement qui a causé le refus doit être déclenché. Après cela, l'enregistrement doit être arrêté. Dans cet exemple, en utilisant Ctrl-c , l'échantillon aurait dû être capturé :

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

Enfin, simpleperf report peut être utilisé pour inspecter le stacktrace capturé. Par exemple:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

La chaîne d'appel ci-dessus est une chaîne d'appel unifiée du noyau et de l'espace utilisateur. Il vous donne une meilleure vue du flux de code en démarrant la trace depuis l'espace utilisateur jusqu'au noyau où le refus se produit. Pour plus d'informations sur simpleperf , consultez la référence des commandes exécutables Simpleperf

Passage au permissif

L'application de SELinux peut être désactivée via ADB sur les versions userdebug ou eng. Pour ce faire, basculez d'abord ADB vers root en exécutant adb root . Ensuite, pour désactiver l'application de SELinux, exécutez :

adb shell setenforce 0

Ou sur la ligne de commande du noyau (lors de la mise en service précoce du périphérique) :

androidboot.selinux=permissive
androidboot.selinux=enforcing

Ou via bootconfig dans Android 12 :

androidboot.selinux=permissive
androidboot.selinux=enforcing

Utiliser audit2allow

L'outil audit2allow prend les refus dmesg et les convertit en déclarations de politique SELinux correspondantes. En tant que tel, il peut considérablement accélérer le développement de SELinux.

Pour l'utiliser, lancez :

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

Néanmoins, il faut prendre soin d'examiner chaque ajout potentiel pour les autorisations excessives. Par exemple, alimenter audit2allow le refus rmt_storage montré précédemment entraîne la déclaration de stratégie SELinux suggérée suivante :

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

Cela donnerait à rmt la possibilité d'écrire dans la mémoire du noyau, une faille de sécurité flagrante. Souvent, les instructions audit2allow ne sont qu'un point de départ. Après avoir utilisé ces instructions, vous devrez peut-être modifier le domaine source et l'étiquette de la cible, ainsi qu'incorporer les macros appropriées, pour arriver à une bonne politique. Parfois, le refus examiné ne devrait entraîner aucun changement de politique ; l'application incriminée doit plutôt être modifiée.