Shim 如何在安全启动中验证二进制文件?

dir*_*obs 5 boot grub2 bootloader uefi secure-boot

UEFI 垫片加载器

shim 是一个简单的 EFI 应用程序,它在运行时会尝试打开并执行另一个应用程序。它最初会尝试通过标准 EFILoadImage()StartImage()调用来执行此操作。如果这些失败(例如,因为启用了安全启动并且二进制文件未使用适当的密钥签名),它将根据内置证书验证二进制文件。如果此操作成功并且二进制或签名密钥未列入黑名单,则 shim 将重新定位并执行二进制。

我一直在阅读以了解启用安全启动选项时验证过程是如何发生的:

vmlinuz *-generic 和 *-generic.efi.signed 的区别

安全启动实际上是如何工作的?

管理 Linux 的 EFI 引导加载程序:控制安全引导

我现在可以说程序是这样的:

Shim 首先由机器的固件运行。现在 shim 必须运行引导加载程序。我不明白的是 shim 如何验证二进制文件?例如,上面引用的段落指出 shim 尝试通过标准 EFILoadImage()StartImage()调用启动另一个应用程序,如果失败,shim 尝试从内置证书验证二进制文件。那么这个内置证书属于 shim 吗?本质上,为什么 shim 被称为 Machine Owner Key Manager (MOK)?因为它有自己的密钥数据库来验证二进制文件。

简单地说,机器的固件在 NVRAM 中有自己的密钥数据库来验证二进制文件,而 shim 有自己的密钥数据库来验证二进制文件?

在验证并执行引导加载程序后,引导加载程序在哪里查找需要引导的已签名内核的密钥,例如从固件的密钥数据库中?

Rod*_*ith 9

Kaz Wolfe 的回答非常好,但我想强调和扩展几点......

我上次检查时,Shim 基本上提供了一种并行安全启动验证功能。它旨在供 GRUB 使用,GRUB 旨在启动非 EFI 程序的 Linux 内核。因此,Shim 以某种方式向 EFI 注册自己,使后续程序能够调用 Shim 以验证二进制文件是否已签名。Shim 通过以下两种方式之一执行此操作:

  • Shim 的内置密钥——大多数 Shim 二进制文件,包括作为 Ubuntu 一部分提供的二进制文件,都包含一个内置的安全启动密钥。Ubuntu 的 Shim 包括 Canonical 的公钥,用于验证 Ubuntu 的 GRUB 和 Linux 内核。因此,此密钥存储在 RAM 中,并且随着这些事情的进行而相当临时。Shim 的主要目的是使其后续程序 (GRUB) 能够执行安全启动类型验证——但 GRUB 本身并没有真正进行安全启动验证,正如稍后所述。如果没有 Shim,Canonical 将需要依靠 Microsoft 来签署每个新版本的 GRUB 和每个新的 Linux 内核,这将介于不切实际和不可能之间。
  • Machine Owner Keys (MOKs) ——MOKs 基本上是 Shim 内置密钥的扩展,但它们是供普通用户操作的。如果要启动未使用 Canonical 密钥签名的二进制文件,则可以使用 MOK。MOK 与固件的内置安全启动密钥一样,都存储在 NVRAM 中;但它们更容易通过名为 MokManager 的程序添加到 NVRAM。将 MOK 放入 NVRAM 仍然很乏味,以至于大多数人都不会打扰,而且很多人确实遇到了问题;但它比完全控制您的安全启动子系统更容易,如您引用的我的页面中所述(管理 Linux 的 EFI 启动加载程序:控制安全启动)。

在大多数情况下,不使用 MOK;如果您想双启动 Windows 和 Ubuntu,您可能会使用固件的内置密钥和嵌入在 Ubuntu 的 Shim 二进制文件中的密钥。如果您想添加另一个 Linux 发行版、编译您自己的内核、使用 GRUB 以外的引导加载程序、使用第三方内核模块等,您将使用 MOK。

除了这两个来源之外,固件中还内置了安全启动密钥。我不记得 Shim 是否使用这些键。如果它使用 EFI和调用(确实如此,但我尚未查看此答案的上下文),它将隐式使用它们。我记得当 GRUB 回调以查看内核是否已签名时,它自己的验证码不使用固件的安全启动密钥,但我可能没有正确记住这一点。LoadImage()StartImage()

