供應商初始化

初始化程序幾乎沒有限制權限,並使用系統和供應商分區的輸入指令碼,在開機程序期間初始化系統。這類存取權會導致 Treble 系統/供應商分割出現巨大漏洞,因為供應商指令碼可能會指示初始化程序存取檔案、屬性等,而這些檔案、屬性等並非穩定的系統供應商應用程式二進位介面 (ABI) 的一部分。

供應商初始化功能旨在利用單獨的安全強化 Linux (SELinux) 網域 vendor_init 來執行 /vendor 中的指令,並使用供應商專屬權限。

機制

供應商初始化會在啟動程序初期,使用 SELinux 內容 u:r:vendor_init:s0 分支初始化的子程序。此 SELinux 內容的權限比預設初始化內容少得多,且其存取權限僅限於特定廠商或穩定的系統廠商 ABI 的檔案、屬性等。

初始化會檢查載入的每個指令碼,查看其路徑是否以 /vendor 開頭,如果是的話,就會標記該指令碼,指出其指令必須在供應商初始化內容中執行。每個初始化內建項目都會加上布林註解,指出指令是否必須在供應商初始化子程序中執行:

  • 大多數存取檔案系統的指令都會標註為在供應商初始化子程序中執行,因此會受到供應商初始化 SEPolicy 的管制。
  • 影響內部初始化狀態的大部分指令 (例如啟動和停止服務) 會在正常的初始化程序中執行。這些指令會知道供應商指令碼會呼叫它們,以便自行處理非 SELinux 權限。

init 的主要處理迴圈包含檢查,如果指令已註解要在供應商子程序中執行,且來自供應商指令碼,則該指令會透過程序間通訊 (IPC) 傳送至供應商初始子程序,該子程序會執行指令並將結果傳回至 init。

使用供應商初始化

供應商初始化功能預設為啟用,且其限制會套用至 /vendor 分區中的所有初始化指令碼。如果供應商的指令碼已不再存取系統專屬檔案、屬性等,供應商初始化應對供應商保持透明。

不過,如果特定供應商指令碼中的指令違反供應商初始化限制,則指令會失敗。失敗的指令會在內核記錄中顯示一行訊息 (可透過 dmesg 查看),表示失敗。任何因 SELinux 政策而失敗的指令都會伴隨 SELinux 稽核作業。包含 SELinux 稽核的失敗範例:

type=1400 audit(1511821362.996:9): avc: denied { search } for pid=540 comm="init" name="nfc" dev="sda45" ino=1310721 scontext=u:r:vendor_init:s0 tcontext=u:object_r:nfc_data_file:s0 tclass=dir permissive=0
init: Command 'write /data/nfc/bad_file_access 1234' action=boot (/vendor/etc/init/hw/init.walleye.rc:422) took 2ms and failed: Unable to write to file '/data/nfc/bad_file_access': open() failed: Permission denied

如果指令失敗,您可以採取以下兩種做法:

  • 如果指令因預期限制而失敗 (例如指令存取系統檔案或屬性),則必須以 Treble 相容的方式重新實作指令,並只透過穩定的介面進行。禁止規則可防止新增權限,以便存取不屬於穩定系統供應商 ABI 的系統檔案。
  • 如果 SELinux 標籤是新的,且尚未在系統 vendor_init.te 中授予權限,也未透過 neverallow 規則排除權限,則新標籤可能會在裝置專屬的 vendor_init.te 中授予權限。

如果是 Android 9 之前推出的裝置,您可以將 data_between_core_and_vendor_violators type 屬性新增至裝置專屬的 vendor_init.te 檔案,藉此略過 neverallows 規則。

程式碼位置

供應商初始化 IPC 的大部分邏輯位於 system/core/init/subcontext.cpp

指令表位於 system/core/init/builtins.cpp 中的 BuiltinFunctionMap 類別,並包含註解,指出指令是否必須在供應商初始子程序中執行。

供應商初始化程序的 SEPolicy 會分散在 system/sepolicy 中的私人 (system/sepolicy/private/vendor_init.te) 和公開 (system/sepolicy/public/vendor_init.te) 目錄。