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