Android настоятельно рекомендует OEM-производителям тщательно тестировать свои реализации SELinux. Поскольку производители внедряют SELinux, им следует сначала применить новую политику к тестовому пулу устройств.
После применения новой политики убедитесь, что SELinux работает в правильном режиме на устройстве, выполнив команду getenforce .
Это выводит глобальный режим SELinux: либо Enforcing, либо Permissive. Чтобы определить режим SELinux для каждого домена, необходимо изучить соответствующие файлы или запустить последнюю версию sepolicy-analyze с соответствующим флагом ( -p ), присутствующим в /platform/system/sepolicy/tools/ .
Читать опровержения
Проверьте наличие ошибок, которые направляются в виде журналов событий в dmesg и logcat и доступны для локального просмотра на устройстве. Производители должны проверить вывод SELinux в dmesg на этих устройствах и уточнить настройки перед публичным выпуском в разрешающем режиме и последующим переключением в принудительный режим. Сообщения журнала SELinux содержат avc: и поэтому их можно легко найти с помощью grep . Можно захватить текущие журналы отказов, запустив cat /proc/kmsg или захватить журналы отказов с предыдущей загрузки, запустив cat /sys/fs/pstore/console-ramoops .
Сообщения об ошибках SELinux ограничены по частоте после завершения загрузки, чтобы избежать заполнения журналов. Чтобы убедиться, что вы видите все соответствующие сообщения, вы можете отключить это, выполнив adb shell auditctl -r 0 .
С помощью этого вывода производители могут легко определить, когда пользователи системы или компоненты нарушают политику SELinux. Затем производители могут исправить это плохое поведение, либо изменив программное обеспечение, либо политику SELinux, либо и то, и другое.
В частности, эти сообщения журнала указывают, какие процессы могут выйти из строя в принудительном режиме и почему. Вот пример:
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
Интерпретируйте этот вывод следующим образом:
-
{ connectto }выше представляет собой предпринимаемое действие. Вместе сtclassв конце (unix_stream_socket) он примерно говорит вам, что и с чем делалось. В этом случае что-то пыталось подключиться к потоковому сокету unix. -
scontext (u:r:shell:s0)сообщает вам, какой контекст инициировал действие. В данном случае это что-то запущенное как оболочка. -
tcontext (u:r:netd:s0)сообщает вам контекст цели действия. В данном случае это unix_stream_socket, принадлежащийnetd. -
comm="ping"вверху дает вам дополнительную подсказку о том, что было запущено в момент генерации отказа. В данном случае это довольно хорошая подсказка.
Другой пример:
adb shell su root dmesg | grep 'avc: '
Выход:
<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
Вот ключевые элементы этого отрицания:
- Действие — предпринятое действие выделено скобками,
read writeилиsetenforce. - Актер — запись
scontext(исходный контекст) представляет актера, в данном случае демонаrmt_storage. - Объект — запись
tcontext(целевой контекст) представляет объект, над которым выполняется действие, в данном случае kmem. - Результат — запись
tclass(целевой класс) указывает тип объекта, над которым выполняется действие, в данном случаеchr_file(символьное устройство).
Дамп стеков пользователя и ядра
В некоторых случаях информация, содержащаяся в журнале событий, недостаточна для точного определения источника отказа. Часто бывает полезно собрать цепочку вызовов, включая ядро и пользовательское пространство, чтобы лучше понять, почему произошел отказ.
Последние ядра определяют точку трассировки с именем avc:selinux_audited . Используйте Android simpleperf для включения этой точки трассировки и захвата цепочки вызовов.
Поддерживаемая конфигурация
- Linux kernel >= 5.10, в частности, поддерживаются ветки Android Common Kernel mainline и android12-5.10 . Ветка android12-5.4 также поддерживается. Вы можете использовать
simpleperf, чтобы определить, определена ли точка трассировки на вашем устройстве:adb root && adb shell simpleperf list | grep avc:selinux_audited. Для других версий ядра вы можете выбрать коммиты dd81662 и 30969bc . - Должно быть возможно воспроизвести отлаживаемое вами событие. События загрузки не поддерживаются с помощью simpleperf; однако вы все равно можете перезапустить службу, чтобы вызвать событие.
Захватите цепочку вызовов
Первый шаг — записать событие с помощью simpleperf record :
adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"
Затем должно быть вызвано событие, вызвавшее отказ. После этого запись должна быть остановлена. В этом примере с помощью Ctrl-c должен быть захвачен образец:
^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.
Наконец, simpleperf report может быть использован для проверки захваченного стектрейса. Например:
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
Цепочка вызовов выше — это унифицированная цепочка вызовов ядра и пространства пользователя. Она дает вам лучшее представление о потоке кода, запуская трассировку от пространства пользователя до самого ядра, где происходит отказ. Для получения дополнительной информации о simpleperf см. справочник по исполняемым командам Simpleperf
Переключиться на разрешительный режим
SELinux-принудительное исполнение можно отключить с помощью adb в сборках userdebug или eng. Для этого сначала переключите ADB в режим root, запустив adb root . Затем, чтобы отключить SELinux-принудительное исполнение, выполните:
adb shell setenforce 0
Или в командной строке ядра (во время раннего запуска устройства):
androidboot.selinux=permissiveandroidboot.selinux=enforcing
Или через bootconfig в Android 12:
androidboot.selinux=permissiveandroidboot.selinux=enforcing
Используйте аудит2allow
Инструмент audit2allow принимает отказы dmesg и преобразует их в соответствующие заявления политики SELinux. Таким образом, он может значительно ускорить разработку SELinux.
Чтобы использовать его, запустите:
adb pull /sys/fs/selinux/policyadb logcat -b events -d | audit2allow -p policy
Тем не менее, необходимо проявлять осторожность, чтобы проверить каждое потенциальное добавление на предмет превышения разрешений. Например, кормление audit2allow отказа rmt_storage , показанного ранее, приводит к следующему предложенному заявлению политики SELinux:
#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };
Это предоставило бы rmt возможность записывать память ядра, что является вопиющей дырой в безопасности. Часто операторы audit2allow являются лишь отправной точкой. После использования этих операторов вам может потребоваться изменить исходный домен и метку цели, а также включить соответствующие макросы, чтобы прийти к хорошей политике. Иногда проверяемый отказ вообще не должен приводить к каким-либо изменениям политики; вместо этого следует изменить приложение-нарушитель.