实施 SELinux

SELinux 设置为默认拒绝,这意味着内核中具有钩子的每个访问都必须由政策显式允许。这意味着政策文件包含大量关于规则、类型、类、权限等信息。对 SELinux 的全面考虑超出了本文档的范围,但当启动新的 Android 设备时,了解如何编写政策规则现在至关重要。关于 SELinux 已经有很多可用的信息。有关建议的资源,请参阅支持文档

关键文件

要启用 SELinux,请集成最新的 Android 内核,然后合并 system/sepolicy 目录中找到的文件。编译后,这些文件构成 SELinux 内核安全政策,并涵盖上游 Android 操作系统。

通常,您不应直接修改 system/sepolicy 文件。而是将您自己的设备专用政策文件添加到 /device/manufacturer/device-name/sepolicy 目录中或在其中进行编辑。在 Android 8.0 及更高版本中,您对这些文件所做的更改应仅影响您的供应商目录中的政策。有关 Android 8.0 及更高版本中公共 sepolicy 分离的更多详细信息,请参阅在 Android 8.0 及更高版本中自定义 SEPolicy。无论 Android 版本如何,您仍然在修改这些文件

政策文件

*.te 结尾的文件是 SELinux 政策源文件,用于定义域及其标签。您可能需要在 /device/manufacturer/device-name/sepolicy 中创建新的政策文件,但您应尽可能尝试更新现有文件。

上下文文件

上下文文件是您指定对象标签的位置。

  • file_contexts 将标签分配给文件,并由各种用户空间组件使用。当您创建新政策时,创建或更新此文件以将新标签分配给文件。要应用新的 file_contexts,请重建文件系统映像,或对要重新标记的文件运行 restorecon。在升级时,对 file_contexts 的更改会自动应用于系统和 userdata 分区,作为升级的一部分。通过在分区以读写方式挂载后,将 restorecon_recursive 调用添加到您的 init.board.rc 文件,也可以在升级时自动将更改应用于其他分区。
  • genfs_contexts 将标签分配给文件系统(例如 procvfat),这些文件系统不支持扩展属性。此配置作为内核政策的一部分加载,但更改可能不会对核心 inode 生效,这需要重新启动或卸载并重新挂载文件系统才能完全应用更改。也可以将特定标签分配给特定挂载,例如使用 context=mount 选项的 vfat
  • property_contexts 将标签分配给 Android 系统属性,以控制哪些进程可以设置它们。此配置在启动期间由 init 进程读取。
  • service_contexts 将标签分配给 Android Binder 服务,以控制哪些进程可以为该服务添加(注册)和查找(查找)Binder 引用。此配置在启动期间由 servicemanager 进程读取。
  • seapp_contexts 将标签分配给应用进程和 /data/data 目录。此配置在每次应用启动时由 zygote 进程读取,并在启动期间由 installd 读取。
  • mac_permissions.xml 根据应用的签名以及可选的应用包名称,为应用分配 seinfo 标记。seinfo 标记随后可用作 seapp_contexts 文件中的键,以便为具有该 seinfo 标记的所有应用分配特定标签。此配置在启动期间由 system_server 读取。
  • keystore2_key_contexts 将标签分配给 Keystore 2.0 命名空间。这些命名空间由 keystore2 守护进程强制执行。Keystore 始终提供基于 UID/AID 的命名空间。Keystore 2.0 还强制执行 sepolicy 定义的命名空间。有关此文件的格式和约定的详细说明,请参见此处

BoardConfig.mk Makefile

在编辑或添加政策和上下文文件后,更新您的 /device/manufacturer/device-name/BoardConfig.mk Makefile 以引用 sepolicy 子目录和每个新的政策文件。有关 BOARD_SEPOLICY 变量的更多信息,请参阅 system/sepolicy/README 文件

BOARD_SEPOLICY_DIRS += \
        <root>/device/manufacturer/device-name/sepolicy

BOARD_SEPOLICY_UNION += \
        genfs_contexts \
        file_contexts \
        sepolicy.te

重建后,您的设备将启用 SELinux。现在,您可以自定义您的 SELinux 政策,以适应您对 Android 操作系统所做的添加,如自定义中所述,或者验证您现有的设置,如验证中所述。

当新的政策文件和 BoardConfig.mk 更新到位后,新的政策设置将自动构建到最终的内核政策文件中。有关 sepolicy 如何在设备上构建的更多信息,请参阅构建 sepolicy

