Merge pull request #1141 from tlaurion/oem_reownership_add_reencryption_passphrase_change

OEM Factory Reset/Re-Ownership: Addition of LUKS reencryption + passphrase change options
This commit is contained in:
tlaurion 2022-03-28 15:03:59 -04:00 committed by GitHub
commit 182bd6bd75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 347 additions and 16 deletions

View File

@ -7,6 +7,7 @@ export BG_COLOR_MAIN_MENU=""
. /etc/functions
. /tmp/config
. /bin/reencrypt-luks
mount_boot()
{
@ -155,7 +156,7 @@ generate_totp_htop()
update_totp()
{
# update the TOTP code
date=`date "+%Y-%m-%d %H:%M:%S"`
date=`date "+%Y-%m-%d %H:%M:%S %Z"`
if [ "$CONFIG_TPM" = n ]; then
TOTP="NO TPM"
else
@ -269,6 +270,7 @@ check_gpg_key()
whiptail $BG_COLOR_ERROR --clear --title "ERROR: GPG keyring empty!" \
--menu "ERROR: Heads couldn't find any GPG keys in your keyring.\n\nIf this is the first time the system has booted,\nyou should add a public GPG key to the BIOS now.\n\nIf you just reflashed a new BIOS, you'll need to add at least one\npublic key to the keyring.\n\nIf you have not just reflashed your BIOS, THIS COULD INDICATE TAMPERING!\n\nHow would you like to proceed?" 30 90 4 \
'g' ' Add a GPG key to the running BIOS' \
'F' ' OEM Factory Reset / Re-Ownership' \
'i' ' Ignore error and continue to main menu' \
'x' ' Exit to recovery shell' \
2>/tmp/whiptail || recovery "GUI menu failed"
@ -281,6 +283,10 @@ check_gpg_key()
i )
return 1
;;
F )
oem-factory-reset
;;
x )
recovery "User requested recovery shell"
;;
@ -306,7 +312,7 @@ prompt_auto_default_boot()
show_main_menu()
{
date=`date "+%Y-%m-%d %H:%M"`
date=`date "+%Y-%m-%d %H:%M:%S %Z"`
whiptail $BG_COLOR_MAIN_MENU --clear --title "$MAIN_MENU_TITLE" \
--menu "$date\nTOTP: $TOTP | HOTP: $HOTP" 20 90 10 \
'd' ' Default boot' \
@ -347,6 +353,8 @@ show_options_menu()
'f' ' Flash/Update the BIOS -->' \
'g' ' GPG Options -->' \
'F' ' OEM Factory Reset / Re-Ownership -->' \
'R' ' Reencrypt LUKS container -->' \
'C' ' Change LUKS Disk Recovery Key passphrase ->' \
'x' ' Exit to recovery shell' \
'r' ' <-- Return to main menu' \
2>/tmp/whiptail || recovery "GUI menu failed"
@ -374,6 +382,14 @@ show_options_menu()
F )
oem-factory-reset
;;
R )
luks_reencrypt
luks_secrets_cleanup
;;
C )
luks_change_passphrase
luks_secrets_cleanup
;;
x )
recovery "User requested recovery shell"
;;

View File

@ -27,8 +27,8 @@ if [ -e /sys/class/net/eth0 ]; then
fi
hwclock -w
echo "" > /dev/tty0
echo "UTC/GMT current date and time:" > /dev/tty0
date > /dev/tty0
date=`date "+%Y-%m-%d %H:%M:%S %Z"`
echo "Time: $date" > /dev/tty0
fi
fi
fi

View File

