自定义 SELinux

在您集成 SELinux 功能的基本级别并彻底分析结果后,您可以添加自己的政策设置,以涵盖您对 Android 操作系统的自定义设置。这些政策仍必须满足Android 兼容性计划要求,并且不得移除默认的 SELinux 设置。

制造商不应移除现有的 SELinux 政策。否则,他们可能会破坏 Android SELinux 实现及其管控的应用。这包括第三方应用,这些应用可能需要改进才能符合规范并正常运行。应用必须无需修改即可在启用 SELinux 的设备上继续运行。

在开始自定义 SELinux 时,请务必:

  • 为所有新守护进程编写 SELinux 政策
  • 在适当时使用预定义的域
  • 为作为 init 服务生成的任何进程分配一个域
  • 在编写政策之前熟悉宏
  • 将对核心政策的更改提交到 AOSP

并记住不要:

  • 创建不兼容的政策
  • 允许最终用户自定义政策
  • 允许 MDM 自定义政策
  • 用政策违规吓唬用户
  • 添加后门

有关具体要求,请参阅Android 兼容性定义文档的“内核安全功能”部分。

SELinux 使用白名单方法,这意味着所有访问都必须在政策中明确允许才能获得授权。由于 Android 的默认 SELinux 政策已支持 Android 开放源代码项目,因此您无需以任何方式修改 SELinux 设置。如果您确实要自定义 SELinux 设置,请务必小心,不要破坏现有应用。要开始使用:

  1. 使用最新的 Android 内核
  2. 采用最小特权原则
  3. 仅处理您自己对 Android 的添加。默认政策自动适用于Android 开放源代码项目代码库。
  4. 将软件组件划分为执行单一任务的模块。
  5. 创建 SELinux 政策,将这些任务与不相关的功能隔离开来。
  6. 将这些政策放在 *.te 文件(SELinux 政策源文件的扩展名)中,该文件位于 /device/manufacturer/device-name/sepolicy 目录中,并使用 BOARD_SEPOLICY 变量将其包含在您的构建中。
  7. 最初使新域处于宽容模式。这可以通过在域的 .te 文件中使用宽容声明来完成。
  8. 分析结果并优化您的域定义。
  9. 当 userdebug 版本中不再出现拒绝时,移除宽容声明。

在您集成 SELinux 政策更改后,在您的开发工作流程中添加一个步骤,以确保 SELinux 今后的兼容性。在理想的软件开发过程中,SELinux 政策仅在软件模型发生更改时才会更改,而不是在实际实现发生更改时更改。

当您开始自定义 SELinux 时,首先审核您对 Android 的添加。如果您添加了执行新功能的组件,请在启用强制模式之前,确保该组件符合 Android 的安全政策以及 OEM 制定的任何相关政策。

为防止不必要的问题,最好是过于宽泛和兼容,而不是过于严格和不兼容,后者会导致设备功能损坏。相反,如果您的更改将使其他人受益,您应该将对默认 SELinux 政策的修改作为补丁提交。如果该补丁应用于默认安全政策,您将无需在每个新的 Android 版本中进行此更改。

政策语句示例

SELinux 基于 M4 计算机语言,因此支持各种宏来节省时间。

在以下示例中,所有域都被授予从 /dev/null 读取或写入的权限,以及从 /dev/zero 读取的权限。

# Allow read / write access to /dev/null
allow domain null_device:chr_file { getattr open read ioctl lock append write};

# Allow read-only access to /dev/zero
allow domain zero_device:chr_file { getattr open read ioctl lock };

此相同语句可以使用 SELinux *_file_perms 宏(简写)编写

# Allow read / write access to /dev/null
allow domain null_device:chr_file rw_file_perms;

# Allow read-only access to /dev/zero
allow domain zero_device:chr_file r_file_perms;

政策示例

下面是 DHCP 的完整政策示例,我们将在下面对其进行检查

type dhcp, domain;
permissive dhcp;
type dhcp_exec, exec_type, file_type;
type dhcp_data_file, file_type, data_file_type;

init_daemon_domain(dhcp)
net_domain(dhcp)

allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service
};
allow dhcp self:packet_socket create_socket_perms;
allow dhcp self:netlink_route_socket { create_socket_perms nlmsg_write };
allow dhcp shell_exec:file rx_file_perms;
allow dhcp system_file:file rx_file_perms;
# For /proc/sys/net/ipv4/conf/*/promote_secondaries
allow dhcp proc_net:file write;
allow dhcp system_prop:property_service set ;
unix_socket_connect(dhcp, property, init)

