Android настоятельно рекомендует OEM-производителям тщательно тестировать свои реализации SELinux. По мере внедрения SELinux производители должны сначала применить новую политику к тестовому пулу устройств.
После применения новой политики убедитесь, что SELinux работает на устройстве в правильном режиме, введя команду getenforce
.
Это печатает глобальный режим SELinux: либо принудительное, либо разрешающее. Чтобы определить режим 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 >= 5.10, в частности поддерживаются основные ветки Android Common Kernel и 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=permissive
androidboot.selinux=enforcing
Или через bootconfig в Android 12:
androidboot.selinux=permissive
androidboot.selinux=enforcing
Используйте Audit2allow
Инструмент audit2allow
принимает отказы dmesg
и преобразует их в соответствующие заявления политики SELinux. Таким образом, это может значительно ускорить разработку SELinux.
Чтобы использовать его, запустите:
adb pull /sys/fs/selinux/policy
adb 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
являются лишь отправной точкой. После использования этих операторов вам может потребоваться изменить исходный домен и метку целевого объекта, а также включить соответствующие макросы, чтобы прийти к хорошей политике. Иногда рассматриваемое отрицание вообще не должно приводить к каким-либо изменениям в политике; скорее следует изменить приложение-нарушитель.