From 3fb84f0b425d75ff05b78c810b07aee111ac458c Mon Sep 17 00:00:00 2001
From: Thierry Laurion <insurgo@riseup.net>
Date: Mon, 23 Oct 2023 15:18:28 -0400
Subject: [PATCH] WiP: Clean cached /tmp/secret/tpm_password when sealing
 fails, otherwise reuse it on TPM Reset/TOTP+HOTP Sealing once for
 TPM1/TPM2+TPM Disk Unlock Key gui-init: make sure that
 reseal_tpm_disk_decryption_key happens only on successful TOTP/HOTP sealing,
 reusing cached TPM Owner password

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
---
 initrd/bin/gui-init       | 15 +++++----------
 initrd/bin/kexec-seal-key |  6 +++++-
 initrd/bin/seal-totp      | 22 ++++++++++++++--------
 initrd/bin/tpmr           |  1 +
 initrd/bin/unseal-totp    | 12 ++++++------
 initrd/etc/functions      |  9 +++++++--
 6 files changed, 38 insertions(+), 27 deletions(-)

diff --git a/initrd/bin/gui-init b/initrd/bin/gui-init
index 1ab178f3..a9bfc417 100755
--- a/initrd/bin/gui-init
+++ b/initrd/bin/gui-init
@@ -229,8 +229,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_hotp && update_totp && BG_COLOR_MAIN_MENU=""
-            reseal_tpm_disk_decryption_key
+            generate_totp_hotp && update_totp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
           fi
           ;;
         i )
@@ -238,8 +237,7 @@ update_totp()
           return 1
           ;;
         p )
-          reset_tpm && update_totp && BG_COLOR_MAIN_MENU=""
-          reseal_tpm_disk_decryption_key
+          reset_tpm && update_totp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
           ;;
         x )
           recovery "User requested recovery shell"
@@ -300,8 +298,7 @@ update_hotp()
       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_hotp && BG_COLOR_MAIN_MENU=""
-          reseal_tpm_disk_decryption_key
+          generate_totp_hotp && BG_COLOR_MAIN_MENU="" && reseal_tpm_disk_decryption_key
         fi
         ;;
       i )
@@ -526,12 +523,10 @@ show_tpm_totp_hotp_options_menu()
   option=$(cat /tmp/whiptail)
   case "$option" in 
     g )
-      generate_totp_hotp
-      reseal_tpm_disk_decryption_key
+      generate_totp_hotp && reseal_tpm_disk_decryption_key
       ;;
     r )
-      reset_tpm
-      reseal_tpm_disk_decryption_key
+      reset_tpm && reseal_tpm_disk_decryption_key
       ;;
     t )
       prompt_totp_mismatch
diff --git a/initrd/bin/kexec-seal-key b/initrd/bin/kexec-seal-key
index c8a1891a..881797a5 100755
--- a/initrd/bin/kexec-seal-key
+++ b/initrd/bin/kexec-seal-key
@@ -137,7 +137,11 @@ tpmr pcrread -a 7 "$pcrf"
 
 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"
+	"$TPM_SIZE" "$key_password" || {
+	shred -n 10 -z -u /tmp/secret/tpm_password 2>/dev/null
+	:
+	die "Unable to write TPM Disk Unlock Key to NVRAM"
+}
 
 # should be okay if this fails
 shred -n 10 -z -u "$pcrf" 2>/dev/null ||
diff --git a/initrd/bin/seal-totp b/initrd/bin/seal-totp
index 374e1eab..d3953ce4 100755
--- a/initrd/bin/seal-totp
+++ b/initrd/bin/seal-totp
@@ -25,10 +25,10 @@ dd \
 	of="$TOTP_SECRET" \
 	count=1 \
 	bs=20 \
-	2>/dev/null \
-|| die "Unable to generate 20 random bytes"
+	2>/dev/null ||
+	die "Unable to generate 20 random bytes"
 
-secret="`base32 < $TOTP_SECRET`"
+secret="$(base32 <$TOTP_SECRET)"
 pcrf="/tmp/secret/pcrf.bin"
 DEBUG "Sealing TOTP with actual state of PCR0-3"
 tpmr pcrread 0 "$pcrf"
@@ -36,20 +36,26 @@ tpmr pcrread -a 1 "$pcrf"
 tpmr pcrread -a 2 "$pcrf"
 tpmr pcrread -a 3 "$pcrf"
 DEBUG "Sealing TOTP with boot state of PCR4 (Going to recovery shell extends PCR4)"
