建構 SELinux 政策

本頁說明如何建構 SELinux 政策。SELinux 政策是由核心 AOSP 政策 (平台) 和裝置專屬政策 (供應商) 組合而成。在 Android 4.4 到 Android 7.0 的 SELinux 政策建構流程中,所有 sepolicy 片段都會合併,然後在根目錄中產生單一檔案。這表示每當政策異動時,SoC 供應商和 ODM 製造商都必須修改 boot.img (適用於非 A/B 裝置) 或 system.img (適用於 A/B 裝置)。

在 Android 8.0 以上版本中,平台和供應商政策是分開建構的。系統單晶片 (SOC) 和原始設備製造商 (OEM) 可以更新政策中的部分內容、建構映像檔 (例如 vendor.imgboot.img),然後獨立於平台更新更新這些映像檔。

不過,由於模組化 SELinux 政策檔案儲存在 /vendor 分割區,init 程序必須先掛接系統和供應商分割區,才能從這些分割區讀取 SELinux 檔案,並將這些檔案與系統目錄中的核心 SELinux 檔案合併 (然後載入核心)。

來源檔案

建構 SELinux 的邏輯位於下列檔案:

  • external/selinux:外部 SELinux 專案,用於建構主機指令列公用程式,以編譯 SELinux 政策和標籤。
  • system/sepolicy:核心 Android SELinux 政策設定,包括內容和政策檔案。主要的 sepolicy 建構邏輯也在此處 (system/sepolicy/Android.mk)。

如要進一步瞭解 system/sepolicy 中的檔案,請參閱「實作 SELinux」。

Android 7.x 以下版本

本節說明如何在 Android 7.x 以下版本中建構 SELinux 政策。

Android 7.x 以下版本的建構程序

SELinux 政策是結合核心 AOSP 政策和裝置專屬自訂項目所建立。然後,系統會將合併後的政策傳遞至政策編譯器和各種檢查工具。裝置專屬自訂作業是透過裝置專屬 Boardconfig.mk 檔案中定義的 BOARD_SEPOLICY_DIRS 變數完成。這個全域建構變數包含目錄清單,指定搜尋其他政策檔案的順序。

舉例來說,SoC 供應商和 ODM 可能會各自新增一個目錄,一個用於 SoC 專屬設定,另一個用於裝置專屬設定,以便為特定裝置產生最終的 SELinux 設定:

  • BOARD_SEPOLICY_DIRS += device/SOC/common/sepolicy
  • BOARD_SEPOLICY_DIRS += device/SoC/DEVICE/sepolicy

system/sepolicyBOARD_SEPOLICY_DIRS 中的 file_contexts 檔案內容會串連,以在裝置上產生 file_contexts.bin

這張圖片顯示 Android 7.x 的 SELinux 建構邏輯。

圖 1. SELinux 建構邏輯。

sepolicy 檔案包含多個來源檔案:

  • 純文字 policy.conf 是依序串連 security_classesinitial_sids*.te 檔案、genfs_contextsport_contexts 所產生。
  • 每個檔案 (例如 security_classes) 的內容都是 system/sepolicy/BOARDS_SEPOLICY_DIRS 底下同名檔案的串連。
  • policy.conf 會傳送至 SELinux 編譯器進行語法檢查,並在裝置上編譯為 sepolicy 的二進位格式。
    這張圖片顯示產生 Android 7.x 適用的 SELinux 政策檔案的檔案。

    圖 2. SELinux 政策檔案。

SELinux 檔案

編譯後,搭載 Android 7.x 以下版本的裝置通常會包含下列 SELinux 相關檔案:

  • selinux_version
  • sepolicy:合併政策檔案後產生的二進位輸出內容 (例如 security_classesinitial_sids*.te)
  • file_contexts
  • property_contexts
  • seapp_contexts
  • service_contexts
  • system/etc/mac_permissions.xml

詳情請參閱「導入 SELinux」。

SELinux 初始化

