供應商初始化

init 進程幾乎具有不受限制的權限,並使用來自系統和供應商分區的輸入腳本在引導過程中初始化系統。這種存取導致 Treble 系統/供應商分裂中出現巨大漏洞,因為供應商腳本可能指示 init 存取不構成穩定係統-供應商應用程式二進位介面 (ABI) 一部分的檔案、屬性等。

Vendor init旨在透過使用單獨的安全增強型 Linux (SELinux) 網域vendor_init來運行/vendor中具有特定於供應商的權限的命令來彌補此漏洞。

機制

供應商 init 在引導過程的早期使用 SELinux 上下文u:r:vendor_init:s0派生一個 init 子程序。此 SELinux 上下文的權限比預設 init 上下文少得多,且其存取僅限於特定於供應商或穩定係統供應商 ABI 的一部分的檔案、屬性等。

Init 檢查它載入的每個腳本,查看其路徑是否以/vendor開頭,如果是,則用其命令必須在供應商 init 上下文中執行的指示對其進行標記。每個 init 內建函數都以一個布林值進行註釋,該布林值指定該命令是否必須在供應商 init 子進程中執行:

  • 大多數存取檔案系統的命令都被註釋為在供應商 init 子進程中運行,因此受到供應商 init SEPolicy 的約束。
  • 大多數影響內部初始化狀態的命令(例如,啟動和停止服務)都在正常的初始化進程中運行。這些命令會意識到供應商腳本正在呼叫它們來執行自己的非 SELinux 權限處理。

init 的主處理循環包含一項檢查,如果命令被註釋為在供應商子進程中運行並且源自供應商腳本,則該命令將透過進程間通訊(IPC) 傳送到執行該命令的供應商init 子進程並將結果傳回 init。

使用供應商初始化

預設啟用供應商 init,其限制適用於/vendor分區中存在的所有 init 腳本。供應商 init 對於腳本已經不存取僅係統檔案、屬性等的供應商應該是透明的。

但是,如果給定供應商腳本中的命令違反供應商 init 限制,則命令將失敗。失敗的命令在 init 的核心日誌(透過 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 友好的方式重新實作該命令,僅透過穩定的介面。 Neverallow 規則阻止新增存取不屬於穩定係統供應商 ABI 的系統檔案的權限。
  • 如果 SELinux 標籤是新的,並且尚未在系統vendor_init.te中授予權限,也未透過 neverallow 規則排除權限,則新標籤可能會在裝置特定的vendor_init.te中授予權限。

對於 Android 9 之前啟動的設備,可以透過將data_between_core_and_vendor_violators類型屬性新增至裝置特定的vendor_init.te檔案來繞過 neverallows 規則。

程式碼位置

供應商 init IPC 的大部分邏輯位於system/core/init/subcontext.cpp中。

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

供應商 init 的 SEPolicy 分為 system/sepolicy 中的私有 ( system/sepolicy/private/vendor_init.te ) 和公用 ( system/sepolicy/public/vendor_init.te ) 目錄。