-# pcr 4 is expected to either: 
+# pcr 4 is expected to either:
 #  zero on bare coreboot+linuxboot on x86 (boot mode: init)
 #  already extended on ppc64 per BOOTKERNEL (skiboot) which boots heads.
 # Read from event log to catch both cases, even when called from recovery shell.
-tpmr calcfuturepcr 4 >> "$pcrf"
+tpmr calcfuturepcr 4 >>"$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"
-shred -n 10 -z -u "$TOTP_SEALED" 2> /dev/null
+#Make sure we clear the TPM Owner Password from memory in case it failed to be used to seal TOTP
+tpmr seal "$TOTP_SECRET" "$TPM_NVRAM_SPACE" 0,1,2,3,4,7 "$pcrf" 312 "" "$TPM_PASSWORD" ||
+	{
+		shred -n 10 -z -u /tmp/secret/tpm_password 2>/dev/null
+		:
+		die "Unable to write sealed secret to NVRAM"
+	}
+#Make sure we clear TPM TOTP sealed if we succeed to seal TOTP
+shred -n 10 -z -u "$TOTP_SEALED" 2>/dev/null
 
 url="otpauth://totp/$HOST?secret=$secret"
 secret=""
diff --git a/initrd/bin/tpmr b/initrd/bin/tpmr
index 20ac4a47..ddcf5e88 100755
--- a/initrd/bin/tpmr
+++ b/initrd/bin/tpmr
@@ -533,6 +533,7 @@ tpm2_unseal() {
 	# 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"
+		warn "No TPM primary handle. You must reset TPM to seal secret"
 		exit 1
 	fi
 
diff --git a/initrd/bin/unseal-totp b/initrd/bin/unseal-totp
index 32db3192..ed8f18af 100755
--- a/initrd/bin/unseal-totp
+++ b/initrd/bin/unseal-totp
@@ -8,14 +8,14 @@ TOTP_SECRET="/tmp/secret/totp.key"
 TRACE "Under /bin/unseal-totp"
 
 if [ "$CONFIG_TPM" = "y" ]; then
-	tpmr unseal 4d47 0,1,2,3,4,7 312 "$TOTP_SECRET" \
-		|| die "Unable to unseal totp secret"
-fi
+	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
+if ! totp -q <"$TOTP_SECRET"; then
+	shred -n 10 -z -u "$TOTP_SECRET" 2>/dev/null
 	die 'Unable to compute TOTP hash?'
 fi
 
-shred -n 10 -z -u "$TOTP_SECRET" 2> /dev/null
+shred -n 10 -z -u "$TOTP_SECRET" 2>/dev/null
 exit 0
diff --git a/initrd/etc/functions b/initrd/etc/functions
index 816eb902..60ff97c7 100755
--- a/initrd/etc/functions
+++ b/initrd/etc/functions
@@ -99,7 +99,7 @@ reseal_tpm_disk_decryption_key() {
 		warn "TPM sealed Disk Unlock Key secret needs to be resealed alongside TOTP/HOTP secret"
 		echo "Resealing TPM LUKS Disk Unlock Key to be unsealed by TPM Disk Unlock Key passphrase"
 		while ! kexec-seal-key /boot; do
-			warn "Recovery Disk Encryption key passphrase invalid. Try again!"
+			warn "Recovery Disk Encryption key passphrase/TPM Owner Password may be invalid. Please try again"
 		done
 		warn "LUKS header hash changed under /boot/kexec_luks_hdr_hash.txt"
 		echo "Updating checksums and signing all files under /boot/kexec.sig"
@@ -206,6 +206,11 @@ prompt_tpm_password() {
 
 	read -s -p "TPM Owner Password: " tpm_password
 	echo # new line after password prompt
+
+	# Cache the password externally to be reused by who needs it
+	DEBUG "Caching TPM Owner Password to /tmp/secret/tpm_password"
+	mkdir -p /tmp/secret || die "Unable to create /tmp/secret"
+	echo "$key_password" >/tmp/secret/tpm_password || die "Unable to cache TPM password under /tmp/secret"
 }
 
 # Prompt for a new owner password when resetting the TPM.  Returned in
@@ -232,7 +237,7 @@ prompt_new_owner_password() {
 	# Cache the password externally to be reused by who needs it
 	DEBUG "Caching TPM Owner Password to /tmp/secret/tpm_password"
 	mkdir -p /tmp/secret || die "Unable to create /tmp/secret"
-	echo "$key_password" > /tmp/secret/tpm_password || die "Unable to cache TPM password under /tmp/secret"
+	echo "$key_password" >/tmp/secret/tpm_password || die "Unable to cache TPM password under /tmp/secret"
 }
 
 check_tpm_counter() {