自主访问控制 (DAC)

频繁添加到 build 的文件系统对象和服务需要单独的唯一 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 已被修改。)如需了解更多详情,请参阅 build/make/tools/fs_config 中的工具和 README 文件。

添加 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 部分支持在构建中的文件系统对象上设置 文件系统功能(文件系统本身也必须支持此功能)。

由于在 Android 中以 root 身份运行稳定服务会导致 兼容性测试套件 (CTS) 失败,因此之前在运行进程或服务时保留 capability 的要求涉及到设置 capability,然后使用 setuid/setgid 切换到适当的 AID 来运行。使用 capability,您可以跳过这些要求,并让内核为您完成。当控制权交给 main() 时,您的进程已经拥有所需的 capability,因此您的服务可以使用非 root 用户和组(这是启动特权服务的首选方式)。

caps 部分使用以下语法

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

在不同的文件中指定具有相同 [path] 的多个节是错误的。在 Python 版本 <= 3.2 中,同一文件可能包含覆盖前一节的节;在 Python 3.2 中,它设置为严格模式。
模式 八进制文件模式 至少 3 位数字的有效八进制文件模式。如果指定了 3 位数字,则在前面加上 0,否则按原样使用模式。
用户 AID_<user> 有效的 AID 的 C define 或友好的名称(例如,AID_RADIOradio 都是可接受的)。要定义自定义 AID,请参阅配置 AID 部分
AID_<group> 与用户相同。
caps cap* bionic/libc/kernel/uapi/linux/capability.h 中声明的名称,不带前导 CAP_。允许混合大小写。Caps 也可以是原始的
  • 二进制 (0b0101)
  • 八进制 (0455)
  • 整数 (42)
  • 十六进制 (0xFF)
使用空格分隔多个 cap。

有关用法示例,请参阅使用文件系统 capability

配置 AID 部分

AID 部分包含 OEM AID,并使用以下语法

定义
[AID_<name>] <name> 可以包含大写字母、数字和下划线。小写版本用作友好的名称。为代码包含生成的头文件使用精确的 AID_<name>

指定具有相同 AID_<name> 的多个节是错误的(不区分大小写,约束条件与 [path] 相同)。

<name> 必须以分区名称开头,以确保它不与不同的来源冲突。
<number> 有效的 C 样式数字字符串(十六进制、八进制、二进制和十进制)。

指定具有相同 value 选项的多个节是错误的。

Value 选项必须在与 <name> 中使用的分区对应的范围内指定。有效分区及其对应范围的列表在 system/core/libcutils/include/private/android_filesystem_config.h 中定义。选项包括
  • Vendor 分区
    • AID_OEM_RESERVED_START(2900) - AID_OEM_RESERVED_END(2999)
    • AID_OEM_RESERVED_2_START(5000) - AID_OEM_RESERVED_2_END(5999)
  • System 分区
    • AID_SYSTEM_RESERVED_START(6000) - AID_SYSTEM_RESERVED_END(6499)
  • ODM 分区
    • AID_ODM_RESERVED_START(6500) - AID_ODM_RESERVED_END(6999)
  • Product 分区
    • 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 以及如何启用文件系统 capability。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 AID

要在您的 C 代码中使用 OEM AID,请在关联的 Makefile 中包含 oemaids_headers,并添加 #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 执行,因此必须挂载 vendor 分区。

关联友好的名称

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

从 AID 转换为友好的名称

对于 OEM AID,Android 8.x 需要将 oem_####getpwnam 和类似函数一起使用,以及在使用 getpwnam 处理查找的位置(例如 init 脚本)。在 Android 9 中,您可以使用 Bionic 中的 getpwnamgetgrnam 函数在 Android ID (AID) 和友好的名称之间进行转换,反之亦然。

使用文件系统 capability

要启用文件系统 capability,请在 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 执行时,它会在没有 setuidsetgid 调用的情况下以 capability CAP_SYS_ADMINCAP_SYS_NICE 启动。此外,vendor_foo 服务的 SELinux 策略不再需要 capability 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

包含覆盖文件

要包含文件,请确保 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. fs_config_dirs 和/或 fs_config_files 添加到板配置文件(例如,$(TARGET_DEVICE_DIR)/device.mk)中的 PRODUCT_PACKAGES

覆盖示例

此示例显示了一个补丁,用于覆盖 system/bin/glgps 守护程序,以在 device/vendor/device 目录中添加唤醒锁定支持。请记住以下几点

  • 每个结构条目都是模式、uid、gid、capability 和名称。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
+ *
+ *      https://apache.ac.cn/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() 的自定义目标可执行文件的实现必须确保访问。