3. SELinux 模式与策略:Enforcing、Permissive、Disabled 模式切换,策略文件结构概述

SELinux 在 Android 内核中的运行模式决定了安全策略的生效程度。理解三种模式的区别、切换方法以及策略文件的组织架构,是进行 SELinux 策略配置与调试的基础。本章将系统讲解模式切换的实战操作,并剖析策略文件的物理结构与逻辑层次。

3.1 SELinux 三种运行模式

SELinux 提供三种运行模式,分别对应不同的安全策略执行力度。在 Android 系统中,通常仅在调试或开发阶段使用非 Enforcing 模式。

模式 内核行为 日志记录 权限拒绝效果 Android 生产环境
Enforcing 强制加载并执行策略 记录被拒绝的访问(AVC) 直接拒绝,进程无法访问 必须使用
Permissive 加载策略但不执行 记录所有被拒绝的访问(AVC) 仅记录,不拒绝,允许访问 禁止使用
Disabled 完全禁用 SELinux 不记录任何 SELinux 日志 无任何 SELinux 限制 禁止使用
实战提示:在 Android 开发板上调试新策略时,建议先切换至 Permissive 模式收集完整的 AVC 拒绝日志,待策略完善后再切回 Enforcing 模式验证。切勿在用户设备上长期开启 Permissive 模式。

3.2 模式切换实战操作

模式切换分为运行时临时切换与启动时持久化配置两种方式。以下为具体操作命令与配置方法。

3.2.1 运行时临时切换(需 root 权限)

使用 setenforce 命令可以立即切换当前系统的 SELinux 模式,重启后失效。

# 查看当前模式
getenforce

# 切换为 Permissive 模式(用于调试)
adb shell su -c "setenforce 0"

# 切换为 Enforcing 模式(恢复强制)
adb shell su -c "setenforce 1"

# 查看详细状态
adb shell su -c "cat /sys/fs/selinux/enforce"

3.2.2 启动时持久化配置

通过修改内核启动参数或设备树(DT)中的 androidboot.selinux 属性,可以实现开机时自动设定模式。

配置位置 参数示例 说明
BoardConfig.mk BOARD_KERNEL_CMDLINE += androidboot.selinux=permissive 编译时写入内核 cmdline
设备树(DTS) bootargs = "androidboot.selinux=disabled"; 在 DTS 中指定启动参数
fastboot 临时覆盖 fastboot oem selinux permissive 部分设备支持,重启后失效
安全警告:切勿在生产固件中设置 androidboot.selinux=disabled 或 permissive。这将完全关闭 Android 的安全增强机制,使系统暴露在恶意攻击风险之下。

3.3 模式切换流程示意图

下图展示了从系统启动到运行时模式切换的完整流程,以及各阶段对应的配置点。

SELinux 模式切换流程 系统启动 解析内核 cmdline 初始化 SELinux 模式判定 Enforcing Permissive Disabled 运行时 setenforce 说明: 虚线表示运行时切换仅影响当前启动周期,重启后恢复为启动参数指定的模式。 生产环境必须使用 Enforcing 模式,调试时优先使用 Permissive 而非 Disabled。

3.4 策略文件结构概述

SELinux 策略文件在 Android 源码中具有清晰的物理存储结构与逻辑编译层次。理解这些文件的作用是编写和修改策略的前提。

3.4.1 物理文件结构

Android 的 SELinux 策略文件主要位于 system/sepolicy 目录下,采用模块化组织方式。

system/sepolicy/
├── public/                  # 公共策略,所有平台共享
│   ├── attributes           # 属性定义
│   ├── types                # 类型定义
│   ├── roles                # 角色定义
│   ├── users                # 用户定义
│   └── initial_sids         # 初始安全标识符
├── vendor/                  # 厂商定制策略
│   ├── file_contexts        # 文件安全上下文
│   ├── genfs_contexts       # 虚拟文件系统上下文
│   ├── property_contexts    # 属性安全上下文
│   ├── service_contexts     # 服务安全上下文
│   └── seapp_contexts       # 应用进程安全上下文
├── plat_sepolicy.cil        # 平台编译后的 CIL 策略
├── vendor_sepolicy.cil      # 厂商编译后的 CIL 策略
├── mapping/                 # 版本映射文件
└── prebuilts/               # 预编译策略文件

3.4.2 核心策略文件功能对照

文件名 功能描述 编译产物
types 定义所有 SELinux 类型(Type) 类型标识符
attributes 定义类型属性,用于批量授权 属性集合
file_contexts 为文件系统路径分配安全上下文 二进制上下文文件
property_contexts 为系统属性分配安全上下文 二进制上下文文件
service_contexts 为 Binder 服务分配安全上下文 二进制上下文文件
seapp_contexts 为 Android 应用进程分配安全上下文 二进制上下文文件
te_macros 定义常用的 TE 规则宏 宏展开

3.5 策略文件编译与加载流程

下图展示了从策略源文件到内核加载的完整编译与部署链路。

策略文件编译与加载流程 策略源文件 .te / .cil / contexts 编译工具链 checkpolicy / secilc 二进制策略 sepolicy / .cil 内核加载 SELinux LSM 上下文文件 二进制上下文 挂载到 selinuxfs 关键说明: 1. Android 10 之后全面采用 CIL(Common Intermediate Language)作为策略中间表示。 2. 编译后的二进制策略文件通常位于 /sys/fs/selinux/policy。 3. 上下文文件通过 selinuxfs 挂载点动态加载,用于运行时标签查询。

3.6 策略文件编写示例

以下是一个简单的 TE(Type Enforcement)规则文件示例,展示了类型声明、属性关联和访问向量规则的写法。

# 文件: system/sepolicy/public/my_daemon.te

# 声明类型
type my_daemon, domain;
type my_daemon_exec, exec_type, file_type;

# 声明属性
typeattribute my_daemon mlstrustedsubject;

# 初始化进程域转换
init_daemon_domain(my_daemon)

# 允许 my_daemon 读取系统日志
allow my_daemon logd:unix_dgram_socket sendto;
allow my_daemon logd:fd use;

# 允许 my_daemon 访问自己的数据目录
allow my_daemon my_daemon_data_file:dir create_dir_perms;
allow my_daemon my_daemon_data_file:file create_file_perms;

# 允许 my_daemon 与 netd 通信
allow my_daemon netd:unix_stream_socket connectto;
关键概念:
  • Type(类型):SELinux 安全上下文的核心组成部分,用于标识主体(进程)和客体(文件、套接字等)。
  • Domain(域):进程的类型称为域,是 TE 规则中主体的身份。
  • Attribute(属性):类型的标签,用于批量授权,减少规则数量。
  • Allow 规则:格式为 allow source_type target_type:class perm_set;,是策略配置的基本单元。

3.7 模式与策略的关联关系

理解模式与策略文件之间的依赖关系,有助于快速定位问题。

场景 模式 策略文件状态 系统行为
正常生产 Enforcing 完整加载 所有访问受策略约束
策略调试 Permissive 完整加载 记录拒绝但不拦截
策略开发初期 Permissive 部分加载 仅记录已加载策略的拒绝
内核调试 Disabled 不加载 无任何 SELinux 功能
常见误区:Permissive 模式下策略文件仍然被完整加载和解析,只是内核不执行权限检查。因此,Permissive 模式可以用于验证策略语法是否正确,以及收集完整的 AVC 拒绝日志。Disabled 模式则完全跳过策略加载,无法用于策略调试。