Skip to content

Commit

Permalink
Merge pull request #3259 from balena-os/luks-passphrase-pcr7
Browse files Browse the repository at this point in the history
Seal LUKS passphrase with PCR7
  • Loading branch information
flowzone-app[bot] committed Mar 22, 2024
2 parents b551e7d + 18e35c5 commit 41b411c
Show file tree
Hide file tree
Showing 28 changed files with 1,159 additions and 134 deletions.
30 changes: 22 additions & 8 deletions docs/secure-boot.md → docs/uefi-secure-boot.md
Expand Up @@ -28,8 +28,6 @@ Additionally, UEFI must be configured properly prior to provisioning - at this m

Manually loading the keys is not possible at this moment as the installer is unsigned by default.

The system must be provisioned and booted at least once in a trusted environment - the full protection is only enabled on first boot, not immediately after the installer powers the device down.

## Chain of Trust

Multiple system components are involved in the validation of a "trusted operating system":
Expand Down Expand Up @@ -107,10 +105,28 @@ The above is commonly referred to as the "chain of trust". For `balenaOS`, the t
* **Format:** 32 bytes long random string
* **Stored in:** Encrypted in the EFI partition (`balena-luks.enc`), encryption key in the TPM (TPM key slot is indicated by `balena-luks.ctx` in the EFI partition)
* **Updated by:** `balenaOS` installer during provisioning
* **Verified by:** `cryptsetup` initrd script (verified by GRUB as a part of kernel+initrd bundle), encryption key locked to PCRs 0,1,2,3
* **Verified by:** `cryptsetup` initrd script (verified by GRUB as a part of kernel+initrd bundle), encryption key locked to PCRs 0,2,3,7
* **Verifies:** Nothing, only used for encryption

*Note:* When the installer exits, the TPM key is only protected using PCRs 0,2,3. PCR 1 contains UEFI configuration, which the installer tampers with (by changing the boot order). The system is only fully locked to PCRs 0,1,2,3 on first boot after provisioning.

## TPM

The TPM is a hardware peripheral that performs a number of security related functions, most relevant being generating and storing cryptographic keys.

### PCRs

The TPM stores state during boot in so-called platform configuration registers (PCRs). Several PCRs are extended by the firmware at boot with various boot time measurements.

| PCR | Measurement |
| --- | ------------- |
| 0 | Firmware code |
| 2 | Pluggable code (OpRom/UEFI driver) |
| 3 | Pluggable firmware data (e.g. RAID configuration) |
| 7 | Secure Boot configuration |

Of note is PCR7, which measures all UEFI variables that affect the state of the secure boot configuration on the affected system, including whether secure boot is enabled, and all secure boot variables and keys (PK, KEK, db, dbx, etc.).

When installing balenaOS with secure boot, the passphrase is sealed using a policy that requires the PCRs to match certain known good values. As long as the digests of the PCRs haven't changed, we can be assured that the system is still secure, and the disk can be unlocked. However, tampering with the secure boot configuration, such as through the firmware menu, will change the value of PCR7. This renders the disk un-decryptable, and the operating system unbootable until the known good configuration is restored.

## boot and EFI partition split

Expand Down Expand Up @@ -144,13 +160,11 @@ Each OS build ships update files for `db` and `dbx` in the `/resin-boot` directo

### BIOS/UEFI updates

To protect the encryption keys in the TPM we lock against PCRs 0, 1, 2 and 3. Namely PCR0 is the checksum of the UEFI image, which would change by a BIOS/UEFI update. PCR1 is the checksum of the UEFI configuration which would generally change by updating anything in the UEFI configuration (e.g. disabling secure boot or changing boot order). That said, if users updates BIOS/UEFI or changes settings, the device needs to be reprovisioned afterwards.

