LSM框架介绍

LSM是Linux Security Module的简写,是一种安全框架,它定义了很多钩子函数,安插在内核的关键路径上,当执行到这些关键路径时,调用安插的钩子进行安全检查。每个钩子上安装一个函数指针链,当调用钩子时,依次执行挂载在钩子上的函数,为linux实现的不同的安全模块实际上就是在这些钩子上安装函数。

由于钩子时事先定义好的,并且已经安插到了内核的关键路径上,所以开发一个新的安全模块并不会修改内核的其他的部分,由于一个钩子上可以挂在多个函数指针,所以不能的安全模块也是可以共存的,结果就是执行钩子函数时,依次调用安装到钩子上的安全模块函数进行检查。

LSM定义的钩子有:

//所有的钩子都定义在security/security.c文件中
int security_file_open(struct file *file, const struct cred *cred)
  {
      int ret; 
  
      ret = call_int_hook(file_open, 0, file, cred);
      if (ret)
          return ret; 
  
      return fsnotify_perm(file, MAY_OPEN);
  }
  
  int security_task_create(unsigned long clone_flags)
  {
      return call_int_hook(task_create, 0, clone_flags);
  }
  
  void security_task_free(struct task_struct *task)
  {
      call_void_hook(task_free, task);
  }

在内核其他模块需要安全检查的地方调用这些钩子函数进行检查:

  static int do_dentry_open(struct file *f,
                struct inode *inode,
                int (*open)(struct inode *, struct file *),
                const struct cred *cred)
  {
  		...
        error = security_file_open(f, cred);
      if (error)
          goto cleanup_all;
         ...
 }

下面具体来分析钩子函数:

  int security_task_create(unsigned long clone_flags)
  {
      return call_int_hook(task_create, 0, clone_flags);
  }
 // 看看call_int_hook的定义
    #define call_int_hook(FUNC, IRC, ...) ({            \                                                                                                                  
      int RC = IRC;                       \
      do {                            \
          struct security_hook_list *P;           \
                                  \
          list_for_each_entry(P, &security_hook_heads.FUNC, list) { \
              RC = P->hook.FUNC(__VA_ARGS__);     \
              if (RC != 0)                \
                  break;              \
          }                       \
      } while (0);                        \
      RC;                         \
  })

从security_hook_heads.FUNC 头中指向的链表依次取出执行,那么这些链表是怎么生成的呢,可以通过阅读loadpin和yama这两个安全模块来了解,这两个模块非常简单,只有一个文件:

  //loadpin/loadpin.c
    static struct security_hook_list loadpin_hooks[] = { 
      LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
      LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
  };
  
  void __init loadpin_add_hooks(void)
  {
      pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
      security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks));
  }
static void loadpin_sb_free_security(struct super_block *mnt_sb)
{
	...
}
static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
{
	...
}
//include/linux/lsm_hooks.h
  #define LSM_HOOK_INIT(HEAD, HOOK) \                                                                                                                                    
      { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }

  struct security_hook_list {                                                                                                                                            
      struct list_head        list;
      struct list_head        *head;
      union security_list_options hook;
  };
  
struct security_hook_heads security_hook_heads = {
		...
      .sb_free_security =                                                                                                                                                
          LIST_HEAD_INIT(security_hook_heads.sb_free_security),
      .kernel_read_file =                                                                                                                                                
          LIST_HEAD_INIT(security_hook_heads.kernel_read_file),
      ...
}

struct security_hook_heads { 
	...
	struct list_head sb_free_security;
	struct list_head kernel_read_file;
	...
}

  static inline void security_add_hooks(struct security_hook_list *hooks,
                        int count)
  {
      int i;
  
      for (i = 0; i < count; i++) 
          list_add_tail_rcu(&hooks[i].list, hooks[i].head);
  }

以上关于链表的操作我就不过多讲解了,自己安全数据结构的定义画个图就一目了然了。
通过上面的分析可以知道,不同的安全模块理论上来说是可以共存的,即不同的安全模块的函数钩子可以挂在同一个security_hook_heads.head, 这样不同的安全模块将被依次调用。

但是实际上并不是这样的,有些安全模块之间做了一些互斥选择:

static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =                                                                                                             
      CONFIG_DEFAULT_SECURITY;
 //通过配置可默认选择一个
 //也可以通过内核的cmdline指定这次使用哪个
   static int __init choose_lsm(char *str)
  {
      strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
      return 1;
  }
  __setup("security=", choose_lsm);
 
	int __init security_module_enable(const char *module)
  {
      return !strcmp(module, chosen_lsm);                                                                                                                                
  }

安全模块在加载时会检查:
./…/security/smack/smack_lsm.c:4791: if (!security_module_enable(“smack”))
./…/security/tomoyo/tomoyo.c:542: if (!security_module_enable(“tomoyo”))
./…/security/apparmor/lsm.c:871: if (!apparmor_enabled || !security_module_enable(“apparmor”)) {
./…/security/selinux/hooks.c:6460: if (!security_module_enable(“selinux”)) {

因此smack,tomoyo, apparmor,selinux只能同时使用一个,因为它们都是实现了MAC,只是实现的方法不一样罢了,因此没有必要共存,它们和其他的安全模块之间可以共存。

  • 1
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值