type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
allow dhcp dhcp_data_file:dir create_dir_perms;
allow dhcp dhcp_data_file:file create_file_perms;

allow dhcp netd:fd use;
allow dhcp netd:fifo_file rw_file_perms;
allow dhcp netd:{ dgram_socket_class_set unix_stream_socket } { read write };
allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket
netlink_nflog_socket } { read write };

让我们剖析一下这个示例

在第一行,类型声明中,DHCP 守护进程继承自基本安全政策 (domain)。从之前的语句示例中,DHCP 可以从 /dev/null 读取和写入。

在第二行,DHCP 被标识为宽容域。

init_daemon_domain(dhcp) 行中,该政策声明 DHCP 是从 init 生成的,并且允许与其通信。

net_domain(dhcp) 行中,该政策允许 DHCP 使用来自 net 域的常见网络功能,例如读取和写入 TCP 数据包、通过套接字通信以及执行 DNS 请求。

allow dhcp proc_net:file write; 行中,该政策声明 DHCP 可以写入 /proc 中的特定文件。此行演示了 SELinux 的细粒度文件标记。它使用 proc_net 标签将写入访问权限限制为仅限 /proc/sys/net 下的文件。

allow dhcp netd:fd use; 开始的示例的最后一个块描述了如何允许应用相互交互。该政策规定 DHCP 和 netd 可以通过文件描述符、FIFO 文件、数据报套接字和 UNIX 流套接字相互通信。DHCP 只能读取和写入数据报套接字和 UNIX 流套接字,而不能创建或打开它们。

可用控件

权限
文件
ioctl read write create getattr setattr lock relabelfrom relabelto append
unlink link rename execute swapon quotaon mounton
目录
add_name remove_name reparent search rmdir open audit_access execmod
套接字
ioctl read write create getattr setattr lock relabelfrom relabelto append bind
connect listen accept getopt setopt shutdown recvfrom sendto recv_msg send_msg
name_bind
文件系统
mount remount unmount getattr relabelfrom relabelto transition associate
quotamod quotaget
进程
fork transition sigchld sigkill sigstop signull signal ptrace getsched setsched
getsession getpgid setpgid getcap setcap share getattr setexec setfscreate
noatsecure siginh setrlimit rlimitinh dyntransition setcurrent execmem
execstack execheap setkeycreate setsockcreate
安全
compute_av compute_create compute_member check_context load_policy
compute_relabel compute_user setenforce setbool setsecparam setcheckreqprot
read_policy
功能
chown dac_override dac_read_search fowner fsetid kill setgid setuid setpcap
linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock
ipc_owner sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin
sys_boot sys_nice sys_resource sys_time sys_tty_config mknod lease audit_write
audit_control setfcap

更多

还有更多

neverallow 规则

SELinux neverallow 规则禁止绝不应发生的行为。通过兼容性测试,SELinux neverallow 规则现在在设备上强制执行。

以下指南旨在帮助制造商避免在自定义期间出现与 neverallow 规则相关的错误。此处使用的规则编号与 Android 5.1 对应,并且可能会因版本而异。

规则 48:neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;
请参阅 ptrace 的手册页。sys_ptrace 功能授予 ptrace 任何进程的能力,这允许对其他进程进行大量控制,并且应仅属于规则中概述的指定系统组件。对该功能的需求通常表明存在不适用于面向用户的版本或不需要的功能。移除不必要的组件。

规则 76:neverallow { domain -appdomain -dumpstate -shell -system_server -zygote } { file_type -system_file -exec_type }:file execute;
此规则旨在防止在系统上执行任意代码。具体来说,它断言仅执行 /system 上的代码,这要归功于经过验证的启动等机制,从而实现了安全保证。通常,遇到此 neverallow 规则问题时的最佳解决方案是将有问题的代码移动到 /system 分区。

在 Android 8.0 及更高版本中自定义 SEPolicy

本节提供了 Android 8.0 及更高版本中供应商 SELinux 政策的指南,包括 Android 开放源代码项目 (AOSP) SEPolicy 和 SEPolicy 扩展程序的详细信息。如需详细了解如何在分区和 Android 版本之间保持 SELinux 政策的兼容性,请参阅兼容性

政策放置

在 Android 7.0 及更早版本中,设备制造商可以将政策添加到 BOARD_SEPOLICY_DIRS,包括旨在跨不同设备类型扩充 AOSP 政策的政策。在 Android 8.0 及更高版本中,将政策添加到 BOARD_SEPOLICY_DIRS 会将政策仅放置在供应商映像中。

