LSM 的 Security Blob 机制

Linux 内核主线相关补丁:

 1ecd5f82e05dd LSM: Infrastructure management of the ipc security blob
 2019bcca4626a Smack: Abstract use of ipc security blobs
 37c6538280ae9 SELinux: Abstract use of ipc security blobs
 4f4ad8f2c4076 LSM: Infrastructure management of the task security
 5afb1cbe37440 LSM: Infrastructure management of the inode security
 6fb4021b6fb58 Smack: Abstract use of inode security blob
 780788c229116 SELinux: Abstract use of inode security blob
 833bf60cabcc7 LSM: Infrastructure management of the file security
 9f28952ac9008 Smack: Abstract use of file security blob
10bb6c6b02ccb7 SELinux: Abstract use of file security blob
11bbd3662a8348 Infrastructure management of the cred security blob
1243fc460907dc TOMOYO: Abstract use of cred security blob
1369b5a44a95bb AppArmor: Abstract use of cred security blob
143d252529480c SELinux: Remove unused selinux_is_enabled
1598c886513657 SELinux: Remove cred security blob poisoning
160c6cfa622cf5 SELinux: Abstract use of cred security blob
17b17103a8b8ae Smack: Abstract use of cred security blob

原理解析

内核代码中一些重要结构体,如 struct credstruct filestruct inodestruct task_structstruct kern_ipc_perm,都有一个 void * 类型的 security 成员:cred->securityfile->f_securityinode->i_securitytask->securityipc->security,指向的都是自身实例与 LSM 相关的属性与信息。

LSM 本质上是一套数量众多的钩子函数,各自嵌入到系统执行的各个安全关键路径上。它们的具体实现则是由如 SELinux、AppArmor、Smack 这样的安全模块来提供。直到 5.1 之前(也就是这组补丁合入之前),这些安全模块在代码逻辑上都是独占上述那些 security 成员的,并且它们都会在内部直接操作那些 security 成员1。从而系统一次只能部署一个安全模块(如 SELinux 与 AppArmor 不能同时使能),毕竟各结构体只有一个 security 指针。另外,各安全模块保存信息的内容和形式不同,相关结构体的定义也不同,因此那些 security 成员就都只能定义为 void * 类型。

 1                                                             S E L I N U X
 2                                                    ┌──────────────────────────────────────────────────────────────────────┐
 3                                                    │                                                                      │
 4 struct         struct         struct               │ ┌────────────────────┐ ┌─────────────────────┐ ┌───────────────────┐ │
 5 cred           inode          kern_ipc_perm        │ │task_security_struct│ │inode_security_struct│ │ipc_security_struct│ │
 6┌──────────┐   ┌──────────┐   ┌──────────┐          │ └────────────────────┘ └─────────────────────┘ └───────────────────┘ │
 7│          │   │          │   │          │          │                                                                      │
 8│          │   │          │   │          │          │ ┌────────────────────┐ ┌────┐                  ┌───────────────────┐ │
 9├──────────┤   ├──────────┤   ├──────────┤          │ │file_security_struct│ │NULL│                  │msg_security_struct│ │
10│ security ├─┐ │i_security├─┐ │ security ├─┐        │ └────────────────────┘ └────┘                  └───────────────────┘ │
11├──────────┤ │ ├──────────┤ │ ├──────────┤ │        │                                                                      │
12│          │ │ │          │ │ │          │ │        └──────────────────────────────────────────────────────────────────────┘
13└──────────┘ │ └──────────┘ │ └──────────┘ │                A P P A R M O R
14             │              │              │        ┌───────────────────────────────────┐
15 struct      │  struct      │  struct      │        │                                   │
16 file        │  task        │  msg_msg     │        │ ┌──────────┐ ┌────┐        ┌────┐ │
17┌──────────┐ │ ┌──────────┐ │ ┌──────────┐ │        │ │aa_label *│ │NULL│        │NULL│ │
18│          │ │ │          │ │ │          │ │        │ └──────────┘ └────┘        └────┘ │
19│          │ │ │          │ │ │          │ │        │                                   │
20├──────────┤ │ ├──────────┤ │ ├──────────┤ │        │ ┌────┐       ┌───────────┐ ┌────┐ │
21│f_security├─┤ │ security ├─┤ │ security ├─┤        │ │NULL│       │aa_task_ctx│ │NULL│ │
22├──────────┤ │ ├──────────┤ │ ├──────────┤ │        │ └────┘       └───────────┘ └────┘ │
23│          │ │ │          │ │ │          │ │        │                                   │
24└──────────┘ │ └──────────┘ │ └──────────┘ │        └───────────────────────────────────┘
25             │              │              └───►             S M A C K
26             │              │                       ┌───────────────────────────────────────────┐
27             │              └──────────────────►    │                                           │
28             │                                      │ ┌──────────┐  ┌───────────┐ ┌───────────┐ │
29             └─────────────────────────────────►    │ │task_smack│  │inode_smack│ │smack_known│ │
30                                                    │ └──────────┘  └───────────┘ └───────────┘ │
31                                                    │                                           │
32                                                    │ ┌───────────┐ ┌────┐        ┌───────────┐ │
33                                                    │ │smack_known│ │NULL│        │smack_known│ │
34                                                    │ └───────────┘ └────┘        └───────────┘ │
35                                                    │                                           │
36                                                    └───────────────────────────────────────────┘
37                                                             . . . . . .
38                                                    ┌─────────────────────────┐
39                                                    │                         │
40                                                    │ ┌─────┐ ┌─────┐ ┌─────┐ │
41                                                    │ │. . .│ │. . .│ │. . .│ │
42                                                    │ └─────┘ └─────┘ └─────┘ │
43                                                    │                         │
44                                                    │ ┌─────┐ ┌─────┐ ┌─────┐ │
45                                                    │ │. . .│ │. . .│ │. . .│ │
46                                                    │ └─────┘ └─────┘ └─────┘ │
47                                                    │                         │
48                                                    └─────────────────────────┘

