Linux 上进程 Capabilities 可能变化的场景

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 Set
  • prctl() :操作 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、参考资料


Stack Canary 的应用
关于 SELinux 的各种理论知识