Linux 及 Android Capabilities 概述

一、Linux Capabilites

1.1 Linux Capabilities 概述

为了执行权限检查,传统的 UNIX 将进程分为两类:特权进程(user ID为0,对应 root 用户)和非特权进程(UID 非零)。特权进程能绕过所有 kernel 权限检查,而非特权进程则根据进程的凭据(有效 UID、GID 和附加 group)接受完整的权限检查。以前作为普通用户,如果想执行一些只有管理员具备的权限操作,只有两种方法:一种是通过”sudo”提升权限,但用户太多时管理配置十分麻烦;另一种方法是通过 SUID(set-user-id on execution)让普通用于允许一个 owner 为 root 的可执行文件具备 root 权限。但是 SUID 机制存在一个安全风险,即通常只需要一部分特权,但如果使用 SUID 会授予 root 的全部权限,这样被攻击的安全风险就被增加了。于是,从 kernel 2.2 开始,Linux 将传统上与 root 用户相关的特权划分为不同的单元,称为功能(Capabilities),可以独立地被启用和禁用。Capabilities 是每个线程的属性。它可以按需授权,更安全。

1.1.1 Linux Capabilities 列表

下面列出了 Linux 上实现的 capabilities,以及每种 capablities 允许的操作或行为:

capablities Function
CAP_AUDIT_CONTROL 启用和禁用内核;更改审核过滤器规则;检索审核状态和过滤规则。
CAP_AUDIT_READ 允许通过 multicast netlink套接字读取审核日志。
CAP_AUDIT_WRITE 将记录写入内核日志。
CAP_BLOCK_SUSPEND 使用可以阻止系统挂起的功能(/proc/sys/wake_lock)。
CAP_BPF Linux 5.8 中添加了特权 BPF 操作 capability,以将 BPF capability 与过载的 CAP_SYS_ADMIN capability 区分开。
CAP_CHOWN 修改文件 UID 和 GID。
CAP_DAC_OVERRIDE 忽略文件读取、写入和执行权限检查。(DAC – Discretionary Access Control)
CAP_DAC_READ_SEARCH 忽略文件读取权限检查以及目录读取和执行权限检查;调用 open_by_handle_at;使用linkat AT_EMPTY_PATH 标志创建到文件描述符引用的文件的链接。
CAP_FOWNER 忽略权限检查操作,它通常需要进程的文件系统 UID 与文件的 UID 匹配(例如 chmod,utime),但 CAP_DAC_OVERRIDE 和 CAP_DAC_READ_SEARCH 覆盖的操作除外;在任意文件上设置 inode 标志;在任意文件上设置访问控制列表(ACL);在删除文件时忽略目录粘贴(sticky)位;修改任何用户拥有的 sticky 目录上的用户扩展属性;为 open 和 fcntl 中的任意文件指定 O_NOATIME。
CAP_FSETID 修改文件时,不清除 set-user-ID 和 set-group-ID 模式位。当 GID 与文件系统或调用过程的任何补充 GID 不匹配的文件时,设定 set-group-ID 位。
CAP_IPC_LOCK 锁定内存(mlock, mlockall, mmap, shmctl)。
CAP_IPC_OWNER 忽略对 System V IPC 对象的操作的权限检查。
CAP_KILL 忽略发送信号权限检查。这包括使用 ioctl KDSIGACCEPT 操作。
CAP_LEASE 在任意文件上建立租约。
CAP_LINUX_IMMUTABLE 设置 FS_APPEND_FL 和 FS_IMMUTABLE_FL inode 标志。
CAP_MAC_ADMIN 允许 MAC 配置或状态更改。为 Smack Linux Security Module(LSM)实现。
CAP_MAC_OVERRIDE 覆盖 Mandatory Access Control(MAC)。为 Smack LSM 实现。
CAP_MKNOD 使用 mknod 创建特殊文件。
CAP_NET_ADMIN 执行各种与网络相关的操作:接口配置;IP 防火墙的管理,伪装和计费;修改路由表;绑定到任何地址以进行透明代理;设置服务类型(TOS);清除驾驶员统计信息;设置混杂模式;启用多播;使用 setsockopt 设置以下套接字选项:SO_DEBUG,SO_MARK,SO_PRIORITY(优先级在0到6之外),SO_RCVBUFFORCE 和 SO_SNDBUFFORCE。
CAP_NET_BIND_SERVICE 将套接字绑定到 Internet 域特权端口(端口号小于1024)。
CAP_NET_BROADCAST (未使用)进行套接字广播,并收听 multicast。
CAP_NET_RAW 使用 RAW 和 PACKET 插槽;绑定到任何地址以进行透明代理。
CAP_PERFMON 采用各种性能监视机制,包括: 2个调用 perf_event_open;使用各种对性能有影响的 BPF 操作。Linux 5.8中添加了此功能,以将性能监视功能与过载的 CAP_SYS_ADMIN 功能区分开。
CAP_SETGID 任意处理进程 GID 和附加 GID 列表;通过 UNIX 域套接字传递套接字凭证时,伪造 GID;在用户名称空间中编写组 ID 映射。
CAP_SETFCAP 在文件上设置任意功能。
CAP_SETPCAP 从 Linux 2.6.24 开始支持该文件功能:从调用线程的边界集到其可继承集添加任何功能;从边界集中删除功能(通过 prctlPR_CAPBSET_DROP);更改 securebits 标志。
CAP_SETUID Linux 2.6.24之前的内核不支持该文件功能:向或从任何其他进程授予或删除呼叫者允许的功能集中的任何功能。(当将内核配置为支持文件功能时,CAP_SETPCAP 的此属性不可用,因为 CAP_SETPCAP 的语义完全不同。)对进程 UID(setuid,setreuid,setresuid,setfsuid)进行任意操作;通过UNIX域套接字传递套接字凭证时伪造UID;在用户名称空间中编写用户ID映射)。
CAP_SYS_ADMIN 执行一系列系统管理操作,包括:quotactl,mount,umount,pivot_root,swapon,swapoff,sethostname 和 setdomainname;执行特权的 syslog 操作(从Linux 2.6.37开始,应使用 CAP_SYSLOG允许此类操作);执行 VM86_REQUEST_IRQ vm86 命令;对任意 System V IPC 对象执行 IPC_SET和IPC_RMID 操作;覆盖 RLIMIT_NPROC 资源限制;对可信和安全扩展属性执行操作);使用lookup_dcookie;使用ioprio_set分配IOPRIO_CLASS_RT和(在Linux 2.6.25之前)IOPRIO_CLASS_IDLE I/O 调度类;通过 UNIX 域套接字传递套接字凭证时伪造 PID;超过 /proc/sys/fs/file-max,即系统范围内打开文件数量的限制,在打开文件的系统调用中(例如 accept,execve,open,pipe);使用 CLONE_ * 标志创建带有 clone 和 unshare 的新名称空间(但是,从 Linux 3.8开始,创建用户名称空间不需要任何功能);采用各种性能监视机制(对于 CAP_PERFMON);访问特权性能事件信息;调用 setns(在目标名称空间中需要 CAP_SYS_ADMIN);调用 fanotify_init;执行各种 BPF 操作;见 CAP_BPF;执行特权 KEYCTL_CHOWN 和 KEYCTL_SETPERM keyctl 操作;执行 madvise MADV_HWPOISON 操作;使用 TIOCSTI ioctl 将字符插入到调用者控制终端以外的终端的输入队列中;使用过时的 nfsservctl系统调用;使用过时的 bdflush 系统调用;执行各种特权块设备 ioctl 操作;执行各种特权文件系统 ioctl 操作;在 /dev/random 设备上执行特权 ioctl 操作);安装seccomp过滤器,而无需首先设置no_new_privs线程属性;修改设备控制组的允许/拒绝规则;使用 ptrace PTRACE_SECCOMP_GET_FILTER 操作来转储 Tracee 的 seccomp 过滤器;使用 ptrace PTRACE_SETOPTIONS 操作来挂起跟踪的 seccomp 保护(即 PTRACE_O_SUSPEND_SECCOMP 标志);在许多设备驱动程序上执行管理操作。通过写入 /proc/[pid]/autogroup 来修改 autogroup nice 值)。
CAP_SYS_BOOT 使用 reboot 和 kexec_load。
CAP_SYS_CHROOT 使用 chroot;使用 setns 更改安装名称空间。
CAP_SYS_MODULE 加载和卸载内核模块;在2.6.25之前的内核中:从系统范围的功能边界集中删除功能。
CAP_SYS_NICE 降低进程的 nice 值(nice,setpriority)并为任意进程更改 nice 值;为调用进程设置实时调度策略,并为任意进程设置调度策略和优先级(sched_setscheduler,sched_setparam,sched_setattr);为任意进程设置CPU关联性(sched_setaffinity);为任意进程设置 I/O 调度类和优先级(ioprio_set);将 migration_pages 应用到任意进程,并允许将进程迁移到任意节点;将 move_pages 应用于任意进程;在 mbind 和 move_pages 中使用 MPOL_MF_MOVE_ALL 标志。
CAP_SYS_PACCT 使用acct。
CAP_SYS_PTRACE 使用 ptrace 跟踪任意进程;将 get_robust_list 应用于任意进程;使用 process_vm_readv 和 process_vm_writev 在任意进程的内存之间传输数据;使用 kcmp 检查进程。
CAP_SYS_RAWIO 执行 I/O 端口操作(iopl和 ioperm);访问 /proc/kcore;采用 FIBMAP ioctl 操作;打开设备以访问特定于 x86 模型的寄存器);更新 /proc/sys/vm/mmap_min_addr;在低于 /proc/sys/vm/mmap_min_addr 指定的值的地址处创建内存映射;在 /proc/bus/pci 中映射文件;打开 /dev/mem 和 /dev/kmem;执行各种 SCSI 设备命令;在 hpsa(4) 和 cciss(4) 设备上执行某些操作;在其他设备上执行一系列特定于设备的操作。
CAP_SYS_RESOURCE 在 ext2 文件系统上使用保留空间;进行 ioctl 调用以控制 ext3 日记记录;覆盖磁盘配额限制;增加资源限制(参阅setrlimit);覆盖 RLIMIT_NPROC 资源限制;在控制台分配中覆盖最大控制台数;覆盖最大按键映射数;允许来自实时时钟的超过64hz的中断;将 System V 消息队列的 msg_qbytes 限制提高到 /proc/sys/kernel/msgmnb 中的限制;当通过UNIX域套接字将文件描述符传递到另一个进程时,允许绕过“运行中”文件描述符数量的RLIMIT_NOFILE资源限制);使用 F_SETPIPE_SZ fcntl 命令设置管道的容量时,请覆盖/ proc / sys / fs / pipe-size-max限制;使用 F_SETPIPE_SZ 将管道的容量增加到超过 /proc/sys/fs/pipe-max-size 指定的限制;创建 POSIX 消息队列时,请覆盖 /proc/sys/fs/mqueue/queues_max,/proc/sys/fs/mqueue/msg_max 和 /proc/sys/fs/mqueue/msgsize_max 限制;使用 prctl PR_SET_MM 操作;将 /proc/[pid] / oom_score_adj 设置为小于使用 CAP_SYS_RESOURCE 的进程最后设置的值。
CAP_SYS_TIME 设置系统时钟(settimeofday,stime,adjtimex);设置实时(硬件)时钟。
CAP_SYS_TTY_CONFIG 使用 vhangup;在虚拟终端上使用各种特权的 ioctl 操作。
CAP_SYSLOG 执行特权的 syslog 操作。当 /proc/sys/kernel/kptr_restrict 的值为1时,通过 /proc 和其他接口查看公开的内核地址。)
CAP_WAKE_ALARM 触发一些将唤醒系统的事件(设置CLOCK_REALTIME_ALARM和CLOCK_BOOTTIME_ALARM计时器)。
1.1.2 支持功能及注意事项

