自定义 SELinux

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

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

当开始着手自定义 SELinux 时,制造商应记得做以下事情:

  • 为所有新的守护进程编写 SELinux 政策
  • 尽可能使用预定义的域
  • 为作为 init 服务衍生的所有进程分配域
  • 在编写政策之前先熟悉相关的宏
  • 向 AOSP 提交对核心政策进行的更改

不要做以下事情:

  • 创建不兼容的政策
  • 允许对最终用户政策进行自定义
  • 允许对 MDM 政策进行自定义
  • 恐吓违反政策的用户
  • 添加后门程序

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

SELinux 采用白名单方法,这意味着只能授予政策中明确允许的访问权限。由于 Android 的默认 SELinux 政策已经支持 Android 开放源代码项目,因此原始设备制造商 (OEM) 无需以任何方式修改 SELinux 设置。如果他们要自定义 SELinux 设置,则应格外谨慎,以免破坏现有应用。以下是我们建议的做法:

  1. 使用最新的 Android 内核
  2. 采用最小权限原则
  3. 仅针对您向 Android 添加的内容调整 SELinux 政策。默认政策能够自动适用于 Android 开放源代码项目代码库。
  4. 将各个软件组件拆分成多个负责执行单项任务的模块。
  5. 创建用于将这些任务与无关功能隔离开来的 SELinux 政策。
  6. 将这些政策放在 /device/manufacturer/device-name/sepolicy 目录中的 *.te 文件(te 是 SELinux 政策源代码文件使用的扩展名)内,然后使用 BOARD_SEPOLICY 变量将它们纳入到您的版本中。
  7. 先将新域设为宽容域。通过在相应域的 .te 文件中使用宽容声明,可以做到这一点。
  8. 分析结果并优化域定义。
  9. 当 userdebug 版本中不再出现拒绝事件时,移除宽容声明。

集成工作完成后,原始设备制造商 (OEM) 的 Android 开发过程还应包含一个确保向前兼容 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 命令的权限。拥有该权限后,可以对其他进程进行广泛的控制。应该只有该规则中列出的指定系统组件享有该权限。如果需要该功能,则通常表明存在的某些内容不适用于面向用户的版本或存在不需要的功能。请移除不必要的组件。

规则 76:neverallow { domain -appdomain -dumpstate -shell -system_server -zygote } { file_type -system_file -exec_type }:file execute;
该规则旨在防止执行系统中的任意代码。具体来说就是,该规则声明仅执行 /system 中的代码,以便通过验证启动等机制实现安全保证。通常情况下,当遇到与这个 neverallow 规则相关的问题时,最好的解决办法是将违规代码移到 /system 分区。