*Note: While we assume PCR1 will change when BIOS/UEFI settings are changed, the actual implementation of what PCR1 measures is very device-specific. We are aware that on some devices the PCR1 value will not change even when boot order is changed or secure boot is disabled via BIOS/UEFI setup. We therefore strongly recommend to protect the entry to BIOS/UEFI setup with a strong password and, if possible, disable shortcuts in user interaction with the firmware, such as using F-keys for temporary boot order override.*
As previously mentioned, the encryption passphrase is sealed using several PCRs, including PCR0. A firmware/BIOS upgrade will change the digest of PCR0, leaving the system unbootable. It is advised that any necessary firmware upgrades be performed before installing balenaOS.

### Hardware updates

To protect the encryption keys in the TPM we lock against PCRs 0, 1, 2 and 3. Namely PCRs 2 and 3 contain checksums of the loaded UEFI drivers for devices plugged in, their firmware and configuration. These would change if e.g. a PCIe device is replaced, its firmware is updated or even the same card is moved into a different slot. That said if hardware changes are necessary, the device needs to be reprovisioned afterwards.
PCRs 2 and 3 contain checksums of the loaded UEFI drivers for devices plugged in, their firmware and configuration. These would change if e.g. a PCIe device is replaced, its firmware is updated or even the same card is moved into a different slot. If hardware changes are necessary, the device needs to be reprovisioned afterwards.

## Debugging

Expand Down
21 changes: 21 additions & 0 deletions meta-balena-common/conf/image-uefi.conf
@@ -0,0 +1,21 @@
# Location of EFI files inside EFI System Partition
EFIDIR ?= "/EFI/BOOT"

# Prefix where ESP is mounted inside rootfs. Set to empty if package is going
# to be installed to ESP directly
EFI_PREFIX ?= "/boot"

# Location inside rootfs.
EFI_FILES_PATH = "${EFI_PREFIX}${EFIDIR}"

# The EFI name for the architecture
EFI_ARCH ?= "INVALID"
EFI_ARCH:x86 = "ia32"
EFI_ARCH:x86-64 = "x64"
EFI_ARCH:aarch64 = "aa64"
EFI_ARCH:arm = "arm"
EFI_ARCH:riscv32 = "riscv32"
EFI_ARCH:riscv64 = "riscv64"

# Determine name of bootloader image
EFI_BOOT_IMAGE ?= "boot${EFI_ARCH}.efi"
@@ -0,0 +1,44 @@
From 006799e9c4babe8a8340a24501b253e759614a2d Mon Sep 17 00:00:00 2001
From: Khem Raj <raj.khem@gmail.com>
Date: Wed, 13 Jan 2016 19:17:31 +0000
Subject: [PATCH] Disable -mfpmath=sse as well when SSE is disabled

Fixes

configure:20574: i586-poky-linux-gcc -m32 -march=core2 -msse3
-mtune=generic -mfpmath=sse
--sysroot=/usr/local/dev/yocto/grubtest2/build/tmp/sysroots/emenlow -o
conftest -O2 -pipe -g -feliminate-unused-debug-types -Wall -W -Wshadow
-Wpointer-arith -Wmissing-prototypes -Wundef -Wstrict-prototypes -g
-falign-jumps=1 -falign-loops=1 -falign-functions=1 -mno-mmx -mno-sse
-mno-sse2 -mno-3dnow -fno-dwarf2-cfi-asm -m32 -fno-stack-protector
-mno-stack-arg-probe -Werror -nostdlib -Wl,--defsym,___main=0x8100
-Wall -W -I$(top_srcdir)/include -I$(top_builddir)/include
-DGRUB_MACHINE_PCBIOS=1 -DGRUB_MACHINE=I386_PC -Wl,-O1
-Wl,--hash-style=gnu -Wl,--as-needed conftest.c >&5
conftest.c:1:0: error: SSE instruction set disabled, using 387
arithmetics [-Werror]
cc1: all warnings being treated as errors

Signed-off-by: Nitin A Kamble <nitin.a.kamble@intel.com>
Signed-off-by: Khem Raj <raj.khem@gmail.com>

Upstream-Status: Pending

