嗯!如题,一个简单的基于LSM的沙箱设计。环境是Linux v4.4.28。一个比较新的版本,所以在实现过程中很难找到资料,而且还有各种坑逼,所以大部分的时间都是在看源码,虽然写的很烂,但是感觉收获还是挺大的。

  具体思路很简单,每个进程都有对应一个task_struct。可以使用task_struct来对进程的行为进行监测和限制,换句话来说,沙箱是基于进程实现的。

  正如官方文档所说的,LSM在Linux关键数据结构中添加了透明的安全域,具体实现应该是这样的

  

task_struct{
...
void *security;
...
}

  然而,task_struct 的安全透明域在2.6.28以后的版本就没有了(后来才发现放到cred里面去了)刚发现这一点的时候表示很懵逼,所以,为了不去重新设计实现思路或者换版本实现,所以在task_struct里面添加了void * f_security。配套的安全域初始化hook可以换个思路来进行实现,所以不多添加钩子了。

  1:设计一下安全域的数据结构。

  先来看一下SELinux的设计:

 struct task_security_struct {
u32 osid; /* SID prior to last execve */
u32 sid; /* current SID */
u32 exec_sid; /* exec SID */
u32 create_sid; /* fscreate SID */
u32 keycreate_sid; /* keycreate SID */
u32 sockcreate_sid; /* fscreate SID */
};

  简单一点就设计我这样:

    struct security_demo_task{
    int demo_sid;
     u32 task_sid;
  u32 ptrace_sid;
  u32 socket_sid;
  u32 file_sid;
  /*
  u32 cap_sid;
  u32 inode_sid;
  u32 ipc_sid;
  u32 msg_sid;
  u32 task_sid;
  u32 dev_sid;
  u32 audit_sid;
*/
};

  可以根据实现的具体情况对各个sid的值进行宏定义,我的宏定义为:

#define DEMO_ON 1
#define DEMO_OFF 0
#define DEMO_PTRACE_ON 1
#define DEMO_PTRACE_OFF 0
#define DEMO_FILE_ON 1
#define DEMO_FILE_OFF 0
#define DEMO_SOCKET_ON 1
#define DEMO_SOCKET_OFF 0
#define DEMO_SCOPE_DISABLED    0
#define DEMO_SCOPE_LEARNING 1
#define DEMO_SCOPE_ENABLE 2
#define DEMO 0 

#define DEMO_PTRACE 1 

#define DEMO_FILE 2 

#define DEMO_SOCKET 3

  对安全域的初始化,在SELinux,在钩子里队每一个task自动初始化,SElinux因为对每个task都设计了安全域,为了提高内存利用效率,还用了SLAB。而由于我的沙箱只是针对具体进程,所以干脆直接用了kmalloc和kfree进行分配并添加了一个系统调用。代码对比如下:

//SLinux
static void cred_init_security(void)
{
struct cred *cred = (struct cred *) current->real_cred;
struct task_security_struct *tsec; tsec = kzalloc(sizeof(struct task_security_struct), GFP_KERNEL);
if (!tsec)
panic("SELinux: Failed to initialize initial task.\n"); tsec->osid = tsec->sid = SECINITSID_KERNEL;
cred->security = tsec;
}

  而我的:

static int task_alloc_security(struct task_struct *task){
struct security_demo_task *tsec;
if(task->f_security != NULL){
printk("Demo:error!Aready Inint!");
return -EIO;
} tsec = kmalloc(sizeof(struct security_demo_task),GFP_KERNEL);
if (!tsec){
printk("Demo:error!kmalloc fail!");
return -ENOMEM;
}
tsec->demo_flag = DEMO_ON;
tsec->ptrace_flag = DEMO_PTRACE_ON;
tsec->file_flag = DEMO_FILE_ON;
tsec->socket_flag = DEMO_SOCKET_ON;
task->f_security = tsec; return ;
}

  二:对安全域的操作

  SELinux采用了AVC对其进行操作,具体的原理网上烂大街了,也不多做阐述。对于这种玩具型的LSM模块来说,可以自己直接添加一个系统调用就OK了。

