4. TE语言入门:类型强制与allow规则基础

类型强制(Type Enforcement,TE)是SELinux安全机制的核心,它通过定义主体(Subject)对客体(Object)的访问权限,实现精细化的强制访问控制。在Android内核中,TE语言用于编写安全策略文件(.te),这些文件决定了哪些进程可以访问哪些资源。本章将系统讲解TE语言的基本语法、主体/客体/域的概念以及allow规则的基础用法。

4.1 核心概念:主体、客体与域

在TE模型中,所有系统实体都被划分为三类:

概念 定义 Android中的典型实例
主体(Subject) 发起访问操作的进程或线程 init进程、zygote进程、system_server
客体(Object) 被访问的资源或对象 文件、套接字、IPC、属性、设备节点
域(Domain) 主体进程的安全上下文,即主体所拥有的安全标签 u:r:init:s0 中的 init 域
关键理解: 在SELinux中,每个进程都有一个域,每个客体都有一个类型。allow规则的本质就是定义“哪个域可以访问哪个类型,以及可以执行哪些操作”。
主体、客体与域的关系模型 主体 (Subject) 进程 / 线程 域 (Domain) u:r:init:s0 客体 (Object) 文件 / 套接字 / IPC 类型 (Type) system_file / socket allow 规则 定义访问权限

4.2 TE语言基本语法结构

TE策略文件由一系列规则声明组成,每条规则遵循严格的语法格式。最基本的规则类型是allow规则,其语法结构如下:

allow 主体域 客体类型:客体类别 { 权限集合 };

语法要素说明:

语法要素 说明 示例
allow 规则关键字,表示授予访问权限 allow
主体域 发起访问的进程域 init
客体类型 被访问资源的类型 system_file
客体类别 客体的资源类别(class) file
权限集合 允许执行的操作列表 { read write }
语法要点: 每条allow规则必须以分号结尾。权限集合使用花括号包裹,多个权限之间用空格分隔。客体类别和权限集合之间使用冒号分隔。

4.3 完整的allow规则示例

以下是一个典型的allow规则,允许init域对类型为system_file的文件执行读和写操作:

allow init system_file:file { read write };

在实际的Android策略中,规则通常更加复杂,涉及多个域和多种客体类别:

# 允许init域读取system_file类型的文件
allow init system_file:file { read };

# 允许init域创建和写入tmpfs类型的文件
allow init tmpfs:file { create write };

# 允许init域与zygote域进行TCP通信
allow init zygote:tcp_socket { connect listen };
allow规则解析流程 allow init system_file :file { read } 规则含义:允许 init 域对 system_file 类型的 file 客体执行 read 操作 主体域 → 客体类型 : 客体类别 → 权限集合 主体域 进程的安全上下文 决定进程身份 客体类型:类别 资源的安全标签 决定资源身份 权限集合 允许的操作列表 控制访问粒度

4.4 客体类别与权限集合

客体类别(class)定义了资源的类型,每个类别都有自己专属的权限集合。Android SELinux中常用的客体类别包括:

客体类别 描述 常用权限示例
file 普通文件 read, write, create, unlink, rename
dir 目录 read, write, add_name, remove_name, search
socket 套接字 read, write, connect, bind, listen
process 进程 signal, fork, transition, ptrace
binder Binder IPC call, transfer, impersonate
property_service 系统属性服务 set, get
常见错误: 初学者容易混淆客体类别和权限。例如,对目录执行read权限和对文件执行read权限含义不同。目录的read权限允许列出目录内容,而文件的read权限允许读取文件内容。务必根据实际资源类型选择正确的客体类别。

4.5 多域与多类型规则

在实际策略中,经常需要为多个域或多个类型编写规则。TE语言支持使用通配符和属性来简化规则编写:

# 使用通配符 * 表示所有类型
allow init *:file { read };

# 使用属性名统配一组类型
allow init domain:file { read };

# 同时指定多个域
allow { init zygote } system_file:file { read };

# 同时指定多个类型
allow init { system_file tmpfs }:file { read write };
最佳实践: 在Android策略中,应尽量使用属性(attribute)而非通配符。属性可以更精确地控制权限范围,避免过度授权。例如,domain属性包含了所有进程域,使用allow init domain:file { read }比使用allow init *:file { read }更安全。

4.6 规则编写实战:从需求到实现

以下是一个完整的规则编写流程,展示如何将安全需求转化为TE规则:

  1. 明确需求: 假设我们需要让app_domain域中的应用程序能够读取data_app类型的文件。
  2. 确定主体域: 主体域为 app_domain。
  3. 确定客体类型和类别: 客体类型为 data_app,客体类别为 file。
  4. 确定所需权限: 读取操作对应 read 权限。
  5. 编写规则:
allow app_domain data_app:file { read };
规则编写五步流程 明确需求 分析访问场景 确定主体域 谁在访问? 确定客体 访问什么? 确定权限 做什么操作? 编写规则 生成allow语句 实战示例:允许 app_domain 域读取 data_app 类型的文件 allow app_domain data_app:file { read };

4.7 规则验证与调试

编写完规则后,需要通过以下步骤验证其正确性:

  • 语法检查: 使用 checkpolicy 工具检查策略文件的语法错误。
  • 编译测试: 将策略文件编译为二进制策略,加载到测试设备中。
  • 日志监控: 通过 dmesg | grep avc 或 logcat -b events | grep avc 查看AVC拒绝日志。
  • 权限验证: 使用 sesearch 工具查询规则是否生效。
# 检查策略文件语法
checkpolicy -M -c 30 -o policy.30 policy.conf

# 查看AVC拒绝日志
dmesg | grep avc

# 查询特定域对特定类型的权限
sesearch --allow -s init -t system_file -c file
调试技巧: 当遇到权限拒绝时,AVC日志会明确显示被拒绝的域、类型、类别和权限。例如:avc: denied { read } for pid=1234 comm="init" name="file.txt" scontext=u:r:init:s0 tcontext=u:object_r:system_file:s0 tclass=file。这条日志直接告诉你需要添加 allow init system_file:file { read }; 规则。

4.8 本章小结

TE语言是SELinux策略配置的基础,掌握allow规则的编写是Android安全开发的核心技能。本章重点内容:

  • 主体、客体、域是TE模型的三大核心概念,理解它们之间的关系是编写规则的前提。
  • allow规则的基本语法为 allow 主体域 客体类型:客体类别 { 权限集合 };。
  • 客体类别决定了可用的权限集合,选择正确的类别至关重要。
  • 多域和多类型规则可以通过花括号和属性简化编写。
  • 规则验证需要结合语法检查、编译测试和日志监控。