diff --git a/.circleci/config.yml b/.circleci/config.yml index 22244e08..4e6eb812 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -470,6 +470,13 @@ workflows: requires: - x230-hotp-maximized + - build: + name: qemu-coreboot-fbwhiptail-tpm2-hotp + target: qemu-coreboot-fbwhiptail-tpm2-hotp + subcommand: "" + requires: + - x230-hotp-maximized + - build: name: librem_13v2 target: librem_13v2 diff --git a/Makefile b/Makefile index 5e98b234..3dfab865 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,9 @@ include $(CONFIG) # Unless otherwise specified, we are building for heads CONFIG_HEADS ?= y +# Unless otherwise specified, we are building bash to have non-interactive shell for scripts (arrays and bashisms) +CONFIG_BASH ?= y + # Determine arch part for a host triplet ifeq "$(CONFIG_TARGET_ARCH)" "x86" MUSL_ARCH := x86_64 @@ -118,6 +121,8 @@ SHELL := /usr/bin/env bash include modules/musl-cross musl_dep := musl-cross +target := $(shell echo $(CROSS) | grep -Eoe '([^/]*?)-linux-musl') +arch := $(subst -linux-musl, , $(target)) heads_cc := $(CROSS)gcc \ -fdebug-prefix-map=$(pwd)=heads \ -gno-record-gcc-switches \ @@ -143,7 +148,9 @@ CROSS_TOOLS := \ CC="$(heads_cc)" \ $(CROSS_TOOLS_NOCC) \ - +# Targets to build payload only +.PHONY: payload +payload: $(build)/$(BOARD)/bzImage $(build)/$(initrd_dir)/initrd.cpio.xz ifeq ($(CONFIG_COREBOOT), y) @@ -158,7 +165,7 @@ else $(error "$(BOARD): neither CONFIG_COREBOOT nor CONFIG_LINUXBOOT is set?") endif -all: +all payload: @sha256sum $< | tee -a "$(HASHES)" # Disable all built in rules @@ -462,7 +469,9 @@ bin_modules-$(CONFIG_FBWHIPTAIL) += fbwhiptail bin_modules-$(CONFIG_HOTPKEY) += hotp-verification bin_modules-$(CONFIG_MSRTOOLS) += msrtools bin_modules-$(CONFIG_NKSTORECLI) += nkstorecli -bin_modules-$(CONFIG_UTIL_LINUX) += util-linux +bin_modules-$(CONFIG_OPENSSL) += openssl +bin_modules-$(CONFIG_TPM2_TOOLS) += tpm2-tools +bin_modules-$(CONFIG_BASH) += bash $(foreach m, $(bin_modules-y), \ $(call map,initrd_bin_add,$(call bins,$m)) \ diff --git a/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md index c3b5c57b..4217b756 100644 --- a/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md +++ b/boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md @@ -1,4 +1,4 @@ -qemu-coreboot-(fb)whiptail-tpm1-(hotp) board +qemu-coreboot-(fb)whiptail-tpm[1,2](-hotp) boards === The `qemu-coreboot-fbwhiptail-tpm1-hotp` configuration (and their variants) permits testing of most features of Heads. diff --git a/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.config b/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.config new file mode 100644 index 00000000..5db92377 --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.config @@ -0,0 +1,163 @@ +# Configuration for building a coreboot ROM that works in +# the qemu emulator in graphical mode thanks to FBWhiptail +# This version requires a supported HOTP Security dongle (Nitrokey Pro/Storage or Librem Key) +# +# TPM can be used with a qemu software TPM (TIS, 2.0). +export CONFIG_COREBOOT=y +export CONFIG_COREBOOT_VERSION=4.13 +export CONFIG_LINUX_VERSION=5.10.5 + +#Enable DEBUG output +export CONFIG_DEBUG_OUTPUT=y +export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y + +CONFIG_COREBOOT_CONFIG=config/coreboot-qemu-tpm2.config +CONFIG_LINUX_CONFIG=config/linux-qemu.config + +ifeq "$(CONFIG_UROOT)" "y" +CONFIG_BUSYBOX=n +else +CONFIG_KEXEC=y +CONFIG_QRENCODE=y +CONFIG_TPMTOTP=y +CONFIG_POPT=y +CONFIG_FLASHTOOLS=y +CONFIG_FLASHROM=y +CONFIG_PCIUTILS=y +CONFIG_UTIL_LINUX=y +CONFIG_CRYPTSETUP2=y +CONFIG_GPG2=y +CONFIG_LVM2=y +CONFIG_MBEDTLS=y +CONFIG_DROPBEAR=y +CONFIG_MSRTOOLS=y +CONFIG_HOTPKEY=y + +#Uncomment only one of the following block +#Required for graphical gui-init (FBWhiptail) +CONFIG_CAIRO=y +CONFIG_FBWHIPTAIL=y +# +#text-based init (generic-init and gui-init) +#CONFIG_NEWT=y +#CONFIG_SLANG=y + +endif + +export CONFIG_LINUX_USB_COMPANION_CONTROLLER=y +CONFIG_LINUX_USB=y +CONFIG_LINUX_E1000=y + +#Uncomment only one BOOTSCRIPT: +#Whiptail-based init (text-based or FBWhiptail) +export CONFIG_BOOTSCRIPT=/bin/gui-init +# +#text-based original init: +#export CONFIG_BOOTSCRIPT=/bin/generic-init +export CONFIG_BOOT_REQ_HASH=n +export CONFIG_BOOT_REQ_ROLLBACK=n +export CONFIG_BOOT_RECOVERY_SERIAL="/dev/ttyS0" +export CONFIG_BOOT_KERNEL_ADD="console=ttyS0 console=tty systemd.zram=0" +export CONFIG_BOOT_KERNEL_REMOVE="quiet rhgb splash" + +#TPM2 requirements +export CONFIG_TPM2_TOOLS=y +export CONFIG_PRIMARY_KEY_TYPE=ecc +export CONFIG_TPM2_CAPTURE_PCAP=y +CONFIG_TPM2_TSS=y +CONFIG_OPENSSL=y + +export CONFIG_BOOT_DEV="/dev/vda1" +export CONFIG_BOARD_NAME="qemu-coreboot-fbwhiptail-tpm2-hotp" + +# Use the GPG-injected ROM if a key was given, since we can't reflash a GPG +# keyring in QEMU. Otherwise use the plain ROM, some things can still be tested +# that way without a GPG key. +ifneq "$(PUBKEY_ASC)" "" +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE_GPG_INJ) +else +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE) +endif + +#borrowed from https://github.com/orangecms/webboot/blob/boot-via-qemu/run-webboot.sh +TPMDIR=$(build)/$(BOARD)/vtpm +$(TPMDIR)/.manufacture: + mkdir -p "$(TPMDIR)" + swtpm_setup --create-config-files skip-if-exist + swtpm_setup --tpm-state "$(TPMDIR)" --create-platform-cert --lock-nvram --tpm2 + touch "$(TPMDIR)/.manufacture" +ROOT_DISK_IMG=$(build)/$(BOARD)/root.qcow2 +# Default to 20G disk +QEMU_DISK_SIZE?=20G +$(ROOT_DISK_IMG): + qemu-img create -f qcow2 "$(ROOT_DISK_IMG)" $(QEMU_DISK_SIZE) +# Remember the amount of memory so it doesn't have to be specified every time. +# Default to 4G, most bootable OSes are not usable with less. +QEMU_MEMORY_SIZE?=4G +MEMORY_SIZE_FILE=$(build)/$(BOARD)/memory +$(MEMORY_SIZE_FILE): + @echo "$(QEMU_MEMORY_SIZE)" >"$(MEMORY_SIZE_FILE)" +USB_FD_IMG=$(build)/$(BOARD)/usb_fd.raw +$(USB_FD_IMG): + dd if=/dev/zero bs=1M of="$(USB_FD_IMG)" bs=1M count=128 + # Debian obnoxiously does not include /usr/sbin in PATH for non-root, even + # though it is meaningful to use mkfs.vfat (etc.) as non-root + MKFS_VFAT=mkfs.vfat; \ + [ -x /usr/sbin/mkfs.vfat ] && MKFS_VFAT=/usr/sbin/mkfs.vfat; \ + "$$MKFS_VFAT" "$(USB_FD_IMG)" +# Pass INSTALL_IMG= to attach an installer as a USB flash drive instead +# of the temporary flash drive for exporting GPG keys. +ifneq "$(INSTALL_IMG)" "" +QEMU_USB_FD_IMG := $(INSTALL_IMG) +else +QEMU_USB_FD_IMG := $(USB_FD_IMG) +endif +# To forward a USB token, set USB_TOKEN to one of the following: +# - NitrokeyPro - forwards a Nitrokey Pro by VID:PID +# - LibremKey - forwards a Librem Key by VID:PID +# - - Provide the QEMU usb-host parameters, such as +# 'hostbus=<#>,hostport=<#>' or 'vendorid=<#>,productid=<#>' +ifeq "$(USB_TOKEN)" "NitrokeyPro" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16648 +else ifeq "$(USB_TOKEN)" "NitrokeyStorage" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16649 +else ifeq "$(USB_TOKEN)" "Nitrokey3NFC" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=17074 +else ifeq "$(USB_TOKEN)" "LibremKey" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=12653,productid=19531 +else ifneq "$(USB_TOKEN)" "" +QEMU_USB_TOKEN_DEV := -device "usb-host,$(USB_TOKEN)" +endif + +run: $(TPMDIR)/.manufacture $(ROOT_DISK_IMG) $(MEMORY_SIZE_FILE) $(USB_FD_IMG) + swtpm socket \ + --tpm2 \ + --tpmstate dir="$(TPMDIR)" \ + --flags "startup-clear" \ + --terminate \ + --ctrl type=unixio,path="$(TPMDIR)/sock" & + sleep 0.5 + + -qemu-system-x86_64 -drive file="$(ROOT_DISK_IMG)",if=virtio \ + --machine q35,accel=kvm:tcg \ + -rtc base=utc \ + -smp "$$(nproc)" \ + -vga virtio \ + -full-screen \ + -m "$$(cat "$(MEMORY_SIZE_FILE)")" \ + -serial stdio \ + --bios "$(QEMU_BOOT_ROM)" \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -netdev user,id=u1 -device e1000,netdev=u1 \ + -chardev socket,id=chrtpm,path="$(TPMDIR)/sock" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -device qemu-xhci,id=usb \ + -device usb-tablet \ + -drive file="$(QEMU_USB_FD_IMG)",if=none,id=usb-fd-drive,format=raw \ + -device usb-storage,bus=usb.0,drive=usb-fd-drive \ + $(QEMU_USB_TOKEN_DEV) \ + + stty sane + @echo diff --git a/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.md b/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.md new file mode 120000 index 00000000..a9ee0493 --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm2-hotp/qemu-coreboot-fbwhiptail-tpm2-hotp.md @@ -0,0 +1 @@ +../qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file diff --git a/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.config b/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.config new file mode 100644 index 00000000..ce97fed6 --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.config @@ -0,0 +1,162 @@ +# Configuration for building a coreboot ROM that works in +# the qemu emulator in graphical mode thanks to FBWhiptail +# +# TPM can be used with a qemu software TPM (TIS, 2.0). +export CONFIG_COREBOOT=y +export CONFIG_COREBOOT_VERSION=4.13 +export CONFIG_LINUX_VERSION=5.10.5 + +#Enable DEBUG output +export CONFIG_DEBUG_OUTPUT=y +export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y + +CONFIG_COREBOOT_CONFIG=config/coreboot-qemu-tpm2.config +CONFIG_LINUX_CONFIG=config/linux-qemu.config + +ifeq "$(CONFIG_UROOT)" "y" +CONFIG_BUSYBOX=n +else +CONFIG_KEXEC=y +CONFIG_QRENCODE=y +CONFIG_TPMTOTP=y +CONFIG_POPT=y +CONFIG_FLASHTOOLS=y +CONFIG_FLASHROM=y +CONFIG_PCIUTILS=y +CONFIG_UTIL_LINUX=y +CONFIG_CRYPTSETUP2=y +CONFIG_GPG2=y +CONFIG_LVM2=y +CONFIG_MBEDTLS=y +CONFIG_DROPBEAR=y +CONFIG_MSRTOOLS=y +CONFIG_HOTPKEY=n + +#Uncomment only one of the following block +#Required for graphical gui-init (FBWhiptail) +CONFIG_CAIRO=y +CONFIG_FBWHIPTAIL=y +# +#text-based init (generic-init and gui-init) +#CONFIG_NEWT=y +#CONFIG_SLANG=y + +endif + +export CONFIG_LINUX_USB_COMPANION_CONTROLLER=y +CONFIG_LINUX_USB=y +CONFIG_LINUX_E1000=y + +#Uncomment only one BOOTSCRIPT: +#Whiptail-based init (text-based or FBWhiptail) +export CONFIG_BOOTSCRIPT=/bin/gui-init +# +#text-based original init: +#export CONFIG_BOOTSCRIPT=/bin/generic-init +export CONFIG_BOOT_REQ_HASH=n +export CONFIG_BOOT_REQ_ROLLBACK=n +export CONFIG_BOOT_RECOVERY_SERIAL="/dev/ttyS0" +export CONFIG_BOOT_KERNEL_ADD="console=ttyS0 console=tty systemd.zram=0" +export CONFIG_BOOT_KERNEL_REMOVE="quiet rhgb splash" + +#TPM2 requirements +export CONFIG_TPM2_TOOLS=y +export CONFIG_PRIMARY_KEY_TYPE=ecc +export CONFIG_TPM2_CAPTURE_PCAP=y +CONFIG_TPM2_TSS=y +CONFIG_OPENSSL=y + +export CONFIG_BOOT_DEV="/dev/vda1" +export CONFIG_BOARD_NAME="qemu-coreboot-fbwhiptail-tpm2" + +# Use the GPG-injected ROM if a key was given, since we can't reflash a GPG +# keyring in QEMU. Otherwise use the plain ROM, some things can still be tested +# that way without a GPG key. +ifneq "$(PUBKEY_ASC)" "" +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE_GPG_INJ) +else +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE) +endif + +#borrowed from https://github.com/orangecms/webboot/blob/boot-via-qemu/run-webboot.sh +TPMDIR=$(build)/$(BOARD)/vtpm +$(TPMDIR)/.manufacture: + mkdir -p "$(TPMDIR)" + swtpm_setup --create-config-files skip-if-exist + swtpm_setup --tpm-state "$(TPMDIR)" --create-platform-cert --lock-nvram --tpm2 + touch "$(TPMDIR)/.manufacture" +ROOT_DISK_IMG=$(build)/$(BOARD)/root.qcow2 +# Default to 20G disk +QEMU_DISK_SIZE?=20G +$(ROOT_DISK_IMG): + qemu-img create -f qcow2 "$(ROOT_DISK_IMG)" $(QEMU_DISK_SIZE) +# Remember the amount of memory so it doesn't have to be specified every time. +# Default to 4G, most bootable OSes are not usable with less. +QEMU_MEMORY_SIZE?=4G +MEMORY_SIZE_FILE=$(build)/$(BOARD)/memory +$(MEMORY_SIZE_FILE): + @echo "$(QEMU_MEMORY_SIZE)" >"$(MEMORY_SIZE_FILE)" +USB_FD_IMG=$(build)/$(BOARD)/usb_fd.raw +$(USB_FD_IMG): + dd if=/dev/zero bs=1M of="$(USB_FD_IMG)" bs=1M count=128 + # Debian obnoxiously does not include /usr/sbin in PATH for non-root, even + # though it is meaningful to use mkfs.vfat (etc.) as non-root + MKFS_VFAT=mkfs.vfat; \ + [ -x /usr/sbin/mkfs.vfat ] && MKFS_VFAT=/usr/sbin/mkfs.vfat; \ + "$$MKFS_VFAT" "$(USB_FD_IMG)" +# Pass INSTALL_IMG= to attach an installer as a USB flash drive instead +# of the temporary flash drive for exporting GPG keys. +ifneq "$(INSTALL_IMG)" "" +QEMU_USB_FD_IMG := $(INSTALL_IMG) +else +QEMU_USB_FD_IMG := $(USB_FD_IMG) +endif +# To forward a USB token, set USB_TOKEN to one of the following: +# - NitrokeyPro - forwards a Nitrokey Pro by VID:PID +# - LibremKey - forwards a Librem Key by VID:PID +# - - Provide the QEMU usb-host parameters, such as +# 'hostbus=<#>,hostport=<#>' or 'vendorid=<#>,productid=<#>' +ifeq "$(USB_TOKEN)" "NitrokeyPro" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16648 +else ifeq "$(USB_TOKEN)" "NitrokeyStorage" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16649 +else ifeq "$(USB_TOKEN)" "Nitrokey3NFC" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=17074 +else ifeq "$(USB_TOKEN)" "LibremKey" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=12653,productid=19531 +else ifneq "$(USB_TOKEN)" "" +QEMU_USB_TOKEN_DEV := -device "usb-host,$(USB_TOKEN)" +endif + +run: $(TPMDIR)/.manufacture $(ROOT_DISK_IMG) $(MEMORY_SIZE_FILE) $(USB_FD_IMG) + swtpm socket \ + --tpm2 \ + --tpmstate dir="$(TPMDIR)" \ + --flags "startup-clear" \ + --terminate \ + --ctrl type=unixio,path="$(TPMDIR)/sock" & + sleep 0.5 + + -qemu-system-x86_64 -drive file="$(ROOT_DISK_IMG)",if=virtio \ + --machine q35,accel=kvm:tcg \ + -rtc base=utc \ + -smp "$$(nproc)" \ + -vga virtio \ + -full-screen \ + -m "$$(cat "$(MEMORY_SIZE_FILE)")" \ + -serial stdio \ + --bios "$(QEMU_BOOT_ROM)" \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -netdev user,id=u1 -device e1000,netdev=u1 \ + -chardev socket,id=chrtpm,path="$(TPMDIR)/sock" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -device qemu-xhci,id=usb \ + -device usb-tablet \ + -drive file="$(QEMU_USB_FD_IMG)",if=none,id=usb-fd-drive,format=raw \ + -device usb-storage,bus=usb.0,drive=usb-fd-drive \ + $(QEMU_USB_TOKEN_DEV) \ + + stty sane + @echo diff --git a/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.md b/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.md new file mode 120000 index 00000000..a9ee0493 --- /dev/null +++ b/boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.md @@ -0,0 +1 @@ +../qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file diff --git a/boards/qemu-coreboot-whiptail-tpm1/qemu-coreboot-whiptail-tpm1.md b/boards/qemu-coreboot-whiptail-tpm1/qemu-coreboot-whiptail-tpm1.md index a9ee0493..42112c58 120000 --- a/boards/qemu-coreboot-whiptail-tpm1/qemu-coreboot-whiptail-tpm1.md +++ b/boards/qemu-coreboot-whiptail-tpm1/qemu-coreboot-whiptail-tpm1.md @@ -1 +1 @@ -../qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file +boards/qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file diff --git a/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.config b/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.config new file mode 100644 index 00000000..23d59760 --- /dev/null +++ b/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.config @@ -0,0 +1,163 @@ +# Configuration for building a coreboot ROM that works in +# the qemu emulator in console mode thanks to Whiptail +# This version requires a supported HOTP Security dongle (Nitrokey Pro/Storage or Librem Key) +# +# TPM can be used with a qemu software TPM (TIS, 2.0). +export CONFIG_COREBOOT=y +export CONFIG_COREBOOT_VERSION=4.13 +export CONFIG_LINUX_VERSION=5.10.5 + +#Enable DEBUG output +export CONFIG_DEBUG_OUTPUT=y +export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y + +CONFIG_COREBOOT_CONFIG=config/coreboot-qemu-tpm2.config +CONFIG_LINUX_CONFIG=config/linux-qemu.config + +ifeq "$(CONFIG_UROOT)" "y" +CONFIG_BUSYBOX=n +else +CONFIG_KEXEC=y +CONFIG_QRENCODE=y +CONFIG_TPMTOTP=y +CONFIG_POPT=y +CONFIG_FLASHTOOLS=y +CONFIG_FLASHROM=y +CONFIG_PCIUTILS=y +CONFIG_UTIL_LINUX=y +CONFIG_CRYPTSETUP2=y +CONFIG_GPG2=y +CONFIG_LVM2=y +CONFIG_MBEDTLS=y +CONFIG_DROPBEAR=y +CONFIG_MSRTOOLS=y +#CONFIG_HOTPKEY=y + +#Uncomment only one of the following block +#Required for graphical gui-init (FBWhiptail) +#CONFIG_CAIRO=y +#CONFIG_FBWHIPTAIL=y +# +#text-based init (generic-init and gui-init) +CONFIG_NEWT=y +CONFIG_SLANG=y + +endif + +export CONFIG_LINUX_USB_COMPANION_CONTROLLER=y +CONFIG_LINUX_USB=y +CONFIG_LINUX_E1000=y + +#Uncomment only one BOOTSCRIPT: +#Whiptail-based init (text-based or FBWhiptail) +export CONFIG_BOOTSCRIPT=/bin/gui-init +# +#text-based original init: +#export CONFIG_BOOTSCRIPT=/bin/generic-init +export CONFIG_BOOT_REQ_HASH=n +export CONFIG_BOOT_REQ_ROLLBACK=n +export CONFIG_BOOT_RECOVERY_SERIAL="/dev/ttyS0" +export CONFIG_BOOT_KERNEL_ADD="console=ttyS0 console=tty systemd.zram=0" +export CONFIG_BOOT_KERNEL_REMOVE="quiet rhgb splash" + +#TPM2 requirements +export CONFIG_TPM2_TOOLS=y +export CONFIG_PRIMARY_KEY_TYPE=ecc +export CONFIG_TPM2_CAPTURE_PCAP=y +CONFIG_TPM2_TSS=y +CONFIG_OPENSSL=y + +export CONFIG_BOOT_DEV="/dev/vda1" +export CONFIG_BOARD_NAME="qemu-coreboot-whiptail-tpm2-hotp" + +# Use the GPG-injected ROM if a key was given, since we can't reflash a GPG +# keyring in QEMU. Otherwise use the plain ROM, some things can still be tested +# that way without a GPG key. +ifneq "$(PUBKEY_ASC)" "" +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE_GPG_INJ) +else +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE) +endif + +#borrowed from https://github.com/orangecms/webboot/blob/boot-via-qemu/run-webboot.sh +TPMDIR=$(build)/$(BOARD)/vtpm +$(TPMDIR)/.manufacture: + mkdir -p "$(TPMDIR)" + swtpm_setup --create-config-files skip-if-exist + swtpm_setup --tpm-state "$(TPMDIR)" --create-platform-cert --lock-nvram --tpm2 + touch "$(TPMDIR)/.manufacture" +ROOT_DISK_IMG=$(build)/$(BOARD)/root.qcow2 +# Default to 20G disk +QEMU_DISK_SIZE?=20G +$(ROOT_DISK_IMG): + qemu-img create -f qcow2 "$(ROOT_DISK_IMG)" $(QEMU_DISK_SIZE) +# Remember the amount of memory so it doesn't have to be specified every time. +# Default to 4G, most bootable OSes are not usable with less. +QEMU_MEMORY_SIZE?=4G +MEMORY_SIZE_FILE=$(build)/$(BOARD)/memory +$(MEMORY_SIZE_FILE): + @echo "$(QEMU_MEMORY_SIZE)" >"$(MEMORY_SIZE_FILE)" +USB_FD_IMG=$(build)/$(BOARD)/usb_fd.raw +$(USB_FD_IMG): + dd if=/dev/zero bs=1M of="$(USB_FD_IMG)" bs=1M count=128 + # Debian obnoxiously does not include /usr/sbin in PATH for non-root, even + # though it is meaningful to use mkfs.vfat (etc.) as non-root + MKFS_VFAT=mkfs.vfat; \ + [ -x /usr/sbin/mkfs.vfat ] && MKFS_VFAT=/usr/sbin/mkfs.vfat; \ + "$$MKFS_VFAT" "$(USB_FD_IMG)" +# Pass INSTALL_IMG= to attach an installer as a USB flash drive instead +# of the temporary flash drive for exporting GPG keys. +ifneq "$(INSTALL_IMG)" "" +QEMU_USB_FD_IMG := $(INSTALL_IMG) +else +QEMU_USB_FD_IMG := $(USB_FD_IMG) +endif +# To forward a USB token, set USB_TOKEN to one of the following: +# - NitrokeyPro - forwards a Nitrokey Pro by VID:PID +# - LibremKey - forwards a Librem Key by VID:PID +# - - Provide the QEMU usb-host parameters, such as +# 'hostbus=<#>,hostport=<#>' or 'vendorid=<#>,productid=<#>' +ifeq "$(USB_TOKEN)" "NitrokeyPro" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16648 +else ifeq "$(USB_TOKEN)" "NitrokeyStorage" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16649 +else ifeq "$(USB_TOKEN)" "Nitrokey3NFC" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=17074 +else ifeq "$(USB_TOKEN)" "LibremKey" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=12653,productid=19531 +else ifneq "$(USB_TOKEN)" "" +QEMU_USB_TOKEN_DEV := -device "usb-host,$(USB_TOKEN)" +endif + +run: $(TPMDIR)/.manufacture $(ROOT_DISK_IMG) $(MEMORY_SIZE_FILE) $(USB_FD_IMG) + swtpm socket \ + --tpm2 \ + --tpmstate dir="$(TPMDIR)" \ + --flags "startup-clear" \ + --terminate \ + --ctrl type=unixio,path="$(TPMDIR)/sock" & + sleep 0.5 + + -qemu-system-x86_64 -drive file="$(ROOT_DISK_IMG)",if=virtio \ + --machine q35,accel=kvm:tcg \ + -rtc base=utc \ + -smp "$$(nproc)" \ + -vga virtio \ + -full-screen \ + -m "$$(cat "$(MEMORY_SIZE_FILE)")" \ + -serial stdio \ + --bios "$(QEMU_BOOT_ROM)" \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -netdev user,id=u1 -device e1000,netdev=u1 \ + -chardev socket,id=chrtpm,path="$(TPMDIR)/sock" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -device qemu-xhci,id=usb \ + -device usb-tablet \ + -drive file="$(QEMU_USB_FD_IMG)",if=none,id=usb-fd-drive,format=raw \ + -device usb-storage,bus=usb.0,drive=usb-fd-drive \ + $(QEMU_USB_TOKEN_DEV) \ + + stty sane + @echo diff --git a/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.md b/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.md new file mode 120000 index 00000000..a9ee0493 --- /dev/null +++ b/boards/qemu-coreboot-whiptail-tpm2-hotp/qemu-coreboot-whiptail-tpm2-hotp.md @@ -0,0 +1 @@ +../qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file diff --git a/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.config b/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.config new file mode 100644 index 00000000..b4c82237 --- /dev/null +++ b/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.config @@ -0,0 +1,162 @@ +# Configuration for building a coreboot ROM that works in +# the qemu emulator in console mode thanks to Whiptail +# +# TPM can be used with a qemu software TPM (TIS, 2.0). +export CONFIG_COREBOOT=y +export CONFIG_COREBOOT_VERSION=4.13 +export CONFIG_LINUX_VERSION=5.10.5 + +#Enable DEBUG output +export CONFIG_DEBUG_OUTPUT=y +export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y + +CONFIG_COREBOOT_CONFIG=config/coreboot-qemu-tpm2.config +CONFIG_LINUX_CONFIG=config/linux-qemu.config + +ifeq "$(CONFIG_UROOT)" "y" +CONFIG_BUSYBOX=n +else +CONFIG_KEXEC=y +CONFIG_QRENCODE=y +CONFIG_TPMTOTP=y +CONFIG_POPT=y +CONFIG_FLASHTOOLS=y +CONFIG_FLASHROM=y +CONFIG_PCIUTILS=y +CONFIG_UTIL_LINUX=y +CONFIG_CRYPTSETUP2=y +CONFIG_GPG2=y +CONFIG_LVM2=y +CONFIG_MBEDTLS=y +CONFIG_DROPBEAR=y +CONFIG_MSRTOOLS=y +#CONFIG_HOTPKEY=y + +#Uncomment only one of the following block +#Required for graphical gui-init (FBWhiptail) +#CONFIG_CAIRO=y +#CONFIG_FBWHIPTAIL=y +# +#text-based init (generic-init and gui-init) +CONFIG_NEWT=y +CONFIG_SLANG=y + +endif + +export CONFIG_LINUX_USB_COMPANION_CONTROLLER=y +CONFIG_LINUX_USB=y +CONFIG_LINUX_E1000=y + +#Uncomment only one BOOTSCRIPT: +#Whiptail-based init (text-based or FBWhiptail) +export CONFIG_BOOTSCRIPT=/bin/gui-init +# +#text-based original init: +#export CONFIG_BOOTSCRIPT=/bin/generic-init +export CONFIG_BOOT_REQ_HASH=n +export CONFIG_BOOT_REQ_ROLLBACK=n +export CONFIG_BOOT_RECOVERY_SERIAL="/dev/ttyS0" +export CONFIG_BOOT_KERNEL_ADD="console=ttyS0 console=tty systemd.zram=0" +export CONFIG_BOOT_KERNEL_REMOVE="quiet rhgb splash" + +#TPM2 requirements +export CONFIG_TPM2_TOOLS=y +export CONFIG_PRIMARY_KEY_TYPE=ecc +export CONFIG_TPM2_CAPTURE_PCAP=y +CONFIG_TPM2_TSS=y +CONFIG_OPENSSL=y + +export CONFIG_BOOT_DEV="/dev/vda1" +export CONFIG_BOARD_NAME="qemu-coreboot-whiptail-tpm2" + +# Use the GPG-injected ROM if a key was given, since we can't reflash a GPG +# keyring in QEMU. Otherwise use the plain ROM, some things can still be tested +# that way without a GPG key. +ifneq "$(PUBKEY_ASC)" "" +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE_GPG_INJ) +else +QEMU_BOOT_ROM := $(build)/$(BOARD)/$(CB_OUTPUT_FILE) +endif + +#borrowed from https://github.com/orangecms/webboot/blob/boot-via-qemu/run-webboot.sh +TPMDIR=$(build)/$(BOARD)/vtpm +$(TPMDIR)/.manufacture: + mkdir -p "$(TPMDIR)" + swtpm_setup --create-config-files skip-if-exist + swtpm_setup --tpm-state "$(TPMDIR)" --create-platform-cert --lock-nvram --tpm2 + touch "$(TPMDIR)/.manufacture" +ROOT_DISK_IMG=$(build)/$(BOARD)/root.qcow2 +# Default to 20G disk +QEMU_DISK_SIZE?=20G +$(ROOT_DISK_IMG): + qemu-img create -f qcow2 "$(ROOT_DISK_IMG)" $(QEMU_DISK_SIZE) +# Remember the amount of memory so it doesn't have to be specified every time. +# Default to 4G, most bootable OSes are not usable with less. +QEMU_MEMORY_SIZE?=4G +MEMORY_SIZE_FILE=$(build)/$(BOARD)/memory +$(MEMORY_SIZE_FILE): + @echo "$(QEMU_MEMORY_SIZE)" >"$(MEMORY_SIZE_FILE)" +USB_FD_IMG=$(build)/$(BOARD)/usb_fd.raw +$(USB_FD_IMG): + dd if=/dev/zero bs=1M of="$(USB_FD_IMG)" bs=1M count=128 + # Debian obnoxiously does not include /usr/sbin in PATH for non-root, even + # though it is meaningful to use mkfs.vfat (etc.) as non-root + MKFS_VFAT=mkfs.vfat; \ + [ -x /usr/sbin/mkfs.vfat ] && MKFS_VFAT=/usr/sbin/mkfs.vfat; \ + "$$MKFS_VFAT" "$(USB_FD_IMG)" +# Pass INSTALL_IMG= to attach an installer as a USB flash drive instead +# of the temporary flash drive for exporting GPG keys. +ifneq "$(INSTALL_IMG)" "" +QEMU_USB_FD_IMG := $(INSTALL_IMG) +else +QEMU_USB_FD_IMG := $(USB_FD_IMG) +endif +# To forward a USB token, set USB_TOKEN to one of the following: +# - NitrokeyPro - forwards a Nitrokey Pro by VID:PID +# - LibremKey - forwards a Librem Key by VID:PID +# - - Provide the QEMU usb-host parameters, such as +# 'hostbus=<#>,hostport=<#>' or 'vendorid=<#>,productid=<#>' +ifeq "$(USB_TOKEN)" "NitrokeyPro" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16648 +else ifeq "$(USB_TOKEN)" "NitrokeyStorage" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=16649 +else ifeq "$(USB_TOKEN)" "Nitrokey3NFC" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=8352,productid=17074 +else ifeq "$(USB_TOKEN)" "LibremKey" +QEMU_USB_TOKEN_DEV := -device usb-host,vendorid=12653,productid=19531 +else ifneq "$(USB_TOKEN)" "" +QEMU_USB_TOKEN_DEV := -device "usb-host,$(USB_TOKEN)" +endif + +run: $(TPMDIR)/.manufacture $(ROOT_DISK_IMG) $(MEMORY_SIZE_FILE) $(USB_FD_IMG) + swtpm socket \ + --tpm2 \ + --tpmstate dir="$(TPMDIR)" \ + --flags "startup-clear" \ + --terminate \ + --ctrl type=unixio,path="$(TPMDIR)/sock" & + sleep 0.5 + + -qemu-system-x86_64 -drive file="$(ROOT_DISK_IMG)",if=virtio \ + --machine q35,accel=kvm:tcg \ + -rtc base=utc \ + -smp "$$(nproc)" \ + -vga virtio \ + -full-screen \ + -m "$$(cat "$(MEMORY_SIZE_FILE)")" \ + -serial stdio \ + --bios "$(QEMU_BOOT_ROM)" \ + -object rng-random,filename=/dev/urandom,id=rng0 \ + -device virtio-rng-pci,rng=rng0 \ + -netdev user,id=u1 -device e1000,netdev=u1 \ + -chardev socket,id=chrtpm,path="$(TPMDIR)/sock" \ + -tpmdev emulator,id=tpm0,chardev=chrtpm \ + -device tpm-tis,tpmdev=tpm0 \ + -device qemu-xhci,id=usb \ + -device usb-tablet \ + -drive file="$(QEMU_USB_FD_IMG)",if=none,id=usb-fd-drive,format=raw \ + -device usb-storage,bus=usb.0,drive=usb-fd-drive \ + $(QEMU_USB_TOKEN_DEV) \ + + stty sane + @echo diff --git a/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.md b/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.md new file mode 120000 index 00000000..a9ee0493 --- /dev/null +++ b/boards/qemu-coreboot-whiptail-tpm2/qemu-coreboot-whiptail-tpm2.md @@ -0,0 +1 @@ +../qemu-coreboot-fbwhiptail-tpm1-hotp/qemu-coreboot-fbwhiptail-tpm1-hotp.md \ No newline at end of file diff --git a/boards/t430-legacy-flash/t430-legacy-flash.config b/boards/t430-legacy-flash/t430-legacy-flash.config index 8b06966d..e933220e 100644 --- a/boards/t430-legacy-flash/t430-legacy-flash.config +++ b/boards/t430-legacy-flash/t430-legacy-flash.config @@ -4,6 +4,7 @@ export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=4.13 export CONFIG_LINUX_VERSION=4.14.62 +CONFIG_BASH=n CONFIG_FLASHROM=y #CONFIG_GPG=y #CONFIG_FLASHTOOLS=y @@ -17,7 +18,7 @@ CONFIG_LINUX_CONFIG=config/linux-x230-flash.config CONFIG_LINUX_USB=y #CONFIG_LINUX_E1000E=y -export CONFIG_BOOTSCRIPT=/bin/t430-flash.init +export CONFIG_BOOTSCRIPT=/bin/xx30-flash.init export CONFIG_BOARD_NAME="ThinkPad T430-legacy-flash" export CONFIG_FLASHROM_OPTIONS="--force --noverify-all -p internal --ifd --image bios" diff --git a/boards/x230-legacy-flash/x230-legacy-flash.config b/boards/x230-legacy-flash/x230-legacy-flash.config index 31f1d898..9e467750 100644 --- a/boards/x230-legacy-flash/x230-legacy-flash.config +++ b/boards/x230-legacy-flash/x230-legacy-flash.config @@ -4,6 +4,7 @@ export CONFIG_COREBOOT=y export CONFIG_COREBOOT_VERSION=4.13 export CONFIG_LINUX_VERSION=4.14.62 +CONFIG_BASH=n CONFIG_FLASHROM=y #CONFIG_GPG=y #CONFIG_FLASHTOOLS=y @@ -17,7 +18,7 @@ CONFIG_LINUX_CONFIG=config/linux-x230-flash.config CONFIG_LINUX_USB=y #CONFIG_LINUX_E1000E=y -export CONFIG_BOOTSCRIPT=/bin/x230-flash.init +export CONFIG_BOOTSCRIPT=/bin/xx30-flash.init export CONFIG_BOARD_NAME="ThinkPad X230-legacy-flash" export CONFIG_FLASHROM_OPTIONS="--force --noverify-all -p internal --ifd --image bios" diff --git a/config/busybox.config b/config/busybox.config index 3df67ef8..d9dbd318 100644 --- a/config/busybox.config +++ b/config/busybox.config @@ -282,9 +282,9 @@ CONFIG_PASTE=y # CONFIG_PRINTENV is not set CONFIG_PRINTF=y CONFIG_PWD=y -# CONFIG_READLINK is not set -# CONFIG_FEATURE_READLINK_FOLLOW is not set -# CONFIG_REALPATH is not set +CONFIG_READLINK=y +CONFIG_FEATURE_READLINK_FOLLOW=y +CONFIG_REALPATH=y CONFIG_RM=y CONFIG_RMDIR=y CONFIG_SEQ=y @@ -392,7 +392,7 @@ CONFIG_LOADKMAP=y # CONFIG_START_STOP_DAEMON is not set # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set -# CONFIG_WHICH is not set +CONFIG_WHICH=y # # klibc-utils @@ -1096,7 +1096,7 @@ CONFIG_SV_DEFAULT_SERVICE_DIR="" CONFIG_SH_IS_ASH=y # CONFIG_SH_IS_HUSH is not set # CONFIG_SH_IS_NONE is not set -CONFIG_BASH_IS_ASH=y +# CONFIG_BASH_IS_ASH is not set # CONFIG_BASH_IS_HUSH is not set # CONFIG_BASH_IS_NONE is not set CONFIG_SHELL_ASH=y diff --git a/config/coreboot-qemu-tpm1.config b/config/coreboot-qemu-tpm1.config index 38f6c316..2c6ad5fa 100644 --- a/config/coreboot-qemu-tpm1.config +++ b/config/coreboot-qemu-tpm1.config @@ -1,3 +1,4 @@ +CONFIG_CCACHE=y # CONFIG_INCLUDE_CONFIG_FILE is not set CONFIG_ONBOARD_VGA_IS_PRIMARY=y CONFIG_CBFS_SIZE=0x980000 diff --git a/config/coreboot-qemu-tpm2.config b/config/coreboot-qemu-tpm2.config new file mode 100644 index 00000000..ce342539 --- /dev/null +++ b/config/coreboot-qemu-tpm2.config @@ -0,0 +1,20 @@ +CONFIG_CCACHE=y +# CONFIG_INCLUDE_CONFIG_FILE is not set +CONFIG_ONBOARD_VGA_IS_PRIMARY=y +CONFIG_CBFS_SIZE=0xfe0000 +# CONFIG_POST_IO is not set +# CONFIG_POST_DEVICE is not set +CONFIG_BOARD_EMULATION_QEMU_X86_Q35=y +# CONFIG_CONSOLE_SERIAL is not set +CONFIG_LINUX_COMMAND_LINE="debug console=ttyS0,115200 console=tty" +CONFIG_COREBOOT_ROMSIZE_KB_16384=y +CONFIG_PCIEXP_ASPM=y +CONFIG_PCIEXP_COMMON_CLOCK=y +CONFIG_UART_PCI_ADDR=0 +CONFIG_DRIVERS_PS2_KEYBOARD=y +CONFIG_USER_TPM2=y +CONFIG_TPM_MEASURED_BOOT=y +CONFIG_DEFAULT_CONSOLE_LOGLEVEL_6=y +CONFIG_PAYLOAD_LINUX=y +CONFIG_PAYLOAD_FILE="@BOARD_BUILD_DIR@/bzImage" +CONFIG_LINUX_INITRD="@BOARD_BUILD_DIR@/initrd.cpio.xz" diff --git a/initrd/bin/cbfs-init b/initrd/bin/cbfs-init index ab143aad..5343db7a 100755 --- a/initrd/bin/cbfs-init +++ b/initrd/bin/cbfs-init @@ -1,4 +1,4 @@ -#!/bin/ash +#!/bin/bash set -e -o pipefail . /etc/functions @@ -24,7 +24,7 @@ for cbfsname in `echo $cbfsfiles`; do TMPFILE=/tmp/cbfs.$$ echo "$filename" > $TMPFILE cat $filename >> $TMPFILE - tpm extend -ix "$CONFIG_PCR" -if $TMPFILE \ + tpmr extend -ix "$CONFIG_PCR" -if $TMPFILE \ || die "$filename: tpm extend failed" fi fi diff --git a/initrd/bin/cbfs.sh b/initrd/bin/cbfs.sh index 52eedeff..54d2022f 100755 --- a/initrd/bin/cbfs.sh +++ b/initrd/bin/cbfs.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e -o pipefail . /etc/functions . /tmp/config diff --git a/initrd/bin/config-gui.sh b/initrd/bin/config-gui.sh index e6f0b775..071248ba 100755 --- a/initrd/bin/config-gui.sh +++ b/initrd/bin/config-gui.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # set -e -o pipefail . /etc/functions diff --git a/initrd/bin/flash-gui.sh b/initrd/bin/flash-gui.sh index b03bd8c6..e952272f 100755 --- a/initrd/bin/flash-gui.sh +++ b/initrd/bin/flash-gui.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # set -e -o pipefail . /etc/functions diff --git a/initrd/bin/flash.sh b/initrd/bin/flash.sh index 81cc7be5..6cb5fbff 100755 --- a/initrd/bin/flash.sh +++ b/initrd/bin/flash.sh @@ -1,9 +1,11 @@ -#!/bin/sh +#!/bin/ash # # based off of flashrom-x230 # +# NOTE: This script is used on legacy-flash boards and runs with busybox ash, +# not bash set -e -o pipefail -. /etc/functions +. /etc/ash_functions . /tmp/config TRACE "Under /bin/flash.sh" diff --git a/initrd/bin/flashrom-kgpe-d16-openbmc.sh b/initrd/bin/flashrom-kgpe-d16-openbmc.sh index bbc65bd0..4c860d80 100755 --- a/initrd/bin/flashrom-kgpe-d16-openbmc.sh +++ b/initrd/bin/flashrom-kgpe-d16-openbmc.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash . /etc/functions TRACE "Under /bin/flashrom-kgpe-d16-openbmc.sh" diff --git a/initrd/bin/generic-init b/initrd/bin/generic-init index a34817a6..438f29d9 100755 --- a/initrd/bin/generic-init +++ b/initrd/bin/generic-init @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Boot from a local disk installation . /etc/functions diff --git a/initrd/bin/gpg-gui.sh b/initrd/bin/gpg-gui.sh index bf5bb962..e0c484ca 100755 --- a/initrd/bin/gpg-gui.sh +++ b/initrd/bin/gpg-gui.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # set -e -o pipefail . /etc/functions diff --git a/initrd/bin/gpgv b/initrd/bin/gpgv index 116da35b..67631eda 100755 --- a/initrd/bin/gpgv +++ b/initrd/bin/gpgv @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # if we are using the full GPG we need a wrapper for the gpgv executable . /etc/functions diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init index 7ccb2396..ec4ebe13 100755 --- a/initrd/bin/gui-init +++ b/initrd/bin/gui-init @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Boot from a local disk installation BOARD_NAME=${CONFIG_BOARD_NAME:-${CONFIG_BOARD}} @@ -148,11 +148,12 @@ prompt_update_checksums() fi } -generate_totp_htop() +generate_totp_hotp() { - TRACE "Under /bin/gui-init:generate_totp_htop" + tpm_password="$1" # May be empty, will prompt if needed and empty + TRACE "Under /bin/gui-init:generate_totp_hotp" echo "Scan the QR code to add the new TOTP secret" - if /bin/seal-totp "$BOARD_NAME"; then + if /bin/seal-totp "$BOARD_NAME" "$tpm_password"; then if [ -x /bin/hotp_verification ]; then echo "Once you have scanned the QR code, hit Enter to configure your HOTP USB Security Dongle (e.g. Librem Key or Nitrokey)" read @@ -164,7 +165,7 @@ generate_totp_htop() # clear screen printf "\033c" else - warn "Sealing of measurements inside of TPM failed. You might want to take ownership of TPM by resetting it." + warn "Unsealing TOTP/HOTP secret from previous sealed measurements failed. Try "Generate new HOTP/TOTP secret" option if you updated firmware content." fi } @@ -173,7 +174,7 @@ update_totp() TRACE "Under /bin/gui-init:update_totp" # update the TOTP code date=`date "+%Y-%m-%d %H:%M:%S %Z"` - if [ "$CONFIG_TPM" = n ]; then + if [ "$CONFIG_TPM" != "y" ]; then TOTP="NO TPM" else TOTP=`unseal-totp` @@ -182,6 +183,12 @@ update_totp() if [ "$skip_to_menu" = "true" ]; then return 1 # Already asked to skip to menu from a prior error fi + + DEBUG "CONFIG_TPM: $CONFIG_TPM" + DEBUG "CONFIG_TPM2_TOOLS: $CONFIG_TPM2_TOOLS" + DEBUG "Show PCRs" + DEBUG "$(pcrs)" + whiptail $BG_COLOR_ERROR --title "ERROR: TOTP Generation Failed!" \ --menu " ERROR: Heads couldn't generate the TOTP code.\n If you have just completed a Factory Reset, or just reflashed @@ -201,7 +208,7 @@ update_totp() g ) if (whiptail $BG_COLOR_WARNING --title 'Generate new TOTP/HOTP secret' \ --yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 0 80) then - generate_totp_htop && update_totp && BG_COLOR_MAIN_MENU="" + generate_totp_hotp && update_totp && BG_COLOR_MAIN_MENU="" fi ;; i ) @@ -360,8 +367,7 @@ show_main_menu() attempt_default_boot ;; r ) - update_totp - update_hotp + update_totp && update_hotp ;; o ) show_options_menu @@ -474,7 +480,7 @@ show_tpm_totp_hotp_options_menu() option=$(cat /tmp/whiptail) case "$option" in g ) - generate_totp_htop + generate_totp_hotp ;; r ) reset_tpm @@ -510,15 +516,24 @@ reset_tpm() if [ "$CONFIG_TPM" = "y" ]; then if (whiptail $BG_COLOR_WARNING --title 'Reset the TPM' \ --yesno "This will clear the TPM and TPM password, replace them with new ones!\n\nDo you want to proceed?" 0 80) then - /bin/tpm-reset + + if ! prompt_new_owner_password; then + echo "Press Enter to return to the menu..." + read + echo + return 1 + fi + + tpmr reset "$key_password" # now that the TPM is reset, remove invalid TPM counter files mount_boot mount -o rw,remount /boot rm -f /boot/kexec_rollback.txt + rm -f /boot/kexec_primhdl_hash.txt # create Heads TPM counter before any others - check_tpm_counter /boot/kexec_rollback.txt \ + check_tpm_counter /boot/kexec_rollback.txt "" "$key_password" \ || die "Unable to find/create tpm counter" counter="$TPM_COUNTER" @@ -529,7 +544,7 @@ reset_tpm() || die "Unable to create rollback file" mount -o ro,remount /boot - generate_totp_htop + generate_totp_hotp "$key_password" else echo "Returning to the main menu" fi diff --git a/initrd/bin/kexec-boot b/initrd/bin/kexec-boot index 9952cf1e..692ee684 100755 --- a/initrd/bin/kexec-boot +++ b/initrd/bin/kexec-boot @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Launches kexec from saved configuration entries set -e -o pipefail . /tmp/config @@ -134,5 +134,9 @@ echo "$kexeccmd" eval "$kexeccmd" \ || die "Failed to load the new kernel" +if [ "$CONFIG_TPM" = "y" ]; then + tpmr kexec_finalize +fi + echo "Starting the new kernel" exec kexec -e diff --git a/initrd/bin/kexec-insert-key b/initrd/bin/kexec-insert-key index 423dce4c..78a16fab 100755 --- a/initrd/bin/kexec-insert-key +++ b/initrd/bin/kexec-insert-key @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Unseal a disk key from TPM and add to a new initramfs set -e -o pipefail . /etc/functions @@ -49,7 +49,7 @@ if ! kexec-unseal-key "$INITRD_DIR/secret.key" ; then fi # Override PCR 4 so that user can't read the key -tpm extend -ix 4 -ic generic \ +tpmr extend -ix 4 -ic generic \ || die 'Unable to scramble PCR' # Check to continue @@ -78,8 +78,8 @@ dd if="$INITRD" of="$SECRET_CPIO" bs=512 conv=sync \ if [ "$unseal_failed" = "n" ]; then # kexec-save-default might have created crypttab overrides to be injected in initramfs through additional cpio if [ -r "$bootdir/kexec_initrd_crypttab_overrides.txt" ]; then - echo "$bootdir/kexec_initrd_crypttab_overrides.txt found..." - echo "Preparing initramfs crypttab overrides as defined under $bootdir/kexec_initrd_crypttab_overrides.txt to be injected through cpio at next kexec call..." + echo "+++ $bootdir/kexec_initrd_crypttab_overrides.txt found..." + echo "+++ Preparing initramfs crypttab overrides as defined under $bootdir/kexec_initrd_crypttab_overrides.txt to be injected through cpio at next kexec call..." # kexec-save-default has found crypttab files under initrd and saved them cat "$bootdir/kexec_initrd_crypttab_overrides.txt" | while read line; do crypttab_file=$(echo "$line" | awk -F ':' {'print $1'}) @@ -87,14 +87,14 @@ if [ "$unseal_failed" = "n" ]; then # Replace each initrd crypttab file with modified entry containing /secret.key path mkdir -p "$INITRD_DIR/$(dirname $crypttab_file)" echo "$crypttab_entry" | tee -a "$INITRD_DIR/$crypttab_file" > /dev/null - echo "initramfs's $crypttab_file will be overriden with $crypttab_entry" + echo "+++ initramfs's $crypttab_file will be overriden with: $crypttab_entry" done else # No crypttab files were found under selected default boot option's initrd file crypttab_file="etc/crypttab" mkdir -p "$INITRD_DIR/$(dirname $crypttab_file)" # overwrite crypttab to mirror behavior of seal-key - echo "The following /etc/crypttab lines will be passed through cpio into kexec call for default boot option:" + echo "+++ The following /etc/crypttab lines will be passed through cpio into kexec call for default boot option:" for uuid in `cat "$TMP_KEY_DEVICES" | cut -d\ -f2`; do # NOTE: discard operation (TRIM) is activated by default if no crypptab found in initrd echo "luks-$uuid UUID=$uuid /secret.key luks,discard" | tee -a "$INITRD_DIR/$crypttab_file" diff --git a/initrd/bin/kexec-iso-init b/initrd/bin/kexec-iso-init index 6dcb0265..0a3da042 100755 --- a/initrd/bin/kexec-iso-init +++ b/initrd/bin/kexec-iso-init @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Boot from signed ISO set -e -o pipefail . /etc/functions diff --git a/initrd/bin/kexec-parse-bls b/initrd/bin/kexec-parse-bls index c256d0c3..a2d98faf 100755 --- a/initrd/bin/kexec-parse-bls +++ b/initrd/bin/kexec-parse-bls @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e -o pipefail . /etc/functions TRACE "Under /bin/kexec-parse-bls" diff --git a/initrd/bin/kexec-parse-boot b/initrd/bin/kexec-parse-boot index 53b89d78..ada8c13d 100755 --- a/initrd/bin/kexec-parse-boot +++ b/initrd/bin/kexec-parse-boot @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e -o pipefail . /etc/functions diff --git a/initrd/bin/kexec-save-default b/initrd/bin/kexec-save-default index 96dfafe0..54b56c37 100755 --- a/initrd/bin/kexec-save-default +++ b/initrd/bin/kexec-save-default @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Save these options to be the persistent default set -e -o pipefail . /tmp/config @@ -34,6 +34,7 @@ paramsdir="${paramsdir%%/}" TMP_MENU_FILE="/tmp/kexec/kexec_menu.txt" ENTRY_FILE="$paramsdir/kexec_default.$index.txt" HASH_FILE="$paramsdir/kexec_default_hashes.txt" +PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt" if [ ! -r "$TMP_MENU_FILE" ]; then die "No menu options available, please run kexec-select-boot" @@ -47,7 +48,7 @@ fi KEY_DEVICES="$paramsdir/kexec_key_devices.txt" KEY_LVM="$paramsdir/kexec_key_lvm.txt" save_key="n" -if [[ "$CONFIG_TPM" = "y" && "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ]]; then +if [ "$CONFIG_TPM" = "y" ] && [ "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ]; then if [ ! -r "$KEY_DEVICES" ]; then read \ -n 1 \ @@ -93,14 +94,14 @@ if [[ "$CONFIG_TPM" = "y" && "$CONFIG_TPM_NO_LUKS_DISK_UNLOCK" != "y" ]]; then lvm vgscan || true read \ - -p "Encrypted LVM group? ($lvm_suggest): " \ + -p "LVM group containing Encrypted LVs (retype to keep)? ($lvm_suggest): " \ lvm_volume_group echo "+++ Block devices (blkid): " blkid || true read \ - -p "Encrypted devices? ($devices_suggest): " \ + -p "Encrypted devices (retype to keep)? ($devices_suggest): " \ key_devices save_key_params="-s -p $paramsdev" @@ -122,6 +123,12 @@ if [ ! -d $paramsdir ]; then mkdir -p $paramsdir \ || die "Failed to create params directory" fi + +if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then + sha256sum /tmp/primary.handle > "$PRIMHASH_FILE" \ + || die "ERROR: Failed to Hash TPM2 primary key handle!" +fi + rm $paramsdir/kexec_default.*.txt 2>/dev/null || true echo "$entry" > $ENTRY_FILE ( cd $bootdir && kexec-boot -b "$bootdir" -e "$entry" -f| \ @@ -139,14 +146,14 @@ if [ "$save_key" = "y" ]; then current_default_initrd=$(cat /boot/kexec_default_hashes.txt | grep initr | awk -F " " {'print $NF'} | sed 's/\.\//\/boot\//g') # Get crypttab files paths from initrd - echo "Checking current selected default boot's $current_default_initrd for existing crypttab files..." + echo "+++ Checking current selected default boot's $current_default_initrd for existing crypttab files..." # First either decompress or use the original if it's not compressed initrd_decompressed="/tmp/initrd_extract/initrd_decompressed.cpio" zcat < "$current_default_initrd" > "$initrd_decompressed" 2> /dev/null || initrd_decompressed="$current_default_initrd" crypttab_files=$(cpio --list --quiet < "$initrd_decompressed" | grep crypttab 2> /dev/null) || true if [ ! -z "$crypttab_files" ]; then - echo "Extracting current selected default boot's $current_default_initrd for found crypttab files analysis..." + echo "+++ Extracting current selected default boot's $current_default_initrd for found crypttab files analysis..." cpio -id --quiet < $initrd_decompressed $crypttab_files 2> /dev/null rm -f $bootdir/kexec_initrd_crypttab_overrides.txt || true @@ -165,12 +172,12 @@ if [ "$save_key" = "y" ]; then cd - > /dev/null #insert current default boot's initrd crypttab locations into tracking file to be overwritten into initramfs at kexec-inject-key - echo "The following OS crypttab file:entry were modified from default boot's initrd:" + echo "+++ The following OS crypttab file:entry were modified from default boot's initrd:" cat $bootdir/kexec_initrd_crypttab_overrides.txt - echo "Heads added /secret.key in those entries and saved them under $bootdir/kexec_initrd_crypttab_overrides.txt" - echo "Those overrides will be part of detached signed digests and used to prepare cpio injected at kexec of selected default boot entry." + echo "+++ Heads added /secret.key in those entries and saved them under $bootdir/kexec_initrd_crypttab_overrides.txt" + echo "+++ Those overrides will be part of detached signed digests and used to prepare cpio injected at kexec of selected default boot entry." else - echo "No crypttab file found in extracted initrd. Removing $bootdir/kexec_initrd_crypttab_overrides.txt" + echo "+++ No crypttab file found in extracted initrd. Removing $bootdir/kexec_initrd_crypttab_overrides.txt" rm -f "$bootdir/kexec_initrd_crypttab_overrides.txt" || true fi # Cleanup @@ -179,8 +186,10 @@ fi # sign and auto-roll config counter extparam= -if [ "$CONFIG_TPM" = "y" ]; then - extparam=-r +if [ "$CONFIG_TPM" = "y" ];then + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + extparam=-r + fi fi kexec-sign-config -p $paramsdir $extparam \ || die "Failed to sign default config" diff --git a/initrd/bin/kexec-save-key b/initrd/bin/kexec-save-key index 7a20fafa..d6785d67 100755 --- a/initrd/bin/kexec-save-key +++ b/initrd/bin/kexec-save-key @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Generate a TPM key used to unlock LUKS disks set -e -o pipefail . /etc/functions @@ -61,8 +61,12 @@ kexec-seal-key $paramsdir \ || die "Failed to save and generate key in TPM" if [ "$skip_sign" != "y" ]; then + extparam= + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + extparam=-r + fi # sign and auto-roll config counter - kexec-sign-config -p $paramsdir -r \ + kexec-sign-config -p $paramsdir $extparam \ || die "Failed to sign updated config" fi diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key index 96f4d1ce..e892826e 100755 --- a/initrd/bin/kexec-seal-key +++ b/initrd/bin/kexec-seal-key @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # This will generate a disk encryption key and seal / ecncrypt # with the current PCRs and then store it in the TPM NVRAM. # It will then need to be bundled into initrd that is booted. @@ -14,6 +14,8 @@ RECOVERY_KEY="/tmp/secret/recovery.key" . /etc/functions . /tmp/config +TRACE "Under kexec-seal-key" + paramsdir=$1 if [ -z "$paramsdir" ]; then die "Usage $0 /boot" @@ -36,8 +38,10 @@ if [ -r "$KEY_LVM" ]; then || die "$VOLUME_GROUP: unable to activate volume group" fi -# Key slot 0 is the manual recovery pass phrase -# that they user entered when they installed Qubes, +DEBUG "$(pcrs)" + +# LUKS Key slot 0 is the manual recovery pass phrase +# that they user entered when they installed OS, # key slot 1 is the one that we've generated. read -s -p "Enter disk recovery key: " disk_password echo -n "$disk_password" > "$RECOVERY_KEY" @@ -83,73 +87,37 @@ done # since it should still be zero cat "$KEY_DEVICES" | cut -d\ -f1 | xargs /bin/qubes-measure-luks \ || die "Unable to measure the LUKS headers" -luks_pcr=`tpm calcfuturepcr -ix 16 -if /tmp/luksDump.txt` -# HOTP USB Secrity Dongle loads USB modules which changes PCR5. -# In the event HOTP USB Security Dongle is enabled, skip verification of PCR5 -if [ -x /bin/hotp_verification ]; then - pcr_5="X" +pcrf="/tmp/secret/pcrf.bin" +tpmr pcrread 0 "$pcrf" +tpmr pcrread -a 1 "$pcrf" +tpmr pcrread -a 2 "$pcrf" +tpmr pcrread -a 3 "$pcrf" +# Note that PCR 4 needs to be set with the "normal-boot" path value, which is 0. +dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf" +if [ "$CONFIG_USB_KEYBOARD" = "y" -o -r /lib/modules/libata.ko -o -x /bin/hotp_verification ]; then + DEBUG "Sealing TPM disk unlock key with PCR5 involvement (additional kernel modules are loaded per board config)..." + # Here, we take pcr 5 into consideration if modules are expected to be measured+loaded + tpmr pcrread -a 5 "$pcrf" else - pcr_5="0000000000000000000000000000000000000000" + DEBUG "Sealing TPM disk unlock key with PCR5=0 (NO additional kernel modules are loaded per board config)..." + #no kernel modules are expected to be measured+loaded + dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf" fi +# Precompute the value for pcr 6 +DEBUG "Precomputing TPM future value for PCR6 sealing/unsealing of TPM disk unlock key..." +tpmr calcfuturepcr -a "/tmp/luksDump.txt" "$pcrf" +# We take into consideration user files in cbfs +tpmr pcrread -a 7 "$pcrf" -# Note that PCR 4 needs to be set with the "normal-boot" -# path value, which we do not have right now since we are -# in a recovery shell. -# used to be -ix 4 f8fa3b6e32e7c6fe04c366e74636e505b28f3b0d \ -# now just all zeros in a normal boot -# PCR 5 must be all zero since no kernel modules should have -# been loaded during a normal boot, but might have been -# loaded in the recovery shell. -# Otherwise use the current values of the PCRs, which will be read -# from the TPM as part of the sealing ("X"). -tpm sealfile2 \ - -if "$KEY_FILE" \ - -of "$TPM_SEALED" \ - -pwdd "$key_password" \ - -hk 40000000 \ - -ix 0 X \ - -ix 1 X \ - -ix 2 X \ - -ix 3 X \ - -ix 4 0000000000000000000000000000000000000000 \ - -ix 5 $pcr_5 \ - -ix 6 $luks_pcr \ - -ix 7 X \ -|| die "Unable to seal secret" +DO_WITH_DEBUG --mask-position 7 \ + tpmr seal "$KEY_FILE" "$TPM_INDEX" 0,1,2,3,4,5,6,7 "$pcrf" \ + "$TPM_SIZE" "$key_password" +# should be okay if this fails +shred -n 10 -z -u "$pcrf".* 2> /dev/null || true shred -n 10 -z -u "$KEY_FILE" 2> /dev/null \ -|| die "Failed to delete key file" +|| warn "Failed to delete key file - continuing" -# try it without the owner password first -if ! tpm nv_writevalue \ - -in $TPM_INDEX \ - -if "$TPM_SEALED" \ -; then - # to create an nvram space we need the TPM owner password - # and the TPM physical presence must be asserted. - # - # The permissions are 0 since there is nothing special - # about the sealed file - tpm physicalpresence -s \ - || warn "Warning: Unable to assert physical presence" - - read -s -p "TPM Owner password: " tpm_password - echo - - tpm nv_definespace \ - -in $TPM_INDEX \ - -sz $TPM_SIZE \ - -pwdo "$tpm_password" \ - -per 0 \ - || warn "Warning: Unable to define NVRAM space; trying anyway" - - - tpm nv_writevalue \ - -in $TPM_INDEX \ - -if "$TPM_SEALED" \ - || die "Unable to write sealed secret to NVRAM" -fi - -shred -n 10 -z -u "$TPM_SEALED" 2> /dev/null \ -|| warn "Failed to delete the sealed secret - continuing" +cp /tmp/luksDump.txt "$paramsdir/kexec_lukshdr_hash.txt" \ +|| warn "Failed to have hashes of LUKS header - continuing" diff --git a/initrd/bin/kexec-select-boot b/initrd/bin/kexec-select-boot index d0fe1b44..ae9dbbc5 100755 --- a/initrd/bin/kexec-select-boot +++ b/initrd/bin/kexec-select-boot @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Generic configurable boot script via kexec set -e -o pipefail . /tmp/config @@ -50,6 +50,22 @@ bootdir="${bootdir%%/}" paramsdev="${paramsdev%%/}" paramsdir="${paramsdir%%/}" +PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt" +if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then + if [ -r "$PRIMHASH_FILE" ]; then + sha256sum -c "$PRIMHASH_FILE" \ + || { + echo "FATAL: Hash of TPM2 primary key handle mismatch!"; + echo "If you have not intentionally regenerated TPM2 primary key,"; + warn "your system may have been compromised!"; + } + else + echo "WARNING: Hash of TPM2 primary key handle does not exist!" + echo "Please rebuild the boot hash tree." + default_failed="y" + fi +fi + verify_global_hashes() { echo "+++ Checking verified boot hash file " @@ -281,8 +297,15 @@ user_select() { else echo "+++ Rebooting to start the new default option" sleep 2 - reboot \ - || die "!!! Failed to reboot system" + if [ "$CONFIG_DEBUG_OUTPUT" != "y" ]; then + reboot \ + || die "!!! Failed to reboot system" + else + DEBUG "Rebooting is required prior of booting default boot entry" + # Instead of rebooting, drop to a recovery shell + # for a chance to inspect debug output + recovery "Entering recovery to permit inspection of /tmp/debug.log output, reboot to continue" + fi fi fi @@ -299,23 +322,22 @@ do_boot() die "!!! Missing required boot hashes" fi - if [ "$CONFIG_TPM" = "y" \ - -a -r "$TMP_KEY_DEVICES" ]; then + if [ "$CONFIG_TPM" = "y" ] && [ -r "$TMP_KEY_DEVICES" ]; then INITRD=`kexec-boot -b "$bootdir" -e "$option" -i` \ - || die "!!! Failed to extract the initrd from boot option" + || die "!!! Failed to extract the initrd from boot option" if [ -z "$INITRD" ]; then die "!!! No initrd file found in boot option" fi kexec-insert-key $INITRD \ - || die "!!! Failed to insert disk key into a new initrd" + || die "!!! Failed to insert disk key into a new initrd" kexec-boot -b "$bootdir" -e "$option" \ -a "$add" -r "$remove" -o "/tmp/secret/initrd.cpio" \ - || die "!!! Failed to boot w/ options: $option" + || die "!!! Failed to boot w/ options: $option" else kexec-boot -b "$bootdir" -e "$option" -a "$add" -r "$remove" \ - || die "!!! Failed to boot w/ options: $option" + || die "!!! Failed to boot w/ options: $option" fi } @@ -344,11 +366,12 @@ while true; do user_select fi - if [ "$CONFIG_TPM" = "y" \ - -a ! -r "$TMP_KEY_DEVICES" ]; then - # Extend PCR4 as soon as possible - tpm extend -ix 4 -ic generic \ - || die "Failed to extend PCR 4" + if [ "$CONFIG_TPM" = "y" ]; then + if [ ! -r "$TMP_KEY_DEVICES" ]; then + # Extend PCR4 as soon as possible + tpmr extend -ix 4 -ic generic \ + || die "Failed to extend PCR 4" + fi fi # if no saved options, scan the boot directory and generate @@ -368,7 +391,7 @@ while true; do fi fi - if [ -r "$TMP_ROLLBACK_FILE" ]; then + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" -a -r "$TMP_ROLLBACK_FILE" ]; then # in the case of iso boot with a rollback file, do not assume valid valid_rollback="n" diff --git a/initrd/bin/kexec-sign-config b/initrd/bin/kexec-sign-config index a0593fa0..b5d3ac16 100755 --- a/initrd/bin/kexec-sign-config +++ b/initrd/bin/kexec-sign-config @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Sign a valid directory of kexec params set -e -o pipefail . /tmp/config diff --git a/initrd/bin/kexec-unseal-key b/initrd/bin/kexec-unseal-key index d35e8b78..3c9ab21e 100755 --- a/initrd/bin/kexec-unseal-key +++ b/initrd/bin/kexec-unseal-key @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # This will unseal and unecncrypt the drive encryption key from the TPM # The TOTP secret will be shown to the user on each encryption attempt. # It will then need to be bundled into initrd that is booted with Qubes. @@ -9,37 +9,34 @@ TPM_INDEX=3 TPM_SIZE=312 . /etc/functions + +TRACE "Under kexec-unseal-key" + mkdir -p /tmp/secret -sealed_file="/tmp/secret/sealed.key" key_file="$1" if [ -z "$key_file" ]; then key_file="/tmp/secret/secret.key" fi -tpm nv_readvalue \ - -in "$TPM_INDEX" \ - -sz "$TPM_SIZE" \ - -of "$sealed_file" \ -|| die "Unable to read key from TPM NVRAM" +DEBUG "CONFIG_TPM: $CONFIG_TPM" +DEBUG "CONFIG_TPM2_TOOLS: $CONFIG_TPM2_TOOLS" +DEBUG "Show PCRs" +DEBUG "$(pcrs)" for tries in 1 2 3; do read -s -p "Enter unlock password (blank to abort): " tpm_password echo - if [ -z "$tpm_password" ]; then die "Aborting unseal disk encryption key" fi - if tpm unsealfile \ - -if "$sealed_file" \ - -of "$key_file" \ - -pwdd "$tpm_password" \ - -hk 40000000 \ - ; then - # should be okay if this fails - shred -n 10 -z -u /tmp/secret/sealed 2> /dev/null || true + DO_WITH_DEBUG --mask-position 6 \ + tpmr unseal "$TPM_INDEX" "0,1,2,3,4,5,6,7" "$TPM_SIZE" \ + "$key_file" "$tpm_password" + + if [ "$?" -eq 0 ]; then exit 0 fi diff --git a/initrd/bin/key-init b/initrd/bin/key-init index f68921f5..44a9063f 100755 --- a/initrd/bin/key-init +++ b/initrd/bin/key-init @@ -1,4 +1,4 @@ -#!/bin/ash +#!/bin/bash set -e -o pipefail . /etc/functions diff --git a/initrd/bin/media-scan b/initrd/bin/media-scan index ae30d3f0..28a61274 100755 --- a/initrd/bin/media-scan +++ b/initrd/bin/media-scan @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Scan for USB installation options set -e -o pipefail . /etc/functions diff --git a/initrd/bin/mount-usb b/initrd/bin/mount-usb index fa7ca151..29bc5f74 100755 --- a/initrd/bin/mount-usb +++ b/initrd/bin/mount-usb @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Mount a USB device . /etc/functions diff --git a/initrd/bin/network-init-recovery b/initrd/bin/network-init-recovery index aa0bf287..c7b93b84 100755 --- a/initrd/bin/network-init-recovery +++ b/initrd/bin/network-init-recovery @@ -1,4 +1,4 @@ -#!/bin/ash +#!/bin/bash . /etc/functions diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index dcb121f7..8031b94a 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Automated setup of TPM, GPG keys, and disk TRACE "Under /bin/oem-factory-reset" @@ -57,7 +57,7 @@ die() { exit 1 } -whiptail_error() +whiptail_error() { local msg=$1 if [ "$msg" = "" ]; then @@ -66,7 +66,7 @@ whiptail_error() whiptail $BG_COLOR_ERROR --msgbox "${msg}\n\n" $HEIGHT $WIDTH $BG_COLOR_ERROR --title "Error" } -whiptail_error_die() +whiptail_error_die() { whiptail_error "$@" die @@ -131,11 +131,11 @@ gpg_key_reset() echo ${USER_PIN_DEF} echo 0 echo y - echo ${GPG_USER_NAME} + echo ${GPG_USER_NAME} echo ${GPG_USER_MAIL} echo ${GPG_USER_COMMENT} } | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ - > /tmp/gpg_card_edit_output 2>/dev/null + > /tmp/gpg_card_edit_output 2>/dev/null if [ $? -ne 0 ]; then ERROR=`cat /tmp/gpg_card_edit_output` whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR" @@ -159,7 +159,7 @@ gpg_key_change_pin() echo q echo q } | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \ - > /tmp/gpg_card_edit_output 2>/dev/null + > /tmp/gpg_card_edit_output 2>/dev/null if [ $? -ne 0 ]; then ERROR=`cat /tmp/gpg_card_edit_output | fold -s` whiptail_error_die "GPG Key PIN change failed!\n\n$ERROR" @@ -184,26 +184,28 @@ generate_checksums() rm /boot/kexec* 2>/dev/null # create Heads TPM counter - if [ "$CONFIG_TPM" = "y" ]; then - tpm counter_create \ - -pwdo "$TPM_PASS" \ - -pwdc '' \ - -la -3135106223 \ - | tee /tmp/counter \ - || whiptail_error_die "Unable to create TPM counter" - TPM_COUNTER=`cut -d: -f1 < /tmp/counter` + if [ "$CONFIG_TPM" = "y" ];then + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + tpmr counter_create \ + -pwdo "$TPM_PASS_DEF" \ + -pwdc '' \ + -la -3135106223 \ + | tee /tmp/counter \ + || whiptail_error_die "Unable to create TPM counter" + TPM_COUNTER=`cut -d: -f1 < /tmp/counter` - # increment TPM counter - increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 \ - || whiptail_error_die "Unable to increment tpm counter" + # increment TPM counter + increment_tpm_counter $TPM_COUNTER >/dev/null 2>&1 \ + || whiptail_error_die "Unable to increment tpm counter" - # create rollback file - sha256sum /tmp/counter-$TPM_COUNTER > /boot/kexec_rollback.txt 2>/dev/null \ - || whiptail_error_die "Unable to create rollback file" - else - ## needs to exist for initial call to unseal-hotp - echo "0" > /boot/kexec_hotp_counter - fi + # create rollback file + sha256sum /tmp/counter-$TPM_COUNTER > /boot/kexec_rollback.txt 2>/dev/null \ + || whiptail_error_die "Unable to create rollback file" + else + ## needs to exist for initial call to unseal-hotp + echo "0" > /boot/kexec_hotp_counter + fi + fi # set default boot option only if no TPM Disk Unlock Key previously set if [ -z "$TPM_DISK_ENCRYPTION_KEY_SET" ]; then @@ -307,7 +309,7 @@ report_integrity_measurements() date=`date "+%Y-%m-%d %H:%M:%S %Z"` seconds=`date "+%s"` half=`expr \( $seconds % 60 \) / 30` - if [ "$CONFIG_TPM" = n ]; then + if [ "$CONFIG_TPM" != "y" ]; then TOTP="NO TPM" elif [ "$half" != "$last_half" ]; then last_half=$half; @@ -608,14 +610,11 @@ fi ## reset TPM and set password if [ "$CONFIG_TPM" = "y" ]; then echo -e "\nResetting TPM...\n" - { - echo $TPM_PASS - echo $TPM_PASS - } | /bin/tpm-reset >/dev/null 2>/tmp/error - if [ $? -ne 0 ]; then - ERROR=$(tail -n 1 /tmp/error | fold -s) - whiptail_error_die "Error resetting TPM:\n\n${ERROR}" - fi + tpmr reset "$TPM_PASS" >/dev/null 2>/tmp/error +fi +if [ $? -ne 0 ]; then + ERROR=$(tail -n 1 /tmp/error | fold -s) + whiptail_error_die "Error resetting TPM:\n\n${ERROR}" fi # clear local keyring diff --git a/initrd/bin/oem-system-info-xx30 b/initrd/bin/oem-system-info-xx30 index d72f66e0..489a5d78 100755 --- a/initrd/bin/oem-system-info-xx30 +++ b/initrd/bin/oem-system-info-xx30 @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # System Info BOARD_NAME=${CONFIG_BOARD_NAME:-${CONFIG_BOARD}} diff --git a/initrd/bin/poweroff b/initrd/bin/poweroff index 57a25b57..ef4bdf86 100755 --- a/initrd/bin/poweroff +++ b/initrd/bin/poweroff @@ -1,8 +1,13 @@ -#!/bin/sh -. /etc/functions +#!/bin/ash +. /etc/ash_functions TRACE "Under /bin/poweroff" +# Shut down TPM +if [ "$CONFIG_TPM" = "y" ]; then + tpmr shutdown +fi + # Sync all mounted filesystems echo s > /proc/sysrq-trigger diff --git a/initrd/bin/qubes-measure-luks b/initrd/bin/qubes-measure-luks index b277af7e..257ee861 100755 --- a/initrd/bin/qubes-measure-luks +++ b/initrd/bin/qubes-measure-luks @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Measure all of the luks disk encryption headers into # a PCR so that we can detect disk swap attacks. . /etc/functions @@ -17,5 +17,5 @@ done sha256sum /tmp/lukshdr-* > /tmp/luksDump.txt || die "Unable to hash luks headers" rm /tmp/lukshdr-* -tpm extend -ix 6 -if /tmp/luksDump.txt \ +tpmr extend -ix 6 -if /tmp/luksDump.txt \ || die "Unable to extend PCR" diff --git a/initrd/bin/reboot b/initrd/bin/reboot index 66375950..97d37a2b 100755 --- a/initrd/bin/reboot +++ b/initrd/bin/reboot @@ -1,8 +1,13 @@ -#!/bin/sh -. /etc/functions +#!/bin/ash +. /etc/ash_functions TRACE "Under /bin/reboot" +# Shut down TPM +if [ "$CONFIG_TPM" = "y" ]; then + tpmr shutdown +fi + # Sync all mounted filesystems echo s > /proc/sysrq-trigger diff --git a/initrd/bin/seal-hotpkey b/initrd/bin/seal-hotpkey index 6309be68..7e8cc75b 100755 --- a/initrd/bin/seal-hotpkey +++ b/initrd/bin/seal-hotpkey @@ -1,9 +1,8 @@ -#!/bin/sh +#!/bin/bash # Retrieve the sealed TOTP secret and initialize a USB Security dongle with it . /etc/functions -HOTP_SEALED="/tmp/secret/hotp.sealed" HOTP_SECRET="/tmp/secret/hotp.key" HOTP_COUNTER="/boot/kexec_hotp_counter" HOTP_KEY="/boot/kexec_hotp_key" @@ -27,19 +26,11 @@ else HOTPKEY_BRANDING="HOTP USB Security Dongle" fi -tpm nv_readvalue \ - -in 4d47 \ - -sz 312 \ - -of "$HOTP_SEALED" \ -|| die "Unable to retrieve sealed file from TPM NV" - -tpm unsealfile \ - -hk 40000000 \ - -if "$HOTP_SEALED" \ - -of "$HOTP_SECRET" \ -|| die "Unable to unseal HOTP secret" - -shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +if [ "$CONFIG_TPM" = "y" ]; then + DEBUG "Sealing HOTP secret reuses TOTP sealed secret..." + tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" \ + || die "Unable to unseal HOTP secret" +fi # Store counter in file instead of TPM for now, as it conflicts with Heads # config TPM counter as TPM 1.2 can only increment one counter between reboots diff --git a/initrd/bin/seal-totp b/initrd/bin/seal-totp index 57388daf..d9608890 100755 --- a/initrd/bin/seal-totp +++ b/initrd/bin/seal-totp @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Generate a random secret, seal it with the PCRs # and write it to the TPM NVRAM. # @@ -15,6 +15,7 @@ HOST="$1" if [ -z "$HOST" ]; then HOST="TPMTOTP" fi +TPM_PASSWORD="$2" TOTP_SECRET="/tmp/secret/totp.key" TOTP_SEALED="/tmp/secret/totp.sealed" @@ -28,61 +29,21 @@ dd \ || die "Unable to generate 20 random bytes" secret="`base32 < $TOTP_SECRET`" - -# Use the current values of the PCRs, which will be read -# from the TPM as part of the sealing ("X"). -# PCR4 == 0 means that we are still in the boot process and -# not a recovery shell. -# should this read the storage root key? -if ! tpm sealfile2 \ - -if "$TOTP_SECRET" \ - -of "$TOTP_SEALED" \ - -hk 40000000 \ - -ix 0 X \ - -ix 1 X \ - -ix 2 X \ - -ix 3 X \ - -ix 4 0000000000000000000000000000000000000000 \ - -ix 7 X \ -; then - shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null - die "Unable to seal secret" -fi - -shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null - - -# to create an nvram space we need the TPM owner password -# and the TPM physical presence must be asserted. -# -# The permissions are 0 since there is nothing special -# about the sealed file -tpm physicalpresence -s \ -|| warn "Warning: Unable to assert physical presence" - -# Try to write it without the password first, and then create -# the NVRAM space using the owner password if it fails for some reason. -if ! tpm nv_writevalue \ - -in $TPM_NVRAM_SPACE \ - -if "$TOTP_SEALED" \ -; then - warn 'NVRAM space does not exist? Owner password is required' - read -s -p "TPM Owner password: " tpm_password - echo - - tpm nv_definespace \ - -in $TPM_NVRAM_SPACE \ - -sz 312 \ - -pwdo "$tpm_password" \ - -per 0 \ - || die "Unable to define NVRAM space" - - tpm nv_writevalue \ - -in $TPM_NVRAM_SPACE \ - -if "$TOTP_SEALED" \ +pcrf="/tmp/secret/pcrf.bin" +tpmr pcrread 0 "$pcrf" +tpmr pcrread -a 1 "$pcrf" +tpmr pcrread -a 2 "$pcrf" +tpmr pcrread -a 3 "$pcrf" +# pcr 4 is expected to be zero (boot mode: init) +dd if=/dev/zero bs="$(tpmr pcrsize)" count=1 status=none >> "$pcrf" +# pcr 5 (kernel modules loaded) is not measured at sealing/unsealing of totp +DEBUG "Sealing TOTP neglecting PCR5 involvement (Dynamically loaded kernel modules are not firmware integrity attestation related)" +# pcr 6 (drive luks header) is not measured at sealing/unsealing of totp +DEBUG "Sealing TOTP without PCR6 involvement (LUKS header consistency is not firmware integrity attestation related)" +# pcr 7 is containing measurements of user injected stuff in cbfs +tpmr pcrread -a 7 "$pcrf" +tpmr seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PASSWORD" \ || die "Unable to write sealed secret to NVRAM" -fi - shred -n 10 -z -u "$TOTP_SEALED" 2> /dev/null url="otpauth://totp/$HOST?secret=$secret" diff --git a/initrd/bin/t430-flash.init b/initrd/bin/t430-flash.init deleted file mode 100755 index 9b97970e..00000000 --- a/initrd/bin/t430-flash.init +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# Initialize the USB and network device drivers, -# invoke a recovery shell and prompt the user for how to proceed - -. /etc/functions -. /tmp/config - -insmod /lib/modules/ehci-hcd.ko -insmod /lib/modules/ehci-pci.ko -insmod /lib/modules/xhci-hcd.ko -insmod /lib/modules/xhci-pci.ko -insmod /lib/modules/e1000e.ko -insmod /lib/modules/usb-storage.ko - -tpm extend -ix 4 -ic recovery -sleep 2 - -echo '***** Starting recovery shell' -echo '' -echo 'To install from flash drive:' -echo '' -echo ' mount -o ro /dev/sdb1 /media' -echo ' flash.sh /media/t430.rom' -echo '' - -exec /bin/ash diff --git a/initrd/bin/tpm-reset b/initrd/bin/tpm-reset index 8a8b9d1c..f8b0c6a8 100755 --- a/initrd/bin/tpm-reset +++ b/initrd/bin/tpm-reset @@ -1,34 +1,10 @@ -#!/bin/sh +#!/bin/bash . /etc/functions echo '*****' echo '***** WARNING: This will erase all keys and secrets from the TPM' echo '*****' -read -s -p "New TPM owner password: " key_password -echo +prompt_new_owner_password -if [ -z "$key_password" ]; then - die "Empty owner password is not allowed" -fi - -read -s -p "Repeat owner password: " key_password2 -echo - - -if [ "$key_password" != "$key_password2" ]; then - die "Key passwords do not match" -fi - -# Make sure the TPM is ready to be reset -tpm physicalpresence -s -tpm physicalenable -tpm physicalsetdeactivated -c -tpm forceclear -tpm physicalenable -tpm takeown -pwdo "$key_password" - -# And now turn it all back on -tpm physicalpresence -s -tpm physicalenable -tpm physicalsetdeactivated -c +tpmr reset "$key_password" diff --git a/initrd/bin/tpmr b/initrd/bin/tpmr new file mode 100755 index 00000000..c392d306 --- /dev/null +++ b/initrd/bin/tpmr @@ -0,0 +1,633 @@ +#!/bin/bash +# TPM Wrapper - to unify tpm and tpm2 subcommands + +. /etc/functions + +SECRET_DIR="/tmp/secret" +PRIMARY_HANDLE="0x81000000" +ENC_SESSION_FILE="enc.ctx" +DEC_SESSION_FILE="dec.ctx" +PRIMARY_HANDLE_FILE="primary.handle" + +# PCR size in bytes. Set when we determine what TPM version is in use. +# TPM1 PCRs are always 20 bytes. TPM2 is allowed to provide multiple PCR banks +# with different algorithms - we always use SHA-256, so they are 32 bytes. +PCR_SIZE= + +# Export CONFIG_TPM2_CAPTURE_PCAP=y from your board config to capture tpm2 pcaps to +# /tmp/tpm0.pcap; Wireshark can inspect these. (This must be enabled at build +# time so the pcap TCTI driver is included.) +if [ -n "$CONFIG_TPM2_CAPTURE_PCAP" ]; then + export TPM2TOOLS_TCTI="pcap:device:/dev/tpmrm0" + export TCTI_PCAP_FILE="/tmp/tpm0.pcap" +fi + +set -e -o pipefail +if [ -r "/tmp/config" ]; then + . /tmp/config +else + . /etc/config +fi + +TRACE "Under /bin/tpmr" + +# Busybox xxd lacks -r, and we get hex dumps from TPM1 commands. This converts +# a hex dump to binary data using sed and printf +hex2bin() { + sed 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf +} + +# Render a password as 'hex:' for use with tpm2-tools. Passwords +# should always be passed this way to avoid ambiguity. (Passing with no prefix +# would choke if the password happened to start with 'file:' or 'hex:'. Passing +# as a file still chokes if the password begins with 'hex:', oddly tpm2-tools +# accepts 'hex:' in the file content.) +tpm2_password_hex() { + echo "hex:$(echo -n "$1" | xxd -p | tr -d ' \n')" +} + +# usage: tpmr pcrread [-a] +# Reads PCR binary data and writes to file. +# -a: Append to file. Default is to overwrite. +tpm2_pcrread() { + TRACE "Under /bin/tpmr:tpm2_pcrread" + if [ "$1" = "-a" ]; then + APPEND=y + shift + fi + + index="$1" + file="$2" + + if [ -z "$APPEND" ]; then + # Don't append - truncate file now so real command always + # appends + true >"$file" + fi + + DO_WITH_DEBUG tpm2 pcrread -Q -o >(cat >>"$file") "sha256:$index" +} +tpm1_pcrread() { + TRACE "Under /bin/tpmr:tpm1_pcrread" + if [ "$1" = "-a" ]; then + APPEND=y + shift + fi + + index="$1" + file="$2" + + if [ -z "$APPEND" ]; then + # Don't append - truncate file now so real command always + # appends + true >"$file" + fi + + DO_WITH_DEBUG tpm pcrread -ix "$index" | hex2bin >>"$file" +} + +# usage: tpmr calcfuturepcr [-a] +# Uses the scratch PCR to calculate a future PCR value (TPM2 23, TPM1 16). The +# data in input file are hashed into a PCR, and the PCR value is placed in +# output_file. +# -a: Append to output_file. Default is to overwrite +tpm2_calcfuturepcr() { + TRACE "Under /bin/tpmr:tpm2_calcfuturepcr" + if [ "$1" = "-a" ]; then + APPEND=y + shift + fi + + input_file="$1" + output_file="$2" + + if [ -z "$APPEND" ]; then + true >"$output_file" + fi + + tpm2 pcrreset -Q 23 + DO_WITH_DEBUG tpmr extend -ix 23 -if "$input_file" + DO_WITH_DEBUG tpm2 pcrread -Q -o >(cat >>"$output_file") sha256:23 + tpm2 pcrreset -Q 23 +} +tpm1_calcfuturepcr() { + TRACE "Under /bin/tpmr:tpm1_calcfuturepcr" + if [ "$1" = "-a" ]; then + APPEND=y + shift + fi + + input_file="$1" + output_file="$2" + + if [ -z "$APPEND" ]; then + true >"$output_file" + fi + + DO_WITH_DEBUG tpm calcfuturepcr -ix 16 -if "$input_file" | hex2bin >>"$output_file" +} + +tpm2_extend() { + TRACE "Under /bin/tpmr:tpm2_extend" + while true; do + case "$1" in + -ix) + index="$2" + shift 2;; + -ic) + hash="$(echo -n "$2"|sha256sum|cut -d' ' -f1)" + shift 2;; + -if) + hash="$(sha256sum "$2"|cut -d' ' -f1)" + shift 2;; + *) + break;; + esac + done + tpm2 pcrextend "$index:sha256=$hash" + DO_WITH_DEBUG tpm2 pcrread "sha256:$index" +} + +tpm2_counter_read() { + TRACE "Under /bin/tpmr:tpm2_counter_read" + while true; do + case "$1" in + -ix) + index="$2" + shift 2;; + *) + break;; + esac + done + echo "$index: `tpm2 nvread 0x$index | xxd -pc8`" +} + +tpm2_counter_inc() { + TRACE "Under /bin/tpmr:tpm2_counter_inc" + while true; do + case "$1" in + -ix) + index="$2" + shift 2;; + -pwdc) + pwd="$2" + shift 2;; + *) + break;; + esac + done + tpm2 nvincrement "0x$index" > /dev/console + echo "$index: `tpm2 nvread 0x$index | xxd -pc8`" +} + +tpm2_counter_cre() { + TRACE "Under /bin/tpmr:tpm2_counter_cre" + while true; do + case "$1" in + -pwdo) + pwdo="$2" + shift 2;; + -pwdof) + pwdo="file:$2" + shift 2;; + -pwdc) + pwd="$2" + shift 2;; + -la) + label="$2" + shift 2;; + *) + break;; + esac + done + rand_index="1`dd if=/dev/urandom bs=1 count=3 | xxd -pc3`" + tpm2 nvdefine -C o -s 8 -a "ownerread|authread|authwrite|nt=1" \ + -P "$(tpm2_password_hex "$pwdo")" "0x$rand_index" > /dev/console + echo "$rand_index: (valid after an increment)" +} + +tpm2_startsession() { + TRACE "Under /bin/tpmr:tpm2_startsession" + mkdir -p "$SECRET_DIR" + tpm2 flushcontext -Q \ + --transient-object \ + || die "tpm2_flushcontext: unable to flush transient handles" + + tpm2 flushcontext -Q \ + --loaded-session \ + || die "tpm2_flushcontext: unable to flush sessions" + + tpm2 flushcontext -Q \ + --saved-session \ + || die "tpm2_flushcontext: unable to flush saved session" + tpm2 readpublic -Q -c "$PRIMARY_HANDLE" -t "/tmp/$PRIMARY_HANDLE_FILE" + tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$ENC_SESSION_FILE" + tpm2 startauthsession -Q -c "/tmp/$PRIMARY_HANDLE_FILE" --hmac-session -S "/tmp/$DEC_SESSION_FILE" + tpm2 sessionconfig -Q --disable-encrypt "/tmp/$DEC_SESSION_FILE" +} + +# Use cleanup_session() with at_exit to release a TPM2 session and delete the +# session file. E.g.: +# at_exit cleanup_session "$SESSION_FILE" +cleanup_session() { + TRACE "Under /bin/tpmr:cleanup_session" + session_file="$1" + if [ -f "$session_file" ]; then + DEBUG "Clean up session: $session_file" + # Nothing else we can do if this fails, still remove the file + tpm2 flushcontext -Q "$session_file" || DEBUG "Flush failed for session $session_file" + rm -f "$session_file" + else + DEBUG "No need to clean up session: $session_file" + fi +} + +# Clean up a file by shredding it. No-op if the file wasn't created. Use with +# at_exit, e.g.: +# at_exit cleanup_shred "$FILE" +cleanup_shred() { + TRACE "Under /bin/tpmr:cleanup_shred" + shred -n 10 -z -u "$1" 2>/dev/null || true +} + +# tpm2_seal: Seal a file against PCR values and, optionally, a password. +# If a password is given, both the PCRs and password are required to unseal the +# file. PCRs are provided as a PCR list and data file. PCR data must be +# provided - TPM2 allows the TPM to fall back to current PCR values, but it is +# not required to support this. +tpm2_seal() { + TRACE "Under /bin/tpmr:tpm2_seal" + file="$1" #$KEY_FILE + index="$2" + pcrl="$3" #0,1,2,3,4,5,6,7 (does not include algorithm prefix) + pcrf="$4" + sealed_size="$5" # Not used for TPM2 + pass="$6" # May be empty to seal with no password + tpm_password="$7" # Owner password - will prompt if needed and not empty + # Owner password is always needed for TPM2. + + mkdir -p "$SECRET_DIR" + bname="`basename $file`" + + # Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc. + handle="$(printf "0x81%6s" "$index" | tr ' ' 0)" + + DEBUG "tpm2_seal: file=$file handle=$handle pcrl=$pcrl pcrf=$pcrf pass=$(mask_param "$pass")" + + # Create a policy requiring both PCRs and the object's authentication + # value using a trial session. + TRIAL_SESSION=/tmp/sealfile_trial.session + AUTH_POLICY=/tmp/sealfile_auth.policy + rm -f "$TRIAL_SESSION" "$AUTH_POLICY" + tpm2 startauthsession -g sha256 -S "$TRIAL_SESSION" + # We have to clean up the session + at_exit cleanup_session "$TRIAL_SESSION" + # Save the policy hash in case the password policy is not used (we have + # to get this from the last step, whichever it is). + tpm2 policypcr -Q -l "sha256:$pcrl" -f "$pcrf" -S "$TRIAL_SESSION" -L "$AUTH_POLICY" + CREATE_PASS_ARGS=() + if [ "$pass" ]; then + # Add an object authorization policy (the object authorization + # will be the password). Save the digest, this is the resulting + # policy. + tpm2 policypassword -Q -S "$TRIAL_SESSION" -L "$AUTH_POLICY" + # Pass the password to create later. Pass the sha256sum of the + # password to the TPM so the password is not limited to 32 chars + # in length. + CREATE_PASS_ARGS=(-p "$(tpm2_password_hex "$pass")") + fi + + # Create the object with this policy and the auth value. + # NOTE: We disable USERWITHAUTH and enable ADMINWITHPOLICY so the + # password cannot be used on its own, the PCRs are also required. + # (The default is to allow either policy auth _or_ password auth. In + # this case the policy includes the password, and we don't want to allow + # the password on its own.) + tpm2 create -Q -C "/tmp/$PRIMARY_HANDLE_FILE" \ + -i "$file" \ + -u "$SECRET_DIR/$bname.priv" \ + -r "$SECRET_DIR/$bname.pub" \ + -L "$AUTH_POLICY" \ + -S "/tmp/$DEC_SESSION_FILE" \ + -a "fixedtpm|fixedparent|adminwithpolicy" \ + "${CREATE_PASS_ARGS[@]}" + + tpm2 load -Q -C "/tmp/$PRIMARY_HANDLE_FILE" \ + -u "$SECRET_DIR/$bname.priv" -r "$SECRET_DIR/$bname.pub" \ + -c "$SECRET_DIR/$bname.seal.ctx" + prompt_tpm_password + # remove possible data occupying this handle + tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_password")" \ + -c "$handle" 2>/dev/null || true + DO_WITH_DEBUG --mask-position 6 \ + tpm2 evictcontrol -Q -C o -P "$(tpm2_password_hex "$tpm_password")" \ + -c "$SECRET_DIR/$bname.seal.ctx" "$handle" +} +tpm1_seal() { + TRACE "Under /bin/tpmr:tpm1_seal" + file="$1" + index="$2" + pcrl="$3" #0,1,2,3,4,5,6,7 (does not include algorithm prefix) + pcrf="$4" + sealed_size="$5" + pass="$6" # May be empty to seal with no password + tpm_password="$7" # Owner password - will prompt if needed and not empty + + sealed_file="$SECRET_DIR/tpm1_seal_sealed.bin" + at_exit cleanup_shred "$sealed_file" + + POLICY_ARGS=() + + # If a password was given, add it to the policy arguments + if [ "$pass" ]; then + POLICY_ARGS+=(-pwdd "$pass") + fi + + # Transform the PCR list and PCR file to discrete arguments + IFS=',' read -r -a PCR_LIST <<<"$pcrl" + pcr_file_index=0 + for pcr in "${PCR_LIST[@]}"; do + # Read each PCR_SIZE block from the file and pass as hex + POLICY_ARGS+=(-ix "$pcr" + "$(dd if="$pcrf" skip="$pcr_file_index" bs="$PCR_SIZE" count=1 status=none | xxd -p | tr -d ' ')" + ) + pcr_file_index=$((pcr_file_index+1)) + done + + tpm sealfile2 \ + -if "$file" \ + -of "$sealed_file" \ + -hk 40000000 \ + "${POLICY_ARGS[@]}" + + # try it without the owner password first + if ! tpm nv_writevalue -in "$index" -if "$sealed_file"; then + # to create an nvram space we need the TPM owner password + # and the TPM physical presence must be asserted. + # + # The permissions are 0 since there is nothing special + # about the sealed file + tpm physicalpresence -s \ + || warn "Warning: Unable to assert physical presence" + + prompt_tpm_password + + tpm nv_definespace -in "$index" -sz "$sealed_size" \ + -pwdo "$tpm_password" -per 0 \ + || warn "Warning: Unable to define NVRAM space; trying anyway" + + + tpm nv_writevalue -in "$index" -if "$sealed_file" \ + || die "Unable to write sealed secret to NVRAM" + fi +} + +# Unseal a file sealed by tpm2_seal. The PCR list must be provided, the +# password must be provided if one was used to seal (and cannot be provided if +# no password was used to seal). +tpm2_unseal() { + TRACE "Under /bin/tpmr:tpm2_unseal" + index="$1" + pcrl="$2" #0,1,2,3,4,5,6,7 (does not include algorithm prefix) + sealed_size="$3" + file="$4" + pass="$5" + + # TPM2 doesn't care about sealed_size, only TPM1 needs that. We don't + # have to separately read the sealed file on TPM2. + + # Pad with up to 6 zeros, i.e. '0x81000001', '0x81001234', etc. + handle="$(printf "0x81%6s" "$index" | tr ' ' 0)" + + DEBUG "tpm2_unseal: handle=$handle pcrl=$pcrl file=$file pass=$(mask_param "$pass")" + + # If we don't have the primary handle (TPM hasn't been reset), tpm2 will + # print nonsense error messages about an unexpected handle value. We + # can't do anything without a primary handle. + if [ ! -f "/tmp/$PRIMARY_HANDLE_FILE" ]; then + DEBUG "tpm2_unseal: No primary handle, cannot attempt to unseal" + exit 1 + fi + + POLICY_SESSION=/tmp/unsealfile_policy.session + rm -f "$POLICY_SESSION" + tpm2 startauthsession -Q -g sha256 -S "$POLICY_SESSION" --policy-session + at_exit cleanup_session "$POLICY_SESSION" + # Check the PCR policy + tpm2 policypcr -Q -l "sha256:$pcrl" -S "$POLICY_SESSION" + UNSEAL_PASS_SUFFIX="" + + if [ "$pass" ]; then + # Add the object authorization policy (the actual password is + # provided later, but we must include this so the policy we + # attempt to use is correct). + tpm2 policypassword -Q -S "$POLICY_SESSION" + # When unsealing, include the password with the auth session + UNSEAL_PASS_SUFFIX="+$(tpm2_password_hex "$pass")" + fi + + tpm2 unseal -Q -c "$handle" -p "session:$POLICY_SESSION$UNSEAL_PASS_SUFFIX" \ + -S "/tmp/$ENC_SESSION_FILE" > "$file" +} +tpm1_unseal() { + TRACE "Under /bin/tpmr:tpm1_unseal" + index="$1" + pcrl="$2" + sealed_size="$3" + file="$4" + pass="$5" + + # pcrl (the PCR list) is unused in TPM1. The TPM itself knows which + # PCRs were used to seal and checks them. We can't verify that it's + # correct either, so just ignore it in TPM1. + + sealed_file="$SECRET_DIR/tpm1_unseal_sealed.bin" + at_exit cleanup_shred "$sealed_file" + + rm -f "$sealed_file" + + tpm nv_readvalue \ + -in "$index" \ + -sz "$sealed_size" \ + -of "$sealed_file" \ + || die "Unable to read sealed file from TPM NVRAM" + + PASS_ARGS=() + if [ "$pass" ]; then + PASS_ARGS=(-pwdd "$pass") + fi + + tpm unsealfile \ + -if "$sealed_file" \ + -of "$file" \ + "${PASS_ARGS[@]}" \ + -hk 40000000 +} + +tpm2_reset() { + TRACE "Under /bin/tpmr:tpm2_reset" + key_password="$1" + mkdir -p "$SECRET_DIR" + tpm2 clear -c platform || warn "Unable to clear TPM on platform hierarchy!" + tpm2 changeauth -c owner "$(tpm2_password_hex "$key_password")" + tpm2 changeauth -c endorsement "$(tpm2_password_hex "$key_password")" + tpm2 createprimary -C owner -g sha256 -G "${CONFIG_PRIMARY_KEY_TYPE:-rsa}" \ + -c "$SECRET_DIR/primary.ctx" -P "$(tpm2_password_hex "$key_password")" + tpm2 evictcontrol -C owner -c "$SECRET_DIR/primary.ctx" "$PRIMARY_HANDLE" \ + -P "$(tpm2_password_hex "$key_password")" + shred -u "$SECRET_DIR/primary.ctx" + tpm2_startsession + + # Set the dictionary attack parameters. TPM2 defaults vary widely, we + # want consistent behavior on any TPM. + # * --max-tries=10: Allow 10 failures before lockout. This allows the + # user to quickly "burst" 10 failures without significantly impacting + # the rate allowed for a dictionary attacker. + # Most TPM2 flows ask for the owner password 2-4 times, so this allows + # a handful of mistypes and some headroom for an expected unseal + # failure if firmware is updated. + # Remember that an auth failure is also counted any time an unclean + # shutdown occurs (see TPM2 spec part 1, section 19.8.6, "Non-orderly + # Shutdown"). + # * --recovery-time=3600: Forget an auth failure every 1 hour. + # * --lockout-recovery-time: After a failed lockout recovery auth, the + # TPM must be reset to try again. + # + # Heads does not offer a way to reset dictionary attack lockout, instead + # the TPM can be reset and new secrets sealed. + tpm2 dictionarylockout -Q --setup-parameters \ + --max-tries=10 \ + --recovery-time=3600 \ + --lockout-recovery-time=0 \ + --auth="session:/tmp/$ENC_SESSION_FILE" + + # Set a random DA lockout password, so the DA lockout can't be cleared + # with a password. Heads doesn't offer dictionary attach reset, instead + # the TPM can be reset and new secrets sealed. + # + # The default lockout password is empty, so we must set this, and we + # don't need to provide any auth (use the default empty password). + tpm2 changeauth -Q -c lockout \ + "hex:$(dd if=/dev/urandom bs=32 count=1 status=none | xxd -p | tr -d ' \n')" +} +tpm1_reset() { + TRACE "Under /bin/tpmr:tpm1_reset" + key_password="$1" + + # Make sure the TPM is ready to be reset + tpm physicalpresence -s + tpm physicalenable + tpm physicalsetdeactivated -c + tpm forceclear + tpm physicalenable + tpm takeown -pwdo "$key_password" + + # And now turn it all back on + tpm physicalpresence -s + tpm physicalenable + tpm physicalsetdeactivated -c +} + +# Perform final cleanup before boot and lock the platform heirarchy. +tpm2_kexec_finalize() { + TRACE "Under /bin/tpmr:tpm2_kexec_finalize" + + # Flush sessions and transient objects + tpm2 flushcontext -Q --transient-object \ + || warn "tpm2_flushcontext: unable to flush transient handles" + tpm2 flushcontext -Q --loaded-session \ + || warn "tpm2_flushcontext: unable to flush sessions" + tpm2 flushcontext -Q --saved-session \ + || warn "tpm2_flushcontext: unable to flush saved session" + + # Add a random passphrase to platform hierarchy to prevent TPM2 from + # being cleared in the OS. + # This passphrase is only effective before the next boot. + echo "Locking TPM2 platform hierarchy..." + randpass=$(dd if=/dev/urandom bs=4 count=1 status=none | xxd -p) + tpm2 changeauth -c platform "$randpass" \ + || warn "Failed to lock platform hierarchy of TPM2!" +} + +tpm2_shutdown() { + TRACE "Under /bin/tpmr:tpm2_shutdown" + + # Prepare for shutdown. + # This is a "clear" shutdown (do not preserve runtime state) since we + # are not going to resume later, we are powering off (or rebooting). + tpm2 shutdown -Q --clear +} + +if [ "$CONFIG_TPM" != "y" ]; then + echo >&2 "No TPM!" + exit 1 +fi + +# TPM1 - most commands forward directly to tpm, but some are still wrapped for +# consistency with tpm2. +if [ "$CONFIG_TPM2_TOOLS" != "y" ]; then + PCR_SIZE=20 # TPM1 PCRs are always SHA-1 + subcmd="$1" + # Don't shift yet, for most commands we will just forward to tpm. + case "$subcmd" in + pcrread) + shift; tpm1_pcrread "$@";; + pcrsize) + echo "$PCR_SIZE";; + calcfuturepcr) + shift; tpm1_calcfuturepcr "$@";; + seal) + shift; tpm1_seal "$@";; + startsession) + ;; # Nothing on TPM1. + unseal) + shift; tpm1_unseal "$@";; + reset) + shift; tpm1_reset "$@";; + kexec_finalize) + ;; # Nothing on TPM1. + shutdown) + ;; # Nothing on TPM1. + *) + DEBUG "Direct translation from tpmr to tpm1 call" + DO_WITH_DEBUG exec tpm "$@" + ;; + esac + exit 0 +fi + +# TPM2 - all commands implemented as wrappers around tpm2 +PCR_SIZE=32 # We use the SHA-256 PCRs +subcmd="$1" +shift 1 +case "$subcmd" in + pcrread) + tpm2_pcrread "$@";; + pcrsize) + echo "$PCR_SIZE";; + calcfuturepcr) + tpm2_calcfuturepcr "$@";; + extend) + tpm2_extend "$@";; + counter_read) + tpm2_counter_read "$@";; + counter_increment) + tpm2_counter_inc "$@";; + counter_create) + tpm2_counter_cre "$@";; + seal) + tpm2_seal "$@";; + startsession) + tpm2_startsession "$@";; + unseal) + tpm2_unseal "$@";; + reset) + tpm2_reset "$@";; + kexec_finalize) + tpm2_kexec_finalize "$@";; + shutdown) + tpm2_shutdown "$@";; + *) + echo "Command $subcmd not wrapped!" + exit 1 +esac diff --git a/initrd/bin/uefi-init b/initrd/bin/uefi-init index 600d2072..8f9de1b3 100755 --- a/initrd/bin/uefi-init +++ b/initrd/bin/uefi-init @@ -1,4 +1,4 @@ -#!/bin/ash +#!/bin/bash set -e -o pipefail . /etc/functions @@ -19,7 +19,7 @@ if [ -n "GUID" ]; then || die "Failed to read config GUID from ROM" if [ "$CONFIG_TPM" = "y" ]; then - tpm extend -ix "$CONFIG_PCR" -if $TMPFILE \ + tpmr extend -ix "$CONFIG_PCR" -if $TMPFILE \ || die "$filename: tpm extend failed" fi diff --git a/initrd/bin/unseal-hotp b/initrd/bin/unseal-hotp index 3d2f6756..73174617 100755 --- a/initrd/bin/unseal-hotp +++ b/initrd/bin/unseal-hotp @@ -1,9 +1,8 @@ -#!/bin/sh +#!/bin/bash # Retrieve the sealed file and counter from the NVRAM, unseal it and compute the hotp . /etc/functions -HOTP_SEALED="/tmp/secret/hotp.sealed" HOTP_SECRET="/tmp/secret/hotp.key" HOTP_COUNTER="/boot/kexec_hotp_counter" @@ -38,20 +37,10 @@ if [ "$counter_value" == "" ]; then fi #counter_value=$(printf "%d" 0x${counter_value}) - -tpm nv_readvalue \ - -in 4d47 \ - -sz 312 \ - -of "$HOTP_SEALED" \ -|| die "Unable to retrieve sealed file from TPM NV" - -tpm unsealfile \ - -hk 40000000 \ - -if "$HOTP_SEALED" \ - -of "$HOTP_SECRET" \ -|| die "Unable to unseal HOTP secret" - -shred -n 10 -z -u "$HOTP_SEALED" 2> /dev/null +if [ "$CONFIG_TPM" = "y" ]; then + DEBUG "Unsealing HOTP secret reuses TOTP sealed secret..." + tpmr unseal 4d47 0,1,2,3,4,7 312 "$HOTP_SECRET" +fi if ! hotp $counter_value < "$HOTP_SECRET"; then shred -n 10 -z -u "$HOTP_SECRET" 2> /dev/null diff --git a/initrd/bin/unseal-totp b/initrd/bin/unseal-totp index 63a36955..32db3192 100755 --- a/initrd/bin/unseal-totp +++ b/initrd/bin/unseal-totp @@ -1,26 +1,16 @@ -#!/bin/sh +#!/bin/bash # Retrieve the sealed file from the NVRAM, unseal it and compute the totp . /etc/functions -TOTP_SEALED="/tmp/secret/totp.sealed" TOTP_SECRET="/tmp/secret/totp.key" TRACE "Under /bin/unseal-totp" -tpm nv_readvalue \ - -in 4d47 \ - -sz 312 \ - -of "$TOTP_SEALED" \ -|| die "Unable to retrieve sealed file from TPM NV" - -tpm unsealfile \ - -hk 40000000 \ - -if "$TOTP_SEALED" \ - -of "$TOTP_SECRET" \ -|| die "Unable to unseal totp secret" - -shred -n 10 -z -u "$TOTP_SEALED" 2> /dev/null +if [ "$CONFIG_TPM" = "y" ]; then + tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" \ + || die "Unable to unseal totp secret" +fi if ! totp -q < "$TOTP_SECRET"; then shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null diff --git a/initrd/bin/usb-init b/initrd/bin/usb-init index 3ab79d10..b2d3783f 100755 --- a/initrd/bin/usb-init +++ b/initrd/bin/usb-init @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Boot a USB installation . /etc/functions @@ -8,7 +8,7 @@ TRACE "Under /bin/usb-init" if [ "$CONFIG_TPM" = "y" ]; then # Extend PCR4 as soon as possible - tpm extend -ix 4 -ic usb + tpmr extend -ix 4 -ic usb fi media-scan usb diff --git a/initrd/bin/wget-measure.sh b/initrd/bin/wget-measure.sh index e66e48b8..604f83eb 100755 --- a/initrd/bin/wget-measure.sh +++ b/initrd/bin/wget-measure.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # get a file and extend a TPM PCR . /etc/functions @@ -19,6 +19,6 @@ fi wget "$URL" || die "$URL: failed" FILE="`basename "$URL"`" -tpm extend -ix "$INDEX" -if "$FILE" || die "$FILE: tpm extend failed" +tpmr extend -ix "$INDEX" -if "$FILE" || die "$FILE: tpm extend failed" diff --git a/initrd/bin/x230-flash.init b/initrd/bin/x230-flash.init deleted file mode 100755 index 6807dd29..00000000 --- a/initrd/bin/x230-flash.init +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh -# Initialize the USB and network device drivers, -# invoke a recovery shell and prompt the user for how to proceed - -. /etc/functions -. /tmp/config - -TRACE "Under /bin/x230-flash.init" - -insmod /lib/modules/ehci-hcd.ko -insmod /lib/modules/ehci-pci.ko -insmod /lib/modules/xhci-hcd.ko -insmod /lib/modules/xhci-pci.ko -insmod /lib/modules/e1000e.ko -insmod /lib/modules/usb-storage.ko - -tpm extend -ix 4 -ic recovery -sleep 2 - -echo '***** Starting recovery shell' -echo '' -echo 'To install from flash drive:' -echo '' -echo ' mount -o ro /dev/sdb1 /media' -echo ' flash.sh /media/x230.rom' -echo '' - -exec /bin/ash diff --git a/initrd/bin/xx30-flash.init b/initrd/bin/xx30-flash.init new file mode 100755 index 00000000..ca2fa8f6 --- /dev/null +++ b/initrd/bin/xx30-flash.init @@ -0,0 +1,27 @@ +#!/bin/ash +# Initialize the USB and network device drivers, +# invoke a recovery shell and prompt the user for how to proceed + +. /etc/ash_functions +. /tmp/config + +TRACE "Under /bin/xx30-flash.init" + +busybox insmod /lib/modules/ehci-hcd.ko +busybox insmod /lib/modules/ehci-pci.ko +busybox insmod /lib/modules/xhci-hcd.ko +busybox insmod /lib/modules/xhci-pci.ko +busybox insmod /lib/modules/e1000e.ko +busybox insmod /lib/modules/usb-storage.ko + +sleep 2 + +echo '***** Starting recovery shell' +echo '' +echo 'To install from flash drive:' +echo '' +echo ' mount -o ro /dev/sdb1 /media' +echo ' flash.sh /media/xx30-legacy.rom' +echo '' + +exec /bin/sh diff --git a/initrd/etc/ash_functions b/initrd/etc/ash_functions new file mode 100644 index 00000000..9156fae5 --- /dev/null +++ b/initrd/etc/ash_functions @@ -0,0 +1,87 @@ +#!/bin/sh +# +# Core shell functions that do not require bash. These functions are used with +# busybox ash on legacy-flash boards, and with bash on all other boards. + +die() { + echo >&2 "$*"; + sleep 2; + exit 1; +} + +warn() { + echo >&2 "$*"; + sleep 1; +} + +DEBUG() { + if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then + echo "DEBUG: $*" | tee -a /tmp/debug.log >&2; + fi +} + +TRACE() { + if [ "$CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT" = "y" ];then + echo "TRACE: $*" | tee -a /tmp/debug.log >&2; + fi +} + +preserve_rom() { + TRACE "Under /etc/functions:preserve_rom" + new_rom="$1" + old_files=`cbfs -t 50 -l 2>/dev/null | grep "^heads/"` + + for old_file in `echo $old_files`; do + new_file=`cbfs.sh -o $1 -l | grep -x $old_file` + if [ -z "$new_file" ]; then + echo "+++ Adding $old_file to $1" + cbfs -t 50 -r $old_file >/tmp/rom.$$ \ + || die "Failed to read cbfs file from ROM" + cbfs.sh -o $1 -a $old_file -f /tmp/rom.$$ \ + || die "Failed to write cbfs file to new ROM file" + fi + done +} + +recovery() { + TRACE "Under /etc/functions:recovery" + echo >&2 "!!!!! $*" + + # Remove any temporary secret files that might be hanging around + # but recreate the directory so that new tools can use it. + + #safe to always be true. Otherwise "set -e" would make it exit here + shred -n 10 -z -u /tmp/secret/* 2> /dev/null || true + rm -rf /tmp/secret + mkdir -p /tmp/secret + + # ensure /tmp/config exists for recovery scripts that depend on it + touch /tmp/config + + if [ "$CONFIG_TPM" = "y" ]; then + tpmr extend -ix 4 -ic recovery + fi + + while [ true ] + do + echo >&2 "!!!!! Starting recovery shell" + sleep 1 + + if [ -x /bin/setsid ]; then + /bin/setsid -c /bin/sh + else + /bin/sh + fi + done +} + +pause_recovery() { + TRACE "Under /etc/functions:pause_recovery" + read -p $'!!! Hit enter to proceed to recovery shell !!!\n' + recovery $* +} + +combine_configs() { + TRACE "Under /etc/functions:combine_configs" + cat /etc/config* > /tmp/config +} diff --git a/initrd/etc/functions b/initrd/etc/functions index d956b872..ea883583 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -1,71 +1,44 @@ -#!/bin/sh +#!/bin/bash # Shell functions for most initialization scripts +. /etc/ash_functions -die() { - echo >&2 "$*"; - sleep 2; - exit 1; -} - -warn() { - echo >&2 "$*"; - sleep 1; -} - -DEBUG() { - if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then - echo >&2 "DEBUG: $*"; +# Print or depending on whether $1 is empty. Useful to mask an +# optional password parameter. +mask_param() { + if [ -z "$1" ]; then + echo "" + else + echo "" fi } -TRACE() { - if [ "$CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT" = "y" ];then - echo >&2 "TRACE: $*"; - fi -} +# Trace a command with DEBUG, then execute it. +# A password parameter can be masked by passing --mask-position N before the +# command to execute, the debug trace will just indicate whether the password +# was empty or nonempty (which is important when use of a password is optional). +# N=0 is the name of the command to be executed, N=1 is its first parameter, +# etc. +DO_WITH_DEBUG() { + if [ "$1" == "--mask-position" ]; then + mask_position="$2" + shift + shift + DEBUG_ARGS=("$@") - - -recovery() { - TRACE "Under /etc/functions:recovery" - echo >&2 "!!!!! $*" - - # Remove any temporary secret files that might be hanging around - # but recreate the directory so that new tools can use it. - - #safe to always be true. Otherwise "set -e" would make it exit here - shred -n 10 -z -u /tmp/secret/* 2> /dev/null || true - rm -rf /tmp/secret - mkdir -p /tmp/secret - - # ensure /tmp/config exists for recovery scripts that depend on it - touch /tmp/config - - if [ "$CONFIG_TPM" = y ]; then - tpm extend -ix 4 -ic recovery + DEBUG_ARGS[$mask_position]="$(mask_param "${DEBUG_ARGS[$mask_position]}")" + DEBUG "${DEBUG_ARGS[@]}" + else + DEBUG "$@" fi - - while [ true ] - do - echo >&2 "!!!!! Starting recovery shell" - sleep 1 - - if [ -x /bin/setsid ]; then - /bin/setsid -c /bin/ash - else - /bin/ash - fi - done -} - -pause_recovery() { - TRACE "Under /etc/functions:pause_recovery" - read -p 'Hit enter to proceed to recovery shell:' - recovery $* + "$@" } pcrs() { - head -8 /sys/class/tpm/tpm0/pcrs + if [ "$CONFIG_TPM2_TOOLS" = "y" ]; then + tpm2 pcrread sha256 + elif [ "$CONFIG_TPM" = "y" ]; then + head -8 /sys/class/tpm/tpm0/pcrs + fi } confirm_totp() @@ -81,7 +54,7 @@ confirm_totp() date=`date "+%Y-%m-%d %H:%M:%S"` seconds=`date "+%s"` half=`expr \( $seconds % 60 \) / 30` - if [ "$CONFIG_TPM" != y ]; then + if [ "$CONFIG_TPM" != "y" ]; then TOTP="NO TPM" elif [ "$half" != "$last_half" ]; then last_half=$half; @@ -236,20 +209,52 @@ confirm_gpg_card() fi } +# Prompt for an owner password if it is not already set in tpm_password. Sets +# tpm_password. Tools should optionally accept a TPM password on the command +# line, since some flows need it multiple times and only one prompt is ideal. +prompt_tpm_password() { + if [ -n "$tpm_password" ]; then + return 0; + fi + + read -s -p "TPM Owner password: " tpm_password + echo # new line after password prompt +} + +# Prompt for a new owner password when resetting the TPM. Returned in +# key_password. The password must be 1-32 characters and must be entered twice, +# the script will loop until this is met. +prompt_new_owner_password() { + local key_password2 + key_password=1 + key_password2=2 + while [ "$key_password" != "$key_password2" ] || [ "${#key_password}" -gt 32 ] || [ -z "$key_password" ]; do + read -s -p "New TPM owner passphrase (2 words suggested, 1-32 characters max): " key_password + echo + + read -s -p "Repeat chosen TPM owner passphrase: " key_password2 + echo + + if [ "$key_password" != "$key_password2" ]; then + echo "Passphrases entered do not match. Try again!" + echo + fi + done +} check_tpm_counter() { TRACE "Under /etc/functions:check_tpm_counter" LABEL=${2:-3135106223} + tpm_password="$3" # if the /boot.hashes file already exists, read the TPM counter ID # from it. if [ -r "$1" ]; then TPM_COUNTER=`grep counter- "$1" | cut -d- -f2` else warn "$1 does not exist; creating new TPM counter" - read -s -p "TPM Owner password: " tpm_password - echo - tpm counter_create \ + prompt_tpm_password + tpmr counter_create \ -pwdo "$tpm_password" \ -pwdc '' \ -la $LABEL \ @@ -266,14 +271,14 @@ check_tpm_counter() read_tpm_counter() { TRACE "Under /etc/functions:read_tpm_counter" - tpm counter_read -ix "$1" | tee "/tmp/counter-$1" \ + tpmr counter_read -ix "$1" | tee "/tmp/counter-$1" \ || die "Counter read failed" } increment_tpm_counter() { TRACE "Under /etc/functions:increment_tpm_counter" - tpm counter_increment -ix "$1" -pwdc '' \ + tpmr counter_increment -ix "$1" -pwdc '' \ | tee /tmp/counter-$1 \ || die "Counter increment failed" } @@ -307,22 +312,6 @@ check_config() { || die "Failed to copy kexec boot params to tmp" } -preserve_rom() { - TRACE "Under /etc/functions:preserve_rom" - new_rom="$1" - old_files=`cbfs -t 50 -l 2>/dev/null | grep "^heads/"` - - for old_file in `echo $old_files`; do - new_file=`cbfs.sh -o $1 -l | grep -x $old_file` - if [ -z "$new_file" ]; then - echo "+++ Adding $old_file to $1" - cbfs -t 50 -r $old_file >/tmp/rom.$$ \ - || die "Failed to read cbfs file from ROM" - cbfs.sh -o $1 -a $old_file -f /tmp/rom.$$ \ - || die "Failed to write cbfs file to new ROM file" - fi - done -} replace_config() { TRACE "Under /etc/functions:replace_config" CONFIG_FILE=$1 @@ -339,10 +328,6 @@ replace_config() { sort ${CONFIG_FILE}.tmp | uniq > ${CONFIG_FILE} rm -f ${CONFIG_FILE}.tmp } -combine_configs() { - TRACE "Under /etc/functions:combine_configs" - cat /etc/config* > /tmp/config -} update_checksums() { @@ -358,8 +343,10 @@ update_checksums() # sign and auto-roll config counter extparam= - if [ "$CONFIG_TPM" = "y" ]; then - extparam=-r + if [ "$CONFIG_TPM" = "y" ];then + if [ "$CONFIG_IGNORE_ROLLBACK" != "y" ]; then + extparam=-r + fi fi if ! kexec-sign-config -p /boot -u $extparam ; then rv=1 @@ -568,3 +555,37 @@ generate_random_mac_address() #Borrowed from https://stackoverflow.com/questions/42660218/bash-generate-random-mac-address-unicast hexdump -n 6 -ve '1/1 "%.2x "' /dev/urandom | awk -v a="2,6,a,e" -v r="$RANDOM" 'BEGIN{srand(r);}NR==1{split(a,b,",");r=int(rand()*4+1);printf "%s%s:%s:%s:%s:%s:%s\n",substr($1,0,1),b[r],$2,$3,$4,$5,$6}' } + +# Add a command to be invoked at exit. (Note that trap EXIT replaces any +# existing handler.) Commands are invoked in reverse order, so they can be used +# to clean up resources, etc. +# The parameters are all executed as-is and do _not_ require additional quoting +# (unlike trap). E.g.: +# at_exit shred "$file" #<-- file is expanded when calling at_exit, no extra quoting needed +at_exit() { + AT_EXIT_HANDLERS+=("$@") # Command and args + AT_EXIT_HANDLERS+=("$#") # Number of elements in this command +} + +# Array of all exit handler command arguments with lengths of each command at +# the end. For example: +# at_exit echo hello +# at_exit echo a b c +# results in: +# AT_EXIT_HANDLERS=(echo hello 2 echo a b c 4) + +AT_EXIT_HANDLERS=() +# Each handler is an array AT_EXIT_HANDLER_{i} +run_at_exit_handlers() { + local cmd_pos cmd_len + cmd_pos="${#AT_EXIT_HANDLERS[@]}" + # Silence trace if there are no handlers, this is common and occurs a lot + [ "$cmd_pos" -gt 0 ] && DEBUG "Running at_exit handlers" + while [ "$cmd_pos" -gt 0 ]; do + cmd_pos="$((cmd_pos-1))" + cmd_len="${AT_EXIT_HANDLERS[$cmd_pos]}" + cmd_pos="$((cmd_pos-cmd_len))" + "${AT_EXIT_HANDLERS[@]:$cmd_pos:$cmd_len}" + done +} +trap run_at_exit_handlers EXIT diff --git a/initrd/etc/gui_functions b/initrd/etc/gui_functions index 3f7130bf..d1920bd3 100755 --- a/initrd/etc/gui_functions +++ b/initrd/etc/gui_functions @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Shell functions for common operations using fbwhiptail . /etc/functions diff --git a/initrd/etc/luks-functions b/initrd/etc/luks-functions index 8331f7ab..3fa3cd52 100644 --- a/initrd/etc/luks-functions +++ b/initrd/etc/luks-functions @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Reencrypt LUKS container and change Disk Recovery Key associated passphrase (Slot 0: main slot) . /etc/functions diff --git a/initrd/etc/passwd b/initrd/etc/passwd index 75a443fe..51836385 100644 --- a/initrd/etc/passwd +++ b/initrd/etc/passwd @@ -1 +1 @@ -root:x:0:0:root:/:/bin/ash +root:x:0:0:root:/:/bin/sh diff --git a/initrd/init b/initrd/init index 878c84f2..a9255745 100755 --- a/initrd/init +++ b/initrd/init @@ -1,4 +1,7 @@ -#!/bin/ash +#! /bin/ash +# Note this is used on legacy-flash boards that lack bash, it runs with busybox +# ash. Calls to bash scripts must be guarded by checking config. + mknod /dev/ttyprintk c 5 3 echo "hello world" > /dev/ttyprintk @@ -16,6 +19,7 @@ mkdir /proc /sys /dev /tmp /boot /media 2>&- 1>&- mount /dev 2>/dev/ttyprintk mount /proc 2>/dev/ttyprintk mount /sys 2>/dev/ttyprintk + if [ "$CONFIG_LINUXBOOT" = "y" ]; then mount /sys/firmware/efi/efivars fi @@ -28,6 +32,12 @@ if [ ! -r /dev/ptmx ]; then ln -s /dev/pts/ptmx /dev/ptmx fi +# Needed by bash +[ -e /dev/stdin ] || ln -s /proc/self/fd/0 /dev/stdin +[ -e /dev/stdout ] || ln -s /proc/self/fd/1 /dev/stdout +[ -e /dev/stderr ] || ln -s /proc/self/fd/2 /dev/stderr +[ -e /dev/fd ] || ln -s /proc/self/fd /dev/fd + # Recovery shells will erase anything from here mkdir -p /tmp/secret @@ -40,16 +50,16 @@ fi hwclock -l -s # Read the system configuration parameters -. /etc/functions +. /etc/ash_functions . /etc/config TRACE "Under init" # set CONFIG_TPM dynamically before init -if [ -e /dev/tpm0 ]; then - CONFIG_TPM='y' -else +if [ ! -e /dev/tpm0 ]; then CONFIG_TPM='n' + CONFIG_TPM2_TOOLS='n' + warn 'No TPM found...' fi #Specify whiptail background colors cues under FBWhiptail only @@ -61,8 +71,13 @@ else export BG_COLOR_ERROR="${CONFIG_ERROR_BG_COLOR:-""}" fi +if [ "$CONFIG_TPM" = "y" ]; then + # Initialize tpm2 encrypted sessions here + tpmr startsession +fi + if [ "$CONFIG_COREBOOT" = "y" ]; then - /bin/cbfs-init + [ -x /bin/bash ] && /bin/cbfs-init fi if [ "$CONFIG_LINUXBOOT" = "y" ]; then /bin/uefi-init @@ -71,7 +86,7 @@ fi # Set GPG_TTY before calling gpg in key-init export GPG_TTY=/dev/console -/bin/key-init +[ -x /bin/bash ] && /bin/key-init # Setup recovery serial shell if [ ! -z "$CONFIG_BOOT_RECOVERY_SERIAL" ]; then @@ -100,17 +115,19 @@ if [ "$boot_option" = "r" ]; then recovery 'User requested recovery shell' # just in case... if [ "$CONFIG_TPM" = "y" ]; then - tpm extend -ix 4 -ic recovery + tpmr extend -ix 4 -ic recovery fi - exec /bin/ash + exec /bin/sh exit fi -# Override CONFIG_TPM from /etc/config with runtime value determined above. +# Override CONFIG_TPM and CONFIG_TPM2_TOOLS from /etc/config with runtime value +# determined above. # # Values in user config have higher priority during combining thus effectively # changing the value for the rest of the scripts which source /tmp/config. echo "export CONFIG_TPM=\"$CONFIG_TPM\"" >> /etc/config.user +echo "export CONFIG_TPM2_TOOLS=\"$CONFIG_TPM2_TOOLS\"" >> /etc/config.user combine_configs . /tmp/config @@ -157,6 +174,6 @@ fi # belts and suspenders, just in case... if [ "$CONFIG_TPM" = "y" ]; then - tpm extend -ix 4 -ic recovery + tpmr extend -ix 4 -ic recovery fi -exec /bin/ash +exec /bin/sh diff --git a/initrd/mount-boot b/initrd/mount-boot index 5f2d3515..42e4c9ae 100755 --- a/initrd/mount-boot +++ b/initrd/mount-boot @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # Extract the GPG signed dmsetup configuration from # the header of the file system, validate it against # the trusted key database, and execute it to mount diff --git a/initrd/sbin/config-dhcp.sh b/initrd/sbin/config-dhcp.sh index 10169634..6dcb8297 100755 --- a/initrd/sbin/config-dhcp.sh +++ b/initrd/sbin/config-dhcp.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # udhcpc script diff --git a/initrd/sbin/insmod b/initrd/sbin/insmod index 01a988f9..ce1adefd 100755 --- a/initrd/sbin/insmod +++ b/initrd/sbin/insmod @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # extend a TPM PCR with a module and then load it # any arguments will also be measured. # The default PCR to be extended is 5, but can be @@ -25,18 +25,20 @@ if [ ! -r "$MODULE" ]; then fi if [ ! -r /sys/class/tpm/tpm0/pcrs -o ! -x /bin/tpm ]; then - tpm_missing=1 + if [ ! -c /dev/tpmrm0 -o ! -x /bin/tpm2 ]; then + tpm_missing=1 + fi fi if [ -z "$tpm_missing" ]; then - tpm extend -ix "$MODULE_PCR" -if "$MODULE" \ + tpmr extend -ix "$MODULE_PCR" -if "$MODULE" \ || die "$MODULE: tpm extend failed" fi if [ ! -z "$*" -a -z "$tpm_missing" ]; then TMPFILE=/tmp/insmod.$$ echo "$@" > $TMPFILE - tpm extend -ix "$MODULE_PCR" -if $TMPFILE \ + tpmr extend -ix "$MODULE_PCR" -if $TMPFILE \ || die "$MODULE: tpm extend on arguments failed" fi diff --git a/modules/bash b/modules/bash new file mode 100644 index 00000000..8ada1577 --- /dev/null +++ b/modules/bash @@ -0,0 +1,32 @@ +# GNU bash +modules-$(CONFIG_BASH) += bash + +bash_version := 5.1.16 +bash_dir := bash-$(bash_version) +bash_tar := bash-$(bash_version).tar.gz +bash_url := https://ftpmirror.gnu.org/bash/$(bash_tar) +bash_hash := 5bac17218d3911834520dad13cd1f85ab944e1c09ae1aba55906be1f8192f558 + +bash_configure := CFLAGS=-Os ./configure \ + $(CROSS_TOOLS) \ + --host $(target) \ + --prefix="/usr" \ + --enable-largefile \ + --infodir=/usr/share/info \ + --mandir=/usr/share/man \ + --without-bash-malloc \ + --disable-coprocesses \ + --disable-debugger \ + --disable-net-redirections \ + --enable-single-help-strings \ + --disable-nls \ + --disable-readline \ + +bash_target := $(MAKE_JOBS) \ + && $(MAKE) -C $(build)/$(bash_dir) \ + DESTDIR="$(INSTALL)" \ + install \ + +bash_output := bash + +bash_depends := $(musl_dep) diff --git a/modules/linux b/modules/linux index 77fa0e35..19a9c6c6 100644 --- a/modules/linux +++ b/modules/linux @@ -131,6 +131,10 @@ linux_modules-$(CONFIG_LINUX_MEI) += drivers/misc/mei/mei-me.ko EXTRA_FLAGS := -fdebug-prefix-map=$(pwd)=heads -gno-record-gcc-switches +ifeq "$(CONFIG_LINUX_VERSION)" "4.14.62" +EXTRA_FLAGS += -Wno-cast-function-type +endif + linux_target := \ O="$(build)/$(linux_dir)" \ ARCH="$(LINUX_ARCH)" \ diff --git a/modules/openssl b/modules/openssl new file mode 100644 index 00000000..0c4c93f4 --- /dev/null +++ b/modules/openssl @@ -0,0 +1,64 @@ +# OpenSSL library +modules-$(CONFIG_OPENSSL) += openssl + +openssl_version := 3.0.8 +openssl_dir := openssl-$(openssl_version) +openssl_tar := openssl-$(openssl_version).tar.gz +openssl_url := https://www.openssl.org/source/$(openssl_tar) +openssl_hash := 6c13d2bf38fdf31eac3ce2a347073673f5d63263398f1f69d0df4a41253e4b3e + +# hack to provide path to libgcc +LIBGCC_DIR := $(dir $(shell $(heads_cc) -print-libgcc-file-name)) + +# The only optional algorithm that's enabled is SM3. tpm2-tss uses SHA, AES, +# and SM3. +openssl_configure := \ + $(CROSS_TOOLS) \ + CFLAGS="-Os" \ + LDFLAGS="-L$(LIBGCC_DIR)" \ + ./Configure \ + --prefix="/" \ + no-tests \ + linux-$(strip $(arch)) \ + no-aria \ + no-bf \ + no-blake2 \ + no-camellia \ + no-cast \ + no-chacha \ + no-cmac \ + no-des \ + no-dh \ + no-dsa \ + no-ecdh \ + no-ecdsa \ + no-idea \ + no-md4 \ + no-mdc2 \ + no-ocb \ + no-poly1305 \ + no-rc2 \ + no-rc4 \ + no-rmd160 \ + no-scrypt \ + no-seed \ + no-siphash \ + no-siv \ + no-sm2 \ + no-sm4 \ + no-whirlpool \ + +openssl_target := $(MAKE_JOBS) \ + build_programs \ + && \ + $(MAKE) \ + -C "$(build)/$(openssl_dir)" \ + DESTDIR="$(INSTALL)" \ + LIBDIR="lib" \ + install_sw \ + +# Only libcrypto is needed currently, libssl is not included in initrd +openssl_libraries := \ + libcrypto.so.3 \ + +openssl_depends := $(musl_dep) diff --git a/modules/tpm2-tools b/modules/tpm2-tools new file mode 100644 index 00000000..98711195 --- /dev/null +++ b/modules/tpm2-tools @@ -0,0 +1,36 @@ +# TPM2 tools program +modules-$(CONFIG_TPM2_TOOLS) += tpm2-tools + +# CONFIG_TPM means any TPM version. (CONFIG_TPM2_TOOLS differentiates them when +# they must be handled differently, which should be localized.) Boards setting +# CONFIG_TPM2_TOOLS=y imply CONFIG_TPM=y. +ifeq "$(CONFIG_TPM2_TOOLS)" "y" + export CONFIG_TPM=y +endif + +tpm2-tools_version := 5.2 +#tpm2-tools_version := 78a7681 +#tpm2-tools_repo := https://github.com/tpm2-software/tpm2-tools.git + +tpm2-tools_dir := tpm2-tools-$(tpm2-tools_version) +tpm2-tools_tar := tpm2-tools-$(tpm2-tools_version).tar.gz +tpm2-tools_url := https://github.com/tpm2-software/tpm2-tools/releases/download/$(tpm2-tools_version)/$(tpm2-tools_tar) +tpm2-tools_hash := c0b402f6a7b3456e8eb2445211e2d41c46c7e769e05fe4d8909ff64119f7a630 + +# we have ESYS 3.0, but it doesn't figure that out on its own +tpm2-tools_configure := ./bootstrap && ./configure \ + $(CROSS_TOOLS) \ + --host $(MUSL_ARCH)-elf-linux \ + --prefix "/" \ + --disable-fapi \ + TSS2_ESYS_3_0_CFLAGS="-I$(INSTALL)/include" \ + TSS2_ESYS_3_0_LIBS="-ltss2-esys" \ + +tpm2-tools_target := $(MAKE_JOBS) \ + DESTDIR="$(INSTALL)" \ + $(CROSS_TOOLS) \ + install \ + +tpm2-tools_output := tools/tpm2 + +tpm2-tools_depends := openssl tpm2-tss $(musl_dep) diff --git a/modules/tpm2-tss b/modules/tpm2-tss new file mode 100644 index 00000000..0fad79ef --- /dev/null +++ b/modules/tpm2-tss @@ -0,0 +1,45 @@ +# TPM2 TSS library +modules-$(CONFIG_TPM2_TSS) += tpm2-tss + +tpm2-tss_version := 3.2.0 +tpm2-tss_dir := tpm2-tss-$(tpm2-tss_version) +tpm2-tss_tar := tpm2-tss-$(tpm2-tss_version).tar.gz +tpm2-tss_url := https://github.com/tpm2-software/tpm2-tss/releases/download/$(tpm2-tss_version)/$(tpm2-tss_tar) +tpm2-tss_hash := 48305e4144dcf6d10f3b25b7bccf0189fd2d1186feafd8cd68c6b17ecf0d7912 + +tpm2-tss_configure := aclocal && automake --add-missing && autoreconf -fi \ + && ./configure \ + $(CROSS_TOOLS) \ + --host $(MUSL_ARCH)-elf-linux \ + --prefix "/" \ + --disable-doxygen-doc \ + --disable-doxygen-man \ + --disable-doxygen-rtf \ + --disable-doxygen-html \ + --disable-fapi \ + +# Run one build to generate the executables with the pre-defined +# exec_prefix and datarootdir, then a second make to install the binaries +# into our actual target location + +tpm2-tss_target := $(MAKE_JOBS) \ + DESTDIR="$(INSTALL)" \ + $(CROSS_TOOLS) \ + install \ + +# tpm2 binary wants to dlopen some libraries, so be sure that +# they are available. It would be nice to statically link these. +tpm2-tss_libraries := \ + src/tss2-rc/.libs/libtss2-rc.so.0 \ + src/tss2-mu/.libs/libtss2-mu.so.0 \ + src/tss2-sys/.libs/libtss2-sys.so.1 \ + src/tss2-esys/.libs/libtss2-esys.so.0 \ + src/tss2-tcti/.libs/libtss2-tctildr.so.0 \ + src/tss2-tcti/.libs/libtss2-tcti-device.so.0 \ + +# The pcap TCTI driver is only included if enabled in the board config. +ifeq "$(CONFIG_TPM2_CAPTURE_PCAP)" "y" +tpm2-tss_libraries += src/tss2-tcti/.libs/libtss2-tcti-pcap.so.0 +endif + +tpm2-tss_depends := openssl $(musl_dep) diff --git a/modules/tpmtotp b/modules/tpmtotp index 1ce561d0..f6524582 100644 --- a/modules/tpmtotp +++ b/modules/tpmtotp @@ -2,14 +2,11 @@ modules-$(CONFIG_TPMTOTP) += tpmtotp tpmtotp_depends := mbedtls qrencode $(musl_dep) -#tpmtotp_version := git -#tpmtotp_repo := https://github.com/osresearch/tpmtotp - -tpmtotp_version := 18b860fdcf5a55537c8395b891f2b2a5c24fc00a +tpmtotp_version := 4d63d21c8b7db2e92ddb393057f168aead147f47 tpmtotp_dir := tpmtotp-$(tpmtotp_version) tpmtotp_tar := tpmtotp-$(tpmtotp_version).tar.gz tpmtotp_url := https://github.com/osresearch/tpmtotp/archive/$(tpmtotp_version).tar.gz -tpmtotp_hash := 1082f2b0e4af833e04220dddedcc21a39eb39ee4dc5668bb010e7bcc795c606c +tpmtotp_hash := eaac1e8f652f1da7f5a1ed6a8cfefb6511f1e5e1dabf93b44db3b29c18c5ae53 tpmtotp_target := \ $(CROSS_TOOLS) \ diff --git a/patches/tpm2-tools-5.2.patch b/patches/tpm2-tools-5.2.patch new file mode 100644 index 00000000..f4720f23 --- /dev/null +++ b/patches/tpm2-tools-5.2.patch @@ -0,0 +1,33 @@ +diff --git a/Makefile.am b/Makefile.am +index 7132215..32e2193 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -93,7 +93,7 @@ tss2_tools = \ + + # Bundle all the tools into a single program similar to busybox + bin_PROGRAMS += tools/tpm2 +-tools_tpm2_LDADD = $(LDADD) $(CURL_LIBS) ++tools_tpm2_LDADD = $(LDADD) + tools_tpm2_CFLAGS = $(AM_CFLAGS) -DTPM2_TOOLS_MAX="$(words $(tpm2_tools))" + tools_tpm2_SOURCES = \ + tools/tpm2_tool.c \ +@@ -127,7 +127,6 @@ tpm2_tools = \ + tools/tpm2_encryptdecrypt.c \ + tools/tpm2_evictcontrol.c \ + tools/tpm2_flushcontext.c \ +- tools/tpm2_getekcertificate.c \ + tools/tpm2_getrandom.c \ + tools/tpm2_gettime.c \ + tools/tpm2_hash.c \ +diff --git a/configure.ac b/configure.ac +index f1c1711..7279baa 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -59,7 +59,6 @@ PKG_CHECK_MODULES([TSS2_MU], [tss2-mu]) + PKG_CHECK_MODULES([TSS2_RC], [tss2-rc]) + PKG_CHECK_MODULES([TSS2_SYS], [tss2-sys]) + PKG_CHECK_MODULES([CRYPTO], [libcrypto >= 1.1.0]) +-PKG_CHECK_MODULES([CURL], [libcurl]) + + # pretty print of devicepath if efivar library is present + PKG_CHECK_MODULES([EFIVAR], [efivar],,[true]) diff --git a/patches/tpm2-tss-3.2.0.patch b/patches/tpm2-tss-3.2.0.patch new file mode 100644 index 00000000..c1de8d33 --- /dev/null +++ b/patches/tpm2-tss-3.2.0.patch @@ -0,0 +1,20 @@ +--- a/configure.ac 2022-05-21 14:40:59.686470575 +0800 ++++ b/configure.ac 2022-05-21 14:41:21.406671435 +0800 +@@ -488,17 +488,6 @@ AM_CONDITIONAL(SYSD_SYSUSERS, test "x$sy + AC_CHECK_PROG(systemd_tmpfiles, systemd-tmpfiles, yes) + AM_CONDITIONAL(SYSD_TMPFILES, test "x$systemd_tmpfiles" = "xyes") + +-# Check all tools used by make install +-AS_IF([test "$HOSTOS" = "Linux"], +- [ AC_CHECK_PROG(useradd, useradd, yes) +- AC_CHECK_PROG(groupadd, groupadd, yes) +- AC_CHECK_PROG(adduser, adduser, yes) +- AC_CHECK_PROG(addgroup, addgroup, yes) +- AS_IF([test "x$addgroup" != "xyes" && test "x$groupadd" != "xyes" ], +- [AC_MSG_ERROR([addgroup or groupadd are needed.])]) +- AS_IF([test "x$adduser" != "xyes" && test "x$useradd" != "xyes" ], +- [AC_MSG_ERROR([adduser or useradd are needed.])])]) +- + AC_SUBST([PATH]) + + dnl --------- Doxy Gen -----------------------