Secure Boot with Linux
Unwanted binaries like viruses should be prevented from loading. This is known as Secure-Boot. The (U)EFI firmware only loads binaries signed by the “Platform key” (PK) certificates. The PK is pre-installed by the manufacturer. Probably 9x% come with Microsoft Windows pre-installed. Therefor most PCs come with Microsoft key pre-installed. For Qemu/KVM there is “OVMF”: It is based on the EDK2 (EFI Development KIT). It is developed by the “TianoCore” community. It has not keys pre-installed.
You either have to get your boot-loader (GRUB), Linux-kernel signed by that key owner, or install your own PK. Microsoft will not sign GRUB2 directly: Its GPL3 licence could be enforced to reveal your private key, which would break the whole secure boot mechanism. Therefore exists “SHIM”: It will get loaded by EFI instead of GRUB. It itself will only than load GRUB and the Linux kernel if they are also signed. Each Linux distribution builds its own version of SHIM: It embeds a certificate controlled fully by that distribution. This certificate is used to sign GRUB and the Linux kernels. SHIM also allows loading “Machine Owner Keys” (MOK). This allows using SHIM from one distribution to load kernels from other distributions or your own.
There are two databases within the firmware. They contain signatures and certificates for EFI drivers and boot-loaders.
- The “Signatures Database” (DB) is a white-list. The firmware allows loading of binaries allowed by this database.
- The “Forbidden Signatures Database” (DBX) is a black-list. The firmware prevents loading binaries forbidden by this database.
The databases need updates when keys get compromised or new drivers are released. These updates must be signed cryptographically to be applied by the firmware. The “Key Exchange Key” (KEK) is used for this. By default the Microsoft Key is used for this as well. You can change it if you have control over the PK.
Create PK
We create some keys and certificates for our platform:
sudo apt install sbsigntool # Debian Stretch+ mkdir PLATFORM cd PLATFORM
Create new self-signed certificates:
for n in PK KEK db do openssl genrsa -out "$n.rsa" 2048 openssl req -new -x509 -sha256 -subj "/CN=$n/" -key "$n.rsa" -out "$n.pem" -days 7300 openssl x509 -in "$n.pem" -inform PEM -out "$n.der" -outform DER done
Create a GUID to identify us:
Create empty “EFI signature lists”. This is the format expected by EFI. It must contain the DER encoded certificate:
for n in PK KEK db do sbsiglist --owner "$guid" --type x509 --output "$n.esl" "$n.der" done
Create the signed updates for PK, KEK and DB. Please note, that the PEM encoded certificate must be used:
for n in PK=PK KEK=PK db=KEK do sbvarsign --key "$n#*=>.rsa" --cert "$n#*=>.pem" --output "$n%=*>.auth" "$n%=*>" "$n%=*>.esl" done
Install those files into the right location for sbkesync :
install -m 0755 -d etc/secureboot/keys/PK install -m 0644 -t etc/secureboot/keys/PK PK.auth install -m 0755 -d etc/secureboot/keys/KEK install -m 0644 -t etc/secureboot/keys/KEK KEK.auth install -m 0755 -d etc/secureboot/keys/db install -m 0644 -t etc/secureboot/keys/db db.auth install -m 0755 -d etc/secureboot/keys/dbx sudo mount -t efivarfs none /sys/firmware/efi/efivars sbkeysync --keystore etc/secureboot/keys --verbose --pk --dry-run
After installing the new PK the firmware switches from “setup mode” into “user mode”. You are now no longer able to modify any of variables unless you use one of those signed updates. Hopefully your firmware provides a mechanism to reset it back into “setup mode”.
Sign boot-loader
GRUB is modular by design. You must built a customized image: It must not contain some modules. They would allow loading an unsigned Linux kernel. (GRUB has its own mechanism to only load signed things: Using digital signatures, secure boot with grub and signed Linux and initrd)
sudo apt install grub-efi-amd64-bin cat >grub.cfg __CFG__ set debug=linuxefi echo Loading step 1a sleep 1 source /efi/univention/grub.cfg echo Loading step 1b sleep 1 search --file --set=root /efi/univention/grub.cfg source /efi/univention/grub.cfg __CFG__ : >modules echo acpi efifwsetup,_gop,net,_uga> >>modules echo all_video bitmap,_scale> font gfxmenu,term,_background>> jpeg png video,_bochs,_cirrus,_colors,_fb> >>modules echo btrfs ext2,cmd> fat hfsplus iso9660 lvm mdraid09,1x> part_apple,gpt,msdos> >>modules echo bufio disk,filter> gzio lvm lzopio mdraid09,1x> memdisk part_apple,gpt,msdos> search,_fs_file,uuid>,_label> >>modules echo crypto gcry_sha512 >>modules echo chain configfile datetime echo gettext halt keystatus lsefi,mmap,systab>,sal> loadenv minicmd mmap net normal password_,>pbkdf2 priority_queue reboot relocator sleep terminal test trig true >>modules echo linuxefi >>modules grub-mkimage --format x86_64-efi --output grubx64.efi --config grub.cfg --directory /usr/lib/grub/x86_64-efi --prefix /efi/boot $()
You must now sign your boot-loader to get it loaded:
sbsign --key db.rsa --cert db.pem --output grubx64.efi.signed grubx64.efi install -m 644 grubx64.efi.signed /boot/efi/EFI/boot/BOOTX64.EFI install -m 644 grubx64.efi.signed /boot/efi/EFI/univention/BOOTX64.EFI
Microsoft will not do that! See #Shim below.
/boot : The traditional directory for Linux kernel. May be an extra filesystem readable by GRUB.
/boot/efi : The “EFI system partition”. It’s a FAT32 file system.
/boot/efi/EFI : The directory for different boot entries.
/boot/efi/EFI/BOOT : The default directory for removable devices.
/boot/efi/EFI/BOOT/BOOTX64.EFI : The default boot entry. This is used as the last fall back.
Shim
We create our own certificate:
openssl genrsa -out "shim.rsa" 2048 openssl req -new -x509 -sha256 -subj "/CN=shim/" -key "shim.rsa" -out "shim.pem" -days 7300 openssl x509 -in "shim.pem" -inform PEM -out "shim.der" -outform DER ln -s ../CA/archive-subkey-public.der shim.der ln -s ../CA/archive-subkey-public.pem shim.pem ln -s ../CA/private/archive-subkey-private.key shim.rsa
make EFI_PATH=/usr/lib VENDOR_CERT_FILE="shim.der" ENABLE_SBSIGN=1 install-as-data
Microsoft signs our build:
sbsign --key db.rsa --cert db.pem --output shimx64.efi.signed /usr/lib/shim/shimx64.efi
Thus it gets loaded by PCs using the Microsoft key. GRUB needs to be re-signed by our embedded key:
sbsign --key shim.rsa --cert shim.pem --output grubx64.efi.signed grubx64.efi install -m 0644 grubx64.efi.signed /boot/efi/EFI/BOOT/grubx64.efi install -m 0644 grubx64.efi.signed /boot/efi/EFI/univention/grubx64.efi
install -m 0644 shimx64.efi.signed /boot/efi/EFI/univention/shimx64.efi install -m 0644 shimx64.efi.signed /boot/efi/EFI/BOOT/BOOTX64.efi
“Machine Owner Keys” (MOK) allow you to load things yourself. You can while-list individual binaries by adding their hash. You can load certificates to allow loading everything signed by them. This requires the MOK-Manager:
install -m 0644 /usr/lib/shim/mmx64.efi.signed /boot/efi/EFI/univention/mmx64.efi install -m 0644 /usr/lib/shim/mmx64.efi.signed /boot/efi/EFI/BOOT/mmx64.efi
EFI allows booting different binaries. Their paths need to be registered as EFI variables. If they are not configured correctly, nothing gets booted. Install the “Fallback Manager” which can fix this:
echo "shimx64.efi,univention,,This is the boot entry for UCS" | iconv -t UCS-2LE >BOOTX64.CSV install -m 0644 BOOTX64.CSV /boot/efi/EFI/univention/BOOTX64.CSV install -m 0644 /usr/lib/shim/fbx64.efi.signed /boot/efi/EFI/BOOT/fbx64.efi
Sign Linux kernel with key embedded in SHIM:
sbsign --key shim.rsa --cert shim.pem --detached --output vmlinuz-4.9.0-ucs105-amd64.sig vmlinuz-4.9.0-ucs105-amd64 cp vmlinuz-4.9.0-ucs105-amd64 vmlinuz-4.9.0-ucs105-amd64.efi.signed sbattach --attach vmlinuz-4.9.0-ucs105-amd64.sig vmlinuz-4.9.0-ucs105-amd64.efi.signed
NvVars
UEFI requires a writable flash device. qemu -bios will only provide one big read-only ROM. In that case “OVMS” stores the EFI variables in /boot/EFI/NvVars . The correct way for Qemu is
-drive file=/usr/share/OVMF/OVMF_CODE.fd,if=pflash,format=raw,unit=0,readonly=on -drive file=/home/phahn/.config/libvirt/qemu/nvram/uefi_VARS.fd,if=pflash,format=raw,unit=1
This stores the EFI-variables in the VARS file. But this does not implement the restriction, that some variables must only be modified by the boot-service, e.g. all Secure-Boot related ones. This requires System Management Mode (SMM) to work. In that case the EFI-variables flash is write protected, so only SMM can write it. This needs to be turned on:
-global driver=cfi.pflash01,property=secure,value=on
If OVMF does not support SMM mode, writing to the flash will now fail. In that case OVMF will fall back to using NvVars again. But the EFI file system is too late for storing the SecureBoot related keys: After each reboot they will be gone again. So make sure to use the right version of OVMF with the right flash setting!