为什么需要签名
现在很多 OS 都是通过 UEFI 引导的(区别一个 Linux 操作系统是通过 UEFI 还是 BIOS 引导的方法是查看是否存在*”/sys/firmware/efi”* 目录),而如果 UEFI 里使能了 secure boot,那么需要 Signed kernel image 才能加载,对应的 kernel module 也需要是签过名的。
即便 UEFI 里没有使能 secure boot,但如果内核是按照 CONFIG_MODULE_SIG_FORCE 配置的(也可通过内核启动参数*”module.sig_enforce=1″* 打开),那么也只有签过名的驱动能加载。
如果内核只是设置了 CONFIG_MODULE_SIG,那么未签名的驱动虽可被加载,但会被标记为 taint。
对于内核 mainline 中自带的 in-tree 驱动,如果 CONFIG_MODULE_SIG_ALL 配置项打开,则在执行 “modules_install” 时会被同步签名,因此存在签名问题的主要是 out-of-tree 驱动(在 “/proc/modues” 中被标记为 “OE”,其中 ‘O’ 表示 Out-of-tree,’E’ 表示 Unsigned module)。
又由于主流发行版中 “sig_force” 通常默认关闭,所以我们需主要关注开启了 secure boot 的场景。
如何签名
1. 生成 Key
签名需要一组 keypair,而产生 keypair 最通用的方式是借助 openssl,例如:
# openssl req -new -x509 -nodes -days 36500 -subj "/CN=DKMS module signing key" \
-newkey rsa:2048 -keyout $private_key \
-outform DER -out $public_key
它表示证书使用 X.509 标准,有效期是 36500 天(10 年),而 “DER” 是 RSA 公钥的一种二进制格式。
对于 Ubuntu 系统,在安装了 “shim-signed” 软件包后,还可直接使用
# update-secureboot-policy --new-key
生成 keypair,结果位于 “/var/lib/shim-signed/mok” 目录,其中 “MOK.priv” 为私钥,”MOK.der” 为公钥。
其实 “/usr/sbin/update-secureboot-policy” 就是个 shell 脚本,它的核心依然是调用 openssl,只是通过进一步的封装让使用变得更简便。
2. 执行签名
内核既然能给 in-tree 的驱动签名,那么它肯定是有一套签名工具的,这个工具在安装内核头文件后就可获取,具体路径为:
/lib/modules//build/scripts/sign-file
对于 Ubuntu 系统,在安装了 “sbsigntool” 软件包后,也可使用 “kmodsign” 作为签名工具。
根据上一步得到的 keypair,执行如下的签名指令:
# kmodsign/sign-file sha512 "$private_key" "$public_key"
之后使用 “modinfo ” 查看,可以发现增加了 signature 的部分:
signat: PKCS#7
signer:
sig_key:
sig_hashalgo: md4
3. 注册 key
secure boot 的设计目的是为了防止不受信任的软件被恶意加载,那按照上述的方法,似乎谁都可以给驱动签名,那怎样的签名才是被认可的呢?
从 UEFI 到 OS,再从 OS 到 driver,这是一条信任链,只有被已经在 UEFI 里注册过的 key 签名的驱动,才是可信的。除了 UEFI 出厂时由 vendor 设置的 key,我们还可以在后续的使用过程中自行添加(称为 “enroll”),其依据的原理大致是:既然都能够操作 UEFI 了,那该用户添加的 key 应该是可被信赖的。
添加的方法是利用 mokutil工具导入 keypair 中的公钥,并随之设置一个密码(Ubuntu 系统还可以使用 “update-secureboot-policy –enroll-key”,究其实现,本质也是对 mokutil 的一层封装):
# mokutil --import $public_key
之后通过*”mokutil –list-new”* 可以看到这个状态为 “to be enrolled” 的 key。
系统重启进入 UEFI 界面后,选择 “Enroll MOK”,输入 mokutil 导入公钥时设置的密码,以确认 enrollment 操作。这样 key 就被注册成功了,启动后可通过*”mokutil –list-enrolled”* 命令查看。
番外 – Secure Boot 验证
对于没有物理机,或者不能在物理机启动阶段直接操作的情况,可考虑使用 qemu 创建的虚拟机来验证。为此,需要安装 OVMF,一个基于 EDK II 代码,专为虚拟机设计的 UEFI 组件。
具体的安装命令根据 OS 的不同而有所差别, 笔者试验用的主机是 Ubuntu 的 bionic (18.04) 版本,安装得到的 OVMF 不带 secure boot 功能(路径为 “/usr/share/OVMF”),所以从另一台 CentOS-8 的 “/usr/share/edk2/ovmf/” 拷贝了 “secboot” 版本来用。后经查询,Ubuntu 的 focal (20.04) 版本 的 “ms” 应该是使能了 secure boot 的。
qemu 配合 OVMF 使用的参数设置和各项含义,可参考 OvmfPkg 源码的 README(https://github.com/tianocore/edk2/tree/master/OvmfPkg),以及 debian wiki(https://wiki.debian.org/SecureBoot/VirtualMachine):
# qemu-system-x86_64
-machine q35,smm=on \
-drive if=pflash,format=raw,unit=0,file="${OVMF_CODE}",readonly=on \
-drive if=pflash,format=raw,unit=1,file="${OVMF_VARS}" \
-boot menu=on
进入启动的虚机后,执行 “mokutil –sb-state” 命令,将显示 “enabled”,同时系统日志有如下打印:
# dmesg |grep -i secure
secureboot: Secure Boot enabled
kernel is locked down from EFI secure boot
这时如果加载一个未经签名的驱动,将被拒绝:
insmod error could not insert module ... operation not permitted
以上就是良许教程网为各位朋友分享的Linu系统相关内容。想要了解更多Linux相关知识记得关注公众号“良许Linux”,或扫描下方二维码进行关注,更多干货等着你 !