Linux Capabilities 支持的功能:

(1)对于所有特权操作,kernel 必须检查线程在其有效集中是否具有所需的 capabilities 。

(2)kernel 必须提供系统调用,以允许更改和检索线程的 capabilities 集。

(3)文件系统必须支持将 capabilities 附加到可执行文件,以便在执行文件时进程获得这些 capabilities 。

(4)在kernel 2.6.24之前,仅满足前两个要求;从kernel 2.6.24开始,所有这三个要求都得到满足。

添加由 capabilities 控制的新 kernel 功能时,需注意以下几点:

(1)capabilities 的目标是将 root 用户的权限分成多个部分,这样,如果具有一个或多个 capabilities 的程序受到损害,则其破坏系统的能力将小于具有 root 特权的同一程序。

(2)可以选择是为新功能创建新 capabilities ,还是将功能与现有 capabilities 之一相关联。为了使 capabilities 集保持在可管理的大小,后一种选择是可取的,除非有充分的理由采用前一种选择。 (还有一个技术限制: capabilities 集的大小当前限制为64位。)

(3)要确定哪些现有 capabilities 最可能与新功能相关联,需查看上述 capabilities 列表,以找到最适合新功能的“silo”。采取的一种方法是确定是否还有其他功能需要与新功能一起使用的 capabilities 。如果没有这些其他功能,新功能将无用,则应使用与其他 capabilities 相同的功能。

