给linux安全模块LSM添加可链式调用模块(一)
前些日子接了个外包的活,了解了一下Linux安全模块,发现了安全模块中的一些问题。
关于linux安全模块LSM在此就不多说了,大家google下就明白了。
这里主要介绍的是如何修改这个模块,使它可链栈化。
关于LSM,旧版本的提供了register_security/mod_reg_security接口用于注册用户的安全模块,register_security注册接口只支持一个的安全模块存在,mod_reg_security 支持注册多个安全模块,不过模块之间的调用需要用户自己维护(也就是不提供多个安全模块并存的机制)。更不幸的是,2.6.19(据说这个版本)之后内核取消了mod_reg_security注册接口(鬼知道什么原因,据说是出于安全考虑,反正给我制造了不少的麻烦)。现在的内核模块只留下了register_security注册接口了。
问题来了,我是要帮人家写个安全模块的,现在内核不让我插入安全模块,这戏就没法玩了。
什么,还有一个register_security接口,呵呵,linux的安全模块selinux(红帽,centos发行版自带的安全模块)或者apparmor(ubuntu自带的安全模块)还有linux的capability模块都占着了它了,总不能让用户关了自带的安全模块用你的来玩吧(除非你比NSA还牛)。我写的安全模块主要是提供用户定制的一些安全机制,简单的说,就是需要第三方的规则。
好吧,废话不多说了,问题现在很明显了,我们的主题也就出来了。
简单介绍一下LSM模块吧,这里不得不佩服Linux的设计者,LSM模块提供的接口很简单,它提供了一个类似文件系统的抽象层一样,用户只要实现它提供的操作结构(security_operations结构),用它提供的安全模块注册接口注册到LSM模块下面就可以实现它自己的安全功能了。注册接口代码如下,其主要的工作为检查用户的security_operations结构实例,对一些未定义的接口,修改为默认行为,并把security_ops地址指针指向这个结构,这样就完成了安全模块的注册工作。
int __init register_security(struct security_operations *ops)
{
if (verify(ops)) {
printk(KERN_DEBUG "%s could not verify "
"security_operations structure.\n", __func__);
return -EINVAL;
} if (security_ops != &default_security_ops)
return -EAGAIN; security_ops = ops; return ;
}
了解完这些之后,我们来分析下一步该如何改造LSM吧:
LSM提供一个静态指针security_ops做为指向访问模块的渠道,安全模块如selinux等在系统启动之后就把这个指针修改为它们的模块结构地址了。
首先,我们想到的就是能不能找到这个指针,把这个指针引到我们的安全模块这边来。嘿嘿,那样我们的安全模块就活起来了。
其次,我们不能只管自己的模块,不管理系统的死活,至少你不能影响原来系统的安全机制吧。所以,也就是我们在让自己的安全模块活的同时,也让原来的安全模块一直存活着。
好了,思路分析清楚了,动手查查能不能做吧!
我们来找找security_ops在内核中的地址:
太好了,lsm中的静态指针地址就在内核符号表中列着,这就说明我们可以通过修改这个地址,去引用我们的安全模块。
这里给出内核探测引用这个指针的代码
/**
* probe_kernel_read - Wrapper for kernel_read().
*
* @file: Pointer to "struct file".
* @offset: Starting position.
* @addr: Buffer.
* @count: Size of @addr.
*
* Returns return value from kernel_read().
*/
static int __init probe_kernel_read(struct file *file, unsigned long offset,
char *addr, unsigned long count)
{
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8)
/*
* I can't use kernel_read() because seq_read() returns -EPIPE
* if &pos != &file->f_pos .
*/
mm_segment_t old_fs;
unsigned long pos = file->f_pos;
int result;
file->f_pos = offset;
old_fs = get_fs();
set_fs(get_ds());
result = vfs_read(file, (void __user *)addr, count, &file->f_pos);
set_fs(old_fs);
file->f_pos = pos;
return result;
#else
return kernel_read(file, offset, addr, count);
#endif
} /**
* probe_find_symbol - Find function's address from /proc/kallsyms .
*
* @keyline: Function to find.
*
* Returns address of specified function on success, NULL otherwise.
*/
void *__init probe_find_symbol(const char *keyline)
{
struct file *file = NULL;
char *buf;
unsigned long entry = ;
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18)
struct file_system_type *fstype = get_fs_type("proc");
struct vfsmount *mnt = vfs_kern_mount(fstype, , "proc", NULL);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8)
struct file_system_type *fstype = NULL;
struct vfsmount *mnt = do_kern_mount("proc", , "proc", NULL);
#else
struct file_system_type *fstype = get_fs_type("proc");
struct vfsmount *mnt = kern_mount(fstype);
#endif
struct dentry *root;
struct dentry *dentry;
/*
* We embed put_filesystem() here because it is not exported.
*/
if (fstype)
module_put(fstype->owner);
if (IS_ERR(mnt))
goto out;
root = dget(mnt->mnt_root);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16)
mutex_lock(&root->d_inode->i_mutex);
dentry = lookup_one_len("kallsyms", root, );
mutex_unlock(&root->d_inode->i_mutex);
#else
down(&root->d_inode->i_sem);
dentry = lookup_one_len("kallsyms", root, );
up(&root->d_inode->i_sem);
#endif
dput(root);
if (IS_ERR(dentry))
mntput(mnt);
else {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
struct path path = { mnt, dentry };
file = dentry_open(&path, O_RDONLY, current_cred());
#else
file = dentry_open(dentry, mnt, O_RDONLY
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
, current_cred()
#endif
);
#endif
}
}
if (IS_ERR(file) || !file)
goto out;
buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (buf) {
int len;
int offset = ;
while ((len = probe_kernel_read(file, offset, buf,
PAGE_SIZE - )) > ) {
char *cp;
buf[len] = '\0';
cp = strrchr(buf, '\n');
if (!cp)
break;
*(cp + ) = '\0';
offset += strlen(buf);
cp = strstr(buf, keyline);
if (!cp)
continue;
*cp = '\0';
while (cp > buf && *(cp - ) != '\n')
cp--;
entry = simple_strtoul(cp, NULL, );
break;
}
kfree(buf);
}
filp_close(file, NULL);
out:
return (void *) entry;
}
代码很简单,就是打开/proc/kallsyms文件,去找符号对应的地址,当然,这里考虑了内核版本的问题,使用#if/#endif等,代码写得比较乱。
下一步是设计一个合理的链式调用,使安全模块之前能链式调用,这个留着下遍再说吧。
给linux安全模块LSM添加可链式调用模块(一)的更多相关文章
- Linux kernel4.4.12 添加make menuconfig 可选项
Linux kernel 源码添加可选项 闲来无事,顺便记录一篇在Linux kernel make menuconfig 内添加一个可选项. 说不定将来就要用到这个东西呢. linux kernel ...
- apache2添加模块和添加站点
apache2添加模块和添加站点 linux下的apache2的目录和windows上的区别还是很大的,但是用起来却更方便了,详解请看另一篇文章http://www.cnblogs.com/wancy ...
- linux设备驱动归纳总结(二):模块的相关基础概念【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...
- Linux驱动学习(编写一个最简单的模块)
在Linux中想做驱动开发,那么一定要先熟悉module的使用和编写 一.什么是module 从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些 ...
- Kali linux 2016.2(Rolling)中的payloads模块详解
不多说,直接上干货! 前期博客 Kali linux 2016.2(Rolling)中的Exploits模块详解 payloads模块,也就是shellcode,就是在漏洞利用成功后所要做的事情.在M ...
- Lab1:Linux内核编译及添加系统调用(详细版)
实验一:Linux内核编译及添加系统调用(HDU) 花了一上午的时间来写这个,良心制作,发现自己刚学的时候没有找到很详细的,就是泛泛的说了下细节地方也没有,于是自己写了这个,有点长,如果你认真的看完了 ...
- 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念
linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
- linux下查看和添加PATH环境变量
linux下查看和添加PATH环境变量 $PATH:决定了shell将到哪些目录中寻找命令或程序,PATH的值是一系列目录,当您运行一个程序时,Linux在这些目录下进行搜寻编译链接. 编辑你的 PA ...
- linux显示git commit id,同时解决insmod模块时版本不一致导致无法加载问题
linux内核默认会包含git的commit ID. 而linux的内核在insmod模块时,会对模块和内核本身的版本做严格的校验.在开发产品时,改动内核后,由于commit ID变更,会导致linu ...
随机推荐
- jenkins环境搭建
1.官网下载jenkins的安装包,直接解压安装.jenkins-1.641.zip 2.修改jenkins端口,启动jenkins服务.E:\Program Files (x86)\Jenkins\ ...
- 用tcpdump分析tcp三次握手,四次挥手
1.tcpdump 简介 tcpdump是一个对网络上的数据包进行截获的包分析工具,一般linux系统以命令的形式使用 2.tcp三次握手 建立一个tcp连接会发生下面三个过程: 1.服务器必须准备好 ...
- setTimeout用于取消多次执行mouseover或者mouseenter事件,间接实现hover的悬停加载的效果.
Mouseenter在鼠标滑上去不会对其子元素也发生监听, Mouseover在鼠标滑上去会对其子元素发生监听. 所以对于事件的监听,我们要看需求,这里是对父元素的监听,不需要对子元素做监听.就用mo ...
- linux中解压缩并安装.tar.gz后缀的文件
1.解压缩: Linux下以tar.gz为扩展名的软件包,是用tar程序打包并用gzip程序压缩的软件包.要安装这种软件包,需要先对软件包进行解压缩,使用“tar -zxfv filename.tar ...
- JavaScript获取一段html片段中a标签的href值
最近,做项目中有一个需求,页面中有一个文本编辑器,里面写的内容最后生成了html代码片段,在另一个页面需要前一个页面文本编辑器的html代码片段中的a标签的href值,就尝试做了,因为不太熟悉js,所 ...
- 支持向量机(SVM)——python3实现
今天看完soft-margin SVM就又搜了下相关的代码,最后搜到这个,第一次看懂了SVM的实现. 关于代码中cvxopt的使用,可以看下这个简单的介绍. 这里还是将代码贴在这里,里面加了自己的一下 ...
- gnuplot安装的小问题
今天在学习NS2的过程中接触到了awk和gnuplot来分析延迟,丢包等情况. gnuplot是一款非常精巧的绘图工具,使用方法也很简单,功能却很强大. 安装还是通过终端: sudo apt-get ...
- Bash . configure permission denied错误
当你在Linux(我这里是Ubuntu10.04LTS Desktop)下编译安装某个包的时候,你首先是进入到解压目录然后执行“$ ./configure”的,但是有时候你会发现提示错误,错误提示是这 ...
- 一款基于bootstrap的datetimepicker
<!DOCTYPE HTML> <html> <head> <link href="http://netdna.bootstrapcdn.com/t ...
- DotNetBar 第1课,设置整体窗口样式
1. 先引用 DevComponents.DotNetBar2.dll 2. 窗口继承 Office2007Form public partial class Form1 : Office2007Fo ...