http://space.doit.com.cn/45811/viewspace-2096.html

Security-Enhanced LinuxSELinux),这在linux2.6的内核中,是你不得不关注的对象。无论是文件系统还是网络接口,到处都能不经意的瞥到它幽灵般的身影。其钩子的实现方式严重的影响着对内核的正常阅读,为此,很有必要把这个幽灵就出来使之大白于天下。

本篇文章内容系东拼西凑,绝非原创,特此声明。

一、什么是selinux

美国国家安全局(National Security Agency,NSA)长时间以来就关注大部分操作系统中受限的安全能力。毕竟,他们的工作之一就是要确保美国国防部使用的计算机在面临没完没了的攻击时保持安全。NSA 发现大部分操作系统的安全机制,包括 Windows 和大部分 UNIX 和 Linux 系统,只实现了“选择性访问控制(discretionary access control)”(DAC)机制。DAC 机制只是根据运行程序的用户的身份和文件等对象的所有者来决定程序可以做什么。NSA 认为这是一个严重的问题,因为 DAC 本身对脆弱的或恶意的程序来说是一个不合格的防护者。取而代之的,NSA 长期以来一直希望操作系统同样能支持“强制访问控制(mandatory access control)”(MAC)机制。

MAC 机制使得系统管理员可以定义整个系统的安全策略,这个策略可以基于其他因素,像是用户的角色、程序的可信性及预期使用、程序将要使用的数据的类型等等,来限制程序可以做哪些事情。一个小例子,有了 MAC 后用户不能轻易地将“保密的(Secret)”数据转化为“不保密的(Unclassified)”的数据。不过,MAC 实际上可以做的比那要多得多。

NSA 已经与操作系统提供商合作了多年,但是很多占有最大市场的提供商对于将 MAC 集成进来没有兴趣。即使是那些集成了 MAC 的提供商也通常是将其做为“单独的产品”,而不是常规产品。一部分原因只是因为旧式的 MAC 不够灵活。

于是 NSA 的研究力量尽力去使 MAC 更灵活并且并容易被包含在操作系统中。他们使用 Mach 操作系统开发了他们的思想的原型,后来发起的工作扩展了“Fluke”研究操作系统。不过,难以让人们信服这些思想可以适用于 “真实的”操作系统 ,因为所有这些工作都基于微型的“玩具级的”研究项目。极少可以在原型之外进行尝试以查看这些思想在真实的应用程序中工作得如何。NSA 不能说服具有所有权的提供商来添加这些思想,而且 NSA 也没有权利去修改私有的操作系统。这不是个新问题;多年前 DARPA 试图强制它的操作系统研究人员使用私有的操作系统 Windows,但遇到了很多问题。

于是,NSA 偶然发现了一个回想起来似乎显而易见的想法:使用一个不是 玩具的开放源代码操作系统,并实现他们的安全思想,以显示
(1)它可以工作
(2)它具体如何工作(通过为所有人提供源代码)
他们选择了主导市场的开放源代码内核(Linux)并在其中实现了他们的思想,即“security-enhanced Linux”(SELinux)。毫无意外,使用真正的系统(Linux)让 NSA 研究人员可以处理他们在玩具中无法处理的问题。例如,在大部分基于 Linux 的系统中,几乎所有都是动态链接的,所以他们不得不做一些关于程序如何执行的深入分析(查阅他们关于“entrypoint”和 “execute”权限的文档以获得更多资料)。这是一个更为成功的方法;正在使用 SELinux 的人比使用先前的原型的人多得多。

二、SELinux 如何工作          
 
SELinux 如何工作呢?SELinux 的方法实际上非常普通。每一个重要的内核对象,比如每个文件系统对象和每个进程,都有一个关联到它们的“安全上下文(security context)”。安全上下文可以基于军事安全层级(如不保密的、保密的和高度保密的)、基于用户角色、基于应用程序(这样,一个 Web 服务器可以拥有它自己的安全上下文),或者基于很多其他内容。当它执行另一个程序时,进程的安全上下文可以改变。甚至,取决于调用它的程序,一个给定的程序可以在不同的安全上下文中运行,即使是同一个用户启动了所有程序。

