最近打算写一个基于LSM的安全模块,发现国内现有的资料极少。因此打算自己琢磨一下。大致的学习路线如下:

  由易至难使用并阅读两到三个安全模块->参照阅读模块自己实现一个安全模块->在自己实现的同时阅读LSM实现的基本源码,由于Yama代码量小,结构十分清晰,可以作为入门的demo进行参照。

  由于网上关于LSM的相关介绍已经烂大街了,就按自己的初步理解简单介绍一下LSM,详情可以自己阅读文后的相关链接,本文源码基于Linux4.8.0。

一:什么是LSM

  一种轻量级的安全访问控制框架,主要利用Hook函数对权限进行访问控制,并在部分对象中内置了透明的安全属性。

二:Yama的简单介绍和基本使用

  Yama主要是对Ptrace函数调用进行访问控制。

  Ptrace是一个系统调用,它提供了一种方法来让‘父’进程可以观察和控制其它进程的执行,检查和改变其核心映像以及寄存器。 主要用来实现断点调试和系统调用跟踪。利用ptrace函数,不仅可以劫持另一个进程的调用,修改系统函数调用和改变返回值,而且可以向另一个函数注入代码,修改eip,进入自己的逻辑。这个函数广泛用于调试和信号跟踪工具。所以说,对ptrace函数进行访问控制还是很有必要的。

  Yama一共分为四个等级:

#define YAMA_SCOPE_DISABLED    0
#define YAMA_SCOPE_RELATIONAL 1
#define YAMA_SCOPE_CAPABILITY 2
#define YAMA_SCOPE_NO_ATTACH 3

  其中YAMA_SCOPE_DISABLED代表yama并不起任何作用,YAMA_SCOPE_RELATIONAL代表只能ptarce子进程才能进行调试,YAMA_SCOPE_CAPABILITY,拥有CAP_SYS_PTRACE能力的进程才可以使用ptrace。而YAMA_SCOPE_NO_ATTACH代表没有任何进程可以attach,而且只要设置成了3就无法降级了。

  现在,先来测试使用一下,先将等级设为0。在root权限下进行:

  

  此时,任何ptrace都能够直接运行。

  被ptrace的demo程序如下:

//test.c 
#include<stdio.h>
int main()
{
while()
{
sleep();
static int i = ;
}
return ;
}

  得到结果如下:

  

  将等级设为一:

  

  等级设为二:

  可以通过setcap CAP_SYS_PTRACE=ep /usr/bin/strace给strace设置CAP_SYS_PTRACE权限,

  

  等级设为三:

  

 三:源码解析

  从开头看起

void __init yama_add_hooks(void)
{
pr_info("Yama: becoming mindful.\n"); //打印相关信息,可以通过dmesg | grep Yama:查看
security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); //添加安全模块函数
yama_init_sysctl(); //在中sysctl进行注册
}

  先简单解释一下yama_init_sysctl()函数,这个函数的作用是在sysctl中进行注册,使其能通过/proc/sys/kernel/yama/ptrace_scope进行设置参数,看具体源码:

static void __init yama_init_sysctl(void)
{
if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
panic("Yama: sysctl registration failed.\n");
}

  其中,yama_sysctl_path用于注明在/proc/sys目录下的具体位置,yama的定义如下:

struct ctl_path yama_sysctl_path[] = {
{ .procname = "kernel", },
{ .procname = "yama", },
{ }
};

  即在/proc/sys/kernel/yama目录下。

  yama_sysctl_table表示参数的相关信息,源码如下:

static int zero;//自动初始化为0
static int max_scope = YAMA_SCOPE_NO_ATTACH;
static struct ctl_table yama_sysctl_table[] = {
{
.procname = "ptrace_scope", //文件名
.data = &ptrace_scope, //实际参数在内核中的数据结构
.maxlen = sizeof(int), //对超过该最大长度的字符串截掉后面超长的部分.
.mode = 0644, //条目在proc文件系统下的权限
.proc_handler = yama_dointvec_minmax, //如上,对.proc_handler进行hook
.extra1 = &zero, //proc_handler的参数,即范围为0~3
.extra2 = &max_scope,
},
{ }
};

  proc_handler代表读写操作函数,对/proc/sys/kernel/yama/ptrace_scope进行读写时调用的函数。其中

    proc_dointvec 读写一个包含一个或多个整数的数组

    proc_dostring 读写一个字符串

    proc_dointvec_minmax 写的数组必须在min~max范围内。

  在这个数据结构中,自己构造了一个函数,来在操作之前进行了一些操作,如下