---
configure.ac | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/configure.ac b/configure.ac
index cd667a2..8263876 100644
--- a/configure.ac
+++ b/configure.ac
@@ -846,7 +846,7 @@ fi
if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ) && test "x$platform" != xemu; then
# Some toolchains enable these features by default, but they need
# registers that aren't set up properly in GRUB.
- TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow"
+ TARGET_CFLAGS="$TARGET_CFLAGS -mno-mmx -mno-sse -mno-sse2 -mno-sse3 -mno-3dnow -mfpmath=387"
fi

if ( test "x$target_cpu" = xi386 || test "x$target_cpu" = xx86_64 ); then
@@ -0,0 +1,37 @@
From b47029e8e582d17c6874d2622fe1a5b834377dbb Mon Sep 17 00:00:00 2001
From: Khem Raj <raj.khem@gmail.com>
Date: Fri, 26 Mar 2021 11:59:43 -0700
Subject: [PATCH] RISC-V: Restore the typcast to 64bit type

this makes the type promotions clear and explicit
It was already typecasted to long but was accidentally dropped in [1]
which stated to cause failures on riscv32 as reported in [2]

[1] https://git.savannah.gnu.org/cgit/grub.git/commit/?id=2bf40e9e5be9808b17852e688eead87acff14420
[2] https://savannah.gnu.org/bugs/index.php?60283

Upstream-Status: Submitted
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Cc: Andreas Schwab <schwab@suse.de>
Cc: Daniel Kiper <daniel.kiper@oracle.com>
Cc: Chester Lin <clin@suse.com>
Cc: Nikita Ermakov <arei@altlinux.org>
Cc: Alistair Francis <alistair.francis@wdc.com>

---
util/grub-mkimagexx.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/util/grub-mkimagexx.c b/util/grub-mkimagexx.c
index e50b295..2f09255 100644
--- a/util/grub-mkimagexx.c
+++ b/util/grub-mkimagexx.c
@@ -1310,7 +1310,7 @@ SUFFIX (relocate_addrs) (Elf_Ehdr *e, struct section_metadata *smd,
*/

sym_addr += addend;
- off = sym_addr - target_section_addr - offset - image_target->vaddr_offset;
+ off = (grub_int64_t)sym_addr - target_section_addr - offset - image_target->vaddr_offset;

switch (ELF_R_TYPE (info))
{
@@ -0,0 +1,54 @@
From a80592e20f6c4b928a22862f52f268ab9d9908b2 Mon Sep 17 00:00:00 2001
From: Khem Raj <raj.khem@gmail.com>
Date: Wed, 13 Jan 2016 19:28:00 +0000
Subject: [PATCH] grub.d/10_linux.in: add oe's kernel name

Our kernel's name is bzImage, we need add it to grub.d/10_linux.in so
that the grub-mkconfig and grub-install can work correctly.

We only need add the bzImage to util/grub.d/10_linux.in, but also add it
to util/grub.d/20_linux_xen.in to keep compatibility.

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Khem Raj <raj.khem@gmail.com>

Upstream-Status: Inappropriate [OE specific]

---
util/grub.d/10_linux.in | 6 +++---
util/grub.d/20_linux_xen.in | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/util/grub.d/10_linux.in b/util/grub.d/10_linux.in
index cc393be..8545cb6 100644
--- a/util/grub.d/10_linux.in
+++ b/util/grub.d/10_linux.in
@@ -166,12 +166,12 @@ machine=`uname -m`
case "x$machine" in
xi?86 | xx86_64)
list=
- for i in /boot/vmlinuz-* /vmlinuz-* /boot/kernel-* ; do
+ for i in /boot/bzImage-* /bzImage-* /boot/vmlinuz-* /vmlinuz-* /boot/kernel-* ; do
if grub_file_is_not_garbage "$i" ; then list="$list $i" ; fi
done ;;
- *)
+ *)
list=
- for i in /boot/vmlinuz-* /boot/vmlinux-* /vmlinuz-* /vmlinux-* /boot/kernel-* ; do
+ for i in /boot/bzImage-* /boot/vmlinuz-* /boot/vmlinux-* /bzImage-* /vmlinuz-* /vmlinux-* /boot/kernel-* ; do
if grub_file_is_not_garbage "$i" ; then list="$list $i" ; fi
done ;;
esac
diff --git a/util/grub.d/20_linux_xen.in b/util/grub.d/20_linux_xen.in
index 94dd8be..36cd554 100644
--- a/util/grub.d/20_linux_xen.in
+++ b/util/grub.d/20_linux_xen.in
@@ -181,7 +181,7 @@ EOF
}