实现

开始使用 SELinux

  1. 在内核中启用 SELinux:CONFIG_SECURITY_SELINUX=y
  2. 更改 kernel_cmdline 或 bootconfig 参数,以便
    BOARD_KERNEL_CMDLINE := androidboot.selinux=permissive
    BOARD_BOOTCONFIG := androidboot.selinux=permissive
    这仅用于设备政策的初始开发。在您拥有初始引导程序政策后,请移除此参数,以便您的设备强制执行或未能通过 CTS。
  3. 以 permissive 模式启动系统,并查看启动时遇到哪些拒绝
    在 Ubuntu 14.04 或更高版本上
    adb shell su -c dmesg | grep denied | audit2allow -p out/target/product/BOARD/root/sepolicy
    
    在 Ubuntu 12.04 上
    adb pull /sys/fs/selinux/policy
    adb logcat -b all | audit2allow -p policy
    
  4. 评估输出中类似 init: Warning! Service name needs a SELinux domain defined; please fix! 的警告。有关说明和工具,请参阅验证
  5. 识别设备以及其他需要标记的新文件。
  6. 为您的对象使用现有标签或新标签。查看 *_contexts 文件,了解之前的事物是如何标记的,并利用标签含义的知识来分配新标签。理想情况下,这是一个适合政策的现有标签,但有时需要新标签,并且需要访问该标签的规则。将您的标签添加到相应的上下文文件中。
  7. 识别应具有自己的安全域的域/进程。您可能需要为每个域/进程编写全新的政策。例如,从 init 派生的所有服务都应具有自己的安全域。以下命令有助于揭示那些保持运行状态的服务(但所有服务都需要这样的处理)
    adb shell su -c ps -Z | grep init
    
    adb shell su -c dmesg | grep 'avc: '
    
  8. 查看 init.device.rc 以识别没有任何域类型的任何域。在您的开发过程早期为它们指定一个域,以避免将规则添加到 init,或者以其他方式将 init 访问与它们自己的政策中的访问混淆。
  9. 设置 BOARD_CONFIG.mk 以使用 BOARD_SEPOLICY_* 变量。有关设置此项的详细信息,请参阅 system/sepolicy 中的 README
  10. 检查 init.device.rc 和 fstab.device 文件,并确保每次使用 mount 都对应于正确标记的文件系统,或者指定了 context= mount 选项。
  11. 检查每个拒绝并创建 SELinux 政策以正确处理每个拒绝。请参阅自定义中的示例。

您应该从 AOSP 中的政策开始,然后根据您自己的自定义项进行构建。有关政策策略以及更详细地了解其中一些步骤的更多信息,请参阅编写 SELinux 政策

用例

以下是一些在制定您自己的软件和相关的 SELinux 政策时要考虑的具体漏洞示例

符号链接:由于符号链接显示为文件,因此它们通常被读取为文件,这可能会导致漏洞。例如,某些特权组件(例如 init)会更改某些文件的权限,有时会变得过度开放。

攻击者可能会将这些文件替换为指向他们控制的代码的符号链接,从而允许攻击者覆盖任意文件。但是,如果您知道您的应用从不遍历符号链接,则可以使用 SELinux 禁止其这样做。

系统文件:考虑应仅由系统服务器修改的系统文件类。尽管如此,由于 netdinitvold 以 root 身份运行,因此它们可以访问这些系统文件。因此,如果 netd 受到攻击,它可能会破坏这些文件,并可能破坏系统服务器本身。

借助 SELinux,您可以将这些文件标识为系统服务器数据文件。因此,唯一具有对其进行读取/写入访问权限的域是系统服务器。即使 netd 受到攻击,它也无法将域切换到系统服务器域并访问这些系统文件,即使它以 root 身份运行。

应用数据:另一个示例是必须以 root 身份运行但不应访问应用数据的功能类。这非常有用,因为可以做出广泛的断言,例如禁止某些与应用数据无关的域访问互联网。

setattr:对于诸如 chmodchown 之类的命令,您可以识别相关域可以在其中执行 setattr 的文件集。超出该范围的任何内容都可能被禁止进行这些更改,即使是 root 用户也不行。因此,应用可能会对标记为 app_data_files 的文件运行 chmodchown,但不会对 shell_data_filessystem_data_files 运行。