Java 层权限是应用程序级别的"门禁卡",而 SELinux 是系统级别的"防火墙规则和强制访问控制"。即使你拥有进入大楼的"门禁卡"(Java 权限),如果"防火墙规则"(SELinux 策略)不允许你的进程与目标服务或资源通信,访问依然会被拒绝
一. 职责与层面的根本区别
为了理解它们的关系,首先要明确它们各自负责的层面:
特性 | Java 层权限 (DAC - 自主访问控制) | SELinux (MAC - 强制访问控制) |
---|---|---|
控制层面 | 应用框架层 (Application Framework) | Linux 内核层 (Linux Kernel) |
控制对象 | 应用程序 (Application) | 进程 (Process) |
控制逻辑 | "这个应用有没有被用户授予访问相机/位置的权限?" | "这个进程 (属于某个域)是否被允许向那个进程 (属于某个域)发送 Binder 消息,或者访问那个文件(属于某个类型)?" |
决策者 | 用户(在运行时弹窗点击) | 系统安全策略(预先由 Google/OEM 定义,严格强制执行) |
灵活性 | 用户可动态授予和撤销 | 策略在系统编译时或启动时加载,普通用户和应用无法更改 |
目标 | 保护用户隐私和数据(如联系人、短信、位置) | 保护系统完整性,遏制恶意软件破坏系统、提升权限、攻击其他进程 |
二. 工作流程:它们如何协同与分工
让我们用一个经典的例子来说明:一个拥有 android.permission.CAMERA
权限的应用尝试打开相机
第 1 步:Java 层权限检查 (框架层)
1)应用调用 Camera.open()
2)这个调用会通过 Binder IPC 传递到系统的 CameraService
3)CameraService
在它的 Binder 方法中,会执行代码检查:
java
int pid = Binder.getCallingPid();
int uid = Binder.getCallingUid();
if (checkPermission(android.Manifest.permission.CAMERA, pid, uid) != PERMISSION_GRANTED) {
// 如果没有权限,抛出安全异常
throw new SecurityException("Permission denied");
}
4)这个检查会查询 PackageManagerService
,确认调用者的 UID 是否已被授予 CAMERA
权限
5)如果这里失败,流程结束,抛出 SecurityException
。这是第一道关卡
第 2 步:SELinux 权限检查 (内核层)
6)假设应用通过了第一道关卡,CameraService
现在尝试执行它的任务:打开底层的相机硬件设备(例如 /dev/video0
)
7)CameraService
进程(例如,它的 SELinux 域是 cameraserver
)需要向内核发起 open
系统调用来操作 /dev/video0
这个设备文件
8)内核中的 SELinux 安全服务器(Security Server)会介入检查:
○ 源上下文 (Source Context) : 谁发起操作? -> cameraserver
进程(域 cameraserver
)
○ 目标上下文 (Target Context) : 对什么进行操作? -> 文件 /dev/video0
(类型 camera_device
)
○ 操作类别 (Class) : 什么操作? -> chr_file
(字符设备文件)
○ 权限 (Permission) : 具体权限? -> open
, read
, write
9)SELinux 会查询预先加载的策略规则,看是否有这样一条允许规则:
bash
# 这是策略文件中的一条规则示例
allow cameraserver camera_device:chr_file { open read write };
10)如果策略中存在这条 allow
规则,访问被允许,相机成功打开。如果不存在,即使 CameraService
想这么做,内核也会直接返回 Permission Denied
(权限不足)的错误,并在 logcat 中打印一条 avc: denied
的警告。这是第二道,也是最终的关卡
三. 为什么需要两层控制?------ 深度防御
这种设计提供了巨大的安全优势:
1)遏制漏洞 (Containment) :
○ 假设 CameraService
存在一个代码漏洞,允许一个没有 Java 层 CAMERA
权限的应用绕过检查直接调用其内部函数。如果没有 SELinux,这个漏洞就可能被利用来非法使用相机
○ 有了 SELinux : 即使攻击者利用了该漏洞,发起操作的进程(例如一个被入侵的 untrusted_app
进程)试图直接与 camera_device
通信,SELinux 策略也绝对不允许 untrusted_app
域直接访问 camera_device
类型。漏洞被有效遏制,系统依然安全
2)保护系统服务自身
○ SELinux 不仅是限制应用,也限制系统服务。例如,策略规则会明确规定 cameraserver
域只能访问相机设备、它的配置文件和一些必要的库,而不能去访问网络、用户的短信数据等。这极大减少了系统服务被攻破后造成的破坏范围
3)权限的明确性 :
○ Java 权限检查是"黑盒"的,它只问"有没有权限",不管"你要用它做什么"
○ SELinux 的策略是极其明确的:"A 域的进程可以对 B 类型的文件进行 C 操作"。这种粒度是 Java 层无法提供的
四. 从日志看关系:avc: denied
当 SELinux 拒绝一个操作时,你会在 logcat
中看到类似这样的信息:
bash
avc: denied { open } for pid=1234 comm="cameraserver" path="/dev/video0" dev="tmpfs" ino=5678 scontext=u:r:cameraserver:s0 tcontext=u:object_r:camera_device:s0 tclass=chr_file permissive=0
这条日志是理解 SELinux 的钥匙,它清晰地告诉我们:
scontext=u:r:cameraserver:s0
: 源上下文是cameraserver
域tcontext=u:object_r:camera_device:s0
: 目标上下文是camera_device
类型{ open }
和tclass=chr_file
: 试图进行的操作是"打开"一个"字符设备文件"denied
: 因为策略中没有对应规则,所以被拒绝了
结论
Java 层申请的权限和 SELinux 是互补且正交的安全机制:
- Java 权限 是高级别、面向用户 的授权模型,管理应用 能否访问用户数据 和敏感功能
- SELinux 是低级别、面向系统 的强制访问模型,管理进程 能否访问系统资源 和其他进程