linux_list=
-for i in /boot/vmlinu[xz]-* /vmlinu[xz]-* /boot/kernel-*; do
+for i in /boot/bzImage[xz]-* /bzImage[xz]-* /boot/vmlinu[xz]-* /vmlinu[xz]-* /boot/kernel-*; do
if grub_file_is_not_garbage "$i"; then
basename=$(basename $i)
version=$(echo $basename | sed -e "s,^[^0-9]*-,,g")
@@ -0,0 +1,34 @@
From 14c1d0459fb3561e627d3a5f6e91a0d2f7b4aa45 Mon Sep 17 00:00:00 2001
From: Naveen Saini <naveen.kumar.saini@intel.com>
Date: Mon, 15 Mar 2021 14:44:15 +0800
Subject: [PATCH] autogen.sh: exclude .pc from po/POTFILES.in

Exclude the .pc from po/POTFILES.in since quilt uses "patch --backup",
which will create the backup file under .pc, this may cause unexpected
errors, for example, on CentOS 5.x, if the backup file is null
(newfile), it's mode will be 000, then we will get errors when xgettext
try to read it.

Upstream-Status: Inappropriate [OE specific]

Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
Signed-off-by: Anuj Mittal <anuj.mittal@intel.com>
Signed-off-by: Naveen Saini <naveen.kumar.saini@intel.com>

---
autogen.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/autogen.sh b/autogen.sh
index 195daa5..773b7b4 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -26,7 +26,7 @@ fi
export LC_COLLATE=C
unset LC_ALL

-find . -iname '*.[ch]' ! -ipath './grub-core/lib/libgcrypt-grub/*' ! -ipath './build-aux/*' ! -ipath './grub-core/lib/libgcrypt/src/misc.c' ! -ipath './grub-core/lib/libgcrypt/src/global.c' ! -ipath './grub-core/lib/libgcrypt/src/secmem.c' ! -ipath './util/grub-gen-widthspec.c' ! -ipath './util/grub-gen-asciih.c' ! -ipath './gnulib/*' ! -ipath './grub-core/lib/gnulib/*' |sort > po/POTFILES.in
+find . -iname '*.[ch]' ! -ipath './grub-core/lib/libgcrypt-grub/*' ! -ipath './build-aux/*' ! -ipath './grub-core/lib/libgcrypt/src/misc.c' ! -ipath './grub-core/lib/libgcrypt/src/global.c' ! -ipath './grub-core/lib/libgcrypt/src/secmem.c' ! -ipath './util/grub-gen-widthspec.c' ! -ipath './util/grub-gen-asciih.c' ! -ipath './gnulib/*' ! -ipath './grub-core/lib/gnulib/*' ! -path './.pc/*' |sort > po/POTFILES.in
find util -iname '*.in' ! -name Makefile.in |sort > po/POTFILES-shell.in

echo "Importing unicode..."
2 changes: 2 additions & 0 deletions meta-balena-common/recipes-bsp/grub/files/cfg
@@ -0,0 +1,2 @@
search.file ($cmdpath)/EFI/BOOT/grub.cfg root
set prefix=($root)/EFI/BOOT
@@ -0,0 +1,60 @@
From b316ed326bd492106006d78f5bfcd767b49a4f2e Mon Sep 17 00:00:00 2001
From: Hongxu Jia <hongxu.jia@windriver.com>
Date: Wed, 17 Aug 2016 04:06:34 -0400
Subject: [PATCH] grub module explicitly keeps symbole .module_license

While using oe-core toolchain to strip grub module 'all_video.mod',
it stripped symbol table:

---------------
root@localhost:~# objdump -t all_video.mod

all_video.mod: file format elf64-x86-64

