SELinux使用了分级的强制访问控制,是Linux内核的重要安全措施。SELinux的安全策略工具可从http://oss.tresys.com下载。本篇分析了SELinux的安全机制,介绍了安全策略配置语言、内核策略库的结构,简述了SELinux内核模块的实现,还分析了用户空间的客体管理器。
SELinux是安全增强的Linux,是Security-enhanced Linux的简写,SELinux 改进了对内核对象和服务的访问控制,改进了对进程初始化、继承和程序执行的访问控制,改进了对文件系统、目录、文件和打开文件描述的访问控制,还改进了对端口、信息和网络接口的访问控制。
早期的 Linux 内核只提供经典的 UNIX 自主访问控制(root 用户、用户 ID、模式位安全机制),以及部分地支持 POXIS.1e 标准草案中的 capabilities 安全机制,这对于 Linux 系统的安全性是不够的。NSA(the National Security Agency)和 SCC(Secure Computing Corporation)共同开发了强大的基于类型加强(Type Enforcement)的强制访问控制(mandatory access control,简称MAC)机制。这个机制称为 Flask,它提供了动态的安全策略机制。Flask 机制现已被以 SELInux 形式集成入 Linux 操作系统。
Linux内核增加了一个通用的安全访问控制框架,它通过hook函数来控制程序的执行,这个框架就是Linux安全模块(LSM),在LSM上可使用各种安全控制机制(如:Flask)来实现对程序的控制。
SELinux应用了类型增强(Type Enforcement, TE)和基于角色的访问控制(role-based access control, RBAC)技术。
TE给每个主体(进程)和系统中的客体定义了一个类型,这些类型定义在SELinux的安全策略文件中,以安全标签的形式存放在文件本身的扩展属性(extended attributes,简称xattrs)里。
当一个类型与一个进程相关时,这个类型称为域(domain),例如:后台进程httpd的域为httpd_t。
在SElinux中,默认是不允许主体(subject)对客体(object)进行操作的,由策略来定义哪些操作是被允许的。TE使用主体的域和客体的类型,从策略文件中查找操作许可。例如:策略中的一条规则如下:
allow httpd_t net_conf_t : file { read getattr lock ioctl };
这条规则表示:主体的域是httpd,客体的类型是net_conf_t,客体的类别是文件,{}中是被允许的操作权限。
SELinux的访问控制规则存放在安全策略文件中,策略文件分为二进制和源代码文件,源代码以策略配置语言的形式描述,由编程者创建和维护。源代码经策略配置工具编译后生成二进制文件。二进制策略被装载到内核空间,形成在内存中的策略库及缓存,内核就可以使用访问控制规则了。
SELinux可以实现非常细粒度的访问控制,这也造成了安全策略的复杂性。
Linux内核的SELinux安全体系由Flask和LSM框架共同组成,LSM是由hook函数组成的安全访问控制框架,Flask框架将SELinux的策略规则转换成访问控制许可。
在安全策略配置语言中经常用到的名称术语说明如下。
subject主体,常指一个进程
object客体,常指一个文件
object class客体的类
permission许可
context上下文
user用户
role角色
type类型
Type Attributes类型属性
Type Enforcement类型增强
domain域,它是一个进程的类型
source type源类型
target type目标类型
labeling标识
access vector cache(AVC)访问向量缓存
access decision访问决策
在传统的Linux自由访问控制(Discretionary Access Controls, DAC)之后,SELinux在内核中使用强制访问控制机制(MAC)检查允许的操作。
在DAC下,文件客体的所有者提供了客体上的潜在风险控制。用户可以通过错误配置的chmod命令和一个非期望的访问权限传递,暴露一个文件或目录给一个恶意信任者。这个用户启动的进程,如:CGI脚本,可在这个用户拥有的文件做任何的操作。
DAC实际上仅有两个主要的用户分类:管理者和非管理者。为了解决权限分级,它使用了访问控制列表(access control lists,简称ACL),给非管理者用户提供不同的权限。而root用户对文件系统有完全自由的控制权。
MAC访问控制框架可以定义所有的进程(称为主体)对系统的其它部分(如:文件、设备、socket、端口和其他进程,它们被称为客体或目标对象)进行操作的权限或许可。这些许可由进程和客体的安全策略来定义,通过内核实现控制。这种方式可给一个进程授予操作所需要的权限,这遵循了最小权限原则。在MAC下,即使用户用chmod暴露了它们的数据,但进程还是无法修改在策略中没有许可的文件。
DAC和MAC分别使用自己的访问控制属性,两者独立,但都必须通过权限检查。它们的区别有以下二个:
(1) 在主体的访问控制上:
DAC:真实有效的用户和组ID
MAC:安全上下文(user:role:type)
DAC和MAC的UID是独立的。
(2) 在客体的访问控制上:
DAC:(文件)访问模式(rwxr-xr-x)和用户及组ID
MAC:安全上下文(user:role:type)
图 1 描述了Flask框架,图中,一个主体进程对系统中的客体(如:设备文件)进行操作,如:读写文件。这个操作通过DAC权限检查之后,再进行SELinux的权限检查。在DAC检查中,主体通过基于ACL常规文件权限获得了对客体的操作许可。SELinux权限检查的过程如下:
策略强制服务器(policy enforcement server)从主体和客体收集安全上下文,并发送安全上下文“标签对”给安全服务器(security server),安全服务器负责产生策略的决策。
策略强制服务器首先检查AVC,如果AVC有高速缓存的策略决策,它返回决策给策略强制服务器。如果没有,它转向到安全服务器,安全服务器使用系统初始化期间装载到内核的二进制策略作出决策,将决策放到AVC的cache中进行缓存,并将决策返回给策略强制服务器。
如果决策为允许操作,则主体对客体进行操作,否则,操作被禁止,禁止操作信息被记录到log文件,log文件一般为/var/log/messages。
图 1 Flask框架
安全服务器产生策略决策的同时,策略强制服务器处理任务的其他部分。从这个角度来看,策略强制服务器部分可以看作客体管理器,客体管理包括用安全上下文件标识客体、管理内存中的标签、管理客户端和服务器的标识行为。
Flask安全体系结构由客体管理器和安全服务器组成。客体管理器实施安全策略的判定结果,安全服务器作为安全策略的判定。
Flask安全体系结构提供从安全服务器检索访问、标记和多例化判定的接口。访问判定指主体对客体操作的一个权限是否得到批准。标记指分配给一个客体的安全属性标签。多例化判定指一个特定的请求应该从多例化资源中选取哪一个。
Flask安全体系结构还提供一个访问向量缓存(AVC)模块,该模块允许从客体管理器缓存中直接取出缓存的判定结果,以提供执行速度。
Flask框架的安全服务器的安全策略由四个子策略组成:多级安全(MLS)策略、类型加强(TE)策略、基于标识的访问控制(IBAC)策略和基于角色的访问控制(RBAC)策略。安全服务器提供的访问判定必须满足每个子策略的要求。
安全服务器定义了一个由类型加强(TE)策略、基于角色的访问控制(RBAC)策略和多级安全(MLS)策略组合成的策略决策系统。其中,TE和RBAC策略是安全策略的必要部分,MLS策略是可选的,当内核配置选项CONFIG_FLASK_MLS打开时,系统提供MLS策略支持。
Flask体系结构为安全标记定义了两个独立于策略的数据类型:安全上下文(context)和安全标识符(SID)。安全上下文是由可变长字符串表示的安全标记,存在于文件的扩展属性中。
SID是被安全服务器映射到对应安全上下文的整数。Flask客体管理器负责将安全标签绑定到客体上、绑定SID到内核对象上。
一个安全上下文(或称为标签)由用户ID、角色、类型和可选的MLS分级属性或分类属性组成。角色仅与进程相关,因而文件安全上下文有一个通用的object_r的角色。安全服务器仅为安全上下文提供SID。
SELinux系统提供一个与安全服务器相配套的安全策略配置语言,该语言用于对安全服务器中安全策略的配置进行描述。策略配置语言描述的规则策略存于策略文件中,策略文件源代码由工具编译成二进制策略文件,安全服务器在引导时读取二进制形式策略文件,形成策略库。
MLS(Multi-Level Security)机制给用户提供了可以用不同等级的安全水平来访问系统。例如:MLS安全分级从低到高为Confidential、Secret、Top Secret 和 Individuals,不同级别可查看不同的分类信息。低级别不可查看高级别的文档。
SELinux支持分级保护数据,它使用BLP(Bell-LaPadula Model:BLP)模型,这个模型定义了系统内的信息是如何基于粘附在每个主体和客体的标签来进行流动的。如:在Secret级别的用户可与其他同级别用户共享数据,并能提取来自Confidential(比Secret级别低)的信息。但在Secret级别的进程不能查看Top Secret级别(比Secret级别高)的数据。它还阻止高级别进程随便给低级别的数据写入信息。即“不能读较高级别的数据,不能写较低级别的数据”模型。
在多级别系统中,高级别用户不自动获得管理者权限,但它们对计算机上的所有信息可以有访问权限。
安全上下文中,主体和客体用安全级别(Security Levels,SL)标识,SL由敏感性和分类属性组成。Sensitivity(敏感属性)是安全体系的属性,它将数据等分成不同安全级别,如:“Secret”或“Top Secret”。Categories(分类属性)是一套非体系的属性,如:“US Only”或“UFO”,表示仅US使用。分类属性将数据分割成几个独立的小组,每个小组可归属不同的用户等。
一个SL必须有一个敏感属性和0个或多个分类属性。例如,SL是{Secret/UFO, Crypto}、{Top Secret/ UFO, Crypto, Stargate}或{Unclassified}。体系的敏感属性跟踪着0个或多个分类属性,因为敏感属性还可以分隔成彼此独立的分类属性。
客体上的安全级别称为分级(Classifications),主体上的安全级别称为Clearances。
为了加入安全机制,Linux内核从以下5个方面进行了修改:
安全域是一个void*类型的指针,它指向安全模块操作函数,从而将安全信息和内核对象联系起来。下面列出加入了安全域的内核数据结构:
task_struct :进程结构;
linux_binprm :二进制程序运行结构;
super_block :文件系统超级块结构;
inode:文件系统节点结构,表示文件、目录、管道、设备文件或socket套接字;
file :打开的文件结构;
sk_buff :网络包缓冲区结构;
net_device :网络设备结构;
kern_ipc_perm :IPC(进程间通信)权限结构;
msg_msg :消息结构。
Linux安全模块(LSM)提供了各种安全钩子(hook)函数,都在全局类型security_operations结构中,这些函数管理内核对象的安全域,仲裁对这些内核对象的访问。Linux安全模块提供了一个通用的安全系统调用,允许安全模块为安全相关的应用编写新的系统调用。
在内核的引导过程中,Linux初始化了一系列的虚拟钩子函数,当加载一个安全模块时,使用register_security()函数注册安全模块:这个函数将设置全局表security_ops,使其指向安全模块的钩子函数指针,从而内核可向这个安全模块询问访问控制策略。
Linux安全模块将capabilities能力机制移植到安全模块中,保留了对原有使用capabilities的应用的支持。
Linux安全模块的钩子函数包括任务钩子、程序装载钩子、进程间通信IPC钩子、文件系统钩子、网络钩子及模块钩子和顶层的系统钩子。
SELinux模块由安全服务器、AVC(Access Vector Cache)、网络接口表、netlink事件通知代码、selinuxfs伪文件系统和hook函数应用六个部分组成。安全服务器为获得安全策略决定提供通用接口,使模块的其余部分保持独立的安全策略。这些接口定义在include/security.h中,安全服务器的特定应用能在不改变模块其他部分的情况下被改变。
安全服务器的应用有RBAC(基于角色的访问控制 Role-Based Access Control)、TE(类型加强Type Enforcement)的生成和可选的MLS(多级安全Multi-Level Security)。其中RBAC和TE策略是高度可配置的,能用于许多不同的安全对象。安全服务器的源代码在security/selinux/ss目录下载。
AVC提供了从安全服务器获得的访问策略的缓冲区(cache),提高了安全机制的运行性能。它提供了hook函数高效检查授权的接口,提供了安全服务器管理cache的接口。与hook函数的接口定义在include/avc.h中,与服务器的接口定义在incluce/avc_ss.h中,AVC代码在avc.c中。
网络接口表将网络设备映射到安全上下文中。维护一个独立的表是有必要的,因为LSM网络设备没有安全成员。当网络设备被hook函数第一次查找到时网络设备就被加入到这个表中,当设备被设置取消或策略重载时移出这个表。网络接口表提供了查找hook函数以及获得网络设备条目引用或释放引用的接口函数。该接口定义在include/netif.h中,回调函数在设备配置变化或策略重载时注册。网络接口表的代码在netif.c中。
netlink事件通知让SELinux模块在策略重载以及强制状态改变时通知进程。这些通知被用户空间的AVC(libselinux的一部分)用来保持与内核一致的状态。用户空间AVC被用户空间策略强制器(enforces)使用,代码在netlink.c中。
SELinux伪文件系统给进程提供安全服务器的策略API。selinux提供基于策略API的调用,它包括进程属性、文件属性和策略API三个部分,并被高层的libselinux API 封装起来,代码在selinuxfs.c中。
hook函数应用管理与内核对象相关的安全信息以及执行内核每个操作的访问控制。hook函数调用安全服务器和AVC得到安全策略并应用这些策略来表示和控制内核对象。hook函数还调用文件系统扩展属性代码获得和设备文件上面的安全上下文。hook函数在hooks.c中,与内核对象相关的安全信息的数据结构定义在include/objsec.h中。SELinux安全模块及接口如图2所示。
图 2 SELinux安全模块与接口