在Android系统中,强制访问控制(MAC, Mandatory Access Control)的核心实现机制是SELinux(Security-Enhanced Linux)。它与传统的自主访问控制(DAC, Discretionary Access Control)协同工作,显著提升了系统的安全边界,有效遏制了恶意软件或已遭破坏的进程对系统资源的进一步侵害。

1. 核心概念与原理
1.1 DAC与MAC的区别
理解SELinux的前提是厘清DAC和MAC的根本差异:
- DAC(Linux传统机制): 基于用户ID(UID)和组ID(GID)。一个进程如果拥有某个文件的所有权或相应权限,即可对其进行操作。root用户拥有至高无上的权限。
- MAC(SELinux机制): 基于预定义的安全策略(Policy)。系统管理员制定规则,明确规定“谁(主体)”可以对“什么(客体)”执行“何种操作”。即便是root进程,只要策略未明确允许,也无法访问受保护的资源。
1.2 SELinux的核心设计
Android主要采用SELinux的类型强制(Type Enforcement, TE)模型。
- 主体(Subject): 通常指正在运行的进程。
- 客体(Object): 被访问的资源,如文件、目录、Socket、Binder接口、系统属性等。
- 安全上下文(Security Context): 每个主体和客体都被贴上一个唯一的标签。
- 策略(Policy): 定义了拥有特定标签的主体和客体之间被允许的交互规则。
2. 多维度架构解析
2.1 安全上下文(Labeling)
在Android中,可以通过ls -Z或ps -Z命令查看安全上下文。其格式通常为:user:role:type:sensitivity。
例如进程上下文:u:r:system_server:s0
- User (u): SELinux用户。在Android中通常固定为
u。
- Role (r/object_r): 角色。进程通常是
r,文件资源通常是 object_r。
- Type (type): 这是核心标识。
- 对于进程,这被称为域(Domain),如
system_server, zygote, untrusted_app。
- 对于文件,这被称为类型(Type),如
system_file, app_data_file。
- Sensitivity (s0): 安全级别(MCS/MLS),用于多级安全隔离等场景。
2.2 运作流程(LSM Hooks)
SELinux通过Linux内核的LSM(Linux Security Modules)框架实现其拦截机制:
- 进程发起系统调用(如
open(), read(), binder_call)。
- 首先进行DAC检查(传统的Linux权限检查)。若DAC拒绝,直接返回失败。
- 若DAC通过,内核调用LSM Hook进入SELinux模块。
- SELinux查询AVC(Access Vector Cache, 访问向量缓存,用于缓存策略决策结果)或安全服务器。
- 根据策略库(
.te 文件编译而成)判断规则是否匹配:allow domain type : class { permission }。
- 若策略允许,操作继续;若拒绝,则记录
avc: denied 日志并拦截此次访问。
2.3 核心文件与配置
Android的SELinux策略由一系列源文件编译而成,主要位于 /system/sepolicy 和 /device/<vendor>/<board>/sepolicy 目录:
- *`.te
(Type Enforcement)**: 定义类型、属性、宏以及具体的允许(allow`)规则。
file_contexts: 定义文件系统在创建或重置时的默认安全标签。
property_contexts: 定义Android系统属性(System Properties)的安全标签。
service_contexts: 定义Binder服务的安全上下文。
mac_permissions.xml: 用于基于应用签名的中间件层控制(作为SELinux的补充)。
3. 多层次实现细节
3.1 域转换(Domain Transition)
这是实现Android进程沙箱隔离的关键机制。
- 系统启动时,init进程拥有非常广泛的权限(
u:r:init:s0)。
- 当init启动zygote时,发生域转换:从
init 域切换到 zygote 域。
- 当zygote fork应用进程时,再次根据规则转换到特定的应用域(如
untrusted_app)。
关键规则示例(简化表示):
allow init zygote_exec:file execute;
allow init zygote:process transition;
...
这确保了即使由同一个父进程派生,不同的系统服务和用户应用也能运行在严格隔离的沙箱环境中。
3.2 宏与属性(Macros & Attributes)
为了简化策略的管理与编写,Android策略中大量使用了属性进行归类:
domain: 所有进程域的基类属性。
appdomain: 所有应用进程的属性。
netdomain: 需要网络访问权限的进程属性。
通过 allow appdomain system_file:file read; 这样的单条规则,即可一次性授权所有具有 appdomain 属性的应用读取 system_file 类型文件的权限。
3.3 Project Treble 与 策略分离
从Android 8.0开始,为了支持模块化更新(Project Treble),SELinux策略被拆分为两部分:
- 平台策略(Platform Policy): 位于
/system 分区,由Google定义和维护,涵盖Android通用框架。
- 厂商策略(Vendor Policy): 位于
/vendor 分区,由SoC厂商或设备制造商定义,涵盖硬件驱动和厂商定制功能。
两者在系统启动时通过CIL(Common Intermediate Language)格式动态合并。这种分离确保了在进行系统OTA更新时,厂商的硬件特有权限不会丢失或产生冲突。
4. Android 各版本的演进与要求
SELinux在Android上的演进是一个从“宽容”到“强制”,从“整体”到“模块化”的持续强化过程。
| Android 版本 |
模式/状态 |
关键变更与机制 |
| Android 4.3 |
Permissive (宽容模式) |
首次引入SELinux,但默认仅记录违规日志而不拦截,用于策略测试和日志收集。 |
| Android 4.4 |
Enforcing (部分强制) |
对核心系统域(installd, netd, vold, zygote)启用强制模式。应用层策略仍较为宽松。 |
| Android 5.0 |
Full Enforcing (全强制) |
里程碑。所有域(包括第三方应用)默认处于强制模式。“未明确允许即为拒绝”原则全面落实。 |
| Android 6.0 |
Hardening |
策略增强,引入了对 ioctl 系统调用的精细化过滤(ioctl filtering),防范驱动层漏洞利用。 |
| Android 7.0 |
Strict Separation |
进一步收紧应用沙箱,严格限制应用对 /sys 和 /proc 等文件系统的访问。 |
| Android 8.0 |
Treble Split |
架构重构。策略正式拆分为system和vendor两部分,引入版本化机制确保兼容性。 |
| Android 9.0 |
Per-App SELinux |
为不同特权等级的应用(priv-app, system-app, untrusted-app)提供更细粒度的默认沙箱策略。 |
| Android 10+ |
Modularization |
伴随Apex模块的引入,允许部分系统组件的SELinux策略随模块独立更新。 |
| Android 11-14 |
Kernel Restrictions |
引入对内核内存访问的更严格限制,移除了应用对大量非必要设备节点的访问权限。 |
5. 开发中的调试与应对
在进行系统应用或ROM定制开发时,SELinux策略是最常遇到的权限障碍之一。
5.1 常用命令
getenforce: 查看当前SELinux模式(Enforcing / Permissive)。
setenforce 0: 临时切换为宽容模式(需要root权限,通常仅在userdebug或eng版本中有效)。
ls -Z /path/to/file: 查看文件或目录的安全上下文。
ps -Z: 查看所有进程的安全上下文。
5.2 解决 avc: denied 问题
当功能因权限被阻时,查看 dmesg 或 logcat 日志,搜索关键字 avc:。
日志示例:
avc: denied { read } for pid=1234 comm="myapp" name="settings" dev="sda1" ino=5678 scontext=u:r:system_app:s0 tcontext=u:object_r:vendor_file:s0 tclass=file
解读:
- Action:
read (被拒绝的操作)
- Subject:
u:r:system_app:s0 (发起访问的主体)
- Object:
u:object_r:vendor_file:s0 (被访问的客体)
- Class:
file (客体类别)
编写规则:
在对应的 system_app.te 策略文件中添加允许规则:
allow system_app vendor_file:file read;
注意:在实际生产中,直接允许访问宽泛的 vendor_file 类型可能不安全,应优先考虑定义更精确的类型或使用系统已有的安全接口(Binder, Property等)。
5.3 audit2allow 工具
在Android源码开发环境中,提供了 audit2allow 工具,可以自动将avc: denied日志转换为建议的te规则,便于快速分析和策略编写:
adb shell dmesg | audit2allow -p out/target/product/<device>/root/sepolicy
6. 总结
Android的强制访问控制系统是一个基于SELinux构建的深度防御体系。它不再单纯依赖用户身份,而是依托于一个严格定义、中心控制的策略矩阵。从Android 8.0的Treble计划开始,SELinux策略的模块化与版本化已成为Android系统架构现代化与可持续更新的关键支柱。
对于开发者而言,理解DAC是基础,但熟练掌握MAC(SELinux)的原理、策略编写与调试方法,则是深入Android Framework层和系统安全开发领域必须跨越的关键门槛。