Android настоятельно рекомендует производителям оборудования тщательно тестировать свои реализации 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. - Actor — Запись
scontext(исходный контекст) представляет собой актора, в данном случае демонаrmt_storage. - Объект — запись
tcontext(целевой контекст) представляет собой объект, над которым выполняется действие, в данном случае kmem. - Результат - Запись
tclass(целевой класс) указывает тип объекта, над которым выполняется действие, в данном случае этоchr_file(символьное устройство).
Выгрузить стеки пользователя и ядра.
В некоторых случаях информации, содержащейся в журнале событий, недостаточно для точного определения причины отказа. Часто бывает полезно собрать цепочку вызовов, включая ядро и пользовательское пространство, чтобы лучше понять, почему произошел отказ.
В последних версиях ядра определена точка трассировки с именем avc:selinux_audited . Используйте Android simpleperf , чтобы включить эту точку трассировки и захватить цепочку вызовов.
Поддерживаемые конфигурации
- Поддерживаются ядра Linux >= 5.10, в частности, ветки Android Common Kernel mainline и android12-5.10 . Ветка android12-5.4 также поддерживается. Вы можете использовать
simpleperf, чтобы определить, определена ли точка трассировки на вашем устройстве:adb root && adb shell simpleperf list | grep avc:selinux_audited. Для других версий ядра вы можете использовать cherry pick для коммитов 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
Используйте audit2allow
Инструмент 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 — это лишь отправная точка. После использования этих операторов может потребоваться изменить исходный домен и метку целевого объекта, а также добавить соответствующие макросы, чтобы разработать эффективную политику. Иногда рассматриваемый отказ не должен приводить к каким-либо изменениям политики; вместо этого следует изменить проблемное приложение.