(4)如果可以避免,不要选择 CAP_SYS_ADMIN!现有 capabilities 的大部分检查都与此 capabilities 相关联(参阅上面的部分列表)。它可以合理地称为“新 root”,因为一方面它赋予了广泛的权力,另一方面,它的广泛范围意味着这是许多特权程序所需要的 capabilities 。不要让问题变得更糟。应该与 CAP_SYS_ADMIN 关联的唯一新功能是那些与该 silo 中的现有用途紧密匹配的功能。

(5)如果确定确实有必要为功能创建新 capabilities ,不要将其创建或命名为“一次性使用” capabilities 。因此,例如,添加高度特定的 CAP_SYS_PACCT 可能是一个错误。取而代之的是,尝试将您的新功能标识为更广泛的筒仓并将其命名为其他相关的将来用例可能适合的筒仓。

1.2 线程和文件 capabilities
1.2.1 线程 capabilities:

每个线程支持下面5个capabilites集,每个 capalibities 集使用64位掩码(十六进制)表示。

capablities Function
Permitted 线程可使用的 capabilities 的上限。可以由 effective 集不包含 CAP_SETPCAP capability 的线程添加到 inheritable 集中。如果线程从 permitted 集中删除了一项 capability,则它将无法重新获得该 capability(除非它执行一个 set-user-ID-root 程序,或相关文件 capabilities 授予该 capability 的程序)。
Inheritable 在执行 exec() 时保留的一组 capabilities。当执行任何程序时,inheritable capabilities 保持可继承的状态,当执行在文件可继承的集合中具有相应位设置的程序时,inheritable capabilites 会添加到 permitted 集中。因为当以非 root 用户身份运行时,inheritable capabilities 通常不会跨执行保留,因此希望运行具有增强 capabilities 的帮助程序的应用程序应考虑使用下面的 ambient capabilities。
Effective 内核用于对线程执行权限检查的 capabilities 集。
Bounding capability bounding 集是一种可用于限制执行期间获得的 capabilities 的机制。从Linux 2.6.25开始,这是每个线程的 capability 集。在较早的内核中,capability 边界集是系统上所有线程共享的系统范围属性。
Ambient 这是在非特权程序执行 exec() 时保留的一组 capabilities。ambient capabilities 集服从不变性,即 permitted 和 inheritable 未设定的 capabilities,ambient 也不会设定。可以使用 prctl 直接修改 ambient capabilities 集。如果 permitted 或 interitable capabilities 中的任何一个被降低(关闭),则 ambient capabilities 也会自动降低(关闭)。执行由 set-user-ID或 set-group-ID 位而更改 UID 或 GID 的程序,或者执行具有任何文件 capabilities 集的程序,将会清除环境设置。当调用 exec() 时,ambient capabilities 会被添加到 permitted 集中并分配给 effective 集。 如果 ambient capabilities 导致在执行 exec() 期间进程的 permitted 和 effective capabilities 增加,则这不会触发 ld.so 中描述的安全执行模式。

