LSM安全模块开发

内核版本:5.4.120

LSM 安全模块的开发主要步骤

  1. 明确需要的hook(所有的hook都定义在include/linux/lsm_hooks.h中)
  2. 编写hook处理函数(具体来说,hook是一个函数指针,我们要做的就是编写具体的处理函数)
  3. 关联对应hook到上述的hook处理函数,并添加到security_hook_list结构体中。(将这个hook函数指针指向上述的处理函数)
  4. 注册添加了hook处理函数的security_hook_list结构体
  5. 将指定的安全模块添加到LSM框架中

内核代码整体结构中的必要修改

  1. security目录下新建自己的模块主目录security/sec_file
  2. security/sec_file目录下新建三个文件,分别是:sec_file.c( 添加自己的代码逻辑)、Kconfig(构造内核模块的框架)、Makefile(用于内核编译)
  3. 修改目录security下的KconfigMakefile文件,将我们自己的模块包含进来。

具体代码结构及内容

  • security/sec_file/sec_file.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <linux/lsm_hooks.h>
    #include <linux/sysctl.h>
    #include <linux/string.h>
    /*
    *sec_file_file_permission 函数的就是hook处理函数
    *在这里需要注意的是 sec_file_file_permission 的函数头需要和 include/linux/lsm_hooks.h 文件
    *中对应hook的函数头保持一致(在这里就是和 ’file_permission‘ hook的函数头对应)
    */
    static int sec_file_file_permission(struct file *file, int mask)
    {
    // only deal with the file_name which contain '.'
    if (strstr(file->f_path.dentry->d_iname, "."))
    {
    printk(KERN_INFO "[+ xiaolei_test] 'file_name' of the access file is:%s\n",file->f_path.dentry->d_iname);
    printk(KERN_INFO "[+ xiaolei_test] 'mask' of the access file is:%d\n",mask);
    }
    return 0;
    }
    /*
    *LSM_HOOK_INIT 就是将file_permission hook 和 处理函数 sec_file_file_permission 关联起来,并
    *添加到 security_hook_list 结构体中
    */
    static struct security_hook_list sec_file_hooks[] __lsm_ro_after_init = {
    LSM_HOOK_INIT(file_permission,sec_file_file_permission),
    };

    /*
    * 注册添加了hook处理函数的 security_hook_list 结构体
    */

    static __init int sec_file_init(void)
    {
    printk(KERN_ALERT "[+ xiaolei] sec_file: Initializing .!!!!!!]n");
    security_add_hooks(sec_file_hooks,ARRAY_SIZE(sec_file_hooks), "sec_file");
    return 0;
    }

    /*
    *将指定的安全模块添加到LSM框架中
    *这里需要注意 DEFINE_LSM(sec_file) 中的 sec_file 就是指定在LSM安全框架启动过程中要启用的安全模
    *块的标识。(LSM安全框架要启动的模块后续在.config 文件需要进行手动修改或者通过 make menuconfig 过程中的配置来进行修改。)
    */
    DEFINE_LSM(sec_file) = {
    .name = "sec_file",
    .init = sec_file_init,
    };

  • security/sec_file/Kconfig

    1
    2
    3
    4
    5
    6
    config SECURITY_FILE         #安全模块的标识(用于Kconfig中)
    bool "sec_file - LSM" #make menuconfig 界面中显示的模块名称
    depends on SECURITY #依赖模块
    default y #该安全模块的默认值
    help #help 信息
    This our lab LSM module.
  • security/sec_file/Makefile

    1
    2
    3
    obj-$(CONFIG_SECURITY_FILE) := sec_file.o    #将对应的模块编译进内核(obj-y 生成 build-in.a)或者编译成第三方模块(obj-m 生成 xxx.ko)
    sec_file-y := sec_file.o

  • security/Kconfig

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    # SPDX-License-Identifier: GPL-2.0-only
    #
    # Security configuration
    #

    menu "Security options"

    source "security/keys/Kconfig"

    ......

    source "security/selinux/Kconfig"
    source "security/uob/Kconfig"
    source "security/sec_file/Kconfig" # 将sec_file 的Kconfig 包含进来
    source "security/smack/Kconfig"
    source "security/tomoyo/Kconfig"
    source "security/apparmor/Kconfig"
    source "security/loadpin/Kconfig"
    source "security/yama/Kconfig"
    source "security/safesetid/Kconfig"
    source "security/lockdown/Kconfig"

    source "security/integrity/Kconfig"

    ......

    config LSM # 在下面添加对应的模块标识(DEFINE_LSM() 括号中的标识)[这个也可以不添加]
    string "Ordered list of enabled LSMs"
    default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,uob,sec_file" if DEFAULT_SECURITY_SMACK
    default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,uob,sec_file" if DEFAULT_SECURITY_APPARMOR
    default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,uob,sec_file" if DEFAULT_SECURITY_TOMOYO
    default "lockdown,yama,loadpin,safesetid,integrity,uob,sec_file" if DEFAULT_SECURITY_DAC
    default "lockdown,yama,loadpin,safesetid,integrity,uob,sec_file" if DEFAULT_SECURITY_UOB
    default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,uob,sec_file"
    help
    A comma-separated list of LSMs, in initialization order.
    Any LSMs left off this list will be ignored. This can be
    controlled at boot with the "lsm=" parameter.

    If unsure, leave this as the default.

    source "security/Kconfig.hardening"

    endmenu


  • security/Makefile

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    # SPDX-License-Identifier: GPL-2.0
    #
    # Makefile for the kernel security code
    #

    obj-$(CONFIG_KEYS) += keys/
    subdir-$(CONFIG_SECURITY_SELINUX) += selinux
    subdir-$(CONFIG_SECURITY_UOB) += uob
    subdir-$(CONFIG_SECURITY_FILE) += sec_file #添加模块主目录
    subdir-$(CONFIG_SECURITY_SMACK) += smack
    subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo
    subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
    subdir-$(CONFIG_SECURITY_YAMA) += yama
    subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin
    subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid
    subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown

    # always enable default capabilities
    obj-y += commoncap.o
    obj-$(CONFIG_MMU) += min_addr.o

    # Object file lists
    obj-$(CONFIG_SECURITY) += security.o
    obj-$(CONFIG_SECURITYFS) += inode.o
    obj-$(CONFIG_SECURITY_UOB) += uob/
    obj-$(CONFIG_SECURITY_FILE) += sec_file/ #添加对应的编译文件,与sec_file/Makefile中的obj-$(CONFIG_SECURITY_FILE) :=
    #sec_file.o 相对应(sec_file)
    obj-$(CONFIG_SECURITY_SELINUX) += selinux/
    obj-$(CONFIG_SECURITY_SMACK) += smack/
    obj-$(CONFIG_AUDIT) += lsm_audit.o
    obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/
    obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/
    obj-$(CONFIG_SECURITY_YAMA) += yama/
    obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/
    obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/
    obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
    obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o

    # Object integrity file lists
    subdir-$(CONFIG_INTEGRITY) += integrity
    obj-$(CONFIG_INTEGRITY) += integrity/

