建構 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 程序必須先掛接 system 和供應商分割區,才能從這些分割區讀取 SELinux 檔案,並將這些檔案與 system 目錄中的核心 SELinux 檔案合併 (然後載入核心)。

來源檔案

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

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

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

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.bp

這項政策位於下列位置:

位置 包含
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 以上版本) Product sepolicy API
PRODUCT_PRIVATE_SEPOLICY_DIRS (Android 11 以上版本) 產品導入詳細資料 (供應商可忽略)

建構系統會採用這項政策,並在對應的分割區產生 systemsystem_extproductvendorodm 政策元件。步驟如下:

  1. 將政策轉換為 SELinux Common Intermediate Language (CIL) 格式,具體來說:
    • 公開平台政策 (systemsystem_extproduct)
    • 合併的私人和公開政策
    • 公開、供應商和 BOARD_SEPOLICY_DIRS 政策
  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 下定義的所有內容。這部分政策會說明平台功能所需的平台專屬類型、權限和屬性。這些內容不會匯出給供應商和裝置政策撰寫者。非平台政策撰寫者不得根據平台私有 sepolicy 中定義的類型、屬性和規則,撰寫政策擴充功能。此外,這些規則可能會在僅限架構的更新中遭到修改或消失。

平台私人對應

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

Android 11 以上版本

本節說明如何在 Android 11 以上版本中建構 SELinux 政策。

system_ext 和產品 sepolicy

Android 11 新增了 system_ext 政策和 product 政策。與平台 sepolicy 類似,system_ext 政策和 product 政策會分為公開政策和私人政策。

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

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

system_ext 和產品對應

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

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

請參閱範例,瞭解如何新增 Redbull 裝置 product 分區的對應檔案。

預先編譯的 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