SYSCALL_DEFINE3(task_security,pid_t,pid,int,type,int,value){
struct task_struct* pTaskStruct;
struct pid* p;
int err;
struct security_demo_task* st; //get task_struct by pid with rcu
rcu_read_lock();
p = find_vpid(pid);
pTaskStruct = pid_task(p,PIDTYPE_PID);
//printk("Test:name %s\n",pTaskStruct->comm);
rcu_read_unlock();
//下面代码用于检测各个属性域的sid,每个sid初步设置为open和close,其中的错误检测,就偷懒写成-EIO,具体的宏定义可以自己区看type.h
if(type == DEMO){
if(value == DEMO_ON){ err = task_alloc_security(pTaskStruct);
if(err){
printk("Demo:error! Init task_struct_security fail\n");
return -EIO;
}
printk("Demo:Init %s'f_security success!\n",pTaskStruct->comm);
}
else if(value == DEMO_OFF){
err = task_free_security(pTaskStruct);
if(err){
printk("Demo: Free %s's f_security fail!\n",pTaskStruct->comm);
return -EIO;
}
printk("Demo:Free %s'f_security success!\n",pTaskStruct->comm);
}
else{
printk("Demo:Invid Argument!Please check it,and try again");
return -EIO;
}
} else if(type == DEMO_PTRACE){
if(pTaskStruct->f_security == NULL){
printk("Demo:Dont be inint\n");
return -EIO;
}
st = (struct security_demo_task*)(pTaskStruct->f_security);
st->ptrace_flag = value;
printk("Demo:%s'->f_security-> ptrace_flag=%d\n",pTaskStruct->comm,st->ptrace_flag);
} else if(type == DEMO_FILE){
if(pTaskStruct->f_security == NULL){
printk("Demo:Dont be inint\n");
return -EIO;
}
st = (struct security_demo_task*)(pTaskStruct->f_security);
st->file_flag = value;
printk("Demo:%s'->f_security-> file_flag=%d\n",pTaskStruct->comm,st->file_flag);
} else if(type == DEMO_SOCKET){
if(pTaskStruct->f_security == NULL){
printk("Demo:Dont be inint\n");
return -EIO;
}
st = (struct security_demo_task*)(pTaskStruct->f_security);
st->socket_flag = value;
printk("Demo:%s'->f_security-> socket_flag=%d\n",pTaskStruct->comm,st->socket_flag);
} else{
printk("Demo:Invid Argument!Please check it,and try again");
return -EIO;
}
//st = (struct security_demo_task*)(pTaskStruct->f_security);
//printk("Test:Switch %d\n",st->demo_flag);
return ;
}

  三:具体设计

    每一个内核里的每个LSM模块(SELinux,Apparmor等)都分为了几个等级(Enable,Learning,Disable)。在这个简单的demo里我也是这样设计的,可以利用sysctl来注册一个变量,这里可以参考yama的具体实现或者直接看系列(二)的文章。至于Disable级,所有的函数Hook都被禁用,而学习模式,则将目标进程的活动全部进行记录,这里可以专门将一个ASCII码文件作为输出log,也可以省事点直接Printk出来。而在Enable级中,则是根据security_demo_task的具体sid来对进程的活动进行限制,所以说,安全域的安全属性越全,则沙箱的功能越强大:

    这里展示一下对创建进程的函数进行hook:

 

int demo_task_create(unsigned long clone_flags)
{
struct security_demo_task* sdt; if(demo_scope == DEMO_SCOPE_ENABLE && current->f_security != NULL){
  sdt = (struct security_demo_task*)(current->f_security);
  if (sdt->task_flag == DEMO_TASK_ON){
  printk("Demo:Forbid create task!");
       return -EIO;
   }
  else if(sdt->task_flag == DEMO_TASK_OFF){
   printk("Demo:OK!You can fork!");
  }
} if(demo_scope == DEMO_SCOPE_LEARNING && current->f_security != NULL){
  sdt = (struct security_demo_task*)(current->f_security);   if (sdt->task_flag == DEMO_TASK_ON){
   get_time();
  printk("%s try to fork a new task that forbid!\n",current->comm);
    }
}
return ; }

  四:其它

    大致设计就这么些了,想要扩展还是挺方便的,写代码的时候的一个很坑的一点是:模块不能动态加载!!所以呢!如果想好好写的话,最好还是先把源码改成可以动态加载,当然,怎么改就自己看源码咯!

  

 

