鉴于:
运行 GNU/Linux 的服务器,以及
该服务器上的机箱入侵检测开关,以及
该服务器中的 BIOS(或 UEFI),用于跟踪入侵检测状态,以便可以通过
freeipmi
或类似方式读取该状态,
如果服务器机箱打开,如何才能最好地使该服务器执行某些操作(例如发送电子邮件或简单地关闭)?
用户森林关于 security.stackexchange 建议轮询“/dev/nvram
每 0.5 秒左右解析一次,以获取存储机箱入侵计数的任何值。“ (执照:抄送-SA 3.0.)这似乎效率低下。当然有更好的方法吗?
答案1
以下引用自:firmware.intel.com/blog/accessing-uefi-variables-linux
由 mfleming 发表于 01/3/2014
本文总结了 Linux 提供的两种用于访问 UEFI 运行时变量的方法,以及权衡和一些历史记录。
The Legacy efivars Interface
由 Matt Domsch 编写的用于与 UEFI 变量交互的原始接口通过通用 Linux 内核 sysfs 文件系统导出。该接口是在 UEFI 之前的 EFI 时代创建的,用于安腾计算机,因此带有一定的历史包袱。
旧版 efivars 接口通过相对于 sysfs 挂载点的路径“firmware/efi/vars/”导出。假设 sysfs 安装在 /sys,则包含 efivars 文件的目录将是:
/sys/firmware/efi/vars/
除了特殊文件
new_var
和之外del_var
,上述挂载点中的每个条目本身就是一个目录 - 每个在运行时可访问的 UEFI 变量都有一个目录,Boot0000-12345678-abcd-abcd-abcd-123456789abc/ Boot0001-12345678-abcd-abcd-abcd-123456789abc/ BootCurrent-12345678-abcd-abcd-abcd-123456789abc/ BootOrder-12345678-abcd-abcd-abcd-123456789abc/ BootSetup-12345678-abcd-abcd-abcd-123456789abc/ del_var Lang-12345678-abcd-abcd-abcd-123456789abc/ LangCodes-12345678-abcd-abcd-abcd-123456789abc/ new_var Setup-12345678-abcd-abcd-abcd-123456789abc/
UEFI 变量的目录名称由变量名称和变量的全局唯一标识符 (GUID) 组成。这个例子:
Boot0000-12345678-abcd-abcd-abcd-123456789abc/
可能是第一个 UEFI 引导变量的目录。在每个目录中可以找到以下文件,
attributes data guid raw_var size
大多数这些文件的内容是人类可读的,并且对应于同名的 UEFI 变量参数。 “raw_var”文件是个例外,它包含二进制数据。例如,上述引导条目的“属性”文件将包含类似以下 ASCII 字符串的内容,
EFI_VARIABLE_NON_VOLATILE EFI_VARIABLE_BOOTSERVICE_ACCESS EFI_VARIABLE_RUNTIME_ACCESS
指示该变量是非易失性的,并且在调用 UEFI 引导服务 ExitBootServices() 之前和之后都可以访问。不可能写入这些人类可读的文件 - 它们的唯一目的是为用户提供变量的详细信息。
raw_var
允许从用户空间读取/写入内核中使用的 UEFI 变量二进制数据结构,并且是遗留接口的核心部分。对现有变量的所有修改都是通过传递“struct efi_variable”对象通过此文件执行的,struct efi_variable { efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; efi_guid_t VendorGuid; unsigned long DataSize; __u8 Data[1024]; efi_status_t Status; __u32 Attributes; } __attribute__((packed));
顾名思义,顶层目录中的特殊文件“new_var”和“del_var”用于创建和删除 UEFI 变量。这些文件不使用人类可读的文本。相反,“struct efi_variable”对象在内核和用户空间之间来回传递,以创建新变量或删除现有 UEFI 变量。
在 Linux 内核中保留 efivars 接口的主要原因是仍然有一些工具仅使用此接口:efibootmgr 工具在所有 Linux 发行版上都可用,用于控制 UEFI 系统上的启动顺序,但仍然使用此传统工具界面。
然而,旧版 efivars 界面存在一些限制,随着时间的推移,这些限制已开始成为一个令人头疼的问题。
最明显的缺点是可以读取/写入的 UEFI 变量数据的大小有上限。由于内核数据结构的设计方式,无法读取或写入超过 1024 字节的变量数据,并且对于某些用例,该限制是一个问题。
此外,efivars 接口本质上需要工具来操作 UEFI 变量,因为该接口仅通过“new_var”、“del_var”和“raw_var”处理 UEFI 变量的重要“struct efi_variable”数据结构的读写副本特殊文件。如果不构建“struct efi_variable”对象并将其写入“del_var”文件,则无法删除 UEFI 变量。
efivarfs 文件系统
为了避开旧版 efivar 接口的限制,Matthew Garrett 和 Jeremy Kerr 创建了全新的 Linux 文件系统。这个新文件系统被命名为“efivarfs”,是 Linux 内核 v3.10 版本的一部分。
与所有其他 Linux 文件系统一样,efivarfs 必须安装在某个位置才能访问其文件,这可以通过发出以下命令来完成:
mount -t efivarfs none /sys/firmware/efi/efivars
efivarfs 不包含目录,而是包含名称根据 UEFI 变量名称和 GUID 构建的文件,
Boot0000-12345678-abcd-abcd-abcd-123456789abc Boot0001-12345678-abcd-abcd-abcd-123456789abc BootCurrent-12345678-abcd-abcd-abcd-123456789abc BootOrder-12345678-abcd-abcd-abcd-123456789abc BootSetup-12345678-abcd-abcd-abcd-123456789abc
尽管每个文件都包含二进制数据(如旧版接口的
new_var
,del_var
),但 efivarfs 的数据结构要简单得多,仅包含 4 个字节用于 UEFI 变量属性位掩码,其余部分是 UEFI 变量数据,例如struct new_efi_variable { u32 attributes; u8 data[0]; };
所有其他信息(例如变量名称、GUID 和大小)都是从正在读取/写入的文件名推断出来的。这使得通过创建或删除文件来创建或删除 UEFI 变量变得很简单。
例如,以下 shell 命令创建一个新变量,
printf "\x07\x00\x00\x00\x00" > myvar-12345678-1234-1234-1234-123456789abc
myvar
使用 GUID12345678-1234-1234-1234-123456789abc
以及 属性位掩码中设置的EFI_VARIABLE_NON_VOLATILE
、EFI_VARIABLE_BOOTSERVICE_ACCESS
和EFI_VARIABLE_RUNTIME_ACCESS
位命名,后跟 1 个字节的数据(最后的\x00
)。可以使用任何获取文件大小的常用方法来找到变量的大小以及 4 个字节的属性数据。例如,运行以下 shell 命令,
du -b myvar-12345678-1234-1234-1234-12345678abc
将打印“5”——即变量属性位掩码的 4 个字节,加上 1 个字节的数据。
要删除该变量,只需将其从 efivarfs 文件系统中删除即可,
rm myvar-12345678-1234-1234-1234-12345678abc
显然,这是一个更直观的界面,用于从命令行和 shell 脚本访问 UEFI 变量。最重要的是,与传统接口不同,UEFI 变量可读取/写入的数据量没有限制。
当然,尽管 efivarfs 使用的数据结构比遗留接口简单得多,但它仍然作为二进制数据进行读/写,并且比遗留接口提供的人类可读文件需要更多的心理处理。
在本文中,我们简要讨论了 Linux 提供的两种在运行时访问 UEFI 变量的解决方案。虽然新的 efivarfs 文件系统提供了一个有用的接口,用于从 shell 脚本(甚至从命令行)访问 UEFI 变量,但某些工具仍在使用旧接口,并且在某些情况下,人类可读的文件可能比efivarfs 的纯二进制接口。
Matt Fleming 是英特尔开源技术中心的软件工程师和 Linux 内核 (U)EFI 维护者。