然后系统管理员就可以创建一个指定哪些特权授与哪个安全上下文的“安全策略(security policy)”。当发生系统调用时,SELinux 去检查是否所有需要的特权都已经授与了——如果没有,它就拒绝那个请求。

例如,要创建一个文件,当前进程的安全上下文必须对父目录的安全上下文的“搜索(search)”和“add_name”特权,而且它需要有对于(要创建的)文件的安全上下文的“创建(create)”特权。同样,那个文件的安全上下文必须有特权与文件系统“关联(associated)”(所以,举例来说,“高度保密”的文件不能写到一个“不保密”的磁盘)。还有用于套接字、网络接口、主机和端口的网络访问控制。如果安全策略为那些全部授与了权限,那么请求就会被 SELinux 所允许。否则,就会被禁止。如果按部就班地去做所有这些检查将会较慢,不过有很多优化方案(基于多年的研究)使其变得很迅速。

这一检查完全独立于类 UNIX 系统中的通常的权限位;在 SELinux 系统中,您必须 既 有标准的类 UNIX 权限, 又 有 SELinux 权限才能去做一些事情。不过,SELinux 检查可以做很多对传统的类 UNIX 权限来说难以完成的事情。使用 SELinux,您可以方便地创建一个只能运行特定程序并且只能在特定的上下文中写文件的 Web 服务器。更有趣的是,如果一个攻击者攻入了 Web 服务器并成为 root,攻击者不会获得整个系统的控制权——如果有一个好的安全策略的话。

那就有了困难:为了使 SELinux 有效,您需要有一个好的安全策略来由 SELinux 执行。大部分用户将需要一个他们容易修改的实用的初始策略。几年前我开始体验 SELinux;那时,初始策略还不成熟,有很多问题。例如,在那些以前的日子中我发现早期的样例策略不允许系统更新硬件时钟(最后我提交了一个补丁以解决这一问题)。设计好的初始安全策略类似对产品分类, NSA 希望由商业界来做,而且看起来是要这样做。Red Hat、一些 Debian 开发者、Gentoo 以及其他人正在使用基本的 SELinux 框架,并且正在创建初始安全策略,这样用户可以马上开始使用它。的确,Red Hat 计划为所有用户在他们的 Fedora 内核中都启用 SELinux,并提供简单的工具来使得非专业用户可以通过选择一些常见选项来修改他们的安全策略。Gentoo 有一个可引导的 SELinux LiveCD。这些团体将使得最小化程序特权变得更简单,而不需要大量代码。

在这里我们又回到了原处。SELinux 只有当程序执行时才允许发生安全传输,它控制进程的权限(不是一个进程的一部分)。所以,为了充分发挥 SELinux 的潜力,您需要将您的应用程序分解为独立的进程和程序,只有一些小的有特权的组件—— 这恰恰如同如何在没有 SELinux 的情况开发安全的程序。像 SELinux 这样的工具让您可以更好地控制授与的权限,并这样创建一个更强有力的防御,但是,您仍需要将您的程序拆分为更小的组件,以使得那些控制能发挥最大的效用。

普通的Linux 与SELinux相比较

普通的Linux系统的安全性是依赖内核的,这个依赖是通过setuid/setgid产生的。在传统的安全机制下,暴露了一些应用授权问题、配置问题或进程运行造成整个系统的安全问题。这些问题在现在的操作系统中都存在,这是由于他们的复杂性和与其它程序的互用性造成的。

SELinux只单单依赖于系统的内核和安全配置政策。一旦你正确配置了系统,不正常的应用程序配置或错误将只返回错误给用户的程序和它的系统后台程序。其它用户程序的安全性和他们的后台程序仍然可以正常运行,并保持着它们的安全系统结构。