通过 fork 创建的子代继承其父代 capabilities 集的副本。使用 capset,线程可以操纵其自身的 capabilites 集。从 Linux 3.2 开始,文件 /proc/sys/kernel/cap_last_cap 公开了正在运行的内核所支持的最高性能的数值。这可用于确定 capabilities 集中可能设置的最高位。

1.2.2 文件 capabilites:

从 kernel 2.6.24 开始,kernel 支持使用 setcap 将 capabilites 集与可执行文件相关联。文件 capabilites 集存储在名为 security.capability 的扩展属性中。写入此扩展属性需要 CAP_SETFCAP capability。文件 capabilites 集与线程的 capabilites 集一起确定执行 exec() 之后的线程的capabilites 。三个文件 capabilites 集是:

capablities Function
Permitted 无论线程的 inheritable capabilites 如何,这些 capabilites 都会自动授予该线程。
Inheritable 该集合与线程的 inheritable 集进行”与”运算,以确定在执行 exec() 之后的线程的 permitted 集中启用了哪些inheritable capabilites。
Effective 这不是一个集合,而是一个位。如果设置了此位,则在执行 exec() 期间,还将在 effective 集中提高线程的所有新允许 capabilites。如果未设置此位,则在执行 exec() 之后,新的 effective capabilites 中都没有新的 permitted capabilites。启用文件 effective capabilites 位意味着,导致线程在执行 exec() 期间获取相应的 permittied capabilites 的任何文件 permitted 或 inheritable capabilites 也将在其有效集中获取该 capabilites。 因此,在将 capabilites 分配给文件时,如果将 effective 标志指定为对任何 capabilites 启用,则还必须将 effective 标志指定为对所有 capabilites 均启用。
1.3 Ambient capabilities