这种做法有以下两个问题:第一,系统在同时启用多种安全模块的时候,对 security 成员的操作可能会相互冲突;第二,对于像 SELinux 这种可以在运行时动态关闭的安全模块来说,动态关闭可能会导致内存泄漏。具体操作举例如下:

  1. 以使能 SELinux 的方式启动系统;
  2. 做相关操作创建一个 struct file 实例,此时 SELinux 会为 f_security 成员分配内存空间;
  3. 动态关闭 SELinux,此时 LSM 会解绑所有 SELinux 的钩子函数实现;
  4. 释放 struct file 实例,由于执行 f_security 释放的 SELinux 钩子函数实现已被解绑,导致内存泄漏。

为了解决这些问题,上游社区引入了上述的补丁集,主要做了两件事:

  1. 将各安全模块内的信息结构体视为 Blob,在系统初始化时一次性分配长度为各 Blob 之和的连续内存空间,各安全模块记录自己 Blob 在整段空间中的起始位置(即 offset)。
  2. 将各 security 成员指向的内存统一收归 LSM 框架来分配和释放,各安全模块不再管理这块内存,只能查、改 *security 空间中自己 Blob 的部分。
 1 struct
 2 cred
 3┌──────────┐
 4│          │       sel_c                  aa_c         smack_c
 5│          │      ┌──────────────────────┬────────────┬────────────┬───────┐
 6├──────────┤      │                      │            │            │       │
 7│ security ├─────►│ task_security_struct │ aa_label * │ task_smack │ . . . │
 8├──────────┤      │                      │            │            │       │
 9│          │      └──────────────────────┴────────────┴────────────┴───────┘
