目录

1) 引言

kio-admin 是一个 KDE 组件,允许在 GUI 应用程序中执行特权文件操作。它实现了一个作为 root 运行的 D-Bus 服务,并使用 Polkit 进行身份验证。kio-admin 的典型用例在 KDE 的 Dolphin 文件管理器中,它允许进入管理员模式,在该模式下,文件管理器操作将以 root 的身份而不是未授权用户的身份执行。

我们最初在 2022 年提出的添加 kio-admin 到 openSUSE 的请求,由于安全顾虑被拒绝了。不久前,我们被要求重新审视这个软件包,看看现在是否可以添加。对 kio-admin 的安全评估是一个棘手的任务,尽管如此,我们这次决定将其纳入 openSUSE,因为实际利用的概率很低。以下部分探讨了 KDE 中直至 kio-admin 的特权文件操作历史,接受其此时纳入 openSUSE 的理由,以及在复杂场景下安全执行特权文件操作的建议。

2) KDE 中特权文件操作的历史

KTextEditor 文件保存操作

历史上,在 GUI 应用程序中执行特权操作,需要以 root 身份执行整个应用程序。这样做通常是不鼓励的,因为 GUI 程序很复杂,并且不总是考虑到潜在的安全问题而编写。X11 协议和 X 服务器实现的内部工作原理也是图形应用程序以提升权限运行时导致安全问题的根源。

摆脱这种“全有或全无”方法的一种方式是引入 Polkit,它允许为特权操作定义单独的动作。这些动作由 polkitd 守护进程进行身份验证。应用程序通常被分成两部分。包含大部分代码的未授权图形应用程序与后端通信以执行特权操作。后端代码量较小,并以 root 身份运行。

当特权操作的性质具有明确定义的范围时,例如“启用 VPN 连接”或“连接到蓝牙设备”,这种方法效果很好。但是,当情况并非如此时,就会出现问题。这种情况发生在 2017 年的 KDE 中,KTextEditor 中添加了一个具有 Polkit 身份验证的 D-Bus 服务。该服务是允许未授权用户在输入 root 密码后将文件保存到特权文件系统位置的特性的一部分。从最终用户的角度来看,这是非常有道理的:为什么典型单用户桌面系统的用户不应该可以通过这种方式修改配置文件呢?

然而,前安全团队成员 Sebastian Krahmer 对这个想法颇为恼火,因为 Polkit 操作缺乏明确定义的范围。将任意数据写入磁盘上的任意文件可能导致各种问题。让我们考虑一些场景:

  • 一个文件保存在由 root 拥有的系统配置文件目录中,例如 /etc/fstab。这很可能是上游开发人员考虑的典型用例。
  • 一个文件(意外地)保存在用户本身控制的未授权位置,例如 /home/$USERNAME/some_file。在这种情况下,不需要权限提升,但在该位置以 root 权限操作是危险的。
  • 一个文件保存在由未授权服务用户拥有的配置文件或状态目录中,例如 /var/lib/chrony。现在涉及三个不同的用户账户:提供文件内容并请求操作的未授权用户,执行特权操作的 root 用户,以及实际控制即将写入文件系统的 chrony 服务用户。
  • /proc/sys 这样的伪文件系统上的文件可以通过这种方式修改。伪文件通常具有与写入数据方式相关的特殊语义。如果不了解这一点,特权的 KTextEditor 辅助组件可能会导致副作用,或者无法正确实现用户意图。
  • 目标路径可能尚不存在。是否应创建它?新创建的文件应具有什么所有权和模式?
  • 目标路径可能存在,但文件类型特殊。通过盲目访问此类文件,写入可能会被重定向到非预期的位置(通过跟随符号链接),或者可能发生拒绝服务(通过阻塞 FIFO 管道)。在这种情况下应如何处理:写入操作是否应失败,是否应静默替换特殊文件,还是应交互式地询问用户该怎么做?