至于Shim如何集成到安全启动系统中,我上次检查过,它没有。IIRC,为了启动其后续程序 (GRUB),Shim 实现了自己的二进制加载代码,类似于天诺核 UEFI 示例实现中代码的精简版本。此代码调用 Shim 自己的安全启动验证代码,该代码根据其内置密钥和本地 MOK 列表检查二进制文件,以启动二进制文件。(它也可能使用固件自己的安全启动密钥,但我对此并不肯定。)一旦 GRUB 被加载,它就会调用 Shim 的二进制验证函数来验证 Linux 内核,该内核以自己的方式启动(而不是EFI 启动 EFI 程序的方式)。因此,Shim 并没有真正深入地将自己集成到固件中;它只是将其一两个功能提供给后续程序,LoadImage()StartImage()EFI 功能不变。

也就是说,EFI 确实提供了替换或补充正常 EFI 系统调用的方法,并且一些工具确实使用了这些方法。例如,PreLoader 程序是一个工具,可以做类似于 Shim 所做的事情,将自身更深入地集成到固件中;它采用EFI系统调用,旨在打破补丁或过时的功能进行修改StartImage(),以便它会检查这两个平时UEFI安全启动键的MOK。PreLoader 已经被淘汰了。它的开发人员和 Shim 的开发人员合作,专注于 Shim 而不是 PreLoader 作为标准的 Linux 安全启动工具。AFAIK,Shim 没有采用 PreLoader 更深的 UEFI 集成;然而,我已经有一段时间没有仔细查看代码了,所以我可能已经过时了。那个说....

我自己的 rEFInd 引导管理器使用我从 PreLoader 程序中获取的代码,以便将 Shim 的二进制验证代码“粘合”到 UEFI 的正常验证子系统中。因此,在图片中使用 rEFInd 时,任何尝试使用LoadImage()StartImage()调用 Shim 身份验证代码启动 EFI 程序的尝试,如果失败,则第二次调用标准 UEFI 安全启动身份验证。gunmiboot/systemd-boot 引导管理器执行类似的操作。这两个程序这样做是因为它们通过其 EFI 存根加载程序启动 Linux 内核,这意味着它们依赖于 EFILoadImage()StartImage()调用。这与 GRUB 形成对比,后者是一个完整的引导加载程序,以自己的方式启动 Linux 内核,因此 GRUB 不需要这些 EFI 系统调用来识别 Shim 的密钥或本地 MOK 列表。

我希望这有助于澄清事情,但我不确定它会不会。所有这些工作的细节非常混乱,而且我已经有一段时间没有详细处理它们了,所以我自己的想法并不像它们可能那样有条理。

  • Canonical 的密钥包含在某些计算机的固件中,但这种情况很少见。(我自己只在华硕 P8 H77-I 主板上见过。)这样的计算机可以直接启动 Ubuntu 的 GRUB,无需 Shim,启用安全启动。您可以自己签署第三方内核模块 - 请参阅我对[此问题](https://askubuntu.com/questions/914997/install-virtualbox-while-keeping-secure-boot/915108)的回答,了解一种方法。是的,许多 EFI 系统调用可以通过 EFI 程序进行修补。 (2认同)

Kaz*_*lfe 5

正如您正确推断的那样,SHIM 将尝试从LoadImage()StartImage()首先加载。然后 EFI 将验证签名是否匹配(通过使用内部 SecureBoot 机制)。如果LoadImage()返回EFI_SECURITY_VIOLATION,系统将尝试从内部证书回退加载 stage2(在本例中为 GRUB2)。

该证书在编译时嵌入到系统中,在这种情况下是由 Canonical 完成的。可以使用或类似的实用程序从 SHIM 中提取此证书binwalk

实际上,这允许 SecureBoot 将经过验证的签名shim存储在缓存中,然后允许shim验证 GRUB 是否使用上述证书签名。如果是,则 GRUB 会成功引导。

SHIM 将尽可能使用系统密钥 - 这就是为什么LoadImage()并且StartImage()首先使用。只有当它不起作用时,SHIM 才会尝试使用它自己的内部证书加载 stage2。你可以在这里看到这段代码(部分verify_buffer),它在handle_image链的一部分中被调用。

整个验证链如下所示:

  1. 验证系统哈希值和 MOK 列表
  2. 确保二进制文件未列入黑名单
  3. 尝试通过MOK/BIOS 白名单检查二进制文件
  4. 检查由构建密钥和 SHIM 自己的内部密钥定义的内部签名。

MOK 管理器不是 MOK 数据库本身也很重要。后者由 EFI 固件维护,该固件在刷新过程中以及操作系统(或者,在这种情况下,shim)接收制造商添加/删除内容的命令。shim只存储一个非常短的上述编译密钥列表以允许启动 - 其他一切都需要由 EFI 固件处理。