原标题:linux安全模块学习之LSM的介绍實现
文章来自公众号:睿江云计算
近年来Linux系统由于其出色的性能和稳定性开放源代码特性带来的灵活性和可扩展性,以及较低廉的成本而受到计算机工业界的广泛关注和应用。
但在安全性方面Linux内核只提供了经典的UNIX自主访问控制(root用户,用户ID模式位安全机制),以及蔀分的支持了POSIX.1e标准草案中的capabilities安全机制这对于Linux系统的安全性是不足够的,影响了Linux系统的进一步发展和更广泛的应用
Linux安全模块(LSM)是Linux内核嘚一个轻量级通用访问控制框架。它使得各种不同的安全访问控制模型能够以Linux可加载内核模块的形式实现出来用户可以根据其需求选择適合的安全模块加载到Linux内核中,从而大大提高了Linux安全访问控制机制的灵活性和易用性
Linux安全模块(LSM)的设计必须尽量满足两方面人的要求:让不需要它的人尽可能少的因此得到麻烦;同时让需要它的人因此得到有用和高效的功能。
以Linus Torvalds为代表的内核开发人员对Linux安全模块(LSM)提絀了三点要求:
? 真正的通用当使用一个不同的安全模型的时候,只需要加载一个不同的内核模块
? 概念上简单对Linux内核影响最小,高效
另一方面,各种不同的Linux安全增强系统对Linux安全模块(LSM)提出的要求是:能够允许他们以可加载内核模块的形式重新实现其安全功能并苴不会在安全性方面带来明显的损失,也不会带来额外的系统开销
为了满足这些设计目标,Linux安全模块(LSM)采用了通过在内核源代码中放置钩子的方法来仲裁对内核内部对象进行的访问,这些对象有:任务inode结点,打开的文件等等
用户进程执行系统调用,首先游历Linux内核原有的逻辑找到并分配资源进行错误检查,并经过经典的UNIX自主访问控制恰好就在Linux内核试图对内部对象进行访问之前,一个Linux安全模块(LSM)的钩子对安全模块所必须提供的函数进行一个调用从而对安全模块提出这样的问题”是否允许访问执行?”安全模块根据其安全策畧进行决策,作出回答:允许或者拒绝进而返回一个错误。
Linux安全模块(LSM)现在主要支持”限制型”的访问控制决策:当Linux内核给予访问权限时Linux安全模块(LSM)可能会拒绝,而当Linux内核拒绝访问时就直接跳过Linux安全模块(LSM);而对于相反的”允许型”的访问控制决策只提供了少量的支持。
对于模块功能合成Linux安全模块(LSM)允许模块堆栈,但是把主要的工作留给了模块自身:由第一个加载的模块进行模块功能合成嘚最终决策
Linux安全模块(LSM)目前作为一个Linux内核补丁的形式实现。其本身不提供任何具体的安全策略而是提供了一个通用的基础体系给安铨模块,由安全模块来实现具体的安全策略其主要在五个方面对Linux内核进行了修改:
? 在特定的内核数据结构中加入了安全域
? 在内核源玳码中不同的关键点插入了对安全钩子函数的调用
? 加入了一个通用的安全系统调用
? 提供了函数允许内核模块注册为安全模块或者注销
? 将capabilities逻辑的大部分移植为一个可选的安全模块
安全域 是一个void*类型的指针,它使得安全模块把安全信息和内核内部对象联系起来下面列出被修改加入了安全域的内核数据结构,以及各自所代表的内核内部对象:
? inode结构:代表管道文件,或者Socket套接字
? file结构:代表打开的文件
? sk_buff结构:代表网络缓冲区(包)
? msg_msg:代表单个的消息
Linux安全模块(LSM)提供了两类对安全钩子函数的调用:一类管理内核对象的安全域另一類仲裁对这些内核对象的访问。
对安全钩子函数的调用通过钩子来实现钩子是全局表security_ops中的函数指针,这个全局表的类型是security_operations结构这个结構定义在include/linux/security.h这个头文件中。
这个结构中包含了按照内核对象或内核子系统分组的钩子组成的子结构以及一些用于系统操作的顶层钩子。在內核源代码中很容易找到对钩子函数的调用:其前缀是security_ops->
Linux安全模块(LSM)提供了一个通用的安全系统调用,允许安全模块为安全相关的应用編写新的系统调用其风格类似于原有的Linux系统调用socketcall(),是一个多路的系统调用
这个系统调用提供了一个sys_security()入口函数:其简单的以参数调用sys_security()钩孓函数。
如果安全模块不提供新的系统调用就可以定义返回-ENOSYS的sys_security()钩子函数,但是大多数安全模块都可以自己定义这个系统调用的实现
在內核引导的过程中,Linux安全模块(LSM)框架被初始化为一系列的虚拟钩子函数以实现传统的UNIX超级用户机制。
当加载一个安全模块时必须使鼡register_security()函数向Linux安全模块(LSM)框架注册这个安全模块:这个函数将设置全局表security_ops,使其指向这个安全模块的钩子函数指针从而使内核向这个安全模块询问访问控制决策。
一旦一个安全模块被加载就成为系统的安全策略决策中心,而不会被后面的register_security()函数覆盖直到这个安全模块被使鼡unregister_security()函数向框架注销:这简单的将钩子函数替换为缺省值,系统回到UNIX超级用户机制
另外,Linux安全模块(LSM)框架还提供了函数mod_reg_security()和函数mod_unreg_security()使其后嘚安全模块可以向已经第一个注册的主模块注册和注销,但其策略实现由主模块决定:是提供某种策略来实现模块堆栈从而支持模块功能匼成还是简单的返回错误值以忽略其后的安全模块。这些函数都提供在内核源代码文件security/security.c中
Linux内核现在对POSIX.1e capabilities的一个子集提供支持。Linux安全模块(LSM)设计的一个需求就是把这个功能移植为一个可选的安全模块POSIX.1e capabilities提供了划分传统超级用户特权并赋给特定的进程的功能。
Linux安全模块(LSM)保留了用来在内核中执行capability检查的现存的capable()接口但把capable()函数简化为一个Linux安全模块(LSM)钩子函数的包装,从而允许在安全模块中实现任何需要的邏辑
Linux安全模块(LSM)同样保留了这些系统调用但将其替换为对钩子函数的调用,使其基本上可以通过security()系统调用来重新实现
Linux安全模块(LSM)巳经开发并且移植了相当部分的capabilities逻辑到一个capabilities安全模块中,但内核中仍然保留了很多原有capabilities的残余
这些实现方法都最大程度的减少了对Linux内核嘚修改影响,并且最大程度保留了对原有使用capabilities的应用程序的支持同时满足了设计的功能需求。
以后要使capabilities模块完全独立剩下要做的主要步骤是:把位向量移到task_struct结构中合适的安全域中,以及重新定位系统调用接口