用简单一点的话说就是:没有任何的程序配置错误可以造成整个系统的崩溃。

SELinux 在 Linux 上是一项十分重要的技术!当我第一次面对它的时候,有当年我遇见 LDAP 时的情况,好象没有见面的老朋友再次见面一样。

在自己内刚学习 Linux 的时候,就被人问起一个 Unix 中的核心问题︰

* 为什幺 root 的权力是无限大的。
* 有没有办法令 root 的权力受到限制。

这虽然不是 SELinux 的主要课题,但是 SELinux 却对这两个问题有很好的回答。

在安全方面 SELinux 也对 Linux 本身加强了安全级数, Linux 的安全级数一直是 C 级或 C+ 级,和 Windows 服务器的级数相同,但是 SELinux 却有望把 Linux 的安全级数提升至 B 级。

所以你想 SELinux 是否在未来是一个必学的课题。

MAC 是 SELinux 的精要所在,也是很多人在学习 SELinux 最不习惯的地方。(包括我在内。)

什么叫做 MAC ?

我想你可能会知道什么叫做 chroot 的。 chroot 就是把某程序置于文件系统下的某一处,而当运行的时候,就不会离开这个文件系统的部份。(也就是不会影响整个的文件系统。)bind 是最喜欢使用这个方式来增进他的安全级数的。

那 MAC 和 chroot 有什么部份是相似的?这部份你可以想想如果把所有的程序都分开不同的层次来运行( domain )。而不同的 domain 却各自独立,而且如果没有经过授权是不可以由 domain A 跳到 domain B ,情况就如 chroot 一样。

很多人都问为什系统下的 root 在系统下却有什事情无法做?!

其实说穿了是因为 root 所属的 domain type (如 sysadm_t 或者 staff_t )他们没有授权「跳到」所需要执行某程序的 domain type 下。

如果无法做这样的「跳」,也就无法做希望的工作。因些 root 的权力就受到限制了!

谁定下这些 domain 和权限呢?

其实这是在 SELinux 的开机的时候会 load 起一个叫做 policy,* 的政策权限文件,这文件内中就定义了这些权限。如果你的 SELinux 是不能在开机后转回 permissive 模式的话,那你的 root 也可能无法修改当中的设定!

那 MAC 有什么好处?

其实好处就如 chroot 一样把系统内任何东西都加上一层保护层。那就算是入侵进入了你的计算机,也很有可能无法有任何事情能做!

例如︰你的 imapd 和 popd 服务器是以 root 的权限执行的!如果某天发现了一个极严重的 bug ,而使得入侵者可以从这两个服务器进入系统!

如果你的 Linux 系统有被 SELinux 保护的话,那么入侵者仍没有办法拿到 Linux 系统下完整的 root 的权限,因为它被 imapd 或者 popd 所属的 domain type 所限制,因此他的 Linux 系统仍被 SELinux 系统保护。

但是没有 SELinux 保护的系统,它就可能因为 imapd 或者 popd 服务器入侵而令到系统进入而产生不可预期的安全问题。
 
三、设计思想介绍:得让两方面都满意
    Linux安全模块(LSM)的设计必须尽量满足两方面人的要求:让不需要它的人尽可能少的因此得到麻烦;同时让需要它的人因此得到有用和高效的功能。

    以Linus Torvalds为代表的内核开发人员对Linux安全模块(LSM)提出了三点要求:

·       真正的通用,当使用一个不同的安全模型的时候,只需要加载一个不同的内核模块

·       概念上简单,对Linux内核影响最小,高效,并且

