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
.
Les messages d'erreur SELinux sont limités en débit une fois le démarrage terminé pour éviter de submerger les journaux. Pour vous assurer de voir tous les messages pertinents, vous pouvez le désactiver en exécutant adb shell auditctl -r 0
.
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 letclass
à 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
ousetenforce
. - Acteur - L'
scontext
(contexte source) représente l'acteur, dans ce cas le démonrmt_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 unchr_file
(périphérique de caractères).
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.