@ -25,14 +25,6 @@ TPM_PASS=""
# What are the Security components affected by custom passwords
CUSTOM_PASS_AFFECTED_COMPONENTS=""
if [ "$CONFIG_TPM" = "y" ]; then
CUSTOM_PASS_AFFECTED_COMPONENTS="TPM Ownership password"
fi
CUSTOM_PASS_AFFECTED_COMPONENTS="
$CUSTOM_PASS_AFFECTED_COMPONENTS
GPG Admin PIN
GPG User PIN"
RSA_KEY_LENGTH=3072
GPG_USER_NAME="OEM Key"
@ -45,6 +37,7 @@ SKIP_BOOT="n"
. /etc/functions
. /tmp/config
. /bin/reencrypt-luks
## functions
@ -273,6 +266,65 @@ set_default_boot_option()
|| whiptail_error_die "Failed to create hashes of boot files"
}
report_integrity_measurements()
{
#check for GPG key in keyring
GPG_KEY_COUNT=`gpg -k 2>/dev/null | wc -l`
if [ $GPG_KEY_COUNT -ne 0 ]; then
# Check and report TOTP
# update the TOTP code every thirty seconds
date=`date "+%Y-%m-%d %H:%M:%S %Z"`
seconds=`date "+%s"`
half=`expr \( $seconds % 60 \) / 30`
if [ "$CONFIG_TPM" = n ]; then
TOTP="NO TPM"
elif [ "$half" != "$last_half" ]; then
last_half=$half;
TOTP=`unseal-totp` > /dev/null 2>&1
fi
# Check and report on HOTP status
if [ -x /bin/hotp_verification ]; then
HOTP=`unseal-hotp` > /dev/null 2>&1
enable_usb
if ! hotp_verification info > /dev/null 2>&1 ; then
whiptail $CONFIG_WARNING_BG_COLOR --clear --title 'WARNING: Please insert your HOTP enabled USB Security dongle' --msgbox "Your HOTP enabled USB Security dongle was not detected.\n\nPlease remove it and insert it again." 30 90
fi
# Don't output HOTP codes to screen, so as to make replay attacks harder
hotp_verification check $HOTP
case "$?" in
0 )
HOTP="Success"
;;
4 )
HOTP="Invalid code"
MAIN_MENU_BG_COLOR=$CONFIG_ERROR_BG_COLOR
;;
* )
HOTP="Error checking code, Insert USB Security dongle and retry"
MAIN_MENU_BG_COLOR=$CONFIG_WARNING_BG_COLOR
;;
esac
else
HOTP='N/A'
fi
# Check for detached signed digest and report on /boot integrity status
check_config /boot force
TMP_HASH_FILE="/tmp/kexec/kexec_hashes.txt"
if cd /boot && sha256sum -c "$TMP_HASH_FILE" > /tmp/hash_output ; then
HASH="OK"
cd /
else
HASH="ALTERED"
fi
#Show results
whiptail $MAIN_MENU_BG_COLOR --clear --title "Measured Integrity Report" --msgbox "$date\nTOTP: $TOTP | HOTP: $HOTP\n/BOOT INTEGRITY: $HASH\n\nPress OK to continue or Ctrl+Alt+Delete to reboot" 30 90
fi
}
## main script start
# check for args
@ -307,12 +359,46 @@ $TPM_STR
exit 1
fi
# We show current integrity measurements status and time
report_integrity_measurements
# Re-ownership of encrypted disk key, content and passphrase
echo -e -n "Would you like to change the current LUKS Disk Recovery Key passphrase?\n (Highly recommended if you didn't install the Operating System yourself, so that past provisioned passphrase would not permit to access content.\n Note that without re-encrypting disk, a backuped header could be restored to access encrypted content with old passphrase) [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ];then
luks_new_Disk_Recovery_Key_passphrase_desired=1
echo -e "\n"
fi
echo -e -n "Would you like to re-encrypt LUKS encrypted container and generate new Disk Recovery key?\n (Highly recommended if you didn't install the operating system yourself: this would prevent any LUKS backuped header to be restored to access encrypted data) [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
-o "$prompt_output" == "Y" ];then
test_luks_current_disk_recovery_key_passphrase
echo -e "\n"
fi
# Adapt message to be given to user in terms of security components that will be applied.
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -o -n "$luks_new_Disk_Recovery_Key_passphrase" ]; then
CUSTOM_PASS_AFFECTED_COMPONENTS="LUKS Disk Recovery Key passphrase"
fi
if [ "$CONFIG_TPM" = "y" ]; then
CUSTOM_PASS_AFFECTED_COMPONENTS="$CUSTOM_PASS_AFFECTED_COMPONENTS
TPM Ownership password"
fi
CUSTOM_PASS_AFFECTED_COMPONENTS="$CUSTOM_PASS_AFFECTED_COMPONENTS
GPG Admin PIN
GPG User PIN"
# Inform user of security components affected for the following prompts
echo -e "The following security components will be provisioned with defaults or chosen PINs/passwords:
$CUSTOM_PASS_AFFECTED_COMPONENTS\n"
# Prompt to change default passwords
echo -e -n "Would you like to set a single custom password that will be provisioned to all security components? [y/N]: "
echo -e -n "Would you like to set a single custom password that will be provisioned to previously stated security components? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
@ -328,8 +414,13 @@ if [ "$prompt_output" == "y" \
TPM_PASS=$CUSTOM_SINGLE_PASS
USER_PIN=$CUSTOM_SINGLE_PASS
ADMIN_PIN=$CUSTOM_SINGLE_PASS
# Only set if user said desired. Matches rest of logic
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
luks_new_Disk_Recovery_Key_passphrase=$CUSTOM_SINGLE_PASS
fi
else
echo -e -n "Would you like to set distinct PINs/passwords to be provisioned to security components? [y/N]: "
echo -e -n "Would you like to set distinct PINs/passwords to be provisioned to previously stated security components? [y/N]: "
read -n 1 prompt_output
echo
if [ "$prompt_output" == "y" \
@ -355,6 +446,19 @@ else
fi
fi
if [ -n "$luks_new_Disk_Recovery_Key_passphrase_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase" ]; then
# We catch here if changing LUKS Disk Recovery Key passphrase was desired
# but yet undone. This is if not being covered by the single password
echo -e "\nEnter desired replacement for current Disk Recovery Key passphrase (At least 8 characters long):"
while [[ ${#luks_new_Disk_Recovery_Key_passphrase} -lt 8 ]]; do
{
read -r luks_new_Disk_Recovery_Key_passphrase
};done
#We test that current Disk Recovery Key passphrase is known prior of going further
test_luks_current_disk_recovery_key_passphrase
echo -e "\n"
fi
# If nothing is stored in custom variables, we set them to their defaults
if [ "$TPM_PASS" == "" ]; then TPM_PASS=$TPM_PASS_DEF; fi
if [ "$USER_PIN" == "" ]; then USER_PIN=$USER_PIN_DEF; fi
@ -437,6 +541,8 @@ if ! gpg --card-status >/dev/null 2>&1 ; then
fi
fi
# Action time...
# detect and set /boot device
echo -e "\nDetecting and setting boot device...\n"
if ! detect_boot_device ; then
@ -451,6 +557,19 @@ if [[ "$SKIP_BOOT" == "n" ]]; then
combine_configs
fi
if [ -n "$luks_current_Disk_Recovery_Key_passphrase" -a -n "$luks_new_Disk_Recovery_Key_passphrase" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
#Reencryption of disk, disk recovery key and Disk Recovery Key passphrase change is requested
luks_reencrypt
luks_change_passphrase
elif [ -n "$luks_current_Disk_Recovery_Key_passphrase" -a -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
#Reencryption of disk was requested but not passphrase change
luks_reencrypt
elif [ -n "$luks_new_Disk_Recovery_Key_passphrase" -a -n "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
#Passphrase change is requested without disk reencryption
luks_change_passphrase
fi
## reset TPM and set password
if [ "$CONFIG_TPM" = "y" ]; then
echo -e "\nResetting TPM...\n"
@ -559,9 +678,25 @@ if [[ "$SKIP_BOOT" == "n" ]]; then
generate_checksums
fi
## Show user current provisioned PINS/Password prior of reboot
# Prepare whiptail output of provisioned secrets
if [ -z "$luks_new_Disk_Recovery_Key_passphrase" -o -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
luks_passphrase_changed=""
else
luks_passphrase_changed="LUKS Disk Recovery Key passphrase:\n
$luks_new_Disk_Recovery_Key_passphrase"
fi
if [ "$CONFIG_TPM" = "y" ]; then
tpm_password_changed="
TPM Owner Password: $TPM_PASS\n"
else
tpm_password_changed=""
fi
## Show to user current provisioned secrets prior of rebooting
whiptail --msgbox "
TPM Owner Password: $TPM_PASS\n
$luks_passphrase_changed
$tpm_password_changed
GPG Admin PIN: $ADMIN_PIN\n
GPG User PIN: $USER_PIN\n\n" \
$HEIGHT $WIDTH --title "Provisioned secrets"
@ -574,4 +709,9 @@ whiptail --msgbox "
Press Enter to reboot.\n" \
$HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete"
# Clean LUKS secrets
luks_secrets_cleanup
unset luks_passphrase_changed
unset tpm_password_changed
reboot

175
initrd/bin/reencrypt-luks Executable file
View File

@ -0,0 +1,175 @@
#!/bin/sh
# Reencrypt LUKS container and change Disk Recovery Key associated passphrase (Slot 0: main slot)
. /etc/functions
. /etc/gui_functions
. /tmp/config
select_luks_container()
{
if [ -s /boot/kexec_key_devices.txt ]; then
LUKS=$(cut -d ' ' -f1 /boot/kexec_key_devices.txt)
else
#generate a list of devices to choose from that contain a LUKS header
lvm vgscan||true
blkid | cut -d ':' -f 1 | while read device;do cryptsetup isLuks $device;if [ $(echo $?) == 0 ]; then echo $device;fi; done | sort > /tmp/luks_devices.txt
if [ $(cat /tmp/luks_devices.txt | wc -l) -gt 0 ]; then
file_selector "/tmp/luks_devices.txt" "Select LUKS container device"
if [ "$FILE" == "" ]; then
return
else
LUKS=$FILE
detect_boot_device
mount -o remount,rw /boot
echo "$LUKS $(cryptsetup luksUUID $LUKS)" > /boot/kexec_key_devices.txt
mount -o remount,ro /boot
fi
else
warn "No encrypted device found."
fi
fi
}
test_luks_current_disk_recovery_key_passphrase()
{
while : ; do
select_luks_container
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then
#if no external provisioning provides current Disk Recovery Key passphrase
echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
cryptsetup luksOpen $LUKS test --key-file /tmp/luks_current_Disk_Recovery_Key_passphrase
else
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Testing opening "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..."
cryptsetup luksOpen $LUKS test --key-file /tmp/luks_current_Disk_Recovery_Key_passphrase
fi
#Validate past cryptsetup-reencrypt attempts
if [ $(echo $?) -ne 0 ]; then
whiptail --title 'Invalid Actual LUKS Disk Recovery Key passphrase?' --msgbox \
"If you previously changed it and do not remember it, you will have to\n reinstall OS from a an external drive.\n\nTo do so, place ISO file and its signature file on root of external drive,\n and select Options-> Boot from USB \n\nHit Enter to retry." 30 60
shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2> /dev/null
#unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round
unset luks_current_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select luks container.
#maybe the container was not the right one
detect_boot_device
mount -o remount,rw /boot
rm -f /boot/kexec_key_devices.txt
mount -o remount,ro /boot
else
#LuksOpen test was successful. Cleanup should be called only when done
#Exporting successfully used passphrase possibly reused by oem-factory-reset
#We close the volume
cryptsetup luksClose test
export luks_current_Disk_Recovery_Key_passphrase
break;
fi
done
}
luks_reencrypt(){
while : ; do
select_luks_container
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ]; then
#if no external provisioning provides current Disk Recovery Key passphrase
whiptail --title 'Reencrypt LUKS disk encrypted container ?' \
--msgbox "This will replace the encrypted container content and its Disk Recovery Key.\n\nThe passphrase associated with this key will be asked from the user in the\nfollowing conditions:\n 1-Every boot if no Disk unlock key was added to the TPM\n 2-If the TPM fails (Hardware failure)\n 3-If the firmware has been tampered with/upgraded/modified by the user\n\nThis process requires you to type the current Disk Recovery Key passphrase\nand will delete TPM Disk unlock key slot if setuped by setting a default boot\n LUKS header (slot 1) if present.\n\nAt the next prompt, you may be asked to select which file corresponds to\nthe LUKS device container.\n\nHit Enter to continue." 30 90
select_luks_container
echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
cryptsetup-reencrypt -B 64 --use-directio "$LUKS" --key-slot 0 --key-file /tmp/luks_current_Disk_Recovery_Key_passphrase
else
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Reencrypting "$LUKS" LUKS encrypted drive content with current Recovery Disk Key passphrase..."
cryptsetup-reencrypt -B 64 --use-directio "$LUKS" --key-slot 0 --key-file /tmp/luks_current_Disk_Recovery_Key_passphrase
fi
#Validate past cryptsetup-reencrypt attempts
if [ $(echo $?) -ne 0 ]; then
whiptail --title 'Invalid Actual LUKS Disk Recovery Key passphrase?' --msgbox \
"If you previously changed it and do not remember it, you will have to\n reinstall OS from a an external drive.\n\nTo do so, place ISO file and its signature file on root of external drive,\n and select Options-> Boot from USB \n\nHit Enter to retry." 30 60
shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2> /dev/null
#unsetting luks_current_Disk_Recovery_Key_passphrase so we prompt for it again Disk Recovery Key passphrase prompt on next round
unset luks_current_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select luks container.
#maybe the container was not the right one
detect_boot_device
mount -o remount,rw /boot
rm -f /boot/kexec_key_devices.txt
mount -o remount,ro /boot
else
#Reencryption was successful. Cleanup should be called only when done
#Exporting successfully used passphrase possibly reused by oem-factory-reset
export luks_current_Disk_Recovery_Key_passphrase
break;
fi
done
}
luks_change_passphrase()
{
while : ; do
select_luks_container
#if actual or new Disk Recovery Key is not provisioned by oem-provisioning file
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ] || [ -z "$luks_new_Disk_Recovery_Key_passphrase" ] ; then
whiptail --title 'Changing LUKS Disk Recovery Key passphrase' --msgbox \
"Please enter current Disk Recovery Key passphrase (slot 0).\nThen choose a strong passphrase of your own.\n\n**DICEWARE passphrase methodology is STRONGLY ADVISED.**\n\nHit Enter to continue" 30 60
if [ -z "$luks_new_Disk_Recovery_Key_passphrase" ] ; then
echo -e "\nEnter desired replacement for actual Disk Recovery Key passphrase (At least 8 characters long):"
while [[ ${#luks_new_Disk_Recovery_Key_passphrase} -lt 8 ]]; do
{
read -r luks_new_Disk_Recovery_Key_passphrase
};done
fi
if [ -z "$luks_current_Disk_Recovery_Key_passphrase" ];then
echo -e "\nEnter current Disk Recovery Key passphrase (Provisioned at OS installation or by OEM):"
read -r luks_current_Disk_Recovery_Key_passphrase
fi
export luks_current_Disk_Recovery_Key_passphrase
export luks_new_Disk_Recovery_Key_passphrase
echo -n "$luks_new_Disk_Recovery_Key_passphrase" > /tmp/luks_new_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Changing "$LUKS" LUKS encrypted disk passphrase to new Disk Recovery Key passphrase..."
cryptsetup luksChangeKey "$LUKS" --key-slot 0 --key-file=/tmp/luks_current_Disk_Recovery_Key_passphrase /tmp/luks_new_Disk_Recovery_Key_passphrase
else
#If current and new Disk Recovery Key were exported
echo -n "$luks_new_Disk_Recovery_Key_passphrase" > /tmp/luks_new_Disk_Recovery_Key_passphrase
echo -n "$luks_current_Disk_Recovery_Key_passphrase" > /tmp/luks_current_Disk_Recovery_Key_passphrase
warn "Changing "$LUKS" LUKS encrypted disk passphrase to new Disk Recovery Key passphrase..."
cryptsetup luksChangeKey "$LUKS" --key-slot 0 --key-file=/tmp/luks_current_Disk_Recovery_Key_passphrase /tmp/luks_new_Disk_Recovery_Key_passphrase
fi
#Validate past cryptsetup attempts
if [ $(echo $?) -ne 0 ]; then
#Cryptsetup luksChangeKey was unsuccessful
whiptail --title 'Invalid LUKS passphrase?' --msgbox \
"The LUKS Disk Recovery Key passphrase was provided to you by the OEM over\n secure communication channel.\n\nIf you previously changed it and do not remember it,\n you will have to reinstall OS from a USB drive.\nTo do so, put OS ISO file and it's signature file on root of USB drive,\n And select Boot from USB\n\nHit Enter to continue." 30 60
unset luks_current_Disk_Recovery_Key_passphrase
unset luks_new_Disk_Recovery_Key_passphrase
#remove "known good" selected luks container so that next pass asks again user to select LUKS container.
#maybe the container was not the right one
detect_boot_device
mount -o remount,rw /boot
rm -f /boot/kexec_key_devices.txt
mount -o remount,ro /boot
else
#Cryptsetup was successful.
#Cleanup should be called seperately.
#Exporting successfully used passphrase possibly reused by oem-factory-reset
export luks_new_Disk_Recovery_Key_passphrase
break;
fi
done
}
luks_secrets_cleanup()
{
#Cleanup
shred -n 10 -z -u /tmp/luks_new_Disk_Recovery_Key_passphrase 2> /dev/null || true
shred -n 10 -z -u /tmp/luks_current_Disk_Recovery_Key_passphrase 2> /dev/null || true
unset luks_current_Disk_Recovery_Key_passphrase
unset luks_new_Disk_Recovery_Key_passphrase
}