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

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