1.Selinux
2001年3月,美国国家安全局(NSA)发布了安全增强Linux(Selinux),它在Linux内核实现了灵活的和细粒度的非自主存取控制,并能够灵活地支持多种安全策略。Selinux的最初实现形式是作为一个特殊的核心补丁。
(1)安全体系结构
Selinux的安全体系结构被称为Flask,它是在犹他州大学和安全计算公司的协助下由NSA设计的,如图4-7所示。在Flask体系结构中,安全策略和通用接口一起封装在与操作系统独立的组件中,通用接口用于获得安全策略决策。这个单独的组件被称为安全服务器,它是一个内核子系统。
Flask由两部分组成,即策略(Policy)和实施(Enforcement)。策略封装在安全服务器中,实施由对象管理器具体执行。
系统内核的对象管理器执行系统的具体操作,当需要对安全性进行判断时,向安全服务器提出请求。对象管理器只关心SID。请求到达安全服务器后,实现与安全上下文(Security Context)的映射并进行计算,然后将决定的结果返回给对象管理器。
系统中关于安全的请求和决定有3种情况。
●Labeling Decision:确定一个新的主体或客体采用何种安全标签(如创建客体时)。
●Access Decision:确定主体是否能访问客体的某种服务(如文件读写)。
●Polyinstantiation Decision:确定一个进程在访问某个Polyinstantiation客体时,可不可以转为另一个进程(如从Login_T转到Netscape_T)。
图4-7 Selinux安全体系结构图
安全服务器是内核的子系统,用以实现对策略的封装并提供通用接口。Selinux的安全服务器实现了一种混合的安全性策略,包括类型实施(Type Enforcement)、基于角色的访问控制(Role-Based Access Control)和可选的多级别安全性(Optional Multilevel Security)。该策略由另一个称为Checkpolicy的程序编译,它由安全性服务器在引导时读取,生成一个文件/Ss_Policy。这意味着安全性策略在每次系统引导时都会有所不同,事实上策略甚至可以通过使用Security_Load_Policy接口在系统操作期间更改(只要将策略配置成允许这样的更改即可)。
Flask体系结构还提供一个访问向量缓存(AVC)模块,允许对象管理器缓存访问向量,以减小整体性能的损耗。在每次进行安全检查时,系统首先检查存放在AVC中的访问向量,如果存在此访问向量,则直接返回在AVC中的访问向量,否则向安全服务器提出查询请求,在安全服务器中根据主客体的SID及相应的类,针对相关的安全策略对请求进行检查,然后返回相应的访问向量,并把此访问向量存放在AVC中。
Flask有两个用于安全性标签但是与安全策略无关的数据类型:安全性上下文(Security Context)和安全性标识(SID)。安全性上下文是表示安全性标签的变长字符串,由以下几部分组成:用户、角色、类型和可选MLS范围,如Xxx_U:Xxx_R:Xxx_T:MLS。SID是由安全服务器映射到安全性上下文的一个整数。SID作为实际上下文的简单句柄服务于系统,只能由安全服务器解释。Flask通过称为对象管理器的构造执行实际的系统绑定。它们处理SID和安全性上下文,不涉及安全性上下文的属性。任何格式上的更改都不应该对对象管理器进行。
一般来说,对象管理器依据主体和客体的SID对和对象的类来查询安全服务器,目的在于获得访问决定——访问向量。类是标识对象是哪一种类(如是常规文件、目录、进程、UNIX域套接字还是TCP套接字)的整数。访问向量中的许可权通常由对象可以支持的服务和实施的安全性策略定义,并且访问向量许可权基于类来加以解释,因为不同种类的对象有不同的服务。例如,访问向量中使用的许可权位表示文件的“Unlink”许可权,也用于表示套接字的“Connect”许可权。向量可以高速缓存在访问向量高速缓存(AVC)中,也可以和对象一起存储,这样对象管理器就不必被那些已执行决策的请求淹没。
(2)安全策略配置
Selinux系统中的每个主体都有一个域(Domain),每个客体都有一个类型(Type)。在Selinux中统一将域和类型定义成为类型。策略的配置决定对类型的存取是否被允许,以及一个域能否转移到另一个域等。类型的概念应用到应用程序中时,可以决定类型是否可以由域执行。某个类型被执行时,可以从一个域跳转到另一个域,这就保证了每个应用程序属于它们自己的域,以防止恶意程序进行破坏。
角色也在配置中进行了定义。每个进程都有一个与之相关的角色:系统进程以System_R角色运行,而用户可以是User_R或Sysadim_R。配置还枚举了可以由角色输入的域。假设用户执行一个程序“Foobar”。通过执行它,用户转移到User_Foobar_T域。该域可能只包含一小部分与该用户初始登录相关的User_T域中的许可权。
安全策略配置目标包括控制对数据的原始访问、保护内核和系统软件的完整性、防止有特权的进程执行危险的代码,以及防止由于特权进程的缺陷所导致的破坏。
策略可根据策略文件灵活生成,Selinux中的策略定义非常广泛、灵活。客体的类型定义包括:Security、Device、File、Procfs、Devpts、Nfs、Network;主体的域的策略定义包括Admin、Program、System、User。策略是由策略语言生成的,这个生成过程对用户来讲是透明的。Selinux系统采用M4宏处理语言作为系统策略语言。
2.Linux安全模块(LSM)
(1)设计思想(www.xing528.com)
作为对NSA发布Selinux的反应,Linux的创始人Linus Torvalds给出了一组评论,描述了包含主流Linux内核的安全框架。他认为该安全框架必须满足:
●真正通用,使用不同的安全模型仅仅是加载不同的核心模块。
●概念上简单,最小的扩散,有效。
●能够作为一个可选安全模块,支持现有的POSIX.1e权能逻辑。
这个通用安全框架将提供一组安全“钩子”(Hooks)来控制对核心客体的操作,并提供一组在核心数据结构中不透明的安全域来维护安全属性。此外,这个框架也能被用作可加载核心模块,通过这种方式在系统中实现任何所需安全模型。
另外,各种不同的Linux安全增强系统希望能够允许它们以可加载内核模块的形式重新实现其安全功能,并且不会在安全性方面带来明显的损失,也不会带来额外的系统开销。
由Wirex公司开始的Linux安全模块(LSM)项目就是要开发这样一个框架。LSM为主流Linux核心开发了一个轻量级的、通用目的的存取控制框架,使得很多不同的存取控制模型可以作为可加载模块来实现。
LSM采用了在内核源代码中放置钩子的方法,以仲裁对内核内部对象进行的访问。这些对象有任务Inode节点和打开的文件等。如图4-8所示,用户进程执行系统调用时首先遍历Linux内核原有的逻辑,找到并分配资源,再进行错误检查,并经过经典的9bit自主存取控制,恰好就在Linux内核试图对内部对象进行访问之前,一个LSM的“钩子”对安全模块所必须提供的函数进行一个调用,从而对安全模块提出“是否允许访问执行?”这样的问题,安全模块根据其安全策略进行决策,做出回答,如果拒绝进而返回一个错误。
图4-8 LSM“钩子”调用
(2)实现方法
目前LSM是作为一个Linux内核补丁实现的,其主要在以下5个方面对Linux内核进行了修改。
1)在特定的内核数据结构中加入安全域。安全域是一个Void*类型的指针,它使得安全模块把安全信息和内核内部对象联系起来。这些被修改加入了安全域的内核数据结构包括Task_Struct结构、Linux_Binprm结构、Super_Block结构、Inode结构、File结构、Sk_Buff结构、Net_Device结构和Kern_Ipc_Perm结构等。
2)在内核代码中的管理域和实现存取控制的关键点插入对“钩子”函数的调用。每一个LSM的“钩子”调用可以很容易地在内核源代码中通过查找“Security_Ops->”找到,如图4-9所示。
图4-9 LSM“钩子”函数示例
所有的LSM“钩子”可被分为两个主要范畴:用来管理安全域的“钩子”和用来实施存取控制的“钩子”。属于用来管理安全域的“钩子”的例子包括Alloc_Security和Free_Security,它们定义了每一个含有安全域的核心数据结构,并用来分配和释放核心客体的安全结构。这类“钩子”也包括在分配以后用以设置安全域中的信息,如在Inode_Security_Ops结构中的“钩子”Post_Lookup用来在成功完成搜索(Lookup)操作后,为Inode设置安全信息。用来实施存取控制的“钩子”的一个例子是Inode_Security_Ops结构中的“钩子”Permission,用来在存取Inode时检查权限。
3)加入一个通用的安全系统调用。LSM提供了一个通用的安全系统调用,允许安全模块为与安全相关的应用编写新的系统调用,其风格类似于原有的Linux系统调用Socketcall(),是一个多路的系统调用。这个系统调用为Security(),其参数为(Unsigned Int Id,Unsigned Int Call,Unsigned Long*Args),其中Id代表模块描述符,Call代表调用描述符,Args代表参数列表。大多数安全模块都可以自行定义这个系统调用的实现。
4)提供函数允许内核模块,注册为安全模块或者注销一个安全模块。在内核引导的过程中,LSM框架被初始化为一系列的虚拟“钩子”函数,以实现传统的UNIX超级用户机制。当加载一个安全模块时,必须使用Register_Security()函数向LSM框架注册这个安全模块,从而使得内核转向这个安全模块询问访问控制决策,直到这个安全模块被使用函数Unregister_Security()从LSM框架中注销。
5)将大部分权能逻辑移植为一个可选的安全模块。Linux内核现在通过对POSIX.1e权能的一个子集提供支持,提供了划分传统超级用户权限并赋给特定的进程的功能。LSM设计的一个需求就是把这些功能移植为一个可选的安全模块,保留用来在内核中执行Capability检查的现存的Capable()接口,但把函数Capable()简化为一个LSM“钩子”函数的包装,从而允许在安全模块中实现任何需要的逻辑。这些实现方法都最大程度地减少了对Linux内核修改的影响,并且最大程度保留了对原有使用权能的应用程序的支持,同时满足了设计的功能需求。
(3)实际应用
就LSM自身来说,虽然这个框架并不提供任何额外的安全,仅仅提供支持安全模块的基础结构,但它的确提供了一个用来支持安全模块的通用核心框架,并将绝大多数权能逻辑移植到一个可选的权能安全模块,用虚拟安全模块的系统默认值实现了传统的超级用户逻辑。目前许多现有的增强的存取控制机制,包括POSIX.1e的权能,Selinux以及域和类型实施(DTE)等,都已经改编以适应LSM框架。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。