static int yama_dointvec_minmax(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
struct ctl_table table_copy;
/*
capable来对权限做出检查,检查是否有权对指定的资源进行操作,该函数返回0则代表无权操作
这里对ptrace_scope进行读写需要write置1而且需要用户有CAP_SYS_PTRACE权限
*/
if (write && !capable(CAP_SYS_PTRACE))
return -EPERM;
//当设置为最大值时,不再允许修改该参数
table_copy = *table;
if (*(int *)table_copy.data == *(int *)table_copy.extra2)
table_copy.extra1 = table_copy.extra2; return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
}

  核心函数是security_add_hooks函数,这个函数负责对ptrace进行访问控制,如下:

  先来看一下yama_hooks:

static struct security_hook_list yama_hooks[] = {
//上面两个hook就是对ptrace的两种方式进行分别check
LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check),
LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme),
LSM_HOOK_INIT(task_prctl, yama_task_prctl),
LSM_HOOK_INIT(task_free, yama_task_free),
};

  先看一下在内核中关于LSM_HOOK_INIT的相关定义,

  #define LSM_HOOK_INIT(HEAD, HOOK) { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
  可见该宏的作用就是来是填充security_hook_list,security_hook_list的,相关函数定义如下:

struct security_hook_list {
struct list_head list;
struct list_head *head;
union security_list_options hook;
};

  在介绍ptrace_access_check和ptrace_trace前需要补充一些相关知识:

    PTRACE_TRACEME和PTRACE_ATTACH是ptrace()函数TRACE的两种类型。这两种方式的主要区别可以概括为:

    PTRACE_TRACEME是子进程主动申请被TRACE。

    而PTRACE_ATTACH是父进程自己要attach到子进程,

    相当于子进程是被动的trace。

  继续,ptrace_may_access主要发生在发生在ptrace_attach,而ptrace_attch函数发生在ptrace()中。,ptrace_may_access函数的功能正如源码注释

    This check is used both for attaching with ptrace and for allowing access to sensitive information in /proc.

  ptrace_traceme函数发生在ptrace()调用前,主要的功能是做检查和设置PTRACE_TRACEME位,其中PTRACE_TRACEME表示程序已被跟踪。通过对这两个函数进行hook,就廊括了ptrace的所有情况了。

  先来看的yama_ptrace_access_check函数,代码如下:

static int yama_ptrace_access_check(struct task_struct *child,
unsigned int mode)
{
int rc = 0; /* require ptrace target be a child of ptracer on attach */
if (mode & PTRACE_MODE_ATTACH) {
switch (ptrace_scope) {
case YAMA_SCOPE_DISABLED:
/* No additional restrictions. */
break;
case YAMA_SCOPE_RELATIONAL: //进程可以跟踪有血缘关系(后代)的进程
rcu_read_lock();
if (!task_is_descendant(current, child) && //简单的遍历,查看进程是否是后代
!ptracer_exception_found(current, child) && //检测是否已有祖先attach了
!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))
rc = -EPERM;
rcu_read_unlock();
break;
case YAMA_SCOPE_CAPABILITY: //拥有CAP_SYS_PTRACE能力的进程才可以使用ptrace
rcu_read_lock();
if (!ns_capable(__task_cred(child)->user_ns, CAP_SYS_PTRACE))
rc = -EPERM;
rcu_read_unlock();
break;
case YAMA_SCOPE_NO_ATTACH: //无法进行ptrace
default:
rc = -EPERM;
break;
}
} if (rc && (mode & PTRACE_MODE_NOAUDIT) == 0) {
printk_ratelimited(KERN_NOTICE
"ptrace of pid %d was attempted by: %s (pid %d)\n",
child->pid, current->comm, current->pid);
} return rc;
}

  注释讲的很清楚了,通过switch ptrace_scope的值,对每种情况分别讨论。yama_ptrace_traceme 基本如下:

int yama_ptrace_traceme(struct task_struct *parent)
{
int rc = ; /* Only disallow PTRACE_TRACEME on more aggressive settings. */
switch (ptrace_scope) {
case YAMA_SCOPE_CAPABILITY:
/*
当用户父进程有CAP_SYS_PTRACE没有CAP_SYS_PTRACE时,返回失败
*/
if (!has_ns_capability(parent, current_user_ns(), CAP_SYS_PTRACE))
rc = -EPERM;
break;
/*
如果YAMA_SCOPE_NO_ATTACH,直接返回失败
*/
case YAMA_SCOPE_NO_ATTACH:
rc = -EPERM;
break;
}

  对于

    LSM_HOOK_INIT(task_prctl, yama_task_prctl),
LSM_HOOK_INIT(task_free, yama_task_free),

  这两个hook,主要是为了构建调试函数和被调试函数的关系,不多阐述,有兴趣可以自由阅读源码。

  

学习LSM(Linux security module)之一:解读yama的更多相关文章

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

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

  2. 学习LSM(Linux security module)之四:一个基于LSM的简单沙箱的设计与实现

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

  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. Linux Security模块

    一.Linux Security Modules Linux Security Modules (LSM) 是一种 Linux 内核子系统,旨在将内核以模块形式集成到各种安全模块中.在 2001 年的 ...

  7. [转帖]22个必须学习的Linux安全命令

    22个必须学习的Linux安全命令 http://os.51cto.com/art/201808/581401.htm Linux系统的安全性涉及很多方面,从设置帐户到确保用户合法,限制比完成工作所需 ...

  8. Automotive Security的一些资料和心得(8):Hardware Security Module (HSM)

    1. Introduction - 保护软件的安全性措施,作为值得信赖的安全锚,- 安全地生成,存储和处理安全性关键材料屏蔽任何潜在的恶意软件,?- 通过运用有效的限制硬件篡改攻击的可能性篡改保护措施 ...

  9. 学习嵌入式Linux有没有一个最佳的顺序(持续更新)

    作为一个嵌入式Linux的初学者,我知道我可能将长期处于初学者阶段,因为我至今仍然没有能够摸索出一条很好的道路让我由初学者进入到更高级阶段.但是我始终没有放弃,本篇文章就是用来记录我学习嵌入式Linu ...

随机推荐

  1. [LG1886]滑动窗口 单调队列

    ---题面--- 题解: 观察数据范围,这应该是一个复杂度O(n)的题.以最大值为例,考虑单调队列,维护一个单调递减的队列.从前向后扫,每次答案取队首,如果后面进入的比前面大,那么就弹出前面的数,因为 ...

  2. BZOJ_day???

    哇哈哈哈哈,这周能不能保持这个呢?

  3. 东北育才冲刺noip(day9)

    这十天来呢,感觉自己进步很大,(虽然被碾压的很惨),看到了自己以前完全没见过,也没想过的算法,打开新世界的大门. 同时呢,也感觉自己太弱了,于是就注册了这个博客. 为了促进进步,在这里立下flag,我 ...

  4. 插头dp题表

    bzoj1814: Ural 1519 Formula 1 bzoj3125: CITY bzoj1210: [HNOI2004]邮递员 bzoj2331: [SCOI2011]地板 bzoj1187 ...

  5. 【BZOJ 1724】[Usaco2006 Nov]Fence Repair 切割木板 堆+贪心

    堆对于stl priority_queue ,我们自己定义的类自己重载<,对于非自定义类我们默认大根堆,如若改成小根堆则写成std::priority<int,vector<int& ...

  6. Spring AOP execution表达式

    Spring中事务控制相关配置: <bean id="txManager" class="org.springframework.jdbc.datasource.D ...

  7. noip 2011观光公交

    P1315 观光公交 95通过 244提交 题目提供者该用户不存在 标签贪心递推2011NOIp提高组 难度提高+/省选- 提交该题 讨论 题解 记录   题目描述 风景迷人的小城Y 市,拥有n 个美 ...

  8. webpack3基础知识

    ## 本地化安装webpack ## 1. npm init //npm初始化生成package.json文件 2. npm install --save-dev webpack //安装webpac ...

  9. JS遮罩层弹框效果

    对于前端开发者来说,js是不可缺少的语言.现在我开始把我日常积累的一些js效果或者通过搜索自己总结的一些效果分享给大家,希望能够帮助大家一起进步,也希望大家能够多多支持! 1.今天我先分享一个遮罩层弹 ...

  10. Ansible 创建用户 Playbook 脚本

    创建用户,设置wheel组sudo不需要密码,然后将用户添加到wheel组,并将用户的公钥传输到节点上: --- - name: Linux Create User and Upload User P ...