在 Android 8.0 及更高版本中,政策存在于 AOSP 的以下位置:

  • system/sepolicy/public。包括为在供应商特定政策中使用而导出的政策。所有内容都进入 Android 8.0 兼容性基础架构。公共政策旨在跨版本持久存在,因此您可以在自定义政策中包含任何 /public 内容。因此,可以放置在 /public 中的政策类型受到更多限制。将其视为平台的导出政策 API:任何处理 /system/vendor 之间接口的内容都应放在此处。
  • system/sepolicy/private。包括系统映像正常运行所必需的政策,但供应商映像政策不应了解这些政策。
  • system/sepolicy/vendor。包括用于放置在 /vendor 中但在核心平台树(而非设备特定目录)中存在的组件的政策。这是构建系统区分设备和全局组件的产物;从概念上讲,这是下面描述的设备特定政策的一部分。
  • device/manufacturer/device-name/sepolicy。包括设备特定政策。还包括对政策的设备自定义设置,在 Android 8.0 及更高版本中,这对应于供应商映像上组件的政策。

在 Android 11 及更高版本中,system_ext 和 product 分区还可以包括分区特定政策。system_ext 和 product 政策也分为公共和私有,供应商可以使用 system_ext 和 product 的公共政策,就像系统政策一样。

  • SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS。包括为在供应商特定政策中使用而导出的政策。已安装到 system_ext 分区。
  • SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS。包括 system_ext 映像正常运行所必需的政策,但供应商映像政策不应了解这些政策。已安装到 system_ext 分区。
  • PRODUCT_PUBLIC_SEPOLICY_DIRS。包括为在供应商特定政策中使用而导出的政策。已安装到 product 分区。
  • PRODUCT_PRIVATE_SEPOLICY_DIRS。包括 product 映像正常运行所必需的政策,但供应商映像政策不应了解这些政策。已安装到 product 分区。
注意:使用 GSI 时,OEM 的 system_ext 和 product 分区将不会被挂载。供应商 sepolicy 中使用 OEM 的 system_ext 和 product 公共政策的规则将变为 NOP,因为 OEM 特定的类型定义缺失。
注意:使用 system_ext 和 product 公共政策时要格外小心。公共政策充当 system_ext/product 和供应商之间的导出 API。合作伙伴应自行管理兼容性问题。

支持的政策情景

在搭载 Android 8.0 及更高版本的设备上,供应商映像必须与 OEM 系统映像和 Google 提供的参考 AOSP 系统映像配合使用(并且在此参考映像上通过 CTS)。这些要求确保了框架代码和供应商代码之间的清晰分离。此类设备支持以下情景。

仅限供应商映像的扩展程序

示例:从供应商映像向 vndservicemanager 添加新服务,以支持来自供应商映像的进程。

与搭载之前 Android 版本的设备一样,在 device/manufacturer/device-name/sepolicy 中添加设备特定的自定义设置。管理供应商组件如何(仅)与其他供应商组件交互的新政策应涉及仅在 device/manufacturer/device-name/sepolicy 中存在的类型。此处编写的政策允许供应商代码工作,不会作为仅框架 OTA 的一部分进行更新,并且存在于具有参考 AOSP 系统映像的设备上的组合政策中。

供应商映像支持与 AOSP 配合使用

示例:添加新进程(在供应商映像中注册到 hwservicemanager),以实现 AOSP 定义的 HAL。

与搭载之前 Android 版本的设备一样,在 device/manufacturer/device-name/sepolicy 中执行设备特定的自定义设置。作为 system/sepolicy/public/ 一部分导出的政策可供使用,并作为供应商政策的一部分进行交付。公共政策中的类型和属性可用于指示与新的供应商特定位交互的新规则,但须遵守提供的 neverallow 限制。与仅限供应商的情况一样,此处的新政策不会作为仅框架 OTA 的一部分进行更新,并且存在于具有参考 AOSP 系统映像的设备上的组合政策中。

仅限系统映像的扩展程序

示例:添加新服务(在 servicemanager 中注册),该服务仅由来自系统映像的其他进程访问。

将此政策添加到 system/sepolicy/private。您可以添加额外的进程或对象来启用合作伙伴系统映像中的功能,前提是这些新位不需要与供应商映像上的新组件交互(具体而言,这些进程或对象必须在没有供应商映像政策的情况下完全运行)。由 system/sepolicy/public 导出的政策在此处可用,就像它用于仅限供应商映像的扩展程序一样。此政策是系统映像的一部分,可以在仅框架 OTA 中更新,但在使用参考 AOSP 系统映像时将不存在。