capabilities允许 Linux 进程舍弃大多数类似于 root 的权限,同时保留执行其功能所需的部分权限。capabilities的原始实现使得经过创建分支和执行的进程无法继承 capabilities ,除非已经为正在执行的文件配置了文件 capabilities 。文件 capabilities 反过来又会带来安全风险,因为任何进程只要执行具有文件 capabilities 的文件,就能够获得这些 capabilities 。

二、Android Capabilites

2.1 Android Capabilities 概述

Android 7.x及更低版本通过使用特定于设备的 android_filesystem_config.h 文件来指定文件系统 capabilities 和自定义设备制造商 AID 来扩展Android ID(AID)机制。

Android 8.0及更高版本支持扩展文件系统 capabilities 的新方法。

Ambient capabilities 允许由 init 启动的系统服务在其 .rc 文件中配置 capabilities ,从而将配置放入单个文件中,而不是在 fs_config.c 文件中拆分配置。这意味着,对于由 init 启动的任何服务,都可以使用与服务关联的 .rc 文件为该服务配置 capabilities 。

Ambient capabilities 是为由 init 启动的服务设置 capabilities 的首选机制。建议使用 Ambient capabilities,而不是在 config.fs 文件中使用 caps 部分配置文件系统 capabilities 。在为并非由 init 启动的服务设置 capabilities 时,请继续使用 fs_config.c 来配置文件系统 capabilities 。如需为给定的服务启用 Ambient capabilities,需在 init 中使用 capabilities 关键字。例如 wificond 服务:

1
2
3
4
5
service wificond /system/bin/wificond
class main
user wifi
group wifi net_raw net_admin
capabilities NET_RAW NET_ADMIN
2.2 自主访问控制 DAC

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

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

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

(1)配置文件可以有多个源位置(支持可扩展的编译环境配置)。
(2)在编译时对 OEM AID 值进行健全性检查。
(3)生成需要在源文件中使用的自定义 OEM AID 头。
(4)将好记的名称与实际的 OEM AID 值相关联。支持为用户和群组指定非数字的字符串参数,即”foo”代替”2901”。