系統啟動時,SELinux 會處於寬鬆模式 (而非強制執行模式)。初始化程序會執行下列工作:

  • 透過 /sys/fs/selinux/loadsepolicy 檔案從 RAM 磁碟載入核心。
  • 將 SELinux 切換為強制執行模式。
  • 執行 re-exec(),將 SELinux 網域規則套用至自身。

為縮短啟動時間,請盡快執行re-exec()init程序。

Android 8.0 以上版本

在 Android 8.0 中,SELinux 政策會分成平台和供應商元件,以便獨立更新平台/供應商政策,同時維持相容性。

平台 sepolicy 會進一步分割為平台私有和平台公開部分,以便將特定類型和屬性匯出給供應商政策撰寫者。平台公開型別/屬性保證會維持穩定 API 的狀態,適用於特定平台版本。使用平台對應檔案,即可確保多個版本與先前的平台公開類型/屬性相容。

Android 8.0 的建構程序

Android 8.0 的 SELinux 政策是由 /system/vendor 的片段組合而成。適當設定此項的邏輯位於 /platform/system/sepolicy/Android.mk

政策位於下列位置:

位置 包含
system/sepolicy/public 平台 sepolicy API
system/sepolicy/private 平台實作詳細資料 (供應商可忽略)
system/sepolicy/vendor 供應商可使用的政策和環境檔案 (供應商可視需要忽略)
BOARD_SEPOLICY_DIRS 供應商 sepolicy
BOARD_ODM_SEPOLICY_DIRS (Android 9 以上版本) Odm sepolicy
SYSTEM_EXT_PUBLIC_SEPOLICY_DIRS (Android 11 以上版本) System_ext 的 sepolicy API
SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS (Android 11 以上版本) System_ext 實作詳細資料 (供應商可忽略)
PRODUCT_PUBLIC_SEPOLICY_DIRS (Android 11 以上版本) 產品的 sepolicy API
PRODUCT_PRIVATE_SEPOLICY_DIRS (Android 11 以上版本) 產品導入詳細資料 (供應商可忽略)

建構系統會採用這項政策,並在對應的分區產生系統、system_ext、產品、供應商和 ODM 政策元件。步驟如下:

  1. 將政策轉換為 SELinux Common Intermediate Language (CIL) 格式,具體來說:
    1. 公開平台政策 (system + system_ext + product)
    2. 私人和公共政策合併
    3. public + vendor 和 BOARD_SEPOLICY_DIRS policy
  2. 將公眾提供的政策納入供應商政策,並進行版本控管。 方法是使用產生的公開 CIL 政策,告知合併的公開 + 廠商 + BOARD_SEPOLICY_DIRS 政策,哪些部分必須轉換為將連結至平台政策的屬性。
  3. 建立對應檔案,連結平台和供應商零件。 一開始,這只會將公開政策中的型別與供應商政策中的對應屬性連結;之後,這也會成為未來平台版本中維護檔案的基礎,確保與指定這個平台版本的供應商政策相容。
  4. 合併政策檔案 (說明裝置端和預先編譯的解決方案)。
    1. 合併對應、平台和供應商政策。
    2. 編譯輸出二進位政策檔案。

平台 public sepolicy

平台公開 sepolicy 包含 system/sepolicy/public 下定義的所有項目。平台可以假設公用政策中定義的型別和屬性,是特定平台版本的穩定 API。這會形成平台匯出的 sepolicy 一部分,供應商 (即裝置) 政策開發人員可在此撰寫其他裝置專屬政策。

類型會根據供應商檔案編寫時所依據的政策版本進行版本控管,並由 PLATFORM_SEPOLICY_VERSION 建構變數定義。然後,版本化的公開政策會納入供應商政策,並以原始形式納入平台政策。因此,最終政策包含私有平台政策、目前平台的公開安全政策、裝置專屬政策,以及與裝置政策編寫時所用平台版本對應的版本化公開政策。

平台私有 sepolicy

平台私有 sepolicy 包含 /system/sepolicy/private 下定義的所有項目。這部分政策會說明平台功能所需的平台專屬類型、權限和屬性。這些內容不會匯出給 vendor/device 政策撰寫者。非平台政策撰寫者不得根據平台私有 sepolicy 中定義的類型/屬性/規則,撰寫政策擴充功能。此外,這些規則可能會在僅限架構的更新中修改或消失。

