前些日子接了个外包的活,了解了一下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添加可链式调用模块(一)的更多相关文章

  1. Linux kernel4.4.12 添加make menuconfig 可选项

    Linux kernel 源码添加可选项 闲来无事,顺便记录一篇在Linux kernel make menuconfig 内添加一个可选项. 说不定将来就要用到这个东西呢. linux kernel ...

  2. apache2添加模块和添加站点

    apache2添加模块和添加站点 linux下的apache2的目录和windows上的区别还是很大的,但是用起来却更方便了,详解请看另一篇文章http://www.cnblogs.com/wancy ...

  3. linux设备驱动归纳总结(二):模块的相关基础概念【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59415.html linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10 ...

  4. Linux驱动学习(编写一个最简单的模块)

    在Linux中想做驱动开发,那么一定要先熟悉module的使用和编写 一.什么是module 从名字上看就是模块的意思,我个人的理解就是一个一个的小程序,可以进行动态的安装和卸载,而在这里面就实现一些 ...

  5. Kali linux 2016.2(Rolling)中的payloads模块详解

    不多说,直接上干货! 前期博客 Kali linux 2016.2(Rolling)中的Exploits模块详解 payloads模块,也就是shellcode,就是在漏洞利用成功后所要做的事情.在M ...

  6. Lab1:Linux内核编译及添加系统调用(详细版)

    实验一:Linux内核编译及添加系统调用(HDU) 花了一上午的时间来写这个,良心制作,发现自己刚学的时候没有找到很详细的,就是泛泛的说了下细节地方也没有,于是自己写了这个,有点长,如果你认真的看完了 ...

  7. 【Linux开发】linux设备驱动归纳总结(二):模块的相关基础概念

    linux设备驱动归纳总结(二):模块的相关基础概念 系统平台:Ubuntu 10.04 开发平台:S3C2440开发板 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  8. linux下查看和添加PATH环境变量

    linux下查看和添加PATH环境变量 $PATH:决定了shell将到哪些目录中寻找命令或程序,PATH的值是一系列目录,当您运行一个程序时,Linux在这些目录下进行搜寻编译链接. 编辑你的 PA ...

  9. linux显示git commit id,同时解决insmod模块时版本不一致导致无法加载问题

    linux内核默认会包含git的commit ID. 而linux的内核在insmod模块时,会对模块和内核本身的版本做严格的校验.在开发产品时,改动内核后,由于commit ID变更,会导致linu ...

随机推荐

  1. Socket异步存储示例

    异步客户端存储示例: using System; using System.Net; using System.Net.Sockets; using System.Threading; using S ...

  2. WIN7-64位安装PLSQL-Developer步骤

    可参与网址http://tech.ddvip.com/2012-07/1343104017178927.html 以下操作是从网上搜索在64位WIN7测试通过,64位无法使用PL/SQL Develo ...

  3. Theoretical comparison between the Gini Index and Information Gain criteria

    Knowledge Discovery in Databases (KDD) is an active and important research area with the promise for ...

  4. Java Base64 类

    package org.yp.ypfinancing.core.service.payV2.domain.service.Sdp.utils; public final class Base64 { ...

  5. Entity Framework Linq 动态组合where条件

    public static class PredicateExtensions { public static Expression<Func<T, bool>> True&l ...

  6. 区分苹果Safari浏览器

    区分苹果Safari浏览器 (function() { var Sys = {}; var ua = navigator.userAgent.toLowerCase(); var s; (s = ua ...

  7. Java技能树

    自古程序员爱黑Java,不黑不痛快的说,最近一周在V2EX上至少看到过不下5个黑Java的帖子,而且都是顶的老高,一共点开看了一个感觉好无趣啊.我到现在为止已经写了4年的C了,主要是做嵌入式驱动的开发 ...

  8. opensuse sublime 配置

    fcitx输入法支持: 准备文件sublime-imfix.c: /* sublime-imfix.c Use LD_PRELOAD to interpose some function to fix ...

  9. pip自动生成requirements.txt依赖关系清单

    Python项目中经常会带requirements.txt文件,里面是项目所依赖的包的列表,也就是依赖关系清单,这个清单也可以使用pip命令自动生成. pip命令: 1 pip freeze > ...

  10. 利用Ajax+MSMQ(消息队列)+WebService实现服务器端向客户端的信息推送

    需求: 每当数据库有数据更新时,推送到客户端 软需求: 1.服务器资源有限,要求资源占用尽可能小: 2.项目可控,不许调用第三方不可信不稳定的方法. 已有事例: 1.58到家采用的方法是TCP的长连接 ...