修改LSM 安全框架启动的安全模块集合的两种方法:

  • 手动修改linux源代码根目录/.config文件

    找到下图中的行,将模块标识手动添加进去(此处的模块标识就是 DEFINE_LSM()中的字符串)

  • 通过make menuconfig来进行配置
    • 在下图配置界面中,进入security options子目录

    • 确保我们的模块被选中开启

    • Ordered list of enable LSMs中加上我们的模块标识。

附录

内核打印函数介绍

  • printk

    printf类似,但是printf运行在用户态,printk运行在内核态

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    格式:
    printk(fmt, <print message>)

    其中fmt为printk打印的控制函数,fmt的定义:

    #define KERN_EMERG "<0>" //紧急事件消息,系统崩溃之前提示,表示系统不可用
    #define KERN_ALERT "<1>" //报告消息,表示必须立即采取措施
    #define KERN_CRIT "<2>" //临界条件,通常涉及严重的硬件或软件操作失败
    #define KERN_ERR "<3>" //错误条件,驱动程序常用KERN_ERR来报告硬件的错误
    #define KERN_WARNING "<4>" //警告条件,对可能出现问题的情况进行警告
    #define KERN_NOTICE "<5>" //正常但又重要的条件,用于提醒
    #define KERN_INFO "<6>" //提示信息,如驱动程序启动时,打印硬件信息
    #define KERN_DEBUG "<7>" //调试级别的消息



    没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上)
  • pr_xxx

    是内核中对printk的封装

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #define pr_emerg(fmt, ...) \
    printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_alert(fmt, ...) \
    printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_crit(fmt, ...) \
    printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_err(fmt, ...) \
    printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_warning(fmt, ...) \
    printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_notice(fmt, ...) \
    printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_info(fmt, ...) \
    printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_debug(fmt, ...) \
    printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
    #define pr_cont(fmt, ...) \
    printk(KERN_CONT fmt, ##__VA_ARGS__)

相关资料

  1. LSM内核源代码分析与测试(一)

  2. 学习LSM(Linux security module)之二:编写并运行一个简单的demo

  3. LSM: Convert security_initcall() into DEFINE_LSM()

  4. Lab 3: Building an LSM

  5. Analysis of incomplete LSM framework startup

  6. Linux Security Modules框架源码分析

Author

FatAngle, Chaos Chen

Posted on

2021-07-14

Updated on

2023-06-30

Licensed under

Commentaires