4、Android安全策略编译流程:策略源文件、编译工具链、生成策略二进制文件
好,咱们今天聊聊安全策略是怎么从一堆文本文件,变成手机内核能认的二进制文件的。
说实话,我刚接触AOSP安全框架那会儿,也觉得这玩意儿挺玄乎。不就是写几条规则吗?怎么还要编译?后来踩了几个坑才明白——策略编译是整个安全体系的基石,这一步要是错了,后面全白搭。
4.1 策略源文件长什么样?
先看看我们手头有什么原料。在AOSP源码里,安全策略文件主要散落在几个地方:
system/sepolicy/—— 核心策略,所有设备通用device/<vendor>/<device>/sepolicy/—— 厂商定制策略build/target/board/generic/sepolicy/—— 通用板级策略
这些目录里,你会看到一堆后缀为 .te 的文件。对,就是 Type Enforcement 的缩写。每个 .te 文件定义了一组规则,比如:
# 允许 system_app 读取 /data 目录下的文件
allow system_app data_file:dir r_dir_perms;
allow system_app data_file:file r_file_perms;
我个人习惯把策略文件分成三类:
- 类型声明文件(
types.te)—— 定义各种安全上下文 - 权限规则文件(
domain.te)—— 定义主体对客体的访问权限 - 属性文件(
attributes.te)—— 定义属性,方便批量管理
嗯,这里要注意:千万别把规则写到类型声明文件里。我见过有同事图省事,把 allow 语句直接塞进 types.te,结果编译报错,排查了半天。
4.2 编译工具链:谁在干活?
策略源文件写好了,谁来把它变成二进制?答案是 checkpolicy 和 sepolicy-analyze 这两个工具。
在AOSP的编译流程里,它们被封装在 BuildSystem 的 sepolicy.mk 里。大致流程是这样的:
| 步骤 | 工具 | 输入 | 输出 |
|---|---|---|---|
| 1. 预处理 | m4 + cpp | .te 文件 + .conf 配置 |
展开后的 .te 文件 |
| 2. 编译 | checkpolicy | 展开后的 .te 文件 |
sepolicy 二进制策略 |
| 3. 打包 | secilc | 多个 .cil 文件 |
sepolicy 最终镜像 |
为什么需要预处理?因为策略文件里可以用 include 和宏定义。比如:
# 在 .te 文件里引用公共规则
include <global_macros.te>
# 定义一个宏
define(`my_app_domain', `my_app')
预处理阶段会把所有 include 展开,把宏替换掉。这一步要是漏了,编译出来的策略就不完整。
global_macros.te 里的一个宏定义,但忘了重新编译所有依赖它的策略文件。结果刷机后,某个系统服务直接崩溃,因为权限规则没生效。从那以后,我每次改宏定义都会 make clean 再重新编译。
4.3 生成策略二进制文件
预处理完,就该 checkpolicy 上场了。它会读取展开后的 .te 文件,进行语法检查,然后生成一个 策略二进制文件(通常叫 sepolicy)。
这个二进制文件是内核能直接解析的格式。它包含了:
- 所有类型和属性的定义
- 所有
allow、dontaudit、neverallow规则 - 角色和用户的映射关系
- 初始安全上下文(
initial_sid_context)
你可以用 sepolicy-analyze 来查看这个二进制文件的内容:
# 查看策略中的类型列表
sepolicy-analyze sepolicy -t
# 查看某个类型的权限
sepolicy-analyze sepolicy -a system_app
# 检查是否有冲突规则
sepolicy-analyze sepolicy -c
我个人习惯在编译完成后,先用 sepolicy-analyze 快速检查一下,确保没有明显的错误。比如,我会检查 neverallow 规则是否被违反:
sepolicy-analyze sepolicy -n
如果输出为空,说明所有 neverallow 规则都得到了遵守。如果有输出,那就要回去改策略了。
4.4 编译流程中的避坑指南
说了这么多,总结几个我实际项目中遇到的坑:
- 文件编码问题:策略文件必须是 UTF-8 编码,不能有 BOM。我遇到过从 Windows 拷贝过来的文件,带了个 BOM 头,编译直接报错。
- 宏定义冲突:不同厂商的
.te文件里可能定义了同名的宏,导致预处理阶段互相覆盖。解决办法是给宏加前缀,比如VENDOR_MY_MACRO。 - 依赖顺序:如果 A 文件引用了 B 文件里定义的类型,那么 B 文件必须在 A 文件之前被编译。AOSP 的
sepolicy.mk已经处理了大部分依赖,但如果你自己加文件,要小心。 - 二进制兼容性:不同 Android 版本生成的策略二进制格式可能不同。比如 Android 10 和 Android 11 的
sepolicy文件就不能互换。刷机前一定要确认版本匹配。
checkpolicy -d 开启调试模式,它会输出详细的编译过程信息,方便定位问题。
好了,关于策略编译流程,今天就聊到这儿。说白了,就是三步:写源文件、用工具链编译、生成二进制。但每一步都有细节,稍不注意就会翻车。下次咱们聊聊怎么把编译好的策略刷进设备,以及如何验证它是否生效。