專屬存取權控管 (DAC)

加入建構檔的檔案系統物件和服務通常需要專屬 ID,稱為 Android ID (AID)。目前,許多檔案和服務等資源都會不必要地使用核心 (Android 定義) AID;在許多情況下,您可以改用 OEM (OEM 定義) AID。

較舊的 Android 版本 (Android 7.x 以下) 會使用裝置專屬的 android_filesystem_config.h 檔案擴充 AID 機制,以指定檔案系統功能和/或自訂 OEM AID。不過,這個系統不支援使用 OEM AID 的易記名稱,因此使用者和群組欄位必須指定原始數字,且無法將易記名稱與數字 AID 建立關聯,因此不夠直覺。

較新版本的 Android (Android 8.0 以上版本) 支援擴充檔案系統功能的新方法。這個新方法支援下列項目:

  • 設定檔的多個來源位置 (可啟用可擴充的建構設定)。
  • 在建構期間對 OEM AID 值進行健全性檢查。
  • 產生可視需要在來源檔案中使用的自訂 OEM AID 標頭。
  • 將友善名稱與實際的 OEM AID 值建立關聯。支援使用者和群組的非數字字串引數,例如「foo」而非「2901」。

其他改善項目包括從 system/core/libcutils/include/private/android_filesystem_config.h 移除 android_ids[] 陣列。這個陣列現在會在 Bionic 中以完全私密產生的陣列形式存在,並提供 getpwnam()getgrnam() 的存取工具。(這會產生副作用,在修改核心 AID 時產生穩定的二進位檔)。如需工具和詳細的 README 檔案,請參閱 build/make/tools/fs_config

新增 Android ID (AID)

Android 8.0 已從 Android 開放原始碼計畫 (AOSP) 中移除 android_ids[] 陣列。產生 Bionic android_ids[] 陣列時,所有 AID 友善名稱都會從 system/core/libcutils/include/private/android_filesystem_config.h 標頭檔案產生。工具會挑選任何與 AID_* 相符的 define,而 * 會變成小寫名稱。

例如,在 private/android_filesystem_config.h 中:

#define AID_SYSTEM 1000

變成:

  • 好記的名稱:system
  • uid:1000
  • gid: 1000

如要新增 AOSP 核心 AID,只要將 #define 新增至 android_filesystem_config.h 標頭檔案即可。AID 會在建構時產生,並提供給使用使用者和群組引數的介面。工具會驗證新 AID 是否不在 APP 或 OEM 範圍內;也會尊重這些範圍的變更,並應在變更或新的 OEM 保留範圍時自動重新設定。

設定 AID

如要啟用新的 AID 機制,請在 BoardConfig.mk 檔案中設定 TARGET_FS_CONFIG_GEN。這個變數會保留設定檔清單,讓您視需要附加檔案。

按照慣例,設定檔會使用 config.fs 這個名稱,但實際上您可以使用任何名稱。config.fs 檔案採用 Python ConfigParser ini 格式,並包含 caps 區段 (用於設定檔案系統功能) 和 AID 區段 (用於設定 OEM AID)。

設定上限部分

在建構中,Caps 區段可支援在檔案系統物件上設定檔案系統功能 (檔案系統本身也必須支援這項功能)。

由於在 Android 中以 root 權限執行穩定服務會導致 Compatibility Test Suite (CTS) 失敗,因此在執行程序或服務時保留功能的先前規定,需要先設定功能,然後使用 setuid/setgid 執行適當的 AID。使用上限時,您可以略過這些要求,讓核心為您執行此操作。當控制權交給 main() 時,程序就已具備所需功能,因此服務可以使用非 root 使用者和群組 (這是啟動特權服務的首選方式)。

上限區段使用以下語法:

章節 定義
[path] 要設定的檔案系統路徑。系統會將結尾為 / 的路徑視為 dir,否則為檔案。

