关注了就能看到更多这么棒的文章哦~
From O_MAYEXEC to trusted_for()
By Jonathan Corbet
October 1, 2020
https://lwn.net/Articles/832959/
DeepL assisted translation
一个文件是否可以被执行,这是由执行权限 bit(execute-permission)来控制的,不过这个说法并不是在任何情况下都是正确的。如果某个文件包含了可以被解释器执行的代码,比如 shell 脚本,或者像 Perl 或 Python 这类语言的代码,那么无论是否启用了执行权限,都可以很简单地用相应的解释器来执行这个文件。Mickaël Salaün 一直致力于让系统管理员能更好地限制这些解释执行的行为,不过他还在想办法找到一个让大家都能接受的实现方式。他在最新的尝试里,采用了一个名为 trusted_for() 的新系统调用。
在 tightly locked-down(严格锁定)的系统中,通常都是设置为不允许执行任何未经系统管理者批准的文件的。二进制机器代码文件,基本上可以完全控制住,特别是当使用了安全模块(security module,应该是指 LSM)时,它可以确保签名验证,并且阻止了例如将文件映射到某个进程的地址空间的执行权限。不过,解释器对代码的执行,在内核看来只是在读一个文件。所以,如果没有解释器本身的配合的话,内核无法知道是否正在尝试执行某个文件中包含的代码。因此,没有办法对这种访问行为施加任何基于内核的策略来管理。
Salaün 工作的重点就是希望能建立起这种配合机制。核心就是实现一种方法,能让解释器利用从而通知内核它打算执行文件内容了。早在 2020 年 5 月,第一次尝试的方法是添加一个 O_MAYEXEC 标志,用在 openat2()系统调用中。如果系统策略不允许执行这个文件,那么用 O_MAYEXEC 打开它的时候就会失败。
出于多种原因,这个功能引起了人们的争议,但 Salaün 坚持开展这项工作。他 8 月份发布了 O_MAYEXEC patch set 的第 7 版。当时,Al Viro 用他那种特有的方式问道,为什么这个检查要加到 openat2()中而不是做成一个独立的系统调用。Florian Weimer 补充说,这样的话就可以对一个已经打开的文件进行检查了,也就说解释器能够指明它会执行从标准输入中读取的代码,这种情况下,使用 O_MAYEXEC 的话就无法做到了。Salaün 回答说,标准输入这方面他本来并没有打算控制。
尽管如此,他还是试图在第 8 版中解决这个反馈建议,也就是为新增的 faccessat2()系统调用实现了一个新的 flag (AT_INTERPRETED)。这样一来,检查就可以在针对文件或者已经打开的文件描述符上进行了。但这一版也没有通过大家的 review,Viro 坚持认为应该为这一功能提供一个单独的系统调用。这种方法还引入了一个潜在的 race condition,如果攻击者可以在 faccessat2()调用和实际打开文件这两个时间点之间改变了文件内容的话,就会逃避过检查。所以 Salaün 同意为这个功能创建一个新的系统调用。
因此,第九个版本引入了 introspect_access(),它会询问内核是否允许对这个已经打开的文件描述符进行某些操作。在内核开发过程中(其实也不限于 kernel 里)的讨论中,有一个标志性的特点,能告诉我们这里实质性的问题都已经被解决了:这就是当大家开始争论名字的时候。这里就已经走到了这一步:Matthew Wilcox 不喜欢这个名字,他说在文件描述符上进行检查的 policy 并不是真正的 "introspection(自省)"。随后,各种建议飞速涌来,包括 security_check()、 interpret_access()、 entrusted_access()、 fgetintegrity()、 permission()、 lsm()、 should_faccessat()等等。
在 9 月 24 日提出的第十个版本中,Salaün 没有选择这些名字。现在提出的新系统调用是:
int trusted_for(const int fd, const enum trusted_for_usage usage,
const unsigned int flags);
这里的 fd 这个参数当然是指打开的文件描述符,而 usage 则描述了调用者打算对该文件描述符做什么。在这个 patch set 中,唯一可能的选项是 TRUSTED_FOR_EXECUTION,但将来还可以添加其他选项。没有定义 flags,所以 flags 参数必须为零。如果系统的安全策略允许指定的用法,则返回 0,否则返回 EACCES。在后一种情况下,调用者应该会拒绝继续执行文件里的内容。
该 patch 还新增了一个名为 fs.trust_policy 的 sysctl 开关,用于配置几种 policy 选项。设置 0 bit,就会禁止执行那些存放在以 noexec 选项 mount 的文件系统上的文件;bit 1 会禁止任何没有设置相应的执行权限位的文件的执行。这两个都是当前内核不会进行的检查。这次并没有新增 security-module hook,但未来应该会有,这样就能允许施加更复杂的策略和技术,比如签名验证。
这一次,连系统调用的名字的抱怨都没有了,至少在写这篇文章的时候是这样。因此,这个等待已久的功能可能最终会进入主线内核。当然,这并不能完全解决这个问题。安全模块的支持最终还是需要的,同时也需要解释器本身支持才行。这需要让用户空间的多个项目要能接受相关 patch。换句话说,完全 lock down(禁止)解释器对文件的访问,还需要一段时间。
全文完
LWN 文章遵循 CC BY-SA 4.0 许可协议。
欢迎分享、转载及基于现有协议再创作~
长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~