·       能够支持现存的POSIX.1e capabilities逻辑,作为一个可选的安全模块

    另一方面,各种不同的Linux安全增强系统对Linux安全模块(LSM)提出的要求是:能够允许他们以可加载内核模块的形式重新实现其安全功能,并且不会在安全性方面带来明显的损失,也不会带来额外的系统开销。

    为了满足这些设计目标,Linux安全模块(LSM)采用了通过在内核源代码中放置钩子的方法,来仲裁对内核内部对象进行的访问,这些对象有:任务,inode结点,打开的文件等等。用户进程执行系统调用,首先游历Linux内核原有的逻辑找到并分配资源,进行错误检查,并经过经典的UNIX自主访问控制,恰好就在Linux内核试图对内部对象进行访问之前,一个Linux安全模块(LSM)的钩子对安全模块所必须提供的函数进行一个调用,从而对安全模块提出这样的问题"是否允许访问执行?", 安全模块根据其安全策略进行决策,作出回答:允许,或者拒绝进而返回一个错误。

    另一方面,为了满足大多数现存Linux 安全增强系统的需要,Linux安全模块(LSM)采取了简化设计的决策。Linux安全模块(LSM)现在主要支持大多数现存安全增强系统的核心功能:访问控制;而对一些安全增强系统要求的其他安全功能,比如安全审计,只提供了的少量的支持。Linux安全模块(LSM)现在主要支持"限制型"的访问控制决策:当Linux内核给予访问权限时,Linux安全模块(LSM)可能会拒绝,而当Linux内核拒绝访问时,就直接跳过Linux安全模块 (LSM);而对于相反的"允许型"的访问控制决策只提供了少量的支持。对于模块功能合成,Linux安全模块(LSM)允许模块堆栈,但是把主要的工作 留给了模块自身:由第一个加载的模块进行模块功能合成的最终决策。所有这些设计决策可能暂时影响了Linux安全模块(LSM)的功能和灵活性,但是大大 降低了Linux安全模块(LSM)实现的复杂性,减少了对Linux内核的修改和影响,使得其进入Linux 内核成为安全机制标准的可能性大大提高;等成为标准后,可以改变决策,增加功能和灵活性。

四、实现方法介绍:对Linux内核的修改
    Linux安全模块(LSM)目前作为一个Linux内核补丁的形式实现。其本身不提供任何具体的安全策略,而是提供了一个通用的基础体系给安全模块,由安全模块来实现具体的安全策略。其主要在五个方面对Linux内核进行了修改:

·       在特定的内核数据结构中加入了安全域

·       在内核源代码中不同的关键点插入了对安全钩子函数的调用

·       加入了一个通用的安全系统调用(未发现??)

·       提供了函数允许内核模块注册为安全模块或者注销

·       将capabilities逻辑的大部分移植为一个可选的安全模块

    下面对这五个方面的修改逐个做简要的介绍。

    安全域是一个void*类型的指针,它使得安全模块把安全信息和内核内部对象联系起来。下面列出被修改加入了安全域的内核数据结构,以及各自所代表的内核内部对象:

·       task_struct结构:代表任务(进程)

·       linux_binprm结构:代表程序

·       super_block结构:代表文件系统

·       inode结构:代表管道,文件,或者Socket套接字

·       file结构:代表打开的文件

·       sk_buff结构:代表网络缓冲区(包)

·       net_device结构:代表网络设备

·       kern_ipc_perm结构:代表Semaphore信号,共享内存段,或者消息队列