SYMBOL TABLE:
no symbols
--------------

It caused grub to load module all_video failed.
--------------
grub> insmod all_video
error: no symbol table.
--------------

Tweak strip option to keep symbol .module_license could workaround
the issue.
--------------
root@localhost:~# objdump -t all_video.mod

all_video.mod: file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .module_license 0000000000000000 .module_license
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .moddeps 0000000000000000 .moddeps
0000000000000000 l d .modname 0000000000000000 .modname
--------------

Upstream-Status: Pending

Signed-off-by: Hongxu Jia <hongxu.jia@windriver.com>

---
grub-core/genmod.sh.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/grub-core/genmod.sh.in b/grub-core/genmod.sh.in
index e57c4d9..42bb1ba 100644
--- a/grub-core/genmod.sh.in
+++ b/grub-core/genmod.sh.in
@@ -56,7 +56,7 @@ if test x@TARGET_APPLE_LINKER@ != x1; then
if test x@platform@ != xemu; then
@TARGET_STRIP@ --strip-unneeded \
-K grub_mod_init -K grub_mod_fini \
- -K _grub_mod_init -K _grub_mod_fini \
+ -K _grub_mod_init -K _grub_mod_fini -K .module_license \
-R .note.gnu.gold-version -R .note.GNU-stack \
-R .gnu.build.attributes \
-R .rel.gnu.build.attributes \
32 changes: 32 additions & 0 deletions meta-balena-common/recipes-bsp/grub/grub-bootconf_1.00.bb
@@ -0,0 +1,32 @@
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
SUMMARY = "Basic grub.cfg for use in EFI systems"
DESCRIPTION = "Grub might require different configuration file for \
different machines."
HOMEPAGE = "https://www.gnu.org/software/grub/manual/grub/grub.html#Configuration"

RPROVIDES:${PN} += "virtual-grub-bootconf"

inherit grub-efi-cfg

require conf/image-uefi.conf

S = "${WORKDIR}"

GRUB_CFG = "${S}/grub-bootconf"
LABELS = "boot"

ROOT ?= "root=/dev/sda2"

python do_configure() {
bb.build.exec_func('build_efi_cfg', d)
}

do_configure[vardeps] += "APPEND ROOT"

do_install() {
install -d ${D}${EFI_FILES_PATH}
install grub-bootconf ${D}${EFI_FILES_PATH}/grub.cfg
}

FILES:${PN} = "${EFI_FILES_PATH}/grub.cfg"
@@ -1,25 +1,26 @@
From 8c231b09bcd8742506e6511009573bd24c46dfc6 Mon Sep 17 00:00:00 2001
From dbc0ac6617a91ad8b37c40fc0e1ea3e429650541 Mon Sep 17 00:00:00 2001
From: Michal Toman <michalt@balena.io>
Date: Tue, 23 Nov 2021 19:37:18 +0100
Subject: [PATCH] Add dummyterm module
Subject: [PATCH 1/1] Add dummyterm module

We want GRUB to output nothing and accept no input under specific. By default
GRUB has no such option and will require at least one input and one output
to be configured. This patch adds a dummy terminal module that throws away
any input or output that goes through it.

Signed-off-by: Michal Toman <michalt@balena.io>
Signed-off-by: Joseph Kogut <joseph@balena.io>
---
grub-core/Makefile.core.def | 5 +++
grub-core/term/dummy.c | 65 +++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
create mode 100644 grub-core/term/dummy.c

diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def
index 8022e1c0a..0c0a344ec 100644
index d2cf29584..00d6c04a4 100644
--- a/grub-core/Makefile.core.def
+++ b/grub-core/Makefile.core.def
@@ -1042,6 +1042,11 @@ module = {
@@ -1071,6 +1071,11 @@ module = {
enable = x86;
};

Expand Down Expand Up @@ -103,5 +104,5 @@ index 000000000..e204a4e3a
+ grub_term_unregister_output (&grub_dummy_term_output);
+}
--
2.31.1
2.42.0

0 comments on commit 41b411c

Please sign in to comment.