验证 SELinux

Android 强烈建议 OEM 彻底测试其 SELinux 实现。在制造商实施 SELinux 时,他们应首先将新政策应用于测试设备池。

应用新策略后,请通过发出命令 getenforce 确保 SELinux 在设备上以正确的模式运行。

此命令会输出全局 SELinux 模式:强制模式或宽容模式。要确定每个域的 SELinux 模式,您必须检查相应的文件或使用适当的 (-p) 标志运行最新版本的 sepolicy-analyze,该标志位于 /platform/system/sepolicy/tools/ 中。

读取拒绝消息

检查错误,这些错误作为事件日志路由到 dmesglogcat,并且可以在设备本地查看。制造商应检查这些设备上 dmesg 的 SELinux 输出,并在公开发布之前以宽容模式优化设置,并最终切换到强制模式。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) 告诉您哪个上下文启动了该操作。在本例中,这是以 shell 身份运行的某些东西。
  • tcontext (u:r:netd:s0) 告诉您操作目标的上下文。在本例中,这是一个由 netd 拥有的 unix_stream_socket。
  • 顶部的 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 writesetenforce
  • 执行者 - scontext(源上下文)条目表示执行者,在本例中为 rmt_storage 守护程序。
  • 对象 - tcontext(目标上下文)条目表示被操作的对象,在本例中为 kmem。
  • 结果 - tclass(目标类)条目指示被操作对象的类型,在本例中为 chr_file(字符设备)。

转储用户和内核堆栈

在某些情况下,事件日志中包含的信息不足以查明拒绝消息的来源。通常,收集调用链(包括内核和用户空间)以更好地了解拒绝消息发生的原因非常有用。

最近的内核定义了一个名为 avc:selinux_audited 的跟踪点。使用 Android simpleperf 启用此跟踪点并捕获调用链。

支持的配置

  • Linux 内核 >= 5.10,特别是 Android 通用内核分支 mainlineandroid12-5.10 受支持。android12-5.4 分支也受支持。您可以使用 simpleperf 确定您的设备上是否定义了跟踪点:adb root && adb shell simpleperf list | grep avc:selinux_audited。对于其他内核版本,您可以选择 cherry-pick 提交 dd8166230969bc
  • 应该可以重现您正在调试的事件。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 可执行命令参考

切换到宽容模式

可以在 userdebug 或 eng build 上使用 adb 禁用 SELinux 强制执行。为此,首先通过运行 adb root 将 ADB 切换到 root。然后,要禁用 SELinux 强制执行,请运行

adb shell setenforce 0

或在内核命令行中(在设备启动初期)

androidboot.selinux=permissive
androidboot.selinux=enforcing

或通过 Android 12 中的 bootconfig

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

尽管如此,必须注意检查每个潜在的添加项是否存在权限过度的情况。例如,将前面显示的 rmt_storage 拒绝消息馈送到 audit2allow 会产生以下建议的 SELinux 策略语句

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

这将授予 rmt 写入内核内存的能力,这是一个明显的安全漏洞。通常,audit2allow 语句只是一个起点。在使用这些语句后,您可能需要更改源域和目标的标签,以及合并适当的宏,才能获得良好的策略。有时,正在检查的拒绝消息根本不应导致任何策略更改;而应更改有问题的应用。