构建 SELinux 策略

本页面介绍了如何构建 SELinux 策略。SELinux 策略是通过组合核心 AOSP 策略(平台)和设备专用策略(供应商)来构建的。Android 4.4 到 Android 7.0 的 SELinux 策略构建流程合并了所有 sepolicy 片段,然后在根目录中生成单体式文件。这意味着每次修改策略时,SoC 供应商和 ODM 制造商都要修改 boot.img(对于非 A/B 设备)或 system.img(对于 A/B 设备)。

在 Android 8.0 及更高版本中,平台和供应商策略是分开构建的。SOC 和 OEM 可以更新其策略部分,构建其映像(例如,vendor.imgboot.img),然后独立于平台更新来更新这些映像。

但是,由于模块化 SELinux 策略文件存储在 /vendor 分区上,因此 init 进程必须更早地挂载系统分区和供应商分区,以便它可以从这些分区读取 SELinux 文件,并将它们与系统目录中的核心 SELinux 文件合并(然后再加载到内核中)。

源文件

用于构建 SELinux 的逻辑位于以下文件中

  • external/selinux:外部 SELinux 项目,用于构建 HOST 命令行实用程序以编译 SELinux 策略和标签。
  • system/sepolicy:核心 Android SELinux 策略配置,包括上下文和策略文件。主要的 sepolicy 构建逻辑也在此处 (system/sepolicy/Android.mk)。

有关 system/sepolicy 中文件的更多详情,请参阅实施 SELinux

Android 7.x 及更低版本

本节介绍了如何在 Android 7.x 及更早版本中构建 SELinux 策略。

Android 7.x 及更低版本的构建流程

SELinux 策略是通过将核心 AOSP 策略与设备专用自定义项相结合来创建的。然后,将组合策略传递给策略编译器和各种检查器。设备专用自定义通过设备专用 Boardconfig.mk 文件中定义的 BOARD_SEPOLICY_DIRS 变量完成。此全局构建变量包含一个目录列表,用于指定搜索其他策略文件的顺序。

例如,SoC 供应商和 ODM 可以各自添加一个目录,一个用于 SoC 专用设置,另一个用于设备专用设置,以生成给定设备的最终 SELinux 配置

  • BOARD_SEPOLICY_DIRS += device/SOC/common/sepolicy
  • BOARD_SEPOLICY_DIRS += device/SoC/DEVICE/sepolicy

system/sepolicyBOARD_SEPOLICY_DIRS 中 file_contexts 文件的内容连接起来,以在设备上生成 file_contexts.bin

This image shows the SELinux build logic for Android 7.x.

图 1. SELinux 构建逻辑。

sepolicy 文件由多个源文件组成

  • 纯文本 policy.conf 是通过按顺序连接 security_classesinitial_sids*.te 文件、genfs_contextsport_contexts 生成的。
  • 对于每个文件(例如 security_classes),其内容是 system/sepolicy/BOARDS_SEPOLICY_DIRS 下同名文件的串联。
  • policy.conf 发送到 SELinux 编译器进行语法检查,并编译为二进制格式,作为设备上的 sepolicy
    This image shows the files that generate the SELinux policy file
                for Android 7.x.

    图 2. SELinux 策略文件。

SELinux 文件

编译后,运行 7.x 及更早版本的 Android 设备通常包含以下与 SELinux 相关的文件

  • selinux_version
  • sepolicy:组合策略文件(例如,security_classesinitial_sids*.te)后的二进制输出
  • file_contexts
  • property_contexts
  • seapp_contexts
  • service_contexts
  • system/etc/mac_permissions.xml

有关更多详情,请参阅实施 SELinux

SELinux 初始化

当系统启动时,SELinux 处于宽容模式(而不是强制模式)。init 进程执行以下任务

  • 通过 /sys/fs/selinux/loadsepolicy 文件从 ramdisk 加载到内核中。
  • 将 SELinux 切换到强制模式。
  • 运行 re-exec() 以将 SELinux 域规则应用于自身。

为了缩短启动时间,请尽快在 init 进程上执行 re-exec()

Android 8.0 及更高版本

在 Android 8.0 中,SELinux 策略分为平台组件和供应商组件,以便在保持兼容性的同时允许独立的平台/供应商策略更新。

平台 sepolicy 进一步分为平台私有部分和平台公共部分,以便将特定类型和属性导出到供应商策略编写者。平台公共类型/属性保证在给定的平台版本中保持为稳定的 API。可以使用平台映射文件来保证与之前的平台公共类型/属性的兼容性。

Android 8.0 的构建流程

Android 8.0 中的 SELinux 策略是通过组合来自 /system/vendor 的片段生成的。 /platform/system/sepolicy/Android.mk 中包含用于正确设置此项的逻辑。

策略存在于以下位置

位置 包含
system/sepolicy/public 平台的 sepolicy API
system/sepolicy/private 平台实现详情(供应商可以忽略)
system/sepolicy/vendor 供应商可以使用的策略和上下文文件(如果需要,供应商可以忽略)
BOARD_SEPOLICY_DIRS 供应商 sepolicy
BOARD_ODM_SEPOLICY_DIRS(Android 9 及更高版本) Odm sepolicy
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS(Android 11 及更高版本) System_ext 的 sepolicy API
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS(Android 11 及更高版本) System_ext 实现详情(供应商可以忽略)
PRODUCT_PUBLIC_SEPOLICY_DIRS(Android 11 及更高版本) Product 的 sepolicy API
PRODUCT_PRIVATE_SEPOLICY_DIRS(Android 11 及更高版本) Product 实现详情(供应商可以忽略)

