自主访问控制 (DAC)

添加到 build 中的文件系统对象和服务往往需要单独的唯一 ID,称为 Android ID (AID)。目前,文件和服务等许多资源不必要地使用了由 Android 定义的核心 AID;在许多情况下,您可以改用由 OEM 定义的 OEM AID。

较低版本的 Android(Android 7.x 及更低版本)使用设备专属的 android_filesystem_config.h 文件指定文件系统权能和/或自定义 OEM AID,从而扩展了 AID 机制。但是,此机制不够直观,因为它不支持 OEM AID 使用好记的名称,而是要求您为用户和群组字段指定原始数字,这样一来,便无法将好记的名称与数字 AID 关联起来。

较高版本的 Android(Android 8.0 及更高版本)支持采取一种扩展文件系统权能的新方法。这种新方法支持:

  • 配置文件可以有多个源位置(支持可扩展的 build 配置)。
  • 在编译时对 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[] 数组。所有好记的 AID 名称都改为在生成 Bionic android_ids[] 数组时从 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 部分

caps 部分支持在 build 中对文件系统对象设置文件系统权能(文件系统本身也必须支持此功能)。

由于在 Android 中以 root 身份运行稳定的服务会导致无法通过兼容性测试套件 (CTS) 测试,因此之前有关在运行进程或服务时必须保留权能的要求中规定,必须先设置权能,然后再使用 setuid/setgid 设置适当的 AID。借助 caps 部分,您可以跳过这些要求,让内核为您代劳。当控制权交给 main() 时,您的进程已拥有其所需的权能,因此您的服务可以使用非 root 用户和群组(这是启动特权服务的首选方式)。

caps 部分使用以下语法:

部分 定义
[path] 要配置的文件系统路径。以 / 结尾的路径被视为目录,否则将被视为文件。

在不同文件中使用同一 [path] 指定多个部分的做法是错误的。在 Python 3.2 之前的版本中,同一文件中包含的某些部分可替换它之前的部分;而在 Python 3.2 中,系统设置了严格模式。
mode 八进制文件模式 至少为 3 位数的有效八进制文件模式。如果指定 3,则会附上前缀 0,否则系统会按原样使用模式。
user AID_<user> 有效 AID 的 C 样式的 define 或好记的名称(例如 AID_RADIOradio 皆可)。如需定义自定义 AID,请参阅配置 AID 部分
group AID_<group> 和用户一样。
caps cap* bionic/libc/kernel/uapi/linux/capability.h 中声明的名称,不含前导 CAP_。允许大小写混用。caps 条目也可以是原始值:
  • 二进制 (0b0101)
  • 八进制 (0455)
  • 整型 (42)
  • hex (0xFF)
可以使用空格分隔多个 caps 条目。

如需查看用法示例,请参阅使用文件系统权能

配置 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

总的来说,现在系统已经可以在新 build 中使用自定义 AID 了。

使用 OEM AID

如需使用 OEM AID,请在 C 代码中,将 oemaids_headers 添加到关联的 Makefile 中,再添加 #include "generated_oem_aid.h",然后开始使用声明的标识符。例如,在 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.rc 中的 chown 命令中:
    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
    

由于从好记的名称到 uid 的内部映射由 /vendor/etc/passwd/vendor/etc/group 执行,因此必须装载供应商分区。

关联好记的名称

Android 9 支持将好记的名称与实际 OEM AID 值相关联。您可以为用户和群组使用非数字的字符串参数,即“vendor_foo”而不是“2901”。

从 AID 转换为好记的名称

对于 OEM AID,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

创建文件后,在 BoardConfig.mk 中设置 TARGET_FS_CONFIG_GEN 以指向该文件。例如,在 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 中的二进制文件更新或替换。针对目录和文件分别采用单独的匹配和解析规则(可能会使用其他全局表达式),这样一来,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 要求放入一个缓冲区内,并为 include 文件定义规则,以规定 DAC 规则的用法。

如需使用该工具,请在 device/vendor/device/android_filesystem_config.h 中创建用作替换文件的 include 文件。该文件必须采用 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 的替换文件。

包含替换文件

如需包含文件,请确保 PRODUCT_PACKAGES 包含 fs_config_dirs 和/或 fs_config_files,以便它可以分别将二者安装到 /system/etc/fs_config_dirs/system/etc/fs_config_files。构建系统会在 BoardConfig.mk 所在的 $(TARGET_DEVICE_DIR) 中搜索自定义 android_filesystem_config.h。如果此文件位于其他位置,请设置板级配置变量 TARGET_ANDROID_FILESYSTEM_CONFIG_H 来指向该位置。

配置文件系统

如需在 Android 6.0 及更高版本中配置文件系统,请执行以下操作:

  1. 创建 $(TARGET_DEVICE_DIR)/android_filesystem_config.h 文件。
  2. 在板级配置文件(例如 $(TARGET_DEVICE_DIR)/device.mk)中将 fs_config_dirs 和/或 fs_config_files 添加到 PRODUCT_PACKAGES

替换示例

以下示例展示了一个补丁程序,该补丁程序会替换 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 filesystem
+** 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 移除了部分头文件、结构和内嵌定义。
  • Android 6.x 需要引用 libcutils,而不是直接从 system/core/include/private/android_filesystem_config.h 运行。依赖于 system/code/include/private_filesystem_config.h(对于文件或目录结构)或 fs_config 的设备制造商专用可执行文件必须添加 libcutils 库依赖项。
  • Android 6.x 需要 system/core/include/private/android_filesystem_config.h 的设备制造商专用分支副本,并且需要将现有目标上的额外内容移至 device/vendor/device/android_filesystem_config.h
  • 由于 Android 6.x 保留将 SELinux 强制访问控制 (MAC) 应用于目标系统中的配置文件的权利,因此如果实现包含使用 fs_config() 的自定义目标可执行文件,就必须确保具有访问权限。