进程Capability设置
Linux capability 不是一个整数开关。Zygote specialization 同时处理 permitted、effective、inheritable 和 bounding set,并且必须在 UID 切换前完成关键准备。SystemServer 的 capability 候选由 forkSystemServer() 提供;普通应用请求不能直接指定 capability;native SpecializeCommon() 先保持跨 UID 能力、设置 inheritable、裁剪 bounding set,再在接近最终身份时写入 permitted/effective/inheritable。
1. SystemServer能力
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
long capabilities =
(1L << OsConstants.CAP_IPC_LOCK) |
(1L << OsConstants.CAP_KILL) |
(1L << OsConstants.CAP_NET_ADMIN) |
(1L << OsConstants.CAP_NET_BIND_SERVICE) |
(1L << OsConstants.CAP_NET_BROADCAST) |
(1L << OsConstants.CAP_NET_RAW) |
(1L << OsConstants.CAP_SYS_NICE) |
(1L << OsConstants.CAP_SYS_PTRACE) |
(1L << OsConstants.CAP_SYS_TIME) |
(1L << OsConstants.CAP_SYS_TTY_CONFIG) |
(1L << OsConstants.CAP_WAKE_ALARM) |
(1L << OsConstants.CAP_BLOCK_SUSPEND) |
(1L << OsConstants.CAP_MAC_ADMIN);
StructCapUserData[] data = Os.capget(header);
capabilities &= Integer.toUnsignedLong(data[0].effective)
| (Integer.toUnsignedLong(data[1].effective) << 32);SystemServer 先列出需要的 capability,再与当前 Zygote effective set 取交集。容器或启动环境已删除的能力不会凭参数重新出现;capget() 失败会阻止 fork。
2. 四组Capability
- permitted:进程允许重新获得的能力上限;
- effective:当前实际参与权限检查的能力;
- inheritable:exec/子进程继承关系中的能力集合;
- bounding:后续 permitted/effective 能力不允许超过的全局上限。
SetCapabilities(permitted, effective, inheritable) 只写前三组;bounding 通过 prctl(PR_CAPBSET_DROP) 逐项删除。bounding 一旦删除不可在该进程中恢复,因此必须在确认目标策略后执行。
3. Keepcaps与继承
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static void EnableKeepCapabilities(
fail_fn_t fail_fn) {
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
fail_fn(CREATE_ERROR(
"prctl(PR_SET_KEEPCAPS) failed: %s",
strerror(errno)));
}
}
static void SetInheritable(
uint64_t inheritable, fail_fn_t fail_fn) {
__user_cap_header_struct header = {};
header.version = _LINUX_CAPABILITY_VERSION_3;
__user_cap_data_struct data[2];
if (capget(&header, &data[0]) == -1) {
fail_fn(CREATE_ERROR("capget failed"));
}
data[0].inheritable = inheritable;
data[1].inheritable = inheritable >> 32;
if (capset(&header, &data[0]) == -1) {
fail_fn(CREATE_ERROR("capset inheritable failed"));
}
}目标 uid 非 root 时,PR_SET_KEEPCAPS 防止 setuid 清空 permitted;inheritable 则建立 exec/后续派生所需的继承集合。两者属于 UID 切换前的准备,不能放到 setresuid() 之后。
4. BoundingSet裁剪
static void DropCapabilitiesBoundingSet(
fail_fn_t fail_fn,
jlong bounding_capabilities) {
for (int i = 0;
prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0;
i++) {
if ((1LL << i) & bounding_capabilities) {
continue;
}
if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
if (errno == EINVAL) {
ALOGE("kernel lacks file capabilities support");
} else {
fail_fn(CREATE_ERROR(
"PR_CAPBSET_DROP failed: %s",
strerror(errno)));
}
}
}
}循环读取内核支持的 capability 编号,保留目标 bounding mask 中的 bit,删除其他 bit。EINVAL 会记录内核能力支持问题;其他错误终止 specialization。bounding mask 不是 effective mask,删除它不会立即改变当前 effective,但会限制后续获取。
5. 最终capset
static void SetCapabilities(
uint64_t permitted,
uint64_t effective,
uint64_t inheritable,
fail_fn_t fail_fn) {
__user_cap_header_struct header = {};
header.version = _LINUX_CAPABILITY_VERSION_3;
__user_cap_data_struct data[2] = {};
data[0].effective = effective;
data[1].effective = effective >> 32;
data[0].permitted = permitted;
data[1].permitted = permitted >> 32;
data[0].inheritable = inheritable;
data[1].inheritable = inheritable >> 32;
if (capset(&header, &data[0]) == -1) {
fail_fn(CREATE_ERROR("capset final set failed"));
}
}最终写入要求 permitted/effective 不超过 bounding 和内核允许范围。capset() 失败时 fail_fn 终止子进程,不能继续执行 Java main;否则进程可能以部分 capability 运行,造成权限和功能状态不一致。
6. 特化顺序
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
permitted_capabilities |= bounding_capabilities;
if (uid != 0) {
EnableKeepCapabilities(fail_fn);
}
SetInheritable(permitted_capabilities, fail_fn);
DropCapabilitiesBoundingSet(
fail_fn, bounding_capabilities);
SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
if (setresgid(gid, gid, gid) == -1) {
fail_fn(CREATE_ERROR("setresgid failed"));
}
SetUpSeccompFilter(uid, is_child_zygote);
SetSchedulerPolicy(fail_fn, is_top_app);
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid failed"));
}
SetCapabilities(permitted_capabilities,
effective_capabilities,
permitted_capabilities, fail_fn);顺序的核心是:先准备能力继承与 bounding,再设置组/rlimit,趁仍有特权装 seccomp 和调度策略,最后切换 uid,并写入最终 capability。文章不把 permitted_capabilities |= bounding_capabilities 解释为“给进程增加任意能力”:前面的 Java/Native policy 和当前 bounding 共同限制最终结果。
7. 角色差异
SystemServer capability 候选来自 forkSystemServer(),并在 native 中以 is_system_server=true 走专用 post-fork hook;应用 capability 通常为零,普通客户端不能通过 Zygote 命令自由指定。app zygote/child zygote 还会清理 inherited groups、选择不同 seccomp policy,不能套用 SystemServer 的集合。
8. 失败与导航
PR_SET_KEEPCAPS/capget/capset 失败:终止 specialization;- bounding drop 失败:除特定 EINVAL 记录外,其他错误终止;
- UID 切换前 seccomp/scheduler 失败:不能延后到应用 main;
- 最终 capability 超过 bounding:内核拒绝 capset;
- 把 effective 当 permitted:会误判当前可用权限;
- 把 capability 设置成功当 SELinux 允许:两者是独立安全层。
# Java/SystemServer capability 候选与传递。
rg -n "CAP_IPC_LOCK|CAP_MAC_ADMIN|capget|--capabilities|forkSystemServer" \
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
# native capability 四组、UID顺序和失败函数。
rg -n "EnableKeepCapabilities|SetInheritable|DropCapabilitiesBoundingSet|SetCapabilities|SpecializeCommon|setresuid|setresgid|SetUpSeccompFilter" \
frameworks/base/core/jni/com_android_internal_os_Zygote.cpp下一篇将分析 capability 与 SELinux/seccomp 的组合边界,不重复本文的四组集合和 capset 顺序。
