6、SELinux策略文件结构:policy.conf, sepolicy, file_contexts, property_contexts, service_contexts的作用与编写规范
说到SELinux策略文件,很多人第一反应就是「一堆看不懂的配置文件」。其实没那么复杂。我刚开始接触时也头大,后来发现,只要搞清楚了这几个核心文件各自管什么,整个SELinux的脉络就清晰了。
今天我们就来拆解一下这五个关键文件:policy.conf、sepolicy、file_contexts、property_contexts、service_contexts。它们各自扮演什么角色?怎么写才规范?我把自己踩过的坑也一并分享出来。
1. policy.conf —— 策略的「源代码」
policy.conf 是 SELinux 策略的文本形式源文件。说白了,它就是给人读的。里面定义了所有的类型(type)、角色(role)、用户(user)、访问向量规则(allow/deny)等等。
我个人习惯把 policy.conf 看作是策略的「总纲」。它通常由多个 .te 文件合并而成。在 Android 编译过程中,系统会把 system/sepolicy 目录下所有的 .te 文件拼到一起,生成这个 policy.conf。
核心作用:定义主体(domain)对客体(object)的访问权限。
举个例子,你想让 appdomain 能读取 cache_file 类型的文件,规则就是:
allow appdomain cache_file:file read;
嗯,这里要注意:policy.conf 在最终设备上是不存在的。它只是编译过程中的中间产物。真正生效的是编译后的二进制文件。
我的经验:调试时如果遇到权限问题,我通常会先检查 policy.conf 里有没有对应的 allow 规则。有一次客户反馈某个 app 无法写入 /data 目录,我 grep 了一下 policy.conf,发现确实漏了一条 allow appdomain cache_file:file write;。加上就解决了。
2. sepolicy —— 策略的「二进制可执行文件」
sepolicy 是编译后的二进制策略文件。设备启动时,内核加载的就是它。你可以把它理解成 policy.conf 编译后的产物,类似 C 代码编译出来的 .so 文件。
为什么要有二进制格式?因为内核解析文本格式太慢了。二进制格式可以直接加载到内核的安全服务器中,效率高得多。
在 Android 设备上,sepolicy 通常位于 /sepolicy 或 /system/etc/selinux/ 目录下。你可以用 adb pull 把它拽出来分析。
注意:千万不要直接修改设备上的 sepolicy 文件!我曾经见过有人用十六进制编辑器去改它,结果设备直接变砖。正确的做法是修改 .te 源文件,重新编译烧录。
3. file_contexts —— 文件的安全上下文「身份证」
file_contexts 定义了每个文件或目录应该被赋予什么安全上下文。说白了,它就是文件系统的「标签手册」。
格式很简单,每行一个规则:
/system(/.*)? u:object_r:system_file:s0
/data/app(/.*)? u:object_r:apk_data_file:s0
/dev/socket/zygote u:object_r:zygote_socket:s0
左边是文件路径(支持正则),右边是安全上下文。系统启动时,init 进程会根据这个文件给所有文件打标签。
我在项目中遇到过一个问题:有个第三方应用的数据目录总是报权限 denied。排查后发现,是 file_contexts 里没有给这个目录定义上下文,导致它被赋予了默认的 unlabeled 类型。加上一条规则就搞定了。
编写规范:
- 路径尽量用正则,避免写死具体文件名
- 新添加的目录一定要在这里注册,否则就是 unlabeled
- 注意通配符的优先级,精确匹配优先于模糊匹配
4. property_contexts —— 系统属性的安全上下文
Android 系统属性(system property)是全局键值对,很多核心功能依赖它。但属性也是有安全等级的。property_contexts 就是给属性打标签的文件。
格式类似:
ro. u:object_r:default_prop:s0
persist. u:object_r:persist_prop:s0
ctl. u:object_r:ctl_prop:s0
debug. u:object_r:debug_prop:s0
每个属性前缀对应一个安全上下文。比如 ro. 开头的属性是只读的,persist. 开头的属性重启后还能保留。
为什么要单独搞一个文件?因为属性访问非常频繁,如果每次都要去查 file_contexts 那种复杂的正则匹配,性能扛不住。property_contexts 只做前缀匹配,效率高得多。
避坑指南:我曾经在调试一个 OTA 升级问题时,发现升级脚本无法写入 persist.sys.ota 属性。查了半天,原来是 property_contexts 里没有给 persist.sys. 这个前缀定义上下文。加上 persist.sys. u:object_r:system_prop:s0 就正常了。
5. service_contexts —— 系统服务的「门禁卡」
Android 的 Binder 服务也是有安全等级的。service_contexts 定义了每个服务注册时的安全上下文。
格式:
media.player u:object_r:mediaserver_service:s0
activity u:object_r:system_server_service:s0
package u:object_r:system_server_service:s0
window u:object_r:system_server_service:s0
左边是服务名称(就是 ServiceManager 里注册的名字),右边是安全上下文。当一个进程尝试调用某个服务时,SELinux 会检查该进程是否有权限访问对应上下文的服务。
说白了,这就是服务的「门禁卡」。没有权限的进程,连门都摸不到。
重要提醒:如果你新增了一个系统服务,一定要在 service_contexts 里注册。否则其他进程调用时,SELinux 会直接拒绝。我见过有人加了服务但忘了注册,结果 logcat 里全是「Permission Denial」的报错。
总结对比表
| 文件名 | 格式 | 作用 | 设备上是否存在 |
|---|---|---|---|
| policy.conf | 文本(.te 合并) | 定义类型、角色、访问规则 | 否(编译中间产物) |
| sepolicy | 二进制 | 内核加载的策略本体 | 是(/sepolicy) |
| file_contexts | 文本(路径+上下文) | 文件系统打标签 | 是(/etc/selinux/) |
| property_contexts | 文本(前缀+上下文) | 系统属性打标签 | 是(/etc/selinux/) |
| service_contexts | 文本(服务名+上下文) | Binder 服务打标签 | 是(/etc/selinux/) |
这五个文件,就是 Android SELinux 策略的完整拼图。你想想看,从文件系统到系统属性,再到 Binder 服务,每一层都有对应的安全上下文文件在管控。搞懂了它们,SELinux 对你来说就不再是黑盒了。