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 方法中,會執行代碼檢查:
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 會查詢預先加載的策略規則,看是否有這樣一條允許規則:
# 這是策略文件中的一條規則示例
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
?中看到類似這樣的信息:
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?是低級別、面向系統的強制訪問模型,管理進程能否訪問系統資源和其他進程