寫入 SELinux 政策

Android 開放原始碼計畫 (AOSP) 針對所有 Android 裝置通用的應用程式和服務,提供了可靠的基本政策。Android 開放原始碼計畫的貢獻者會定期修正這項政策,核心政策預計會佔最終裝置端政策的 90% 至 95%,而裝置專屬的客製化內容則佔剩下的 5% 至 10%。本文將著重於這些裝置專屬的客製化設定、如何編寫裝置專屬政策,以及過程中應避免的部分陷阱。

裝置啟動

撰寫裝置專用政策時,請按照下列步驟操作。

以寬容模式執行

當裝置處於寬鬆模式時,系統會記錄拒絕項目,但不會強制執行。寬鬆模式的重要性有兩個:

  • 允許模式可確保政策啟動不會延遲其他早期裝置啟動工作。
  • 強制拒絕可能會掩蓋其他拒絕。舉例來說,檔案存取作業通常會涉及目錄搜尋、檔案開啟,然後再讀取檔案。在強制模式下,系統只會拒絕目錄搜尋要求。允許模式可確保所有拒絕項目都會顯示。

將裝置設為開放模式最簡單的方法,就是使用核心指令列。您可以將這項資訊加入裝置的 BoardConfig.mk 檔案:platform/device/<vendor>/<target>/BoardConfig.mk。修改指令列後,請執行 make clean,然後執行 make bootimage,並閃記新的啟動映像檔。

接著,請透過以下方式確認開放式模式:

adb shell getenforce

兩週是全球開放模式的合理時間長度。解決大部分拒絕問題後,請改回強制模式,並在發生時解決錯誤。如果網域仍會產生拒絕,或服務仍處於大量開發階段,可以暫時將其設為許可模式,但請盡快將其移回強制模式。

提早強制執行

在強制執行模式中,系統會記錄並強制執行拒絕。最佳做法是讓裝置盡早進入強制執行模式。等待建立並強制執行裝置專屬政策,通常會導致產品出現錯誤,並造成使用者體驗不佳。儘早開始參與 Dogfood 測試,並確保在實際使用時能夠全面測試所有功能。提早開始這項工作,可確保設計決策能考量安全性疑慮。相反地,如果只根據觀察到的拒絕情況授予權限,則是不安全的做法。請在這段時間內執行裝置安全性稽核,並依據不應允許的行為回報錯誤。

移除或刪除現有政策

在新的裝置上從頭建立裝置專屬政策,有許多好處,包括:

處理核心服務拒絕情形

核心服務產生的拒絕內容通常會透過檔案標記解決。例如:

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

只要正確標示 /dev/kgsl-3d0,就能完全解決這個問題。在本範例中,tcontextdevice。這代表預設的背景資訊,除非指派更具體的標籤,否則 /dev 中的所有項目都會收到「 device」標籤。在此只接受 audit2allow 的輸出內容,會導致規則不正確且過於寬鬆。

如要解決這類問題,請為檔案提供更具體的標籤,在本例中為 gpu_device mediaserver 已在核心政策中取得存取 gpu_device 所需的權限,因此不需要其他權限。

其他應以核心政策中預先定義的類型標示的裝置專屬檔案:

一般來說,為預設標籤授予權限是不正確的做法。許多這類權限都會遭到 neverallow 規則禁止,但即使未明確禁止,最佳做法仍是提供特定標籤。

標示新服務並處理拒絕案件

啟動服務必須在自己的 SELinux 網域中執行。以下範例會將服務「foo」放入專屬的 SELinux 網域,並授予權限。

服務會在裝置的 init.device.rc 檔案中啟動,如下所示:

service foo /system/bin/foo
    class core
  1. 建立新的網域「foo」

    建立含有以下內容的 device/manufacturer/device-name/sepolicy/foo.te 檔案:

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

    這是 foo SELinux 網域的初始範本,您可以根據該可執行檔執行的特定作業,為該範本新增規則。

  2. 標籤 /system/bin/foo

    device/manufacturer/device-name/sepolicy/file_contexts 中新增以下內容:

    /system/bin/foo   u:object_r:foo_exec:s0
    

    這可確保可執行檔已正確標示,讓 SELinux 在適當的網域中執行服務。

  3. 建構並刷新開機和系統映像檔。
  4. 調整網域的 SELinux 規則。

    使用拒絕回應判斷必要權限。audit2allow 工具提供良好的規範,但只能用於提供政策撰寫資訊。請勿只複製輸出內容。

切換回強制模式

您可以在寬鬆模式中進行疑難排解,但請盡快切換回強制模式,並盡量維持該模式。

常見錯誤

以下是撰寫裝置專屬政策時常見錯誤的解決方法。

過度使用否定

下列規則範例就像上鎖前門,但窗戶開著:

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

意圖很明確:除了第三方應用程式以外,所有人都可以存取偵錯裝置。

這項規則在某些方面有所缺失。由於所有應用程式都可以選擇在 isolated_app 網域中執行服務,因此排除 untrusted_app 的做法相當簡單。同樣地,如果 AOSP 新增第三方應用程式的新網域,這些網域也會有權存取 scary_debug_device。規則過於寬鬆。大多數網域都不會因為存取這項偵錯工具而有所助益。規則應已寫入,只允許需要存取權的網域。

在實際工作環境中偵錯功能

偵錯功能不應出現在正式版版本中,相關政策亦然。

最簡單的替代做法是在 eng/userdebug 版本 (例如 adb rootadb shell setenforce 0) 中停用 SELinux 時,只允許偵錯功能。

另一個安全的替代做法,是在 userdebug_or_eng 陳述式中加入偵錯權限。

政策大小爆炸

「Characterizing SEAndroid Policies in the Wild」一文說明瞭裝置政策自訂項目增加的趨勢,裝置專屬政策應涵蓋裝置上整體政策的 5–10%。在 20%以上範圍內的自訂項目幾乎肯定包含權限過高的網域和無效政策。

過大的政策:

  • 政策位於 RAM 磁碟區,且也會載入至核心記憶體,因此會對記憶體造成雙重影響。
  • 需要較大的開機映像檔,因此會浪費磁碟空間。
  • 會影響執行階段政策查詢時間。

以下範例顯示兩部裝置,其中製造商專屬政策分別占裝置上政策的 50% 和 40%。我們重新改寫政策,藉此大幅改善安全性,而且完全沒有失去功能,如下所示。(我們也納入 AOSP 裝置 Shamu 和 Flounder 做為比較)。

圖 1:安全性稽核後的裝置特定政策大小比較。

圖 1. 安全性稽核後,比較裝置特定政策大小。

在兩種情況下,政策的大小和權限數量都大幅減少。政策大小減少,幾乎完全是因為移除了不必要的權限,其中許多權限可能是 audit2allow 產生的規則,而這些規則一律會加入政策中。無效網域也造成兩部裝置都發生問題。

授予 dac_override 功能

dac_override 拒絕表示違規程序嘗試使用錯誤的 Unix 使用者/群組/全域權限存取檔案。在多數情況下,您幾乎不會需要授予 dac_override 權限。請改為 變更檔案或程序的 Unix 權限initvoldinstalld 等少數網域確實需要能夠覆寫 Unix 檔案權限,以便存取其他程序的檔案。詳情請參閱 Dan Walsh 的網誌