建構 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 專案,用於建構 HOST 指令列公用程式,以便編譯 SELinux 政策和標籤。
  • system/sepolicy:核心 Android SELinux 政策設定,包括內容和政策檔案。主要的安全政策建構邏輯也在這裡 (system/sepolicy/Android.mk)。

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

Android 7.x 以下版本

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

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

您可以將核心 AOSP 政策與裝置專屬的客製化內容結合,建立 SELinux 政策。接著,系統會將合併的政策傳遞至政策編譯器和各種檢查器。裝置專屬的自訂設定是透過裝置專屬 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 檔案

編譯完成後,搭載 7.x 以下版本的 Android 裝置通常會包含下列 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 網域規則套用至自身。

如要縮短開機時間,請盡快在 init 程序上執行 re-exec()

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、system_ext、product、vendor 和 odm 政策元件。步驟如下:

  1. 將政策轉換為 SELinux 通用中介語言 (CIL) 格式,具體如下:
    1. 公開平台政策 (system + system_ext + product)
    2. 私人和公共政策的組合
    3. 公開 + 供應商和 BOARD_SEPOLICY_DIRS 政策
  2. 將公開提供的政策版本化,做為供應商政策的一部分。方法是使用產生的公開 CIL 政策,向結合的公開 + 供應商 + BOARD_SEPOLICY_DIRS 政策提供資訊,指出哪些部分必須轉換為連結至平台政策的屬性。
  3. 建立連結平台和供應商零件的對應檔案。一開始,這項功能只會將公開政策中的類型連結至供應商政策中的對應屬性;之後也會為日後平台版本中維護的檔案提供基礎,讓檔案與鎖定此平台版本的供應商政策相容。
  4. 結合政策檔案 (說明裝置端和預先編譯的解決方案)。
    1. 合併對應、平台和供應商政策。
    2. 編譯輸出二進位政策檔案。

平台公開安全政策

平台公開安全政策包含 system/sepolicy/public 下定義的所有內容。平台可以假設在公開政策下定義的類型和屬性,是特定平台版本的穩定 API。這會形成 sepolicy 的一部分,由平台匯出,供應商 (也就是裝置) 政策開發人員可在其中編寫其他裝置專屬政策。

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

平台私人 sepolicy

平台私人安全政策包含 /system/sepolicy/private 中定義的所有內容。這部分的政策會形成平台專屬類型、權限和屬性,這些都是平台功能所需的元素。這些不會匯出至 vendor/device 政策編寫工具。非平台政策編寫者不得根據平台私人安全政策中定義的類型/屬性/規則編寫政策擴充功能。此外,這些規則可供修改,或是可能會在僅限架構更新中消失。

平台私人對應

平台私有對應項目包含政策陳述式,可將先前平台版本的平台公開政策中公開的屬性,對應至目前平台公開政策中使用的具體類型。這可確保根據先前平台公開安全政策版本中的平台公開屬性編寫的供應商政策繼續運作。版本編號是根據 AOSP 中針對特定平台版本設定的 PLATFORM_SEPOLICY_VERSION 建構變數。每個舊版平台都會有個獨立的對應檔案,這個平台應接受供應商政策。詳情請參閱「相容性」。

Android 11 以上版本

system_ext 和產品 sepolicy

在 Android 11 中,系統_ext 政策和產品政策已新增。和平台 sepolicy 一樣,system_ext 政策和產品政策會分為公開政策和私人政策。

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

私人政策包含 system_ext 專用和產品專用類型、權限,以及 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