平台私人對應

平台私有對應包含政策聲明,可將先前平台版本平台公開政策中公開的屬性,對應至目前平台公開 sepolicy 中使用的具體型別。這樣可確保根據先前平台公開 sepolicy 版本撰寫的供應商政策,能繼續運作。版本控管是根據特定平台版本的 Android 開放原始碼計畫中設定的 PLATFORM_SEPOLICY_VERSION 建構變數。每個先前平台版本都有個別的對應檔案,這個平台預計會接受這些版本供應商的政策。詳情請參閱「相容性」。

Android 11 以上版本

system_ext 和產品 sepolicy

Android 11 新增了 system_ext 政策和產品政策。與平台政策類似,system_ext 政策和產品政策會分為公開政策和私有政策。

公開政策會匯出給供應商。類型和屬性會成為穩定的 API,且供應商政策可參照公開政策中的類型和屬性。類型會根據 PLATFORM_SEPOLICY_VERSION 進行版本控管,且版本控管的政策會納入供應商政策。每個 system_ext 和產品分割區都包含原始政策。

私有政策包含 system_ext-only 和 product-only 類型、權限,以及 system_ext 和產品分割區功能所需的屬性。供應商看不到私人政策,表示這些規則是內部規則,可以修改。

system_ext 和產品對應

system_ext 和 product 可將指定的公開型別匯出至供應商。不過,維持相容性是各合作夥伴的責任。為確保相容性,合作夥伴可以提供自己的對應檔案,將舊版本的屬性對應至目前公開 sepolicy 中使用的具體型別。

  • 如要安裝 system_ext 的對應檔案,請將包含所需對應資訊的 cil 檔案放入 {SYSTEM_EXT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil,然後將 system_ext_{ver}.cil 新增至 PRODUCT_PACKAGES
  • 如要安裝產品的對應檔案,請將包含所需對應資訊的 cil 檔案放置到 {PRODUCT_PRIVATE_SEPOLICY_DIRS}/compat/{ver}/{ver}.cil,然後將 product_{ver}.cil 新增至 PRODUCT_PACKAGES

請參閱範例,瞭解如何新增 redbull 裝置產品區隔的對應檔案。

預先編譯的 SELinux 政策

init 開啟 SELinux 前,會先從各個分割區 (systemsystem_extproductvendorodm) 收集所有 CIL 檔案,並編譯成二進位政策,這種格式可載入核心。init由於編譯需要時間 (通常為 1 到 2 秒),CIL 檔案會在建構時預先編譯,並放置在 /vendor/etc/selinux/precompiled_sepolicy/odm/etc/selinux/precompiled_sepolicy,以及輸入 CIL 檔案的 sha256 雜湊。在執行階段,init 會比較雜湊,檢查是否有任何政策檔案已更新。如果沒有任何變更,init 會載入預先編譯的政策。如果沒有,init 會即時編譯,並使用該編譯結果,而非預先編譯的結果。

具體來說,如果符合下列所有條件,系統就會使用預先編譯的政策。其中,{partition} 代表預先編譯政策所在的分割區:vendorodm

  • /system/etc/selinux/plat_sepolicy_and_mapping.sha256/{partition}/etc/selinux/precompiled_sepolicy.plat_sepolicy_and_mapping.sha256 存在且相同。
  • /system_ext/etc/selinux/system_ext_sepolicy_and_mapping.sha256」和「/{partition}/etc/selinux/precompiled_sepolicy.system_ext_sepolicy_and_mapping.sha256」都不存在。或兩者皆存在且相同。
  • /product/etc/selinux/product_sepolicy_and_mapping.sha256」和「/{partition}/etc/selinux/precompiled_sepolicy.product_sepolicy_and_mapping.sha256」都不存在。或兩者皆存在且相同。

如果其中任何一項不同,init 就會改用裝置端編譯路徑。詳情請參閱 system/core/init/selinux.cpp