Android 权限与 UIDs/GIDs 映射机制详解

10次阅读
没有评论

问题描述

在阅读《Android 安全内部》这本书时,了解到应用的权限在安装时由包管理器分配给应用的 UID,且每个 APK 的 UID 与权限之间存在映射关系。但在后续章节中,提到如果应用获得了额外权限,则会将这些权限映射到 GIDs,并将这些 GIDs 分配为进程的附加 GIDs。以 android.permission.INTERNET 权限为例,任何获得该权限的应用进程都会关联到与 inet 组对应的附加 GID。因此,作者提出了疑问,为什么不能直接将权限与 APK 的 UID 映射起来,而必须将 inet GID 添加到应用包中。本文旨在解释这一机制。

解决方案

方案1

Android 的权限管理分为两大部分:Linux 内核和 Android 框架。其中,权限管理主要基于两种控制策略:自主访问控制(DAC)和强制访问控制(MAC)。

自主访问控制(DAC)

在早期的 UNIX 系统中,就已经引入了基于用户、组和访问模式的自主访问控制机制。每个文件(包括目录)和进程都有一个用户拥有者(UID)和一个组拥有者(GID)。进程对资源的读写执行权限取决于其访问模式位(RWX)对于用户、组和其他用户的设置。这些权限属性保存在文件系统的文件中。Android 为系统用途保留了 UID 范围 1000-9999,而 Linux 发行版通常从 1000 开始分配给普通用户。Android 设计为单用户系统,每个 Java 应用(无论是预安装的还是用户安装的)都被视为一个用户。Android 为这些应用保留了 UID 范围 10000-19999。在首次启动(对于预安装应用)或安装新应用时,会为应用分配一个唯一的 UID/GID,除非应用被卸载。这些应用与 UID 的映射可以在 /data/system/packages.list 文件或使用 id 命令查看(如果应用提供了 shell 或可以执行本地二进制文件)。

强制访问控制(MAC)

强制访问控制(MAC)是为了补充 DAC 而引入的,用于提高安全性。Android 使用 SELinux 作为其安全实施的一部分。SELinux 使用扩展属性(XATTR)为文件打上标签,并为每个进程运行 SELinux 上下文。然后定义包含成千上万条规则的策略,允许一个上下文访问另一个上下文,否则默认拒绝访问。更多细节请参见此回答

android.permission.INTERNET 权限与 GID 的映射

android.permission.INTERNET 权限特别地映射到了 Linux 内核中的 INET 组(GID 3003),使得具有该组成员资格的应用可以创建 PACKET 套接字,从而访问互联网。具体来说,Android 对 Linux 内核进行了特殊补丁(PARANOID_NETWORKING),将某些能力映射到特定的 GID(3000-3999),其中 INET 组(3003)允许其成员访问互联网。这些映射可以在 /system/etc/permissions/platform.xml 文件中找到。

因此,当应用获得了 android.permission.INTERNET 权限时,包管理器不会直接将权限与 APK 的 UID 映射起来,而是将应用添加到 INET 组,并保存相应的配置。例如,Termux 的配置如下所示:

~# grep termux /data/system/packages.list
com.termux 10142 0 /data/user/0/com.termux default:targetSdkVersion=28 3003

当应用启动时,它会具有 3003 作为附加组 ID,这样内核在执行 DAC 时就不会限制应用访问互联网:

~# ps -p $(pgrep com.termux) -o cmd,uid,gid,supgrp
CMD                           UID   GID SUPGRP
com.termux                     10142 10142 3003,9997,20142,50142

总结

通过这种方式,Android 可以利用 Linux 内核的自主访问控制和强制访问控制机制,以及文件能力(File Capabilities)和环境能力(Ambient Capabilities),为每个应用提供沙盒化的虚拟机环境,并严格限制其访问系统资源的能力。

正文完