SELinux 是 Security-Enhanced Linux 的简称,是美国国家安全局(NSA=The National Security Agency) 和 SCC(Secure Computing Corporation)开发的基于 Linux 的一个强制访问控制安全模块扩展。原先是在 Fluke 上开发的,2000 年以 GNU GPL 协议发布。对于目前可用的 Linux 安全模块来说,SELinux 是功能最全面 , 而且测试最充分的,它是在 20 年的 MAC 研究基础上建立的。SELinux 已足够成熟、稳定,因此应用非常广泛,现在 Android M 上已经默认开启 SELinux。
标准 Linux 也不是没有一点安全性,对于用户对资源的访问也是有一定的限制的,这个限制由三个因素来决定:用户及其组 ID、资源所属的用户及其组 ID、访问模式。这里以文件访问为例,介绍一下标准 Linux 访问控制及其安全漏洞。
Linux 是一个多用户多任务的分时操作系统,如果要使用系统资源,就必须向系统管理员申请一个账户,然后通过这个账户进入系统。这个账户就是用户 ID。有时需要让多个用户具有相同的权限,为了简化管理,将具有相同特征用户组成一个逻辑集合,就叫用户组。而文件所属用户,一般为文件的创建者,谁创建了该文件 , 就天然的成为该文件的所有者,创建者所在组就是该文件的所属用户组。
ls -l
显示文件的属性如下:
-rwxrw-r‐-1 dengtaiping dengtaiping 4096 6 月 2 09:39 temp
前面 10 个字符代表了标准 Linux 对 temp 这个文件的访问控制:
后面是文件的所有者、所有者所在的组及其它属性。在安全访问中,访问的发起者称为主体,被访问的资源成为客体。标准 Linux 世界里这种由客体的属主对自己的客体进行管理,由属主自己决定是否将自己的客体访问权或部分访问权授予其他主体的访问控制称为自主访问控制(Discretionary Access Control——DAC)。也就是说,在自主访问控制下,用户可以按自己的意愿,有选择地与其他用户共享他的文件。如在标准 Linux 里,可以通过 chmod、chgrp、chown 分别改变文件的权限、用户组及所有者。所以,DAC 最大的缺点是它不能区分用户和程序,即授权许可是给用户的,只要用户有授权,该用户的所有程序都自动获得授权,包括侵入该用户名下的恶意程序。如果恶意程序获得 root 用户权限,那在标准 Liunx 世界里,就无所不能了。
强制访问控制(Mandatory Access Control——MAC),用于将系统中的客体分密级和类进行管理,以保证每个主体只能访问到那些被标明可以由它访问的一种访问约束机制。在 SELinux 的强制访问控制下,访问的主体不再是用户,而是进程。客体可以是文件、目录、共享内存、套接字、网络主机等。主体和客体都被标记了固定的安全属性(如安全等级、访问权限等),在每次访问发生时,系统检测安全属性以便确定一个主体是否有权访问该客体。因此在这里,决定访问权限的不再是主体和客体,而是访问控制策略。这就意味着在 SELinux 中没有默认的超级用户。SELinux 实现了一个灵活的 MAC 机制,引入了两套安全策略,分别是类型强制 (Type Enforcement) 和多层安全 (Multi-Level Security)。
类型强制访问控制通过指定主体类型(即域)和客体类型使用 allow 规则授予访问权限,allow 规则由四部分组成:
下面以 Android 手机中一个例子说明 TE 是如何工作的。在手机的 console 环境中,运行 ls -Z | grep proc
,可看到 proc 目录的安全上下文如下:
dr-xr-xr-x root root u:object_r:proc:s0 proc
再运行 ps -Z | grep adbd
,看看进程 adbd 的安全上下文:
u:r:su:s0 root 2935 1 /sbin/adbd
可以看到,目录 proc 和进程 adbd 各自有自己的安全上下文,安全上下文实际上是附加在对象上的一个标签,由用户、角色、类型和安全级别四部分组成,由冒号隔开。安全级别下一节再介绍,在 SEAndroid 里,只定义了一个用户 u,所以文件和进程安全上下文中,所有的用户都为 u。文件的角色一般都为 object_r,而进程的角色固定为 r。用户和角色定义得这么随意,是因为对安全策略来说它们并不重要,在类型强制访问控制的安全上下文中,最重要的就是类型,它才是定义规则时需要的。假如要想通过 adb 读写 proc 目录下的文件节点,则需要定义如下一条规则来授权:
allow init proc:dir rw_dir_perms;
SELinux 引入的另外一个安全策略是多级安全 (Multi-Level Security)。多级安全是建立在 TE 的基础之上的,这个安全策略在 SELinux 中是可选的,但引入多级安全,不仅是对安全访问的增强,更是因为对于一组对象进行分类访问授权,使用多级安全是非常高效的。在前面所举例子中,proc 和 adbd 都有一个安全级别 s0。
在多级安全中,主体和客体都有一个关联的安全级别,多级安全的访问规则就是:安全级别交高的主体可以读取安全级别较低的客体;而安全级别较低的主体可以写入安全级别较高的客体。前者称为 “read down”,后者称为 “write up”。通过这种规则,可以允许数据从安全级别较低的客体流向安全级别较高的主体,而限制数据从安全级别较高的客体流向安全级别较低的主体,从而有效的保护了数据。对于安全级别相同的主体和客体,主体对客体的读写都是允许的。
基于多级安全策略的 MAC 数据流向,如下所示:
图中定义来两个安全级别,PUBLIC 和 SECRET,其中,SECRET 安全级别高于 PUBLIC。
SELinux 原来是作为一个内核补丁实现的,现在已经将 Linux 安全模块(LSM)重新实现为一个安全模块。嵌入到标准 Linux 中的方式是 LSM 提供一套钩子函数 (hook),MAC 安全策略检查在这些钩子函数中实现。钩子函数通常放在标准 Linux 检查后、内核调用访问真实资源前调用。系统调用进入内核后,会做常规的错误检查 ( 如参数有效性等 ),然后是标准 Linux 的 DAC 检查。MAC 在这些检查都通过之后才会生效。所以,在 SELinux 中,DAC 和 MAC 同时起作用,两者并行而不悖。LSM 具体实现机制及代码解读将在后续文章中介绍。最终 SELinux 的访问控制流程就如下图所示: