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规则的本质就是定义“哪个域可以访问哪个类型,以及可以执行哪些操作”。
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 };
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规则:
- 明确需求: 假设我们需要让
app_domain域中的应用程序能够读取data_app类型的文件。 - 确定主体域: 主体域为
app_domain。 - 确定客体类型和类别: 客体类型为
data_app,客体类别为file。 - 确定所需权限: 读取操作对应
read权限。 - 编写规则:
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 主体域 客体类型:客体类别 { 权限集合 };。 - 客体类别决定了可用的权限集合,选择正确的类别至关重要。
- 多域和多类型规则可以通过花括号和属性简化编写。
- 规则验证需要结合语法检查、编译测试和日志监控。