Android encourage vivement les OEM à tester minutieusement leurs implémentations SELinux. Lorsque les fabricants implémentent SELinux, ils doivent d'abord appliquer la nouvelle règle à un pool de test d'appareils.
Après avoir appliqué une nouvelle règle, assurez-vous que SELinux s'exécute dans le mode approprié sur l'appareil en exécutant la commande getenforce
.
Cela affiche le mode SELinux global : "Enforcing" (Application forcée) ou "Permissive" (Permissif). Pour déterminer le mode SELinux de chaque domaine, vous devez examiner les fichiers correspondants ou exécuter la dernière version de sepolicy-analyze
avec l'indicateur approprié (-p
), présent dans
/platform/system/sepolicy/tools/
.
Lire les refus
Recherchez les erreurs, qui sont routées en tant que journaux d'événements vers dmesg
et logcat
, et qui sont visibles localement sur l'appareil. Les fabricants doivent examiner la sortie SELinux pour dmesg
sur ces appareils et affiner les paramètres avant la publication publique en mode permissif et le passage éventuel au mode renforcé. Les messages du 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
.
Les messages d'erreur SELinux sont limités en fréquence une fois le démarrage terminé pour éviter d'inonder les journaux. Pour vous assurer de voir tous les messages pertinents, vous pouvez désactiver cette option en exécutant adb shell auditctl -r 0
.
Grâce à cette sortie, les fabricants peuvent facilement identifier les cas où les utilisateurs ou les composants du système enfreignent la règle SELinux. Les fabricants peuvent ensuite corriger ce comportement inapproprié en modifiant le logiciel, la stratégie SELinux ou les deux.
Plus précisément, ces messages de journal indiquent les processus qui é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 ce résultat comme suit :
- Le
{ connectto }
ci-dessus représente l'action en cours. Associé àtclass
à la fin (unix_stream_socket
), il vous indique approximativement ce qui a été fait et à quoi. Dans ce cas, une application a tenté de se connecter à un socket de flux Unix. -
scontext (u:r:shell:s0)
vous indique le contexte dans lequel l'action a été lancée. Dans ce cas, il s'agit d'un élément exécuté en tant que shell. -
tcontext (u:r:netd:s0)
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 de l'écran 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, il s'agit d'un indice assez utile.
Autre exemple :
adb shell su root dmesg | grep 'avc: '
Sortie :
<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 évidence entre crochets,
read write
ousetenforce
. - Acteur : l'entrée
scontext
(contexte source) représente l'acteur, en l'occurrence le daemonrmt_storage
. - Objet : l'entrée
tcontext
(contexte cible) représente l'objet sur lequel l'action est effectuée, en l'occurrence kmem. - Résultat : l'entrée
tclass
(classe cible) indique le type d'objet sur lequel l'action est effectuée, dans ce cas unchr_file
(périphérique de caractères).
Vider les piles utilisateur et du noyau
Dans certains cas, les informations contenues dans le journal des événements ne suffisent pas à 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'appel.
Configuration compatible
- Le noyau Linux >= 5.10, en particulier les branches du noyau commun Android mainline et android12-5.10, sont compatibles.
La branche android12-5.4 est également acceptée. 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 doit être possible de reproduire l'événement que vous déboguez. Les événements de temps de démarrage ne sont pas compatibles avec simpleperf. Toutefois, vous pouvez toujours redémarrer le service pour déclencher l'événement.
Enregistrer la chaîne d'appel
La première étape consiste à enregistrer l'événement à l'aide de simpleperf record
:
adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"
L'événement qui a entraîné le refus doit ensuite être déclenché. L'enregistrement doit ensuite être arrêté. Dans cet exemple, l'échantillon aurait dû être capturé en utilisant Ctrl-c
:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Enfin, simpleperf report
peut être utilisé pour inspecter la trace de pile capturée.
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 offre une meilleure visibilité sur le flux de code en commençant la trace depuis l'espace utilisateur jusqu'au noyau où le refus se produit. Pour en savoir plus sur simpleperf
, consultez la documentation de référence sur les commandes exécutables Simpleperf.
Passer à une licence permissive
L'application SELinux peut être désactivée avec adb sur les versions userdebug ou eng. Pour ce faire, commencez par passer ADB en mode root en exécutant adb root
. Ensuite, pour désactiver l'application SELinux, exécutez la commande suivante :
adb shell setenforce 0
Ou sur la ligne de commande du noyau (lors de la mise en service initiale de l'appareil) :
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 instructions de règles SELinux correspondantes. Il peut donc accélérer considérablement le développement de SELinux.
Pour l'utiliser, exécutez la commande suivante :
adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy
Toutefois, vous devez examiner attentivement chaque ajout potentiel pour détecter les autorisations excessives. Par exemple, si vous fournissez à audit2allow
le refus rmt_storage
présenté précédemment, vous obtiendrez l'instruction de règle SELinux suggérée suivante :
#============= shell ============== allow shell kernel:security setenforce; #============= rmt ============== allow rmt kmem_device:chr_file { read write };
Cela permettrait à rmt
d'écrire dans la mémoire du noyau, ce qui constitue une faille de sécurité flagrante. Les instructions audit2allow
ne sont souvent qu'un point de départ. Après avoir utilisé ces instructions, vous devrez peut-être modifier le domaine source et le libellé de la cible, ainsi qu'intégrer les macros appropriées, pour obtenir une stratégie efficace. Il arrive que le refus examiné n'entraîne aucune modification des règles. C'est plutôt l'application concernée qui doit être modifiée.