6、SELinux策略文件结构:policy.conf, sepolicy, file_contexts, property_contexts, service_contexts的作用与编写规范

说到SELinux策略文件,很多人第一反应就是「一堆看不懂的配置文件」。其实没那么复杂。我刚开始接触时也头大,后来发现,只要搞清楚了这几个核心文件各自管什么,整个SELinux的脉络就清晰了。

今天我们就来拆解一下这五个关键文件:policy.confsepolicyfile_contextsproperty_contextsservice_contexts。它们各自扮演什么角色?怎么写才规范?我把自己踩过的坑也一并分享出来。

SELinux 策略文件结构总览 SELinux 策略体系 policy.conf 可读的策略源文件 sepolicy 编译后的二进制策略 file_contexts 文件安全上下文映射 property_contexts 系统属性安全上下文 service_contexts 服务安全上下文 五个文件共同构成 Android SELinux 策略的完整拼图

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 对你来说就不再是黑盒了。


公众号:蓝海资料掘金营,微信deep3321