LSM 启动过程

要使能一个 LSM 有两个步骤:

1、该 LSM 的编译选项被打开

各 LSM 代码中都会使用 DEFINE_LSM() 在内核中创建该模块。这个宏的定义如下:

1// include/linux/lsm_hooks.h
2
3#define DEFINE_LSM(lsm)                                                 \
4        static struct lsm_info __lsm_##lsm                              \
5                __used __section(".lsm_info.init")                      \
6                __aligned(sizeof(unsigned long))

其实就是创建了一个 struct lsm_info 的实例,只不过指定放置在了 __section(".lsm_info.init") 这个特殊的节中。这个节在内核文件中的位置由链接脚本安排,起始和终止地址分别由 __start_lsm_info__end_lsm_info 指定:

 1// include/asm-generic/vmlinux.lds.h
 2
 3#ifdef CONFIG_SECURITY
 4#define LSM_TABLE()     . = ALIGN(8);                                   \
 5                        __start_lsm_info = .;                           \
 6                        KEEP(*(.lsm_info.init))                         \
 7                        __end_lsm_info = .;
 8#define EARLY_LSM_TABLE()       . = ALIGN(8);                           \
 9                        __start_early_lsm_info = .;                     \
10                        KEEP(*(.early_lsm_info.init))                   \
11                        __end_early_lsm_info = .;
12#else
13#define LSM_TABLE()
14#define EARLY_LSM_TABLE()
15#endif

因此通过这两个地址,我们就可以找到所有编译进内核的 LSM 模块的 struct lsm_info 实例。在下面提到的函数中就有这样的过程。

2、用户明确指定要开启该 LSM

指定的方式有以下几种,优先级递减:

  • 内核启动参数 lsm=:以逗号分隔的 LSM 名称列表
  • 内核启动参数 security=:某一个 LSM 的名称
  • 编译选项 CONFIG_LSM:以逗号分隔的 LSM 名称列表
    • 还有几个形如 CONFIG_DEFAULT_SECURITY_* 的编译选项,仅为向后兼容而保留,会被转换成 CONFIG_LSM

启动过程的具体函数调用如下:

1# init/main.c
2start_kernel
3# security/security.c
4  security_init
5    ordered_lsm_init
6      ordered_lsm_parse # 在 LSM_TABLE 中查找由"lsm="、"security="、CONFIG_LSM 指定的模块,将它们加入 ordered_lsms
7      prepare_lsm       # (遍历 ordered_lsms 时逐个调用此函数)确认模块是否开启,并配置 lsm->enabled 标志
8      initialize_lsm    # (遍历 ordered_lsms 时逐个调用此函数)对开启的模块进行初始化

几个指定方式的优先级主要体现在 ordered_lsm_init() 中:

 1/* 
 2 * lsm=       ~> chosen_lsm_order
 3 * security=  ~> chosen_major_lsm
 4 * CONFIG_LSM ~> builtin_lsm_order
 5 */
 6if (chosen_lsm_order) {
 7    if (chosen_major_lsm) {
 8        pr_info("security= is ignored because it is superseded by lsm=\n");
 9        chosen_major_lsm = NULL;
10    }
11    ordered_lsm_parse(chosen_lsm_order, "cmdline");
12} else
13    ordered_lsm_parse(builtin_lsm_order, "builtin");

另外 ordered_lsm_parse() 中也有一些,主要是区分 security=CONFIG_LSM

注意:SELinux、AppArmor、Smack 等几个主要的 LSM 模块在定义时有标注 LSM_FLAG_EXCLUSIVE,表明它们相互之间是互斥的,每次只能使能其中的一个。


Git Relevant Operations
基于 Jenkins 搭建针对内核的 CI