首先对LSM 进行简单介绍。虽然linux下的各位基本都知道一些,但是还要罗嗦一下。
LSM中文全称是linux安全模块。英文全称:linux security module.
LSM是一种轻量级、通用的访问控制框架,适合多种访问控制模型以内核模块的形式实现。其特点是通用、简单、高效、支持POSIX。1e能力机制。
LSM的架构图如下:
通过系统调用进入内核之后,系统首先进行传统的权限检查(传统权限检查主要是基于用户的,用户通过验证之后就可以访问资源),通过之后才会进行强制访问控制。(强制访问控制是不允许主体干涉的一种访问控制,其采用安全标识、信息分级等信息敏感性进行访问控制。并且通过比较主体的级别和资源的敏感性来确定是否允许访问。比如说系统设置A用户不允许访问文件B,即便A是文件B的所有者,访问也是受限制的。)从图上看来,LSM实现访问控制主要通过安全模块的钩子函数实现。
LSM框架主要由五部分组成:这个网上资料很多。
在关键的特定内核数据结构中加入了安全域;
在内核源码中不同的关键点处插入对安全钩子函数的调用;
提供了一个通用的安全系统调用;
提供了注册和注销函数,使得访问控制策略可以以内核模块方式实现;
将capabilities逻辑的大部分功能移植为一个可选的安全模块。
我们这里重点结合源码对LSM框架进行解释。我使用的源码是3.5.4
首先介绍安全域字段,它是一个空类型的指针,在内核中的很多内核结构中都存在,比如inode、superblock、dentry、file等等。类型字段为void * security;
那么安全域怎么和安全模块中的信息关联起来?
当安全模块加载之后,安全域中的指针便指向安全模块中的安全信息。这里以selinux为例进行介绍。
内核里面security/selinux/include/objsec.h中定义了不同对象的安全信息,格式为XXX_security_strut.
上面的文件的安全信息里面包含打开文件描述符时的安全ID、文件所有者的安全ID等等。
要联系安全模块中安全信息和安全域需要几个控制钩子函数。这些钩子函数实现了对内核关键信息的设置和管理。这里主要介绍alloc_security、free_security。
selinux里面通过实现安全信息空间分配实现关联。 比如以文件安全信息为例
这里分配空间成功之后,通过file->f_security = fsec实现了关联。
撤销关联是在安全模块卸载之后调用file_free_security.
这里具体通过设置file->f_secrity为NULL,然后释放安全信息结构实现。
现在来看看内核如何实现selinux的访问控制。这里主要就是实现LSM里面的钩子函数了。LSM里面给出了结构体security_operations ,里面给出了很多钩子函数,实现了相关钩子函数就可以实现访问控制了。
上面的函数就实现了file_permission钩子函数。可以看下inode结构体的获得,感受内核是通过文件->目录项->inode。该函数主要实现自己的访问控制策略就OK 了。
哪selinux来说,在获得文件安全ID之后,主要对掩码和文件打开时相关的安全信息进行检测,符合就通过访问控制。
selinux基本实现了LSM里面的所有钩子函数,待钩子函数实现后,对LSM里面钩子域进行填充就OK了。
做完以上这些还需要注册安全模块到LSM,这里注册和注销使用了register_security和un register_security 。
比如selinux在注册时使用语句register_security(&selinux_ops)实现。
接下来通过上面的分析我们可以实现简单的基于LSM的访问控制。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>
#include <linux/file.h>
#include <linux/namei.h>
static int lsm_test_file_permission(struct file *file,int mask)
{
int path=0;
struct file *filp;
struct nameidata nd;
path = path_lookup(FILENAME,LOOKUP_FOLLOW,&nd);
if(!mask)
return 0;
if(path)
{
printk("lookup file failed!\n");
return -1;
}
filp = filp_open("/home/yuyunchao/code/sb.c",O_RDONLY,0);
{
printk("open failed!\n");
}
return 0;
}
static struct security_operations lsm_test_security_ops = {
.file_permission = lsm_test_file_permission,
};
static int __init lsm_file_init(void)
{
if(register_security(&lsm_test_security_ops)){
printk("register error ..........\n");
return -1;
}
printk("lsm_file init..\n ");
return 0;
}
static void __exit lsm_file_exit(void)
{
if(unregister_security(&lsm_test_security_ops)){
printk("unregister error................\n");
return ;
}
printk("module exit.......\n");
}
MODULE_LICENSE("GPL");
module_init(lsm_file_init);
module_exit(lsm_file_exit);
LSM中文全称是linux安全模块。英文全称:linux security module.
LSM是一种轻量级、通用的访问控制框架,适合多种访问控制模型以内核模块的形式实现。其特点是通用、简单、高效、支持POSIX。1e能力机制。
LSM的架构图如下:
![](/attachment/201305/14/26126915_1368498293S6C4.png)
通过系统调用进入内核之后,系统首先进行传统的权限检查(传统权限检查主要是基于用户的,用户通过验证之后就可以访问资源),通过之后才会进行强制访问控制。(强制访问控制是不允许主体干涉的一种访问控制,其采用安全标识、信息分级等信息敏感性进行访问控制。并且通过比较主体的级别和资源的敏感性来确定是否允许访问。比如说系统设置A用户不允许访问文件B,即便A是文件B的所有者,访问也是受限制的。)从图上看来,LSM实现访问控制主要通过安全模块的钩子函数实现。
LSM框架主要由五部分组成:这个网上资料很多。
在关键的特定内核数据结构中加入了安全域;
在内核源码中不同的关键点处插入对安全钩子函数的调用;
提供了一个通用的安全系统调用;
提供了注册和注销函数,使得访问控制策略可以以内核模块方式实现;
将capabilities逻辑的大部分功能移植为一个可选的安全模块。
我们这里重点结合源码对LSM框架进行解释。我使用的源码是3.5.4
首先介绍安全域字段,它是一个空类型的指针,在内核中的很多内核结构中都存在,比如inode、superblock、dentry、file等等。类型字段为void * security;
那么安全域怎么和安全模块中的信息关联起来?
当安全模块加载之后,安全域中的指针便指向安全模块中的安全信息。这里以selinux为例进行介绍。
内核里面security/selinux/include/objsec.h中定义了不同对象的安全信息,格式为XXX_security_strut.
![](/attachment/201305/14/26126915_1368499470ZVWl.png)
上面的文件的安全信息里面包含打开文件描述符时的安全ID、文件所有者的安全ID等等。
要联系安全模块中安全信息和安全域需要几个控制钩子函数。这些钩子函数实现了对内核关键信息的设置和管理。这里主要介绍alloc_security、free_security。
selinux里面通过实现安全信息空间分配实现关联。 比如以文件安全信息为例
![](/attachment/201305/14/26126915_1368499771Q88Z.png)
这里分配空间成功之后,通过file->f_security = fsec实现了关联。
撤销关联是在安全模块卸载之后调用file_free_security.
![](/attachment/201305/14/26126915_136849990569V4.png)
这里具体通过设置file->f_secrity为NULL,然后释放安全信息结构实现。
现在来看看内核如何实现selinux的访问控制。这里主要就是实现LSM里面的钩子函数了。LSM里面给出了结构体security_operations ,里面给出了很多钩子函数,实现了相关钩子函数就可以实现访问控制了。
![](/attachment/201305/14/26126915_13685005298La7.png)
上面的函数就实现了file_permission钩子函数。可以看下inode结构体的获得,感受内核是通过文件->目录项->inode。该函数主要实现自己的访问控制策略就OK 了。
哪selinux来说,在获得文件安全ID之后,主要对掩码和文件打开时相关的安全信息进行检测,符合就通过访问控制。
selinux基本实现了LSM里面的所有钩子函数,待钩子函数实现后,对LSM里面钩子域进行填充就OK了。
![](/attachment/201305/14/26126915_1368500912eEzZ.png)
做完以上这些还需要注册安全模块到LSM,这里注册和注销使用了register_security和un register_security 。
比如selinux在注册时使用语句register_security(&selinux_ops)实现。
接下来通过上面的分析我们可以实现简单的基于LSM的访问控制。
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/types.h>
#include <asm/uaccess.h>
#include <linux/fcntl.h>
#include <linux/uaccess.h>
#include <linux/file.h>
#include <linux/namei.h>
static int lsm_test_file_permission(struct file *file,int mask)
{
int path=0;
struct file *filp;
struct nameidata nd;
path = path_lookup(FILENAME,LOOKUP_FOLLOW,&nd);
if(!mask)
return 0;
if(path)
{
printk("lookup file failed!\n");
return -1;
}
filp = filp_open("/home/yuyunchao/code/sb.c",O_RDONLY,0);
{
printk("open failed!\n");
}
return 0;
}
static struct security_operations lsm_test_security_ops = {
.file_permission = lsm_test_file_permission,
};
static int __init lsm_file_init(void)
{
if(register_security(&lsm_test_security_ops)){
printk("register error ..........\n");
return -1;
}
printk("lsm_file init..\n ");
return 0;
}
static void __exit lsm_file_exit(void)
{
if(unregister_security(&lsm_test_security_ops)){
printk("unregister error................\n");
return ;
}
printk("module exit.......\n");
}
MODULE_LICENSE("GPL");
module_init(lsm_file_init);
module_exit(lsm_file_exit);
LSM在内核中很多地方已经插入了hook函数,并且在security.c函数中声明了security_ops结构,要实现你自己的安全模块,只需要定义你自己的struct security_operations,并且用register_security注册即可,下面举个简单例子:
test.c代码如下:
复制代码
将该文件以模块的形式放到security/下编译进内核,启用新的内核后,当你操作文件test.txt时,通过dmesg命令就能再终端看到”you can have your control code here!“输出
所以一般的做法是:定义你自己的struct security_operations,实现你自己的hook函数,具体有哪些hook函数可以查询include/linux/security.h 文件,然后调用register_security来用你的test_security_ops初始化全局的security_ops指针
test.c代码如下:
- /*
- * Test Linux Security Module
- *
- * Author: penghuan <penghuanmail@126.com>
- *
- * Copyright (C) 2010 UbuntuKylin, Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2, as
- * published by the Free Software Foundation.
- *
- */
- #include <linux/security.h>
- #include <linux/sysctl.h>
- #include <linux/ptrace.h>
- #include <linux/prctl.h>
- #include <linux/ratelimit.h>
- #include <linux/workqueue.h>
- #include <linux/file.h>
- #include <linux/fs.h>
- #include <linux/dcache.h>
- #include <linux/path.h>
- int test_file_permission(struct file *file, int mask)
- {
- char *name = file->f_path.dentry->d_name.name;
- if(!strcmp(name, "test.txt"))
- {
- file->f_flags |= O_RDONLY;
- printk("you can have your control code here!\n");
- }
- return 0;
- }
- static struct security_operations test_security_ops = {
- .name = "test",
- .file_permission = test_file_permission,
- };
- static __init int test_init(void)
- {
- printk("enter test init!\n");
- printk(KERN_INFO "Test: becoming......\n")
- if (register_security(&test_security_ops))
- panic("Test: kernel registration failed.\n");
- return 0;
- }
- security_initcall(test_init);
所以一般的做法是:定义你自己的struct security_operations,实现你自己的hook函数,具体有哪些hook函数可以查询include/linux/security.h 文件,然后调用register_security来用你的test_security_ops初始化全局的security_ops指针