用于服务扩展 AOSP 组件的供应商映像扩展程序

示例:一个新的非 AOSP HAL,供也存在于 AOSP 系统映像中的扩展客户端使用(例如扩展的 system_server)。

系统和供应商之间交互的政策必须包含在供应商分区上交付的 device/manufacturer/device-name/sepolicy 目录中。这类似于上述添加供应商映像支持以与参考 AOSP 映像配合使用的情况,只是修改后的 AOSP 组件可能还需要额外的政策才能与系统分区的其余部分正常运行(只要它们仍然具有公共 AOSP 类型标签,这就可以了)。

公共 AOSP 组件与仅限系统映像的扩展程序交互的政策应放在 system/sepolicy/private 中。

仅访问 AOSP 接口的系统映像扩展程序

示例:一个新的非 AOSP 系统进程必须访问 AOSP 依赖的 HAL。

这类似于仅限系统映像的扩展程序示例,只是新的系统组件可以在 system/vendor 接口之间交互。新系统组件的政策必须放在 system/sepolicy/private 中,这是可以接受的,前提是它通过 AOSP 在 system/sepolicy/public 中已建立的接口(即,功能所需的类型和属性在那里)。虽然政策可以包含在设备特定政策中,但它将无法使用其他 system/sepolicy/private 类型,或者因仅框架更新而更改(以任何影响政策的方式)。该政策可以在仅框架 OTA 中更改,但在使用 AOSP 系统映像时将不存在(AOSP 系统映像也不会有新的系统组件)。

用于服务新系统组件的供应商映像扩展程序

示例:添加一个新的非 AOSP HAL,供没有 AOSP 类似物(因此需要自己的域)的客户端进程使用。

AOSP 扩展程序示例类似,系统和供应商之间交互的政策必须放在供应商分区上交付的 device/manufacturer/device-name/sepolicy 目录中(以确保系统政策不了解供应商特定的详细信息)。您可以添加新的公共类型来扩展 system/sepolicy/public 中的政策;这应该仅在现有 AOSP 政策的基础上完成,即不要移除 AOSP 公共政策。然后,新的公共类型可用于 system/sepolicy/privatedevice/manufacturer/device-name/sepolicy 中的政策。

请记住,每次向 system/sepolicy/public 添加内容都会增加复杂性,因为它会公开新的兼容性保证,该保证必须在映射文件中跟踪,并且受到其他限制。只有新的类型和相应的允许规则可以添加到 system/sepolicy/public 中;不支持属性和其他政策语句。此外,新的公共类型不能用于直接标记 /vendor 政策中的对象。

不支持的政策情景

搭载 Android 8.0 及更高版本的设备不支持以下政策情景和示例。

在仅框架 OTA 后,需要获得新供应商映像组件权限的系统映像的其他扩展程序

示例:一个新的非 AOSP 系统进程(需要自己的域)在下一个 Android 版本中添加,并且需要访问新的非 AOSP HAL。

新的(非 AOSP)系统和供应商组件交互类似,只是新的系统类型是在仅框架 OTA 中引入的。虽然新的类型可以添加到 system/sepolicy/public 中的政策中,但现有供应商政策不了解新的类型,因为它仅跟踪 Android 8.0 系统公共政策。AOSP 通过属性(例如,hal_foo 属性)公开供应商提供的资源来处理此问题,但由于 system/sepolicy/public 中不支持属性合作伙伴扩展程序,因此供应商政策无法使用此方法。必须由先前存在的公共类型提供访问权限。

示例:对系统进程(AOSP 或非 AOSP)的更改必须更改其与新的非 AOSP 供应商组件的交互方式。

系统映像上的政策必须在不了解特定供应商自定义设置的情况下编写。因此,与 AOSP 中特定接口相关的政策通过 system/sepolicy/public 中的属性公开,以便供应商政策可以选择加入未来使用这些属性的系统政策。但是,system/sepolicy/public 中不支持属性扩展程序,因此,所有指示系统组件如何与新的供应商组件交互的政策(以及 AOSP system/sepolicy/public 中已存在的属性未处理的政策)都必须在 device/manufacturer/device-name/sepolicy 中。这意味着,作为仅框架 OTA 的一部分,系统类型无法更改允许供应商类型访问的权限。