从这个列表中可以看出,最初看起来只是一个简单的“文件保存”操作,可能会导致许多不同的情况。我们更仔细地审查了 KTextEditor 此特性的代码,并发现了 CVE-2018-10361,这是一个本地 root 漏洞,当特权后端尝试在另一个未授权用户拥有的目录中保存文件时触发。

在发现该漏洞被上游修复后,我们要求进行一系列改进,然后才接受此 D-Bus 服务进入 openSUSE。

  • 目标路径应添加到 Polkit 身份验证消息中(bsc#1147035)。目前它只会显示一个关于操作特权文件的通用消息。为防止潜在的意外或欺骗,消息应清楚说明正在验证*什么*。
  • 目标文件的所有权和模式应完全由后端或前端定义(bsc#1147038)。目前前端可以选择传递文件所有者和组,但不能传递模式。对于创建新文件,GUI 部分最好主动询问用户期望的文件属性。这是因为后端和前端都无法可靠地猜测新文件的模式。它应该是全局可读还是不可读?它应该由 root、服务帐户还是另一个交互式用户控制?这是只有执行操作的用户才能回答的问题。
  • 后端不应替换除常规文件以外的任何内容。不应跟随符号链接,也不应访问或替换任何其他特殊文件(bsc#1147041)。
  • 当写入一个非 root 拥有的目录时,后端组件应在其中执行文件操作之前将权限降至目录所有者的权限(bsc#1147043)。
  • 应实现对目标文件系统的限制,例如防止在伪文件系统上进行操作(bsc#1147045)。

我们从未收到上游或我们的 KDE 维护者对这些问题的答复,因此 KTextEditor 的这部分在 openSUSE 上仍然未启用。我们要求的大部分更改都在可接受的范围内;只有动态身份验证消息遇到了一些障碍,因为当时 Qt 和 KDE 框架库对 Polkit 的支持并不完全。

用于特权文件系统操作的 KIO 框架

虽然在 KTextEditor 中“将文件保存到任意位置”特性的改进没有进展,但 KIO 框架的开发人员大约在同一时间试图进一步扩展通过 D-Bus 和 Polkit 进行特权文件操作的概念。有一项努力,旨在使 GUI 应用程序能够进行一系列完整的特权文件操作chmod()chown()unlink()mkdir()rmdir()open()opendir()rename()symlink()utime()。我们被邀请参与了关于此特性的设计讨论

由此产生的 D-Bus 服务最终成为用户空间中的一个迷你内核,提供各种文件系统 API。在 KTextEditor 中观察到的一些问题在此方法中变得更糟。虽然在 KTextEditor 中至少清楚目标是访问常规文件,但对于 KIO 中的文件操作的上下文一无所知。所有单独的操作都是相互分离的。应用程序通常需要执行一个特定的任务,该任务涵盖一系列逻辑上相关的文件操作。一项这样的任务可能是以原子方式将特权位置的文件替换为新版本的文件。这将需要一系列调用,例如用于创建新文件的 open()、用于分配所需所有权的 chown(),以及最后用于用新文件替换旧文件的 rename()。KIO D-Bus 服务缺乏此附加上下文信息,因此无法清楚地告知用户将要发生什么。

只有一个 Polkit 操作“org.kde.kio.file.exec”用于身份验证这些特权文件系统操作中的任何一个。显示给用户的身份验证消息与 KTextEditor 中的一样,是通用的。用户将无法准确确定他们正在验证哪种类型的文件操作。用户要么会为单个任务收到多个身份验证请求(auth_admin Polkit 设置),要么 D-Bus 服务会缓存身份验证一段时间(auth_admin_keep Polkit 设置),从而允许在无限的时间跨度内执行未知数量和范围的文件操作。在这两种情况下,对于普通最终用户来说,经过身份验证的范围都是不清楚的。

由于一个通用的 D-Bus 服务提供文件操作,它无法知道客户端应用程序的逻辑目标是什么,该服务基本上需要将所有类型的文件系统操作和影响它们的标志暴露给应用程序。在 D-Bus 上正确且完整地建模这一点是一项艰巨的任务。这种方法也给应用程序带来了负担,这些应用程序现在需要通过异步 D-Bus IPC 接口间接实现复杂的系统调用序列。

另一种使此类接口能够稳健安全地工作的途径可能是实现某种形式的事务处理。应用程序将请求一个任务,例如“替换 /etc/fstab”,并为此任务注册一组逻辑上相关的调用,例如:打开 /etc/fstab.new,设置 /etc/fstab.new 的权限,将 /etc/fstab.new 重命名为 /etc/fstab。然后后端只会在请求的路径上进行身份验证并允许这些文件操作。然而,这又会导致一个高度复杂的接口。

使用提升的权限在任意文件系统位置安全地操作已经是一项非常艰巨的任务,即使使用纯粹的系统调用。程序必须考虑到在许多情况下需要将权限下降到目录所有者,它需要避免跟随符号链接,它可能需要使用 O_PATH 标志打开文件,以避免无意中访问危险的特殊文件。使用抽象的 IPC 接口通用地覆盖这项任务似乎过于复杂。Polkit 作为一种身份验证机制也不适合这种类型的通用 API。

鉴于所涉及的复杂性,经过长时间的讨论,上游放弃了这个特性。代码仍然存在于 KIO 存储库中,但特权的 file_helper 未安装

kio-admin 后端

至此,我们来到了 kio-admin。我们在 2022 年被要求将这个 D-Bus 服务纳入。这是“特权文件操作”主题的另一种变体。这次它不是 KIO 框架的组成部分,而是一个作为 root 运行的独立组件,它作为 KIO 的普通客户端。

我们决定不接受 kio-admin,原因与我们之前关于 KTextEditor 和 KIO 框架特性的理由基本相同。在 2024 年,我们被要求重新审视 kio-admin,以检查它在此期间是否有所改进。

遗憾的是,情况变化不大。提供的文件操作范围与之前为 KIO 提出的非常相似:chown()chmod()mkdir()、列出目录内容等。在某些方面,API 甚至比之前为 KIO 提出的 API 更糟糕,因为所有操作都针对路径而不是文件描述符。对于遵循符号链接和其他系统调用行为的单个操作的控制更少。D-Bus 服务的实现更复杂,请求由 kio-admin 异步转发给 KIO。kio-admin D-Bus API 还使用 URI,例如“file:///etc/fstab”,而不是纯路径。

同样,只有一个 Polkit 操作“org.kde.kio.admin.commands”,它使用通用的身份验证消息来授权任何提供的操作。被授权的请求范围对用户来说仍然不清楚。

KIO 框架中文件操作的实际实现经常对符号链接的出现显得天真,并且如果系统中第三个用户帐户控制操作发生所在的目录,则容易出现竞争条件。

将 kio-admin 集成到 Dolphin 文件管理器

kio-admin 组件的一个主要用例是在 Dolphin 文件管理器的“管理员模式”特性中。这是一种模式,所有文件操作都转发到 kio-admin 后端,以提升权限执行。

Dolphin 中实现此特性的方式实际上考虑周全。只要“以管理员身份操作”模式处于活动状态,就会有明确的警告和顶部的可见红色条。Dolphin 还拒绝更改符号链接的目标,并正确显示链接权限无法更改。

然而,这并不能完全解决 kio-admin 后端方面的竞争条件等问题。例如,当 Dolphin 检测到一个常规文件并触发 kio-admin 请求对其进行操作时,到 KIO 框架开始对其进行操作时,该路径可能已被替换为其他文件类型。

3) 安全问题评估

我们对通过 D-Bus API 暴露的特权文件操作的担忧会影响本地系统安全。如今,人们常常认为几乎所有 Linux 桌面系统都是单用户桌面系统,因此本地系统安全并不重要。然而,kio-admin 中存在的攻击面仍然会影响纵深防御。考虑发生在未授权服务用户或 nobody 拥有的目录中的文件操作。如果该帐户被攻破,那么像符号链接攻击这样的攻击向量可能导致完全的权限提升。从这个意义上说,即使没有其他人类交互式用户存在,任何 Linux 系统都可以被视为多用户系统。

此类 API 的通用性质使得判断未来可能的用途变得困难。一旦这样的 API 被纳入发行版,就很难跟踪其附加的消费者。其使用的扩散,可能也会在非交互式后台任务领域,会增加我们已经识别到的危险。

出于这些原因,我们到目前为止拒绝将 kio-admin API 纳入 openSUSE。

4) 接受 kio-admin 进入 openSUSE 的理由

自 2017 年以来,我们一直在处理 KDE 中的这些 API 类型,但未取得任何显著改进。作为产品安全负责人,我们试图保护用户免受潜在有害组件的侵害。然而,此时,我们认为这种情况不会很快改变。与此同时,用户仍然希望使用 Dolphin 中存在的特性,并且不理解为什么 openSUSE 不包含它们。

我们认识到,使用不健壮的 API 仍然比完全以 root 身份运行图形应用程序要好。而且,就目前而言,通过 kio-admin 交互式执行的操作被利用的可能性很低。

还有一个名为 gvfs 的 GNOME 桌面组件,它与 kio-admin 非常相似。它在 2017 年被纳入 openSUSE,当时我们并未详细审查其目的和 API 设计。在关于 KTextEditor 的讨论中,我们进行了第二次更深入的审查,在此期间我们发现了与本文讨论的 kio-admin 的顾虑非常相似的问题。尽管如此,出于历史原因,我们还是决定将其保留在 openSUSE 中。

因此,本着平等待遇的原则,并为了在 openSUSE 上获得良好的用户体验,我们现在决定搁置对 kio-admin 的担忧,并将其纳入 openSUSE。考虑到目前的情况,这似乎是我们务实的最佳选择。我们本希望看到更健壮和透明的 API 设计。我们希望上游开发人员将来能找到更好的方法来解决我们的担忧,与此同时,我们仍然建议最终用户在使用这些特性时要小心,并注意我们在下一节中给出的建议。

5) kio-admin 或 gvfs 用户建议

不幸的是,在执行特权文件操作时存在许多陷阱。我们认为,即使是高级用户,在 root 身份下操作由非 root 用户控制或影响的目录(例如在 /tmp 中)时,也容易犯错误。以下是一些通用建议,有助于避免此类错误。

首先,当文件操作发生在 root 拥有的目录中,例如 /etc(请注意,/etc 的子目录可能再次由非 root 用户拥有)时,像 kio-admin 和 gvfs 这样的 API 通常是安全的。在更改由其他用户控制的目录中的文件时,应格外小心,例如其他用户的家庭目录或服务帐户拥有的文件。

在这种情况下,更安全的方法是在 root shell 中执行操作,并且在此过程中应非常小心不要跟随符号链接。许多文件管理实用程序提供了避免跟随它们的特定开关。例如,chmod 实用程序默认会跟随目标文件路径中的符号链接,除非传递了 -h 开关。

即使是这些开关也只能防止路径的最后一个组件中的符号链接。考虑命令 chmod -h 644 /var/lib/chrony/sub-dir/target/var/lib/chronychrony 服务帐户控制。因此,未授权的 chrony 用户可以将 sub-dir 变成一个指向特权位置(如 /etc)的符号链接。如果 /etc/target 存在,则上述命令将使该文件全局可读。

因此,编辑其他帐户拥有的文件的一个更好的方法是假定其身份,例如通过调用 sudo -u <user> -g <group> /bin/bash。这样,一开始就不存在可能被被盗帐户滥用的提升权限。

6) kio-admin 集成的后续步骤

在此博客文章中记录我们的担忧是将 kio-admin 添加到 openSUSE 过程的第一步。我们将引用这篇博客文章以及在 kio-admin 和 gvfs 包中添加的专用 README 文件中的一些提示。我们还将把这些记录在 openSUSE wiki 中。完成所有这些之后,我们将执行必要步骤,允许 kio-admin 进入 openSUSE Tumbleweed,我们相信这将在未来两周内发生。

7) 参考