其他改进包括从 system/core/include/private/android_filesystem_config.h 中移除了 android_ids[] 数组。此数组现在作为完全私自生成的数组存在于 Bionic 中,访问函数可通过 getpwnam() 和 getgrnam() 获取此数组中的数据。具体可查阅 build/make/tools/fs_config 中的 README 文件。

2.2.1 添加 AID

Android 8.0 移除了 android_ids[] 数组。所有好记的 AID 名称都改为在生成 Bionic android_ids[] 数组时从 system/core/include/private/android_filesystem_config.h 头文件生成。这种机制会发现与 AID_ 匹配的所有 define,且 会变为小写名称。例如,在 private/android_filesystem_config.h 中:

1
#define AID_SYSTEM 1000

会变为:

1
2
3
name:system
uid:1000
gid:1000

要添加新的 AOSP 核心 AID,只需将 #define 添加到 android_filesystem_config.h 头文件中即可。AID 将在编译时生成,并会提供给使用用户和群组参数的接口。这种机制会验证新的 AID 是否不在 APP 或 OEM 范围内;它还会遵循对这些范围的更改,并且应根据更改或新的 OEM 保留范围自动重新配置。

2.2.2 配置 AID

如需启用新的 AID 机制,需在 BoardConfig.mk 文件中设置 TARGET_FS_CONFIG_GEN。此变量存储的是配置文件列表,然后可以根据需要附加文件。

注:不要将 TARGET_FS_CONFIG_GEN 与 TARGET_ANDROID_FILESYSTEM_CONFIG_H 方法一起使用,否则会报错。

一般配置文件使用名称 config.fs,但实际上可以使用任意名称。config.fs 文件采用 Python ConfigParser ini 格式,并包含 caps 部分(用于配置文件系统 capabilities)和 AID 部分(用于配置 OEM AID)。

(1)配置 caps 部分

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

部分 定义
[path] - 要配置的文件系统路径。以/结尾的路径被视为目录,否则将被视为文件。在不同文件中使用同一 [path] 指定多个部分的做法是错误的。在 Python 3.2之前的版本中,同一文件中包含的某些部分可替换它之前的部分;而在 Python 3.2中,系统设置了严格模式。
mode 八进制文件模式 至少为3位数的有效八进制文件模式。如果指定3,则会附上前缀0,否则系统会按原样使用模式。
user AID_ 有效 AID 的 C 样式的 define 或好记的名称。
group AID_ 和用户一样。
caps cap* system/core/include/private/android_filesystem_capability.h 中声明的名称,不含前导 CAP_。允许大小写混用。caps 条目也可以是原始值:二进制 (0b0101),八进制 (0455),整型 (42),hex (0xFF)可以使用空格分隔多个 caps 条目。

(2)配置 AID 部分

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

部分 定义
[AID_name] name 可以包含大写字母、数字和下划线字符。小写版本用作好记的名称。生成的代码收录头文件使用确切的 AID_。使用同一 AID_ 指定多个部分(不区分大小写,约束条件与 [path] 相同)的做法是错误的。 必须以分区名称开头,以确保不会与其他来源发生冲突。
value number 有效的 C 样式的数字字符串(十六进制、八进制、二进制和十进制)。使用同一值选项指定多个部分的做法是错误的。值选项必须在 所使用的分区对应的范围内指定。有效分区及其对应范围的列表在 system/core/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 名称 ([AID_name]) 必须以分区名称(如”vendor_”)开头,以确保不会与将来的 AOSP 名称或其他分区发生冲突。

(3)定义 OEM AID 名称

如需定义 OEM AID,需创建一个 config.fs 文件并设置 AID 值。例如在 device/x/y/config.fs 中设置以下内容:

1
2
[AID_VENDOR_FOO]
value: 2900

创建文件后,设置 TARGET_FS_CONFIG_GEN 变量并在 BoardConfig.mk 中指向它。例如在 device/x/y/BoardConfig.mk 中设置以下内容:

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

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

2.2.3 使用 OEM AID

如需使用 OEM AID,需在 C 代码中,将 oemaids_headers 添加到关联的 Makefile 中,再添加 #include “generated_oem_aid.h”,然后开始使用声明的标识符。例如,在 my_file.c 中添加以下内容:

1
2
3
4
5
6
#include "generated_oem_aid.h"


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

在关联的 Android.bp 文件中,添加以下内容:

1
header_libs: ["oemaids_headers"],

如果使用的是 Android.mk 文件,添加以下内容:

1
LOCAL_HEADER_LIBRARIES := oemaids_headers

在 Android 9 中,可以对支持 AID 名称的任何接口使用好记的名称。例如:

在 some/init.rc 中的 chown 命令中:

1
chown vendor_foo /vendor/some/vendor_foo/file

在 some/init.rc 中的 service 中:

1
2
3
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 值相关联。可以为用户和群组使用非数字的字符串参数,即用”vendor_foo”替换”2901”。

2.2.4 从 AID 转换为好记的名称

对于 OEM AID,Android 8.x 要求将 oem_#### 与 getpwnam 和类似函数一起使用,在通过 getpwnam 处理查询时也是如此。在 Android 9 中,可以使用 Bionic 中的 getpwnam 和 getgrnam 这对组合函数从 AID 转换为好记的名称,反之亦然。

如需启用文件系统 capabilities,需在 config.fs 文件中创建一个 caps 部分。例如,在 device/x/y/config.fs 中添加以下部分:

1
2
3
4
5
[system/bin/foo_service]
mode: 0555
user: AID_VENDOR_FOO
group: AID_SYSTEM
caps: SYS_ADMIN | SYS_NICE

注:此处也可以使用好记的名称 vendor_foo 和 system。

创建文件后,在 BoardConfig.mk 中设置 TARGET_FS_CONFIG_GEN 以指向该文件。例如在 device/x/y/BoardConfig.mk 中设置以下内容:

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

执行 vendor_foo 服务时,会先使用 CAP_SYS_ADMIN 和 CAP_SYS_NICE capabilities,而不使用 setuid 和 setgid 调用。此外,vendor_foo 服务的 SELinux 政策不再需要 setuid 和 setgid capabilities,因此可以删除这两项capabilities。

2.2.5 配置替换(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 格式,并对目录和文件符号进行以下结构初始化:

(1)对于目录,使用 android_device_dirs[]。
(2)对于文件,使用 android_device_files[]。

在不使用 android_device_dirs[] 和 android_device_files[] 时,可以定义 NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS 和 NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_FILES。还可以在 boardconfig 中使用 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。如果此文件位于其他位置,需设置 boardconfig 变量 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 目录中添加唤醒锁定支持。请注意以下几点:

(1)每个结构条目都包含 mode、uid、gid、capabilities 和 name。自动包含了 system/core/include/private/android_filesystem_config.h 来提供清单 #defines(AID_ROOT、AID_SHELL 和 CAP_BLOCK_SUSPEND)。
(2)android_device_files[] 部分包含在未指定时禁止访问 system/etc/fs_config_dirs 的操作,其作用是在缺少目录替换内容时提供额外的 DAC 保护。但此保护的强度较弱;如果有人拥有对 /system 的控制权,那么他们通常可以执行所需的任何操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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 @@
+#define NO_ANDROID_FILESYSTEM_CONFIG_DEVICE_DIRS
+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)
2.2.6 从早期版本迁移文件系统

从 Android 5.x 及更低版本移植文件系统时,需注意:

(1)Android 6.x 移除了部分头文件、结构和内嵌定义。
(2)Android 6.x 需要引用 libcutils,而不是直接从 system/core/include/private/android_filesystem_config.h 运行。依赖于 system/code/include/private_filesystem_config.h(对于文件或目录结构)或 fs_config 的设备制造商专用可执行文件必须添加 libcutils 库依赖项。
(3)Android 6.x 需要 system/core/include/private/android_filesystem_config.h 的设备制造商专用分支副本,并且需要将现有目标上的额外内容移至 device/vendor/device/android_filesystem_config.h。
(4)由于 Android 6.x 保留将 SELinux 强制访问控制 (MAC) 应用于目标系统中的配置文件的权利,因此如果实现包含使用 fs_config() 的自定义目标可执行文件,就必须确保具有访问权限。