0、前言
- 本文不介绍各个 Cap 集的含义,只列举它们各自发生变化的场景和方式。
- 只讨论 Linux 2.6.33 之后的场景,即:1、Bounding Set 为 per-thread 属性;2、支持文件 Cap。
1、线程自行调整
相较于 DAC 和 MAC,Capabilities 的一个特点是可在运行时动态增减。程序的代码中可带有调整 Cap 的相关系统调用和逻辑,在执行时根据需要主动调整线程的 Cap。例如,在执行某些需要特权的操作前将(Permitted Set 范围内)必要的 Cap 添加至 Effective Set 中,在执行完毕后再将这些 Cap 从 Effective Set 中去除。这种会主动调整 Cap 的可执行文件也被称为 Capability-aware Binary。
与调整 Cap 相关的系统调用:
capget(),capset():读取,设置 Effective、Permitted、Inheritable Setprctl():操作 Bounding(PR_CAPBSET_{DROP,READ})和 Ambient Set(PR_CAP_AMBIENT及其下PR_CAP_AMBIENT_*二级参数)
线程自行调整其各 Cap 集的相关规则,可参考内核代码 security/commoncap.c 中的 cap_capset() 实现:
- Effective Set:可向其中加入 Permitted Set 中的 Cap;可随意移除其中的 Cap。
- Permitted Set:不可向其中加入 Cap;可随意移除其中的 Cap。
- Inheritable Set:若线程拥有
CAP_SETPCAP,可向其中加入的 Cap 必须在其 Bounding Set 范围内;否则,可加入的 Cap 还必须在其 Permitted Set 范围内;可随意移除其中的 Cap。 - Bounding Set:不可向其中加入 Cap;若线程拥有
CAP_SETPCAP,可移除其中的 Cap。 - Ambient Set:可向其中加入的 Cap 必须在
Permitted & Inheritable范围内,且随两者的缩减而自动缩减;可随意移除其中的 Cap。
| 各 Cap 集 | 可往里增添的 Cap | 可从中移除的 Cap |
|---|---|---|
| Effective Set | Permitted 范围内 |
任意 |
| Permitted Set | 无 | 任意 |
| Inheritable Set | Bounding (& Permitted,若无 CAP_SETPCAP)范围内 |
任意 |
| Bounding Set | 无 | 任意(需拥有 CAP_SETPCAP) |
| Ambient Set | Permitted & Inheritable 范围内,随两者自动缩减 |
任意 |
可见,一个线程实际可拥有的 Cap 由 Permitted Set 圈定,因为其中的 Cap 可随时通过 capset() 调配进 Effective Set 中,或通过 prctl() 调配进 Ambient Set 中(从而对子线程的 Cap 产生影响)。
参考内核源码 security_capset() 流程中的 cap_capset()。
2、更改文件 Cap
可使用 setcap 工具配置文件的文件 Cap,这些文件 Cap 会在文件被运行时影响对应线程的 Cap。文件 Cap 保存在文件拓展属性 security.capability 中,配置该拓展属性需要 CAP_SETFCAP。
需要注意的是,对如今的 Linux 内核而言,文件 Cap 是 User Namespace 相关的;换言之,其仅在指定的 User Namespace 中有效。有关 User Namespace 的详情将另写文章介绍。
参考内核源码 vfs_setxattr() 流程中的 cap_convert_nscap()。
3、线程执行 execve()
线程初始的 Cap(记为 P)与可执行文件的文件 Cap(记为 F)两者共同决定线程执行 execve() 后的 Cap(记为 P')。顺带说一句,fork() 不会改变线程 Cap,只会将父进程的 Cap 拷贝给子进程。
线程执行 execve() 前后 Cap 的转换关系:
1P'(ambient) = (file is privileged) ? 0 : P(ambient)
2
3P'(permitted) = (P(inheritable) & F(inheritable)) |
4 (F(permitted) & P(bounding)) | P'(ambient)
5
6P'(effective) = F(effective) ? P'(permitted) : P'(ambient)
7
8P'(inheritable) = P(inheritable) [i.e., unchanged]
9
10P'(bounding) = P(bounding) [i.e., unchanged]
参考内核源码 begin_new_exec() 流程中的 bprm_creds_from_file()。
引入 Ambient set 之前的转换关系,来自 die.net 上比较旧的 man 手册,仅供参考:
1P'(permitted) = (P(inheritable) & F(inheritable)) | 2 (F(permitted) & P(bounding)) 3 4P'(effective) = F(effective) ? P'(permitted) : 0 5 6P'(inheritable) = P(inheritable) [i.e., unchanged] 7 8P'(bounding) = P(bounding) [i.e., unchanged]这篇博文 提供了一副不错的转换示意图:
![]()
4、参考资料
- Understanding Linux Capabilities (tbhaxor.com)
- man 7 capabilities: man7.org (new) , die.net (old)
- man 8 setcap