Android 强烈建议 OEM 彻底测试其 SELinux 实现。在制造商实施 SELinux 时,他们应首先将新政策应用于测试设备池。
应用新策略后,请通过发出命令 getenforce
确保 SELinux 在设备上以正确的模式运行。
此命令会输出全局 SELinux 模式:强制模式或宽容模式。要确定每个域的 SELinux 模式,您必须检查相应的文件或使用适当的 (-p
) 标志运行最新版本的 sepolicy-analyze
,该标志位于 /platform/system/sepolicy/tools/
中。
读取拒绝消息
检查错误,这些错误作为事件日志路由到 dmesg
和 logcat
,并且可以在设备本地查看。制造商应检查这些设备上 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 write
或setenforce
。 - 执行者 -
scontext
(源上下文)条目表示执行者,在本例中为rmt_storage
守护程序。 - 对象 -
tcontext
(目标上下文)条目表示被操作的对象,在本例中为 kmem。 - 结果 -
tclass
(目标类)条目指示被操作对象的类型,在本例中为chr_file
(字符设备)。
转储用户和内核堆栈
在某些情况下,事件日志中包含的信息不足以查明拒绝消息的来源。通常,收集调用链(包括内核和用户空间)以更好地了解拒绝消息发生的原因非常有用。
最近的内核定义了一个名为 avc:selinux_audited
的跟踪点。使用 Android simpleperf
启用此跟踪点并捕获调用链。
支持的配置
- Linux 内核 >= 5.10,特别是 Android 通用内核分支 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 可执行命令参考
切换到宽容模式
可以在 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
语句只是一个起点。在使用这些语句后,您可能需要更改源域和目标的标签,以及合并适当的宏,才能获得良好的策略。有时,正在检查的拒绝消息根本不应导致任何策略更改;而应更改有问题的应用。