10└──────────┘
11 struct
12 file
13┌──────────┐
14│          │       sel_f                  smack_f
15│          │      ┌──────────────────────┬─────────────┬───────┐              S E L I N U X        A P P A R M O R
16├──────────┤      │                      │             │       │           ┌─────────────────┐   ┌─────────────────┐
17│f_security├─────►│ file_security_struct │ smack_known │ . . . │           │  struct         │   │  struct         │
18├──────────┤      │                      │             │       │           │  lsm_blob_sizes │   │  lsm_blob_sizes │
19│          │      └──────────────────────┴─────────────┴───────┘           │ ┌─────────┐     │   │ ┌─────────┐     │
20└──────────┘                                                               │ │  sel_c  │     │   │ │   aa_c  │     │
21 struct                                                                    │ ├─────────┤     │   │ ├─────────┤     │
22 inode                                                                     │ │  sel_f  │     │   │ │   N/A   │     │
23┌──────────┐                                                               │ ├─────────┤     │   │ ├─────────┤     │
24│          │       sel_i                   smack_i                         │ │  sel_i  │     │   │ │   N/A   │     │
25│          │      ┌───────────────────────┬─────────────┬───────┐          │ ├─────────┤     │   │ ├─────────┤     │
26├──────────┤      │                       │             │       │          │ │   N/A   │     │   │ │   aa_t  │     │
27│i_security├─────►│ inode_security_struct │ inode_smack │ . . . │          │ ├─────────┤     │   │ ├─────────┤     │
28├──────────┤      │                       │             │       │          │ │ sel_ipc │     │   │ │   N/A   │     │
29│          │      └───────────────────────┴─────────────┴───────┘          │ ├─────────┤     │   │ ├─────────┤     │
30└──────────┘                                                               │ │  sel_m  │     │   │ │   N/A   │     │
31 struct                                                                    │ └─────────┘     │   │ └─────────┘     │
32 task                                                                      │                 │   │                 │
33┌──────────┐                                                               └─────────────────┘   └─────────────────┘
34│          │       aa_t
35│          │      ┌─────────────┬───────┐                                      S M A C K             . . . . . .
36├──────────┤      │             │       │                                  ┌─────────────────┐   ┌─────────────────┐
37│ security ├─────►│ aa_task_ctx │ . . . │                                  │  struct         │   │  struct         │
38├──────────┤      │             │       │                                  │  lsm_blob_sizes │   │  lsm_blob_sizes │
39│          │      └─────────────┴───────┘                                  │ ┌─────────┐     │   │ ┌─────────┐     │
40└──────────┘                                                               │ │ smack_c │     │   │ │  ..._c  │     │
41 struct                                                                    │ ├─────────┤     │   │ ├─────────┤     │
42 kern_ipc_perm                                                             │ │ smack_f │     │   │ │  ..._f  │     │
43┌──────────┐                                                               │ ├─────────┤     │   │ ├─────────┤     │
44│          │       sel_ipc               smack_ipc                         │ │ smack_i │     │   │ │  ..._i  │     │
45│          │      ┌─────────────────────┬─────────────┬───────┐            │ ├─────────┤     │   │ ├─────────┤     │
46├──────────┤      │                     │             │       │            │ │   N/A   │     │   │ │  ..._t  │     │
47│ security ├─────►│ ipc_security_struct │ smack_known │ . . . │            │ ├─────────┤     │   │ ├─────────┤     │
48├──────────┤      │                     │             │       │            │ │smack_ipc│     │   │ │ ..._ipc │     │
49│          │      └─────────────────────┴─────────────┴───────┘            │ ├─────────┤     │   │ ├─────────┤     │
50└──────────┘                                                               │ │ smack_m │     │   │ │  ..._m  │     │
51 struct                                                                    │ └─────────┘     │   │ └─────────┘     │
52 msg_msg                                                                   │                 │   │                 │
53┌──────────┐                                                               └─────────────────┘   └─────────────────┘
54│          │       sel_m                 smack_m
55│          │      ┌─────────────────────┬─────────────┬───────┐
56├──────────┤      │                     │             │       │
57│ security ├─────►│ msg_security_struct │ smack_known │ . . . │
58├──────────┤      │                     │             │       │
59│          │      └─────────────────────┴─────────────┴───────┘
60└──────────┘

重点代码注解

lsm_set_blob_size

在 bbd3662a8348 (“Infrastructure management of the cred security blob”) 引入的代码中有如下部分:

 1diff --git a/security/security.c b/security/security.c
 2index 60b39db95c2f..09be8ce007a2 100644
 3--- a/security/security.c
 4+++ b/security/security.c
 5@@ -139,6 +141,25 @@ static bool __init lsm_allowed(struct lsm_info *lsm)
 6        return true;
 7 }
 8
 9+static void __init lsm_set_blob_size(int *need, int *lbs)
10+{
11+       int offset;
12+
13+       if (*need > 0) {
14+               offset = *lbs;
15+               *lbs += *need;
16+               *need = offset;
17+       }
18+}
19+
20+static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
21+{
22+       if (!needed)
23+               return;
24+
25+       lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
26+}
27+

注意这里的 lsm_set_blob_size() 是将旧的 offset 赋值给了 *need,而这个 needed 实际上指向的是各安全模块内部记录 Offset 的 struct lsm_blob_sizes 实例。因此 needed->lbs_cred 原本记录的是安全模块某个 Blob 所需长度,执行完 lsm_set_blob_size() 之后就变成记录 Blob 的偏移地址了。

POISON_FREE

98c886513657 (“SELinux: Remove cred security blob poisoning”) 涉及对 POISON_FREE 的修改。此文 解释了 POINSON_FREE 由谁设置,代表的含义以及如何使用。

bdfedb76f4f5a (“mm, mempool: poison elements backed by slab allocator”):一个与 POINSON_FREE 相关的主线补丁。

还可以参考:Linux Kernel Heap Tampering Detection by Larry Highsmith, 2009.


  1. 说实话这一点很奇怪。明明 LSM 的各个钩子函数已经做成了函数指针列表的形式,调用时沿列表逐个调用,摆明了是考虑到了多个安全模块叠加多套函数实现的场景,怎么这里又是独占的呢? ↩︎


CVE-2021-43057 分析
动态加载过程与 PLT、GOT 表