验证 SELinux

Android 强烈建议原始设备制造商 (OEM) 全面测试其 SELinux 实现。制造商在实现 SELinux 时,应先为设备上需要测试的所有内容应用新政策。

应用新政策后,可以通过执行 getenforce 命令来确认 SELinux 在设备上的运行模式是否正确

该命令将会显示全局 SELinux 模式:强制或宽容。请注意,该命令只会显示全局 SELinux 模式。要确定每个域的 SELinux 模式,您必须查看相应的文件,或运行带有适当 (-p) 标记的最新版 sepolicy-analyze(位于 /platform/system/sepolicy/tools/ 中)。

读取拒绝事件

接下来是检查是否存在错误。错误会以事件日志的形式路由到 dmesg 和 logcat,并可在设备上从本地查看。制造商应先检查这些设备上路由到 dmesg 的 SELinux 输出并优化设置,然后再在宽容模式下公开发布,最后切换到强制模式。SELinux 日志消息中包含“avc:”,因此可以通过 grep 轻松找到。可以通过运行 cat /proc/kmsg 来获取当前的拒绝事件日志,也可以通过运行 cat /proc/last_kmsg 来获取上次启动时的拒绝事件日志。

借助这种输出,制造商可以轻松发现系统用户或组件违反 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(字符设备)。

切换到宽容模式

重要提示:生产设备不支持宽容模式。CTS 测试可确认是否已启用强制模式。

SELinux 强制模式可以在 userdebug 或 eng 版本中通过 ADB 启用。为此,请先运行 adb root 以将 ADB 切换为 root 权限。然后,要停用 SELinux 强制模式,请运行以下命令:

adb shell setenforce 0

或在内核命令行中输入以下命令(在设备启动初期):

androidboot.selinux=permissive
androidboot.selinux=enforcing

使用 audit2allow

selinux/policycoreutils/audit2allow 工具可以获取 dmesg 拒绝事件并将其转换成相应的 SELinux 政策声明。因此,该工具有助于大幅加快 SELinux 开发速度。 audit2allow 会作为 Android 源代码树的一部分被移植到设备上,并会在您基于源代码编译 Android 时自动进行编译。

要使用该工具,请运行以下命令:

adb pull /sys/fs/selinux/policy
adb logcat -b all -d | audit2allow -p policy

注意:运行这些命令不会更改 bugreport.txt,因为所有日志都已经存在,包括上次重新启动之前存在的日志。在运行 OTA 或开发闪存的设备上,旧的和新的违规行为会混合在一起,直到下一次重新启动。要解决此问题,请重新启动设备,或者从您的错误报告中过滤出 LAST_KMSG 和 LAST_LOGCAT。

不过,在检查各种潜在增加项是否存在越界权限时务必要谨慎。例如,为 audit2allow 馈送之前显示的 rmt_storage 拒绝事件会导致以下建议的 SELinux 政策声明:

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

这会授予 rmt 向内核内存写入内容的权限,从而形成明显的安全漏洞。通常情况下,audit2allow 声明只是一个起点。在使用这些声明之后,您可能需要更改来源域和目标的标签,并纳入适当的宏,才能获得好的政策。有时,要解决正在检查的拒绝事件,不应对政策进行任何更改;而是应更改违规的应用。