构建系统采用此策略并在相应的分区上生成系统、system_ext、product、vendor 和 odm 策略组件。步骤包括

  1. 将策略转换为 SELinux 通用中间语言 (CIL) 格式,具体而言
    1. 公共平台策略(系统 + system_ext + product)
    2. 组合的私有 + 公共策略
    3. 公共 + 供应商和 BOARD_SEPOLICY_DIRS 策略
  2. 对公共策略提供的策略进行版本控制,作为供应商策略的一部分。通过使用生成的公共 CIL 策略来告知组合的公共 + 供应商 + BOARD_SEPOLICY_DIRS 策略,哪些部分必须转换为将链接到平台策略的属性,从而完成版本控制。
  3. 创建映射文件,链接平台部分和供应商部分。最初,这只是将公共策略中的类型与供应商策略中的相应属性链接起来;稍后,它还将为未来平台版本中维护的文件奠定基础,从而实现与面向此平台版本的供应商策略的兼容性。
  4. 组合策略文件(描述设备端和预编译解决方案)。
    1. 组合映射、平台和供应商策略。
    2. 编译输出二进制策略文件。

平台公共 sepolicy

平台公共 sepolicy 包括 system/sepolicy/public 下定义的所有内容。平台可以假定公共策略下定义的类型和属性是给定平台版本的稳定 API。这构成了平台导出的 sepolicy 的一部分,设备(即设备)策略开发者可以在此基础上编写其他设备专用策略。

类型根据供应商文件编写时所依据的策略版本进行版本控制,该版本由 PLATFORM_SEPOLICY_VERSION 构建变量定义。然后,版本化的公共策略会与供应商策略一起包含,并(以其原始形式)包含在平台策略中。因此,最终策略包括私有平台策略、当前平台的公共 sepolicy、设备专用策略以及与编写设备策略时所依据的平台版本对应的版本化公共策略。

平台私有 sepolicy

平台私有 sepolicy 包括 /system/sepolicy/private 下定义的所有内容。策略的这一部分构成了平台功能所需的仅限平台的类型、权限和属性。这些不会导出到 vendor/device 策略编写者。非平台策略编写者不得基于平台私有 sepolicy 中定义的类型/属性/规则编写其策略扩展。此外,允许修改这些规则,或者这些规则可能会作为仅框架更新的一部分而消失。

平台私有映射

平台私有映射包括策略语句,这些语句将先前平台版本的平台公共策略中公开的属性映射到当前平台公共 sepolicy 中使用的具体类型。这确保了基于先前平台公共 sepolicy 版本的平台公共属性编写的供应商策略能够继续工作。版本控制基于 AOSP 中为给定平台版本设置的 PLATFORM_SEPOLICY_VERSION 构建变量。对于预期此平台接受供应商策略的每个先前平台版本,都存在单独的映射文件。有关更多详情,请参阅兼容性

Android 11 及更高版本

system_ext 和 product sepolicy

在 Android 11 中,添加了 system_ext 策略和 product 策略。与平台 sepolicy 一样,system_ext 策略和 product 策略也分为公共策略和私有策略。

公共策略导出到供应商。类型和属性成为稳定的 API,供应商策略可以引用公共策略中的类型和属性。类型根据 PLATFORM_SEPOLICY_VERSION 进行版本控制,版本化的策略包含在供应商策略中。原始策略包含在 system_ext 和 product 分区中的每一个中。

私有策略包含 system_ext 和 product 分区功能所需的仅限 system_ext 和 product 的类型、权限和属性。私有策略对供应商不可见,这意味着这些规则是内部规则,允许修改。

system_ext 和 product 映射

system_ext 和 product 允许将其指定的公共类型导出到供应商。但是,维护兼容性的责任由每个合作伙伴自行承担。为了实现兼容性,合作伙伴可以提供自己的映射文件,这些文件将先前版本的版本化属性映射到当前公共 sepolicy 中使用的具体类型。

  • 要为 system_ext 安装映射文件,请将包含所需映射信息的 cil 文件放置到 {SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil,然后将 system_ext_{ver}.cil 添加到 PRODUCT_PACKAGES
  • 要为 product 安装映射文件,请将包含所需映射信息的 cil 文件放置到 {PRODUCT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil,然后将 product_{ver}.cil 添加到 PRODUCT_PACKAGES

请参阅示例,其中添加了 redbull 设备的 product 分区的映射文件。

预编译 SELinux 策略

init 启用 SELinux 之前,init 会收集来自分区(systemsystem_extproductvendorodm)的所有 CIL 文件,并将它们编译为二进制策略,这种格式可以加载到内核中。由于编译需要时间(通常为 1-2 秒),因此 CIL 文件在构建时预编译,并放置在 /vendor/etc/selinux/precompiled_sepolicy/odm/etc/selinux/precompiled_sepolicy 中,以及输入 CIL 文件的 sha256 哈希值。在运行时,init 通过比较哈希值来检查是否有任何策略文件已更新。如果没有任何更改,init 将加载预编译策略。否则,init 会动态编译并使用它来代替预编译策略。

更具体地说,如果满足以下所有条件,则使用预编译策略。此处,{partition} 表示预编译策略所在的分区:vendorodm

  • /system/etc/selinux/plat_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 都存在且相同。
  • /system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256 都不存在。或者两者都存在且相同。
  • /product/etc/selinux/product_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256 都不存在。或者两者都存在且相同。

如果其中任何一个不同,init 将回退到设备端编译路径。有关更多详情,请参阅 system/core/init/selinux.cpp