如果在不同檔案中使用相同的 [path] 指定多個區段,會發生錯誤。在 Python 3.2 以下版本中,同一檔案可能包含覆寫上一節的區段;在 Python 3.2 中,會設定為嚴格模式。
mode 八進位檔案模式 有效的八進位檔案模式,至少有 3 位數。如果指定 3,則會在前面加上 0,否則會使用原始模式。
user AID_<使用者> 您可以使用 C define 提供有效的 AID,也可以使用友善名稱 (例如 AID_RADIOradio 皆可接受)。如要定義自訂 AID,請參閱「設定 AID」一節。
group AID_<group> 與使用者相同。
caps 上限* bionic/libc/kernel/uapi/linux/capability.h 中宣告的名稱,不含前置的 CAP_。大小寫皆可。上限也可以是原始值:
  • 二進位 (0b0101)
  • 八進位 (0455)
  • int (42)
  • 十六進位 (0xFF)
請使用空格分隔多個上限。

如需使用範例,請參閱「使用檔案系統功能」。

設定 AID 部分

AID 部分包含 OEM AID,並使用以下語法:

章節 定義
[AID_<name>] <name> 可包含大寫字母、數字和底線。小寫版本會用做友善名稱。產生的程式碼納入標頭檔案會使用確切的 AID_<name>

在同一個 AID_<name> 中指定多個區段 (不區分大小寫,與 [path] 相同) 是錯誤的做法。

<name> 的開頭必須是分區名稱,以確保不會與不同來源發生衝突。
value <number> 有效的 C 樣式數字字串 (十六進位、八進位、二進位和十進位)。

如果您指定多個區段,但這些區段的值選項相同,則會發生錯誤。

值選項必須在 <name> 中使用的分區對應範圍內指定。有效分區清單及其對應的範圍是在 system/core/libcutils/include/private/android_filesystem_config.h 中定義。選項如下:
  • 供應商分割區
    • AID_OEM_RESERVED_START(2900) - AID_OEM_RESERVED_END(2999)
    • AID_OEM_RESERVED_2_START(5000) - AID_OEM_RESERVED_2_END(5999)
  • 系統分割區
    • AID_SYSTEM_RESERVED_START(6000) - AID_SYSTEM_RESERVED_END(6499)
  • ODM 分區
    • AID_ODM_RESERVED_START(6500) - AID_ODM_RESERVED_END(6999)
  • 產品劃分
    • AID_PRODUCT_RESERVED_START(7000) - AID_PRODUCT_RESERVED_END(7499)
  • System_ext 分割區
    • AID_SYSTEM_EXT_RESERVED_START(7500) - AID_SYSTEM_EXT_RESERVED_END(7999)

如需使用範例,請參閱「定義 OEM AID 名稱」和「使用 OEM AID」。

使用範例

以下範例詳細說明如何定義及使用 OEM AID,以及如何啟用檔案系統功能。OEM AID 名稱 ([AID_name]) 開頭必須是分割區名稱,例如「vendor_」,以確保不會與日後的 AOSP 名稱或其他分割區衝突。

定義 OEM AID 名稱

如要定義 OEM AID,請建立 config.fs 檔案並設定 AID 值。例如,在 device/x/y/config.fs 中設定以下內容:

[AID_VENDOR_FOO]
value: 2900

建立檔案後,請設定 TARGET_FS_CONFIG_GEN 變數,並指向 BoardConfig.mk 中的該檔案。例如,在 device/x/y/BoardConfig.mk 中設定以下內容:

TARGET_FS_CONFIG_GEN += device/x/y/config.fs

系統現在可以在新的建構作業上,使用您的自訂 AID。

使用 OEM 廣告 ID

如要使用 OEM AID,請在 C 程式碼中將 oemaids_headers 加入相關聯的 Makefile 並新增 #include "generated_oem_aid.h",然後開始使用宣告的 ID。例如,在 my_file.c 中新增下列程式碼:

#include "generated_oem_aid.h"


