Подтвердить SELinux

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=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 — это лишь отправная точка. После использования этих операторов может потребоваться изменить исходный домен и метку целевого объекта, а также добавить соответствующие макросы, чтобы разработать эффективную политику. Иногда рассматриваемый отказ не должен приводить к каким-либо изменениям политики; вместо этого следует изменить проблемное приложение.