LSM是什么?
主、次、独占LSM模块
SELINUX – 安全增强的Linux
SMACK – 简化的强制访问控制
APPARMOR
TOMOYO
YAMA
SAFESETID
LOCKDOWN LSM
结论
我猜,你读这篇文章,说明你已经对Linux安全模块(LSM)有所了解。如果你使用过SELinux或AppArmor,其实就已经用过LSM了。甚至,在你使用的Linux发行版本或Android系统之上,也使用了LSM。
内核5.4版本内,有8个LSM模块:SELinux、SMACK、AppArmor、TOMOYO、Yama、LoadPin、SafeSetID、Lockdown。还有一些LSM模块在开发中,比如SARA 和 KRSI,也许不久就会合入Linux内核源码中。如果你是关注安全的系统或软件工程师,理解为什么有这么多的LSM模块是非常值得的。它们有一些是解决通用问题,有一些则是解决特定问题。意识到它们的差异,才能更好地理解Linux的安全特性。
LSM是什么?
一个LSM模块是直接编译Linux内核的代码,利用LSM框架,它可以拒绝某个进程访问重要的内核对象。这些对象包括:文件、inode、任务控制块、凭证和进程间通信对象。通过指定允许的交互,安全管理员可以让攻击者很难利用程序的缺陷从而攻击系统的其它部分。
LSM框架的第一个,也是最流行的使用场景是强制访问控制(MAC)策略。毫无疑问,在内核中实现MAC策略的方法有多种。2001年,美国国家安全局的Peter Loscocco在Linux 2.5 内核峰会上展示了首个实现。Linux Weekly News的Jonathan Corbet后来指出:
经过这么多年的发展,这些标准接口已经形成了LSM框架。截止到5.4内核,该框架已经包含224个hook点,这些hook点包含一个注册函数的API和为LSM模块保留受保护内核对象所使用的内存的API。到Linux 2.6版本,LSM框架和SELinux合并到了内核主线中(使用LSM框架,而不是直接在内核代码中修改)。尽管LSM框架的实现,随着时间的不断推移而发生变化,但是它实现对重要内核对象的细粒度访问控制的基本目标没有改变。
目前的LSM框架已经允许用户将多个LSM模块编译进内核,存储在内核的堆栈空间中,并同时使用它们。下图展示了一个文件打开(open())操作的简略调用流程图(假设为3个LSM模块注册了hook钩子函数。
用户态进程调用open(),打开一个文件;
调度系统调用,使用文件路径作为获取内核文件对象的参数。如果参数非法,返回错误。
正常的自由访问控制(Discretionary Access Control)文件访问权限检查。如果没有权限,系统调用终止,返回给用户错误。
如果满足DAC控制,则LSM框架为每个使能的LSM模块调用file_opne钩子函数。任何一个LSM钩子函数返回错误,则系统调用终止,并返回给用户错误。
如果所有的安全检查通过,则为该进程打开该文件,并返回给用户态进程一个新的文件描述符fd。
主、次、独占LSM模块
对LSM有了初认识之后,我们再来看各个LSM模块能做什么。首先,我们先看看早期的主LSM模块:SELinux、SMACK、AppArmor和TOMOYO,它们都是MAC访问控制策略的实现,从用户空间加载配置策略。他们都以自己的方式解决相同的问题。
早期的LSM框架一次只能允许加载一个LSM模块。所有的主LSM模块都假设自己独占内核保护对象的指针或标识符。因此,所以一次只能使用一个主LSM模块。可以在编译内核时选择编译进镜像,如下图所示;也可以通过内核命令行参数传递。
LSM框架不断优化,已经消除了主、次LSM模块之间的区别。现在区分主、次LSM模块的优选方法是使用LSM_FLAG_EXCLUSIVE独占标志。一个用户可以配置多个LSM,只要给其中的一个设置LSM_FLAG_EXCLUSIVE标志即可。
次LSM是将大部分策略直接编码到内核代码中。通常情况下,次LSM模块只有enable/disable选项,而不是将策略文件在系统启动时从用户空间加载。
SELINUX – 安全增强的Linux
SELinux最早是在Linux2.6版本合入内核的,RedHat发布的Linux发行版将其作为默认的MAC强制访问策略。它以功能强大和复杂著称。
SELinux基于属性实现,将文件的安全属性存储在文件系统的扩展文件属性中。比如,使用ls -Z /bin/bash文件的安全属性,如下所示。我们可以看到有四个:冒号分割的属性,分别代表usertype:level。
$ ls -Z /bin/bash -rwxr-xr-x. root root system_ushell_exec_t:s0 /bin/bash
SELinux的常用方法是指定主体(在此也就是指user)对某种类型的对象采取什么动作。再看上面的ls的输出,自由访问控制(DAC)权限表示所有的用户都允许读、执行bash,但使用 SELinux,安全管理员可以进一步指定允许执行或读取策略文件中的shell_exec_t类型文件的主体。比如说,安全工程师不许web服务器执行shell,因为web服务器易受远程攻击。
SELinux使用扩展属性实现的副作用是,对于那些不支持扩展属性的文件系统中对象无法保护,比如挂载NFSv4版本的NFS文件系统。
由于SELinux的复杂性和强大功能,可以参考Red Hat’s SELinux User’s and Administrator’s Guide获取更多信息。
SMACK – 简化的强制访问控制
与SELinux一样,SMACK也是基于文件扩展属性的MAC实现,是开发者合并到Linux内核中的第二个LSM模块(2.6.24)。但是与SELinux不一样的是,SMACK是专为嵌入式系统设计的,对于系统管理员来说更简单。SMACK是车级Linux(AGL)和Tizen操作系统的默认MAC实现。
APPARMOR
AppArmor是另一种MAC实现,最初由Immunix开发,2.6.36版本合入内核。AppArmor是基于 Debian的系统的主要MAC实现。
除了减少了工具数量和复杂性之外,AppArmor和SELinux最大的不同就是,它是基于Path而不是基于属性。
基于Path的实现有利有弊。积极的一面是,基于Path的策略可以保护任何文件系统的文件,因为存储安全信息不需要扩展属性。甚至可以为不存在的文件指定安全规则,因为这种方式下,可以将Path存储在配置文件中而无需标注任何实际的文件或目录。另一方面,最常被提及的负面影响是,因为能够创建硬链接,对于同一个物理文件可能存在多个Path。那么,单个文件的安全策略可能会因为不同的Path而不同,这可能会导致安全漏洞。
TOMOYO
与AppArmor一样,TOMOYO是另一个基于Path的MAC实现,Linux 2.6.30版本首次合入。TOMOYO是专为嵌入式系统设计的,允许安全管理员在测试时记录所有的用户进程交互,从而根据开发、测试期间互相看见的进程才能够交互。如果使用了TOMOYO策略的系统,落入不可信的用户或敌对环境中,用户态进程仅执行那些之前允许的交互,简化了策略生成。
LOADPIN
LoadPin,是一个次LSM模块,Linux4.7版本合入,用以保证加载内核的所有文件(内核模块、固件等)来自相同的文件系统,并期望这样的文件系统是由只读的设备提供。这旨在简化从只读设备启动的嵌入式系统,让其无需对内核模块进行签名或检查。
因为简单易用,LoadPin能够简化某些类型的嵌入式系统的内核免受恶意代码攻击的过程。
YAMA
Yama,Linux 3.4合入内核的LSM模块,旨在收集主内核没有处理的系统内的DAC安全限制。目前,它支持缩小ptrace()系统调用的范围,阻止通过已经攻击成功的用户进程作为跳板,从相同用户的其它进程中抽取敏感数据信息。
SAFESETID
SafeSetID是在Linux 5.1版本合入的一个LSM模块,用来限制将UID/GID转换成白名单中允许的那些UID/GID。
我认为Linux内核中对SafeSetID使用场景的描述是非常准确的:
This can be used to allow a non-root program to transition to other untrusted uids without full blown CAP_SETUID capabilities. The non-root program would still need CAP_SETUID to do any kind of transition, but the additional restrictions imposed by this LSM would mean it is a “safer” version of CAP_SETUID since the non-root program cannot take advantage of CAP_SETUID to do any unapproved actions (e.g. setuid to uid 0 or create/enter new user namespace). The higher level goal is to allow for uid-based sandboxing of system services without having to give out CAP_SETUID all over the place just so that non-root programs can drop to even-lesser-privileged uids. This is especially relevant when one non-root daemon on the system should be allowed to spawn other processes as different uids, but its undesirable to give the daemon a basically-root-equivalent CAP_SETUID.
通俗的说就是,如果非特权程序想要生成具有不同uid的进程时,无需赋予其CAP_SETUID能力,而是通过SafeSetID就可以修改uid。
— Documentation/admin-guide/LSM/SafeSetID.rst
LOCKDOWN LSM
lockdown是Linux 5.4版本合入内核的,实现对kernel的锁定。启用了lockdown功能后,就可以通过内核命令行参数锁定kernel,以便保护其完整性和机密性。当设置lockdown为保护完整性,它的特性就不允许用户空间修改kernel。这些特性包括:未签名的内核模块加载,访问/dev/{mem,kmem,port},访问/dev/efi_test,未签名镜像的执行,休眠,直接PCI访问,原始IO端口访问,原始MSR访问,修改ACPI表,直接PCMCIA CIS存储,串行端口的重新配置,不安全的内核模块参数,不安全的MMIO内存,以及debugfs访问。当设置lockdown为保护机密性时,所有的完整性保护都被启用,另外还要禁止的功能有:用户空间从正在运行的内核中提取潜在的机密信息,例如/proc/kcore访问,使用kprobe和bpf读取内核RAM,perf 的不安全使用以及tracefs的使用。
lockdown可以通过SELinux、AppArmor、SMACK、或TOMOYO策略文件实现,这种基于静态策略的独立LSM策略文件的实现方式,意味着它可以跨平台运行,而无关于具体的MAC实现机制。
结论
LSM并不是专门设计用来阻止对进程攻击的工具。良好的编程实践,配置管理和内存安全的编程语言才是实现阻止攻击的工具。但是,当系统中运行的程序存在漏洞时,LSM确实能够阻止利用漏洞攻击系统的其它组件。所以说,LSM是Linux系统纵深防御的重要一层,通过理解它们能够提供什么保护,可以增加对保护系统的哪些部分以及如何实现这些保护有一个更好的理解。
审核编辑:汤梓红
评论
查看更多