集成基本级别的 SELinux 功能并全面分析结果后,您可以添加自己的政策设置,以便涵盖对 Android 操作系统所做的自定义。这些政策必须仍然满足 Android 兼容性计划的要求,并且不得移除默认的 SELinux 设置。
制造商不得移除现有的 SELinux 政策,否则可能会破坏 Android SELinux 的实施方式及其管控的应用。这包括可能需要改进以遵守政策并正常运行的第三方应用。应用必须无需任何修改即可继续在启用了 SELinux 的设备上正常运行。
当开始自定义 SELinux 时,请注意:
- 为所有新的守护进程编写 SELinux 政策
- 尽可能使用预定义的域
- 为作为
init
服务衍生的所有进程分配域 - 在编写政策之前先熟悉相关的宏
- 向 AOSP 提交对核心政策进行的更改
同时,谨记下列禁忌:
- 不得创建不兼容的政策
- 不得允许对最终用户政策进行自定义
- 不得允许对移动设备管理 (MDM) 政策进行自定义
- 不得恐吓违反政策的用户
- 不得添加后门程序
如需查看具体要求,请参阅 Android 兼容性定义文档中的“内核安全功能”部分。
SELinux 采用白名单方法,这意味着只能授予政策中明确允许的访问权限。由于 Android 的默认 SELinux 政策已经支持 Android 开放源代码项目,因此您无需以任何方式修改 SELinux 设置。如果您要自定义 SELinux 设置,则应格外谨慎,以免破坏现有应用。要开始使用,请按下列步骤操作:
- 使用最新的 Android 内核。
- 采用最小权限原则。
- 仅针对您向 Android 添加的内容调整 SELinux 政策。默认政策能够自动适用于 Android 开源项目代码库。
- 将各个软件组件拆分成多个负责执行单项任务的模块。
- 创建将这些任务与无关功能隔离开来的 SELinux 政策。
- 将这些政策放在
/device/manufacturer/device-name/sepolicy
目录中的*.te
文件内(te 是 SELinux 政策源代码文件使用的扩展名),然后使用BOARD_SEPOLICY
变量将它们纳入到您的 build 中。 - 先将新域设为宽容域。为此,可以在该域的
.te
文件中使用宽容声明。 - 分析结果并优化域定义。
- 当 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
标签来限定 DHCP 仅对 /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
命令的权限,拥有该权限的组件能够对其他进程进行广泛的控制。只有该规则中列出的指定系统组件才能享有该权限。如果需要该权能,则通常表明存在的某些内容不适用于面向用户的 build 或存在不需要的功能。请移除不必要的组件。
规则 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 扩展的详细信息。如需详细了解 SELinux 政策如何在各分区和 Android 版本中保持兼容,请参阅兼容性。
政策的存放位置
在 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 分区。
支持的政策场景
在搭载 Android 8.0 及更高版本的设备上,供应商映像必须使用 OEM 系统映像和 Google 提供的参考 AOSP 系统映像(并在此参考映像上传递 CTS),这样可确保框架与供应商代码完全分离开来。此类设备支持以下场景。
仅含供应商映像的扩展
示例:从支持相关进程的供应商映像向 vndservicemanager
添加新服务。
与搭载旧版 Android 的设备一样,请在 device/manufacturer/device-name/sepolicy
中添加针对特定设备的自定义配置。管控供应商组件如何与其他供应商组件(仅限这些组件)交互的新政策应涉及仅存在于 device/manufacturer/device-name/sepolicy
中的类型。
此处编写的政策允许运行供应商的代码,不会在仅针对框架的 OTA 期间更新,并且将存在于具有参考 AOSP 系统映像的设备上的组合政策中。
支持使用 AOSP 的供应商映像
示例:添加用于实现 AOSP 定义的 HAL 的新进程(通过供应商映像中的 hwservicemanager
注册)。
与搭载旧版 Android 的设备一样,请在 device/manufacturer/device-name/sepolicy
中执行针对特定设备的自定义配置。
作为 system/sepolicy/public/
的一部分导出的政策可供使用,并且包含在供应商政策中。公共政策中的类型和属性可以用在新规则中,指示与新的供应商专用位的交互,但要遵守所提供的 neverallow
限制。与仅含供应商映像的扩展情形一样,此处的新政策不会在仅针对框架的 OTA 期间更新,并且将存在于具有参考 AOSP 系统映像的设备上的组合政策中。
仅含系统映像的扩展
示例:添加一种仅供系统映像中的其他进程访问的新服务(通过 servicemanager 注册)。
将此政策添加到 system/sepolicy/private
。您可以添加额外的进程或对象以在合作伙伴系统映像中启用功能,前提是这些新位不需要与供应商映像上的新组件互动(具体而言,即使没有供应商映像中的政策,此类进程或对象也必须能够完全正常运行)。system/sepolicy/public
导出的政策在此处的提供方式与仅含供应商映像的扩展相同。此政策包含在系统映像中,可以在仅针对框架的 OTA 期间进行更新,但在使用参考 AOSP 系统映像时不会存在。
提供扩展 AOSP 组件的供应商映像扩展
示例:供同样存在于 AOSP 系统映像中的扩展客户端(例如扩展的 system_server)使用的新增非 AOSP HAL。
系统与供应商之间的交互政策必须纳入供应商分区上的 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 HAL 以供无需 AOSP 模拟的客户端进程使用(因此,该进程需要自己的域)。
与 AOSP 扩展示例类似,系统与供应商之间的交互政策必须位于供应商分区上的 device/manufacturer/device-name/sepolicy
目录中(以确保系统政策不知道与供应商相关的详细信息)。您可以在 system/sepolicy/public
中添加新的用于扩展该政策的公共类型;只能在现有 AOSP 政策的基础上进行添加,即不要移除 AOSP 公共政策。新的公共类型随后可用于 system/sepolicy/private
和 device/manufacturer/device-name/sepolicy
中的政策。
请注意,每次向 system/sepolicy/public
添加内容都会增加复杂程度,因为这会增加必须在映射文件中跟踪的新兼容性保证(会受到其他限制的约束)。只有新类型和相关项允许在 system/sepolicy/public
中添加规则;属性和其他政策声明不受支持。此外,新的公共类型不能用于直接为 /vendor
政策中的对象添加标签。
不受支持的政策场景
搭载 Android 8.0 及更高版本的设备不支持以下政策场景和示例。
系统映像的其他扩展,这些扩展需要在仅支持框架的 OTA 之后获得新供应商映像组件的权限
示例:在下一个 Android 版本中添加新的非 AOSP 系统进程(需要自己的域),该进程需要访问新的非 AOSP HAL。
与新(非 AOSP)系统和供应商组件之间的交互类似,不同之处在于新的系统类型是在仅针对框架的 OTA 期间引入的。虽然这个新类型可以添加到 system/sepolicy/public
中的政策里,但现有的供应商政策不知道这个新类型,因为它仅跟踪 Android 8.0 系统公共政策。
AOSP 可通过某个属性(例如 hal_foo
属性)要求取得供应商提供的资源,进而处理此情况,但由于属性合作伙伴扩展在 system/sepolicy/public
中不受支持,因此供应商政策无法使用此方法。访问权限必须由之前存在的公共类型提供。
示例:对系统进程(AOSP 或非 AOSP)的更改必须更改它与新的非 AOSP 供应商组件进行交互的方式。
在针对系统映像编写政策时,对具体的供应商自定义政策必定不知情。因此,系统会通过 system/sepolicy/public 中的属性公开 AOSP 中涉及特定接口的政策,以便供应商政策可以选择启用将来使用这些属性的系统政策。不过,system/sepolicy/public
中的属性扩展不受支持,因此用于指示系统组件如何与新供应商组件交互的全部政策(该政策不由 AOSP system/sepolicy/public
中已存在的属性进行处理)都必须位于 device/manufacturer/device-name/sepolicy
中。
这意味着系统类型无法在仅针对框架的 OTA 期间更改已为供应商类型授予的访问权限。