If (ipc->uid == AID_VENDOR_FOO) {
  // Do something
...

在相關聯的 Android.bp 檔案中新增下列內容:

header_libs: ["oemaids_headers"],

如果您使用的是 Android.mk 檔案,請新增下列內容:

LOCAL_HEADER_LIBRARIES := oemaids_headers

使用好記的名稱

在 Android 9 中,您可以為任何支援 AID 名稱的介面使用友善名稱。例如:

  • some/init.rcchown 指令中:
    chown vendor_foo /vendor/some/vendor_foo/file
    
  • some/init.rc 中的 service 中:
    service vendor_foo /vendor/bin/foo_service
        user vendor_foo
        group vendor_foo
    

由於 /vendor/etc/passwd/vendor/etc/group 會執行從友善名稱對應至 uid 的內部對應,因此必須掛載供應商分區。

關聯好記的名稱

Android 9 支援將友善名稱與實際的 OEM AID 值建立關聯。您可以為使用者和群組使用非數字字串引數,也就是「vendor_foo」而非「2901」。

將 AID 轉換為友善名稱

對於OEM ID,Android 8.x 要求使用 oem_#### 搭配 getpwnam 和類似函式,以及在處理使用 getpwnam 的查詢 (例如 init 指令碼) 的情況下。在 Android 9 中,您可以使用 Bionic 中的 getpwnamgetgrnam 好友,將 Android ID (AID) 轉換為友善名稱,反之亦然。

使用檔案系統功能

如要啟用檔案系統功能,請在 config.fs 檔案中建立 caps 區段。例如,在 device/x/y/config.fs 中新增下列區段:

[system/bin/foo_service]
mode: 0555
user: AID_VENDOR_FOO
group: AID_SYSTEM
caps: SYS_ADMIN | SYS_NICE

建立檔案後,請將 TARGET_FS_CONFIG_GEN 設為指向 BoardConfig.mk 中的該檔案。例如,在 device/x/y/BoardConfig.mk 中設定以下內容:

TARGET_FS_CONFIG_GEN += device/x/y/config.fs

執行服務 vendor_foo 時,會以 CAP_SYS_ADMINCAP_SYS_NICE 功能啟動,但不會進行 setuidsetgid 呼叫。此外,vendor_foo 服務的 SELinux 政策不再需要 setuidsetgid 功能,可以將其刪除。

設定覆寫值 (Android 6.x-7.x)

Android 6.0 將 fs_config 和相關結構定義 (system/core/include/private/android_filesystem_config.h) 重新配置至 system/core/libcutils/fs_config.c,以便在 /system/etc/fs_config_dirs/system/etc/fs_config_files 中安裝的二進位檔更新或覆寫這些定義。針對目錄和檔案 (可能會使用其他 glob 運算式) 分別套用比對及剖析規則,可讓 Android 處理兩個不同資料表中的目錄和檔案。system/core/libcutils/fs_config.c 中的結構定義不僅允許在執行階段讀取目錄和檔案,主機可以在建構期間使用相同檔案,以 ${OUT}/system/etc/fs_config_dirs${OUT}/system/etc/fs_config_files 的形式建構檔案系統映像檔。

雖然延伸檔案系統的覆寫方法已由 Android 8.0 中推出的模組設定系統取代,但您仍可視需要使用舊版方法。以下各節將詳細說明如何產生及納入覆寫檔案,以及如何設定檔案系統。

產生覆寫檔案

您可以使用 build/tools/fs_config 中的 fs_config_generate 工具產生對齊的二進位檔 /system/etc/fs_config_dirs/system/etc/fs_config_files。這項工具會使用 libcutils 程式庫函式 (fs_config_generate()) 將 DAC 需求納入緩衝區,並定義所包含檔案的規則,以便將 DAC 規則制度化。

如要使用,請在 device/vendor/device/android_filesystem_config.h 中建立包含檔案的覆寫檔案。檔案必須使用 system/core/include/private/android_filesystem_config.h 中定義的 structure fs_path_config 格式,並為目錄和檔案符號使用以下結構初始化:

  • 如果是目錄,請使用 android_device_dirs[]
  • 如要使用檔案,請使用 android_device_files[]

在不使用 android_device_dirs[]android_device_files[] 的情況下,您可以定義 NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRSNO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES (請參閱下方的範例)。您也可以在電路板設定中使用 TARGET_ANDROID_FILESYSTEM_CONFIG_H 指定覆寫檔案,並強制使用 android_filesystem_config.h 做為 base name。

包含覆寫檔案

如要加入檔案,請確認 PRODUCT_PACKAGES 包含 fs_config_dirs 和/或 fs_config_files,以便分別將這些檔案安裝至 /system/etc/fs_config_dirs/system/etc/fs_config_files。建構系統會在 $(TARGET_DEVICE_DIR) 中搜尋自訂 android_filesystem_config.h,其中 BoardConfig.mk 已存在。如果這個檔案位於其他位置,請將電路板設定變數 TARGET_ANDROID_FILESYSTEM_CONFIG_H 設為指向該位置。

設定檔案系統

如要在 Android 6.0 以上版本中設定檔案系統,請按照下列步驟操作:

  1. 建立 $(TARGET_DEVICE_DIR)/android_filesystem_config.h 檔案。
  2. 在 Jamboard 設定檔 (例如fs_config_dirsfs_config_filesPRODUCT_PACKAGES $(TARGET_DEVICE_DIR)/device.mk)。

覆寫範例

本範例顯示了一個修補程式,用於覆寫 system/bin/glgps 守護程序,以便在 device/vendor/device 目錄中新增喚醒鎖定支援。請注意以下幾點:

  • 每個結構體項目都是模式、UID、GID、功能和名稱。系統會自動納入 system/core/include/private/android_filesystem_config.h,提供資訊清單 #defines (AID_ROOTAID_SHELLCAP_BLOCK_SUSPEND)。
  • android_device_files[] 區段包含停用 system/etc/fs_config_dirs 存取權的操作操作 (未指定),這會為缺少目錄覆寫的內容提供額外的 DAC 防護。不過,這項保護機制相當薄弱;如果有人能控制 /system,通常就能為所欲執行任何操作。
diff --git a/android_filesystem_config.h b/android_filesystem_config.h
new file mode 100644
index 0000000..874195f
--- /dev/null
+++ b/android_filesystem_config.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+/* This file is used to define the properties of the file system
+** images generated by build tools (eg: mkbootfs) and
+** by the device side of adb.
+*/
+
+#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+/* static const struct fs_path_config android_device_dirs[] = { }; */
+
+/* Rules for files.
+** These rules are applied based on "first match", so they
+** should start with the most specific path and work their
+** way up to the root. Prefixes ending in * denotes wildcard
+** and will allow partial matches.
+*/
+static const struct fs_path_config android_device_files[] = {
+  { 00755, AID_ROOT, AID_SHELL, (1ULL << CAP_BLOCK_SUSPEND),
"system/bin/glgps" },
+#ifdef NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+  { 00000, AID_ROOT, AID_ROOT, 0, "system/etc/fs_config_dirs" },
+#endif
+};


diff --git a/device.mk b/device.mk
index 0c71d21..235c1a7 100644
--- a/device.mk
+++ b/device.mk
@@ -18,7 +18,8 @@ PRODUCT_PACKAGES := \
     libwpa_client \
     hostapd \
     wpa_supplicant \
-    wpa_supplicant.conf
+    wpa_supplicant.conf \
+    fs_config_files

 ifeq ($(TARGET_PREBUILT_KERNEL),)
 ifeq ($(USE_SVELTE_KERNEL), true)

從先前版本遷移檔案系統

從 Android 5.x 以下版本遷移檔案系統時,請注意 Android 6.x

  • 移除部分包含、結構和內嵌定義。
  • 需要參照 libcutils,而非直接從 system/core/include/private/android_filesystem_config.h 執行。裝置製造商私人可執行檔會依賴檔案或目錄結構的 system/code/include/private_filesystem_config.h,或 fs_config 必須新增 libcutils 程式庫依附元件。
  • 需要裝置製造商的私人分支副本,其中包含 system/core/include/private/android_filesystem_config.h 與現有目標上的額外內容,以便移至 device/vendor/device/android_filesystem_config.h
  • 保留將 SELinux 強制存取控制 (MAC) 套用至目標系統設定檔的權利,而使用 fs_config() 納入自訂目標執行檔的實作必須確保存取權。