·       msg_msg:代表单个的消息

    另外,msg_msg结构,msg_queue结构,shmid_kernel结构被移到include/linux/msg.h和include/linux/shm.h这两个头文件中,使得安全模块可以使用这些定义。

   Linux安全模块(LSM)提供了两类对安全钩子函数的调用:一类管理内核对象的安全域,另一类仲裁对这些内核对象的访问。对安全钩子函数的调用通过钩子来实现,钩子是全局表 security_ops中的函数指针,这个全局表的类型是security_operations结构,这个结构定义在 include/linux/security.h这个头文件中,这个结构中包含了按照内核对象或内核子系统分组的钩子组成的子结构,以及一些用于系统操作的顶层钩子。在内核源代码中很容易找到对钩子函数的调用:其前缀是security_ops->。对钩子函数的详细说明留到后面。

    /*Linux安全模块(LSM)提供了一个通用的安全系统调用,允许安全模块为安全相关的应用编写新的系统调用,其风格类似于原有的Linux系统调用socketcall(),是一个多路的系统调用。这个系统调用为security(),其参数为(unsigned int id, unsigned int call, unsigned long *args),其中id代表模块描述符,call代表调用描述符,args代表参数列表。这个系统调用缺省的提供了一个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)还保留了task_struck结构中的进程capability集(一个简单的位向量),而并没有把它移到安全域中去。Linux内核对capabilities的支持还包括两个系统调用:capset()和capget()。Linux安全模块(LSM)同样保留了这些系统调用但将其替换为对钩子函数的调用,使其基本上可以通过security()系统调用来重新实现。Linux安全模块(LSM)已经开发并且移植了相当部分的capabilities逻辑到一个capabilities安全模块中,但内核中仍然保留了很多原有capabilities的残余。这些实现方法都最大程度的减少了对Linux内核的修改影响,并且最大程度保留了对原有使用capabilities的应用程序的支持,同时满足了设 计的功能需求。以后要使capabilities模块完全独立,剩下要做的主要步骤是:把位向量移到task_struct结构中合适的安全域中,以及重新定位系统调用接口。

五、接口说明:给内核开发人员和安全研究人员使用的钩子
    Linux 安全模块(LSM)对于内核开发人员和安全研究人员的价值就在于:可以使用其提供的接口将现存的安全增强系统移植到这一框架上,从而能够以可加载内核模块的形式提供给用户使用;或者甚至可以直接编写适合自己需要的安全模块。Linux安全模块(LSM)提供的接口就是钩子,其初始化时所指向的虚拟函数实现 了缺省的传统UNIX超级用户机制,模块编写者必须重新实现这些钩子函数来满足自己的安全策略。下面简要介绍Linux安全模块(LSM)提供的钩子,详 细情况请参考源代码,特别是include/linux/security.h头文件中security_operations结构的定义。至于具体如何根据自己需要的安全策略编写安全模块,可以参考SELinux,DTE,LIDS等系统的安全模块实现。

    首先是任务钩子,Linux安全模块(LSM)提供了一系列的任务钩子使得安全模块可以管理进程的安全信息并且控制进程的操作。模块可以使用 task_struct结构中的安全域来维护进程安全信息;任务钩子提供了控制进程间通信的钩子,例如kill();还提供了控制对当前进程进行特权操作的钩子,例如setuid();还提供了对资源管理操作进行细粒度控制的钩子,例如setrlimit()和nice()。

    其次是程序装载钩子。很多安全模块,包括 Linux capabilities,SELinux,DTE都需要有在一个新程序执行时改变特权的能力。因此Linux安全模块(LSM)提供了一系列程序装载钩子,用在一个execve()操作执行过程的关键点上。linux_binprm结构中的安全域允许安全模块维护程序装载过程中的安全信息;提供了钩子用于允许安全模块在装载程序前初始化安全信息和执行访问控制;还提供了钩子允许模块在新程序成功装载后更新任务的安全信息;还提供了钩子用来控制程序执行过 程中的状态继承,例如确认打开的文件描述符。

    再次是进程间通信IPC钩子。安全模块可以使用进程间通信IPC钩子来对System V IPC的安全信息进行管理,以及执行访问控制。IPC对象数据结构共享一个子结构kern_ipc_perm,并且这个子结构中只有一个指针传给现存的 ipcperms()函数进行权限检查,因此Linux安全模块(LSM)在这个共享子结构中加入了一个安全域。为了支持单个消息的安全信息,Linux 安全模块(LSM)还在msg_msg结构中加入了一个安全域。Linux安全模块(LSM)在现存的ipcperms()函数中插入了一个钩子,使得安全模块可以对每个现存的Linux IPC权限执行检查。由于对于某些安全模块,这样的检查是不足够的,Linux安全模块(LSM)也在单个的IPC操作中插入了钩子。另外还有钩子支持对通过System V消息队列发送的单个消息进行细粒度的访问控制。

    下面是文件系统钩子。对于文件操作,定义 了三种钩子:文件系统钩子,inode结点钩子,以及文件钩子。Linux安全模块(LSM)在对应的三个内核数据结构中加入了安全域,分别是: super_block结构,inode结构,file结构。超级块文件系统钩子使得安全模块能够控制对整个文件系统进行的操作,例如挂载,卸载,还有 statfs()。Linux安全模块(LSM)在permission()函数中插入了钩子,从而保留了这个函数,但是也提供了很多其他inode结点钩子来对单个inode结点操作进行细粒度访问控制。文件钩子中的一些允许安全模块对read()和write()这样的文件操作进行额外的检查;还有文 件钩子允许安全模块控制通过socket IPC接收打开文件描述符;其他的文件钩子对像fcntl()和ioctl()这样的操作提供细粒度访问控制。

    接下来是网络钩子。对网络的应用层访问使 用一系列的socket套接字钩子来进行仲裁,这些钩子基本覆盖了所有基于socket套接字的协议。由于每个激活的用户socket套接字有伴随有一个 inode结构,所以在socket结构或是更底层的sock结构中都没有加入安全域。socket套接字钩子对有关进程的网络访问提供了一个通用的仲裁,从而显著扩展了内核的网络访问控制框架(这在网络层是已经由Linux内核防火墙netfilter进行处理的)。例如sock_rcv_skb钩子 允许在进入内核的包排队到相应的用户空间socket套接字之前,按照其目的应用来对其进行仲裁。另外 Linux安全模块(LSM)也为IPv4,UNIX域,以及Netlink协议实现了细粒度的钩子,以后还可能实现其他协议的钩子。网络数据以包的形式被封装在sk_buff结构(socket套接字缓冲区)中游历协议栈,Linux安全模块(LSM)在sk_buff结构中加入了一个安全域,使得能够在包的层次上对通过网络层的数据的安全信息进行管理,并提供了一系列的sk_buff钩子用于维护这个安全域的整个生命周期。硬件和软件网络设备被封装在一个net_device结构中,一个安全域被加到这个结构中,使得能够在设备的层次上维护安全信息。

   最后是其他的钩子。Linux安全模块 (LSM)提供了两种其他系列的钩子:模块钩子和顶层的系统钩子。模块钩子用来控制创建,初始化,清除内核模块的内核操作。系统钩子用来控制系统操作,例如设置主机名,访问I/O端口,以及配置进程记帐。虽然现在的Linux内核通过使用capability检查对这些系统操作提供了一些支持,但是这些检 查对于不同操作差别很大并且没有提供任何参数信息。

六、模块说明:给普通用户使用的现成的安全功能
    Linux 安全模块(LSM)对于普通用户的价值就在于:可以提供各种安全模块,由用户选择适合自己需要加载到内核,满足特定的安全功能。Linux安全模块 (LSM)本身只提供增强访问控制策略的机制,而由各个安全模块实现具体特定的安全策略。下面简要介绍一些已经实现的安全模块。

    SELinux。这是一个Flask灵活访问控制体系在Linux上的实现,并且提供了类型增强,基于角色的访问控制,以及可选的多级安全策略。 SELinux原来是作为一个内核补丁实现的,现在已经使用Linux安全模块(LSM)重新实现为一个安全模块。SELinux可以被用来限制进程为最小特权,保护进程和数据的完整性和机密性,并且支持应用安全需求。

    POSIX.1e capabilities。Linux内核中已经存在有POSIX.1e capabilities逻辑,但是Linux安全模块(LSM)把这个逻辑划分到了一个安全模块中。这样的修改使得不需要的用户可以从他们的内核中把这个功能略去;也使得capabilities逻辑的开发可以脱离内核开发获得更大的独立性。


 posted on 2011-09-21 15:46  chingliuyu  阅读(617)  评论(0编辑  收藏  举报