4. 安全上下文与文件标签

在 Android SELinux 中,策略规则本身并不直接绑定到某个具体文件或进程,而是绑定到安全上下文(Security Context)。系统在启动、创建文件、启动服务、设置属性时,会依据一系列标签文件为对象打上安全上下文,再由策略规则决定谁能访问谁。因此,理解安全上下文的格式以及 file_contexts、genfs_contexts、property_contexts、service_contexts 等标签文件的作用,是策略开发与调试的基础。

一、安全上下文的四元组格式

SELinux 安全上下文的标准格式为 user:role:type[:level],在 Android 中通常表现为三段或四段:

u:object_r:system_file:s0
u:r:init:s0
u:object_r:vendor_configs_file:s0
u:r:untrusted_app:s0:c512,c768

各字段含义如下:

  • user:SELinux 用户,Android 中几乎恒为 u,不用于权限控制。
  • role:角色,进程为 r,文件/属性等客体为 object_r。
  • type:类型,是 Android SELinux 真正起作用的核心字段,策略规则围绕 type 编写。
  • level:MLS/MCS 敏感级别,如 s0;应用常带 c512,c768 这类类别,用于隔离不同应用。
调试时若看到 u:object_r:foo:s0 与策略中定义的 foo 不一致,往往说明标签文件未生效或未重新编译加载。

二、标签文件的作用与加载流程

标签文件是“路径/名称 → 安全上下文”的映射表。系统在编译时由 checkpolicy、sefcontext_compile 等工具将文本标签文件编译为二进制文件,运行时由 init、file_contexts 匹配逻辑、property_service、servicemanager 等组件读取并应用。

下图展示了从标签文件到对象打标签的整体数据流:

标签文件 → 安全上下文 → 策略匹配 数据流 file_contexts 文件/目录路径标签 genfs_contexts 伪文件系统标签 property_contexts 属性名标签 service_contexts Binder 服务名标签 seapp_contexts 应用进程标签 编译与加载 sefcontext_compile checkpolicy / init 二进制 file_contexts.bin 文件 / 目录 u:object_r:system_file:s0 伪文件系统节点 u:object_r:proc:s0 系统属性 u:object_r:default_prop:s0 Binder 服务 u:object_r:activity_service:s0 标签文件决定对象 type,策略规则基于 type 判定 allow / deny

三、各类标签文件详解

Android 源码中,标签文件通常位于 system/sepolicy/ 及其子目录(如 private/、vendor/、public/)。不同标签文件对应不同对象类型,语法也略有差异。

标签文件 作用对象 典型语法 生效时机
file_contexts 普通文件、目录、符号链接 /system/bin/init u:object_r:init_exec:s0 文件创建/访问时由内核匹配
genfs_contexts proc、sysfs、tmpfs 等伪文件系统 genfscon proc / u:object_r:proc:s0 文件系统挂载时
property_contexts Android 系统属性 ro.build.fingerprint u:object_r:build_prop:s0 属性设置/读取时
service_contexts Binder 服务名 activity u:object_r:activity_service:s0 服务注册/查找时
seapp_contexts 应用进程 user=system domain=system_app type=system_app_data_file zygote 孵化进程时

四、案例:为自定义可执行文件打标签

假设新增一个可执行文件 /system/bin/mydaemon,需要让它以 mydaemon 域运行。首先在 file_contexts 中声明其可执行标签:

# system/sepolicy/private/file_contexts
/system/bin/mydaemon    u:object_r:mydaemon_exec:s0

然后在 mydaemon.te 中定义域与类型转换:

type mydaemon, domain;
type mydaemon_exec, exec_type, file_type, system_file_type;

init_daemon_domain(mydaemon, mydaemon_exec)

若该守护进程还需要访问某个属性,则在 property_contexts 中为其属性打标签:

# property_contexts
persist.mydaemon.enable    u:object_r:mydaemon_prop:s0

编译后可通过以下命令验证标签是否生效:

adb shell ls -Z /system/bin/mydaemon
adb shell getprop -Z persist.mydaemon.enable
adb shell dumpsys activity service mydaemon | head
标签文件写错路径或拼错 type,最常见的现象是 avc: denied 中出现的 type 与预期不符,或对象仍为 default_file_type、default_prop。

五、常见误区与复盘

  • 只改 .te 不改标签文件:策略中定义了新 type,但对象没有被打上该 type,规则永远不会命中。
  • file_contexts 路径使用通配符过度:如 /data/.* 会覆盖过多对象,导致应用数据目录标签混乱。
  • genfs_contexts 与 file_contexts 混用:proc、sysfs 等伪文件系统必须用 genfscon,不能靠 file_contexts 匹配。
  • property_contexts 前缀匹配顺序:属性标签按最长前缀匹配,短前缀可能被更长规则覆盖,需注意优先级。
  • service_contexts 与 service.te 不一致:服务名标签与策略中 allow 的 type 必须一致,否则 Binder 调用被拒。
修改标签文件后必须重新编译 sepolicy 并刷入 vendor_boot 或 system 分区,仅 adb push 单个文件通常不会触发重新加载,调试时建议使用 adb shell dmesg | grep avc 确认实际生效的上下文。

掌握安全上下文格式与各类标签文件的对应关系后,就能在遇到 avc denied 时快速判断:是策略规则缺失,还是对象根本没有被正确打标签。下一阶段将进入具体策略规则的编写与调试方法。