学习LSM(Linux security module)之四:一个基于LSM的简单沙箱的设计与实现的更多相关文章

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

    各种折腾,经过了一个蛋疼的周末,终于在Ubuntu14.04上运行了一个基于LSM的简单demo程序. 一:程序编写 先简单的看一下这个demo: //demo_lsm.c#include <l ...

  2. 学习LSM(Linux security module)之一:解读yama

    最近打算写一个基于LSM的安全模块,发现国内现有的资料极少.因此打算自己琢磨一下.大致的学习路线如下: 由易至难使用并阅读两到三个安全模块->参照阅读模块自己实现一个安全模块->在自己实现 ...

  3. 学习LSM(Linux security module)之三:Apparmor的前世今生和基本使用

    感冒了,感觉一脑子浆糊,真是蛋疼. 先粗略讲一些前置知识. 一:MAC和DAC DAC(Discretionary Access Control),自主访问控制,是最常用的一类访问控制机制,意思为主体 ...

  4. Linux LSM(Linux Security Modules) Hook Technology

    目录 . 引言 . Linux Security Module Framework Introduction . LSM Sourcecode Analysis . LSMs Hook Engine: ...

  5. linux security module机制

    linux security module机制 概要 Hook机制,linux MAC的通用框架,可以使用SElinux, AppArmor,等作为不同安全框架的实现

  6. jmGraph:一个基于html5的简单画图组件

    jmGraph:一个基于html5的简单画图组件 特性: 代码书写简单易理解 面向对象的代码结构 对图形控件化 样式抽离 模块化:入seajs实现模块化开发 兼容性:暂只推荐支持html5的浏览器:i ...

  7. Mario是一个基于.NETCore的简单快速开发框架

    Mario .NET Core简单快速开发框架 Mario是一个基于.NET Core的简单快速开发框架 GitHub:https://github.com/deeround/Mario 技术特点 基 ...

  8. RabbitMQ学习之(五)_一个基于PHP的RabbitMQ操作类

    //amqp.php类文件 <?php class Amqp { public $e_name; public $q_name; public $k_route; public $channel ...

  9. 一个基于jQuery的简单树形菜单

    在工作中的项目使用的是一个前端基于 jQuery easyui 的一个系统,其中左侧的主菜单使用的是 easyui 中的 tree 组件,不是太熟悉,不过感觉不是太好用. 比如 easyui 中的 t ...

随机推荐

  1. 【题解】Bzoj2560串珠子

    挺强的……容斥+状压DP.首先想到如果可以求出f[k],f[k]代表联通状态为k的情况下的合法方案数,则f[k] = g[k] - 非法方案数.g[k]为总的方案数,这是容易求得的.那么非法方案数我们 ...

  2. MySQL之数据库及表的修改和删除

    本文章来自实验楼的操作过程和其中相应地解释.(博客园不知道怎么回事,上传图片总是失败.) 一.对数据库修改 1)删除数据库的命令为:DROP DATABASE 数据名; 二.对表的修改 1)重命名一张 ...

  3. 【BZOJ 3316】JC loves Mkk 01分数规划+单调队列

    单调栈不断吞入数据维护最值,数据具有单调性但不保证位置为其排名,同时可以按照进入顺序找出临近较值单调队列队列两端均可删除数据但只有队末可以加入数据,仍然不断吞入数据但同时可以额外刨除一些不符合条件的数 ...

  4. wyh的天鹅~vector的使用

    链接:https://www.nowcoder.com/acm/contest/93/L来源:牛客网 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 262144K,其他语言52428 ...

  5. HDU1859 最小长方形 (水

    最小长方形 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  6. 程序员的那些问题---转载自veryCD

    展望未来,总结过去10年的程序员生涯,给程序员小弟弟小妹妹们的一些总结性忠告   走过的路,回忆起来是那么曲折,把自己的一些心得体会分享给程序员兄弟姐妹们,虽然时代在变化,但是很可能你也会走我已经做过 ...

  7. ListView使用--文章集锦

    详解ListView加载网络图片的优化,让你轻松掌握! ListView具有多种item布局--实现微信对话列 关注公众号,分享干货,讨论技术

  8. struts2学习问题(一)

    一.struts2 Unknown tag (s:property). 解释:不识别标签 解决:这是sturts2的标签,导入相应的包<%@taglib prefix="s" ...

  9. c++对拍实现

    直接上代码吧. #include<bits/stdc++.h> using namespace std; int main(){ while(1){ system("./cute ...

  10. 【CF1023F】Mobile Phone Network(dsu,MST)

    题意: 保证原边以边权单调非减的顺序读入 思路:先把未知边加入,再加入原始边做MST,考虑从大到小,用数据结构维护,每一条原始边相当两个链赋值操作,每一条未知边相当于一个询问,答案即为询问之和 LCT ...