mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-21 05:53:14 +00:00
b500505312
Most logic throughout Heads doesn't need to know TPM1 versus TPM2 (and shouldn't, the differences should be localized). Some checks were incorrect and are fixed by this change. Most checks are now unchanged relative to master. There are not that many places outside of tpmr that need to differentiate TPM1 and TPM2. Some of those are duplicate code that should be consolidated (seal-hotpkey, unseal-totp, unseal-hotp), and some more are probably good candidates for abstracting in tpmr so the business logic doesn't have to know TPM1 vs. TPM2. Previously, CONFIG_TPM could be variously 'y', 'n', or empty. Now it is always 'y' or 'n', and 'y' means "any TPM". Board configs are unchanged, setting CONFIG_TPM2_TOOLS=y implies CONFIG_TPM=y so this doesn't have to be duplicated and can't be mistakenly mismatched. There were a few checks for CONFIG_TPM = n that only coincidentally worked for TPM2 because CONFIG_TPM was empty (not 'n'). This test is now OK, but the checks were also cleaned up to '!= "y"' for robustness. Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
755 lines
25 KiB
Bash
Executable File
755 lines
25 KiB
Bash
Executable File
#!/bin/bash
|
|
# Automated setup of TPM, GPG keys, and disk
|
|
|
|
TRACE "Under /bin/oem-factory-reset"
|
|
|
|
set -o pipefail
|
|
. /etc/functions
|
|
|
|
# use TERM to exit on error
|
|
trap "exit 1" TERM
|
|
export TOP_PID=$$
|
|
|
|
## Static local variables
|
|
|
|
CLEAR="--clear"
|
|
CONTINUE="--yes-button Continue"
|
|
CANCEL="--no-button Cancel"
|
|
HEIGHT="0"
|
|
WIDTH="80"
|
|
|
|
USER_PIN_DEF=123456
|
|
ADMIN_PIN_DEF=12345678
|
|
TPM_PASS_DEF=12345678
|
|
USER_PIN=""
|
|
ADMIN_PIN=""
|
|
TPM_PASS=""
|
|
|
|
#Circumvent Librem Key/Nitrokey HOTP firmware bug https://github.com/osresearch/heads/issues/1167
|
|
MAX_HOTP_GPG_PIN_LENGTH=25
|
|
|
|
# What are the Security components affected by custom passwords
|
|
CUSTOM_PASS_AFFECTED_COMPONENTS=""
|
|
|
|
RSA_KEY_LENGTH=3072
|
|
|
|
GPG_USER_NAME="OEM Key"
|
|
GPG_KEY_NAME=`date +%Y%m%d%H%M%S`
|
|
GPG_USER_MAIL="oem-${GPG_KEY_NAME}@example.com"
|
|
GPG_USER_COMMENT="OEM-generated key"
|
|
SKIP_BOOT="n"
|
|
|
|
## External files sourced
|
|
|
|
. /etc/functions
|
|
. /etc/luks-functions
|
|
. /tmp/config
|
|
|
|
## functions
|
|
|
|
die() {
|
|
|
|
local msg=$1
|
|
if [ -n "$msg" ]; then
|
|
echo -e "\n$msg"
|
|
fi
|
|
kill -s TERM $TOP_PID
|
|
exit 1
|
|
}
|
|
|
|
whiptail_error()
|
|
{
|
|
local msg=$1
|
|
if [ "$msg" = "" ]; then
|
|
die "whiptail error: An error msg is required"
|
|
fi
|
|
whiptail $BG_COLOR_ERROR --msgbox "${msg}\n\n" $HEIGHT $WIDTH $BG_COLOR_ERROR --title "Error"
|
|
}
|
|
|
|
whiptail_error_die()
|
|
{
|
|
whiptail_error "$@"
|
|
die
|
|
}
|
|
|
|
gpg_key_reset()
|
|
{
|
|
# Factory reset GPG card
|
|
{
|
|
echo admin
|
|
echo factory-reset
|
|
echo y
|
|
echo yes
|
|
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
|
|
> /tmp/gpg_card_edit_output 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
ERROR=`cat /tmp/gpg_card_edit_output`
|
|
whiptail_error_die "GPG Key factory reset failed!\n\n$ERROR"
|
|
fi
|
|
# If Nitrokey Storage is inserted, reset AES keys as well
|
|
if lsusb | grep -q "20a0:4109" && [ -x /bin/hotp_verification ] ; then
|
|
/bin/hotp_verification regenerate ${ADMIN_PIN_DEF}
|
|
fi
|
|
# Toggle forced sig (good security practice, forcing PIN request for each signature request)
|
|
if gpg --card-status | grep "Signature PIN" | grep -q "not forced"; then
|
|
{
|
|
echo admin
|
|
echo forcesig
|
|
echo ${ADMIN_PIN_DEF}
|
|
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
|
|
> /tmp/gpg_card_edit_output 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
ERROR=`cat /tmp/gpg_card_edit_output`
|
|
whiptail_error_die "GPG Key forcesig toggle on failed!\n\n$ERROR"
|
|
fi
|
|
fi
|
|
# Set RSA key length
|
|
{
|
|
echo admin
|
|
echo key-attr
|
|
echo 1 # RSA
|
|
echo ${RSA_KEY_LENGTH} #Signing key size set to RSA_KEY_LENGTH
|
|
echo ${ADMIN_PIN_DEF}
|
|
echo 1 # RSA
|
|
echo ${RSA_KEY_LENGTH} #Encryption key size set to RSA_KEY_LENGTH
|
|
echo ${ADMIN_PIN_DEF}
|
|
echo 1 # RSA
|
|
echo ${RSA_KEY_LENGTH} #Authentication key size set to RSA_KEY_LENGTH
|
|
echo ${ADMIN_PIN_DEF}
|
|
} | gpg --command-fd=0 --status-fd=1 --pinentry-mode=loopback --card-edit \
|
|
> /tmp/gpg_card_edit_output 2>/dev/null
|
|
if [ $? -ne 0 ]; then
|
|
ERROR=`cat /tmp/gpg_card_edit_output`
|
|
whiptail_error_die "Setting key attributed to RSA ${RSA_KEY_LENGTH} bits in USB security dongle failed."
|
|
fi
|
|
# Generate OEM GPG keys
|
|
{
|
|
echo admin
|
|
echo generate
|
|
echo n
|
|
echo ${ADMIN_PIN_DEF}
|
|
echo ${USER_PIN_DEF}
|
|
echo 0
|
|
echo y
|
|
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
|
|
if [ $? -ne 0 ]; then
|
|
ERROR=`cat /tmp/gpg_card_edit_output`
|
|
whiptail_error_die "GPG Key automatic keygen failed!\n\n$ERROR"
|
|
fi
|
|
}
|
|
|
|
gpg_key_change_pin()
|
|
{
|
|
# 1 = user PIN, 3 = admin PIN
|
|
PIN_TYPE=$1
|
|
PIN_ORIG=$2
|
|
PIN_NEW=$3
|
|
# Change PIN
|
|
{
|
|
echo admin
|
|
echo passwd
|
|
echo ${PIN_TYPE}
|
|
echo ${PIN_ORIG}
|
|
echo ${PIN_NEW}
|
|
echo ${PIN_NEW}
|
|
echo q
|
|
echo q
|
|
} | gpg --command-fd=0 --status-fd=2 --pinentry-mode=loopback --card-edit \
|
|
> /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"
|
|
fi
|
|
}
|
|
|
|
generate_checksums()
|
|
{
|
|
# ensure /boot mounted
|
|
if ! grep -q /boot /proc/mounts ; then
|
|
mount -o rw /boot || whiptail_error_die "Unable to mount /boot"
|
|
else
|
|
mount -o remount,rw /boot || whiptail_error_die "Unable to mount /boot"
|
|
fi
|
|
|
|
#Check if previous TPM Disk unlock Key was set
|
|
if [ -e /boot/kexec_key_devices.txt ]; then
|
|
TPM_DISK_ENCRYPTION_KEY_SET=1
|
|
fi
|
|
|
|
# clear any existing checksums/signatures
|
|
rm /boot/kexec* 2>/dev/null
|
|
|
|
# create Heads TPM 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"
|
|
|
|
# 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
|
|
set_default_boot_option
|
|
fi
|
|
|
|
# generate hashes
|
|
(
|
|
set -e -o pipefail
|
|
cd /boot
|
|
find ./ -type f ! -path './kexec*' -print0 \
|
|
| xargs -0 sha256sum > /boot/kexec_hashes.txt 2>/dev/null
|
|
print_tree > /boot/kexec_tree.txt
|
|
)
|
|
[ $? -eq 0 ] || whiptail_error_die "Error generating kexec hashes"
|
|
|
|
param_files=`find /boot/kexec*.txt`
|
|
[ -z "$param_files" ] \
|
|
&& whiptail_error_die "No kexec parameter files to sign"
|
|
|
|
# sign kexec boot files
|
|
if sha256sum $param_files 2>/dev/null | gpg \
|
|
--pinentry-mode loopback \
|
|
--passphrase "$USER_PIN" \
|
|
--digest-algo SHA256 \
|
|
--detach-sign \
|
|
-a \
|
|
> /boot/kexec.sig 2>/tmp/error; then
|
|
# successful - update the validated params
|
|
if ! check_config /boot >/dev/null 2>/tmp/error ; then
|
|
cat /tmp/error
|
|
ret=1
|
|
else
|
|
ret=0
|
|
fi
|
|
else
|
|
cat /tmp/error
|
|
ret=1
|
|
fi
|
|
|
|
# done writing to /boot, switch back to RO
|
|
mount -o ro,remount /boot
|
|
|
|
if [ $ret = 1 ] ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error signing kexec boot files:\n\n$ERROR"
|
|
fi
|
|
}
|
|
|
|
set_default_boot_option()
|
|
{
|
|
option_file="/tmp/kexec_options.txt"
|
|
tmp_menu_file="/tmp/kexec/kexec_menu.txt"
|
|
hash_file="/boot/kexec_default_hashes.txt"
|
|
|
|
mkdir -p /tmp/kexec/
|
|
rm $option_file 2>/dev/null
|
|
# parse boot options from grub.cfg
|
|
for i in `find /boot -name "grub.cfg"`; do
|
|
kexec-parse-boot "/boot" "$i" >> $option_file
|
|
done
|
|
# FC29/30+ may use BLS format grub config files
|
|
# https://fedoraproject.org/wiki/Changes/BootLoaderSpecByDefault
|
|
# only parse these if $option_file is still empty
|
|
if [ ! -s $option_file ] && [ -d "/boot/loader/entries" ]; then
|
|
for i in `find /boot -name "grub.cfg"`; do
|
|
kexec-parse-bls "/boot" "$i" "/boot/loader/entries" >> $option_file
|
|
done
|
|
fi
|
|
[ ! -s $option_file ] \
|
|
&& whiptail_error_die "Failed to parse any boot options"
|
|
|
|
# sort boot options
|
|
sort -r $option_file | uniq > $tmp_menu_file
|
|
|
|
## save first option as default
|
|
entry=`head -n 1 $tmp_menu_file | tail -1`
|
|
|
|
# clear existing default configs
|
|
rm "/boot/kexec_default.*.txt" 2>/dev/null
|
|
|
|
# get correct index for entry
|
|
index=$(grep -n "$entry" $option_file | cut -f1 -d ':')
|
|
|
|
# write new config
|
|
echo "$entry" > /boot/kexec_default.$index.txt
|
|
|
|
# validate boot option
|
|
( cd /boot && /bin/kexec-boot -b "/boot" -e "$entry" -f \
|
|
| xargs sha256sum > $hash_file 2>/dev/null ) \
|
|
|| 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" != "y" ]; 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 --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." 0 80
|
|
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"
|
|
else
|
|
HASH="ALTERED"
|
|
fi
|
|
|
|
#Show results
|
|
whiptail $MAIN_MENU_BG_COLOR --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" 0 80
|
|
fi
|
|
}
|
|
|
|
|
|
## main script start
|
|
|
|
# check for args
|
|
if [ "$1" != "" ]; then
|
|
title_text=$1
|
|
else
|
|
title_text="OEM Factory Reset / Re-Ownership"
|
|
fi
|
|
if [ "$2" != "" ]; then
|
|
bg_color=$2
|
|
else
|
|
bg_color=""
|
|
fi
|
|
|
|
# show warning prompt
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
TPM_STR=" * ERASE the TPM and own it with a password\n"
|
|
else
|
|
TPM_STR=""
|
|
fi
|
|
if ! whiptail --yesno "
|
|
This operation will automatically:\n
|
|
$TPM_STR
|
|
* ERASE any keys or passwords on the GPG smart card,\n
|
|
reset it to a factory state, generate new keys\n
|
|
and optionally set custom PIN(s)\n
|
|
* Add the new GPG key to the firmware and reflash it\n
|
|
* Sign all of the files in /boot with the new GPG key\n\n
|
|
It requires that you already have an OS installed on a\n
|
|
dedicated /boot partition. Do you wish to continue?" \
|
|
$HEIGHT $WIDTH $CONTINUE $CANCEL $CLEAR $bg_color --title "$title_text" ; then
|
|
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
|
|
luks_new_Disk_Recovery_Key_desired=1
|
|
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 previously stated security components? [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ] \
|
|
; then
|
|
echo -e "\nThe chosen custom password must be between 8 and $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
|
|
echo
|
|
while [[ ${#CUSTOM_SINGLE_PASS} -lt 8 ]] || [[ ${#CUSTOM_SINGLE_PASS} -gt $MAX_HOTP_GPG_PIN_LENGTH ]] ; do
|
|
echo -e -n "Enter the custom password: "
|
|
read CUSTOM_SINGLE_PASS
|
|
done
|
|
echo
|
|
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 previously stated security components? [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ] \
|
|
; then
|
|
echo -e "\nThey must be each at least 8 characters in length.\n"
|
|
echo
|
|
if [ "$CONFIG_TPM" = "y" ]; then
|
|
while [[ ${#TPM_PASS} -lt 8 ]] ; do
|
|
echo -e -n "Enter desired TPM Ownership password: "
|
|
read TPM_PASS
|
|
done
|
|
fi
|
|
while [[ ${#ADMIN_PIN} -lt 8 ]] || [[ ${#ADMIN_PIN} -gt $MAX_HOTP_GPG_PIN_LENGTH ]] ; do
|
|
echo -e -n "\nThis PIN should be between 8 to $MAX_HOTP_GPG_PIN_LENGTH characters in length.\n"
|
|
echo -e -n "Enter desired GPG Admin PIN: "
|
|
read ADMIN_PIN
|
|
done
|
|
while [[ ${#USER_PIN} -lt 8 ]] || [[ ${#USER_PIN} -gt 64 ]]; do
|
|
echo -e -n "\nThis PIN should be between 8 to 64 characters in length.\n"
|
|
echo -e -n "Enter desired GPG User PIN: "
|
|
read USER_PIN
|
|
done
|
|
echo
|
|
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
|
|
if [ "$ADMIN_PIN" == "" ]; then ADMIN_PIN=$ADMIN_PIN_DEF; fi
|
|
|
|
# Prompt to change default GnuPG key information
|
|
echo -e -n "Would you like to set custom user information for the GnuPG key? [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ] \
|
|
; then
|
|
echo -e "\n\n"
|
|
echo -e "We will generate a GnuPG (PGP) keypair identifiable with the following text form:"
|
|
echo -e "Real Name (Comment) email@address.org"
|
|
|
|
echo -e "\nEnter your Real Name (At least 5 characters long):"
|
|
read -r GPG_USER_NAME
|
|
while [[ ${#GPG_USER_NAME} -lt 5 ]]; do
|
|
{
|
|
echo -e "\nEnter your Real Name (At least 5 characters long):"
|
|
read -r GPG_USER_NAME
|
|
};done
|
|
|
|
echo -e "\nEnter your email@adress.org:"
|
|
read -r GPG_USER_MAIL
|
|
while ! $(expr "$GPG_USER_MAIL" : '.*@' >/dev/null); do
|
|
{
|
|
echo -e "\nEnter your email@address.org:"
|
|
read -r GPG_USER_MAIL
|
|
};done
|
|
|
|
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
|
|
read -r GPG_USER_COMMENT
|
|
while [[ ${#GPG_USER_COMMENT} -gt 60 ]]; do
|
|
{
|
|
echo -e "\nEnter Comment (Optional, to distinguish this key from others with same previous attributes. Must be smaller then 60 characters):"
|
|
read -r GPG_USER_COMMENT
|
|
};done
|
|
fi
|
|
|
|
## sanity check the USB, GPG key, and boot device before proceeding further
|
|
|
|
# Prompt to insert USB drive if desired
|
|
echo -e -n "Would you like to export your public key to an USB drive? [y/N]: "
|
|
read -n 1 prompt_output
|
|
echo
|
|
if [ "$prompt_output" == "y" \
|
|
-o "$prompt_output" == "Y" ] \
|
|
; then
|
|
GPG_EXPORT=1
|
|
# mount USB over /media only if not already mounted
|
|
if ! grep -q /media /proc/mounts ; then
|
|
# mount USB in rw
|
|
if ! mount-usb rw 2>/tmp/error; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Unable to mount USB on /media:\n\n${ERROR}"
|
|
fi
|
|
else
|
|
#/media already mounted, make sure it is in r+w mode
|
|
if ! mount -o remount,rw /media 2>/tmp/error; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Unable to remount in read+write USB on /media:\n\n${ERROR}"
|
|
fi
|
|
fi
|
|
else
|
|
GPG_EXPORT=0
|
|
# needed for USB Security dongle below and is ensured via mount-usb in case of GPG_EXPORT=1
|
|
enable_usb
|
|
fi
|
|
|
|
# ensure USB Security Dongle connected
|
|
echo -e "\nChecking for USB Security Dongle...\n"
|
|
# USB kernel modules already loaded via mount-usb
|
|
if ! gpg --card-status >/dev/null 2>&1 ; then
|
|
whiptail_error "Can't access USB Security Dongle; \nPlease remove and reinsert, then press Enter."
|
|
if ! gpg --card-status >/dev/null 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Unable to detect USB Security Dongle:\n\n${ERROR}"
|
|
fi
|
|
fi
|
|
|
|
assert_signable
|
|
|
|
# Action time...
|
|
|
|
# detect and set /boot device
|
|
echo -e "\nDetecting and setting boot device...\n"
|
|
if ! detect_boot_device ; then
|
|
SKIP_BOOT="y"
|
|
else
|
|
echo -e "Boot device set to $CONFIG_BOOT_DEV\n"
|
|
fi
|
|
|
|
# update configs
|
|
if [[ "$SKIP_BOOT" == "n" ]]; then
|
|
replace_config /etc/config.user "CONFIG_BOOT_DEV" "$CONFIG_BOOT_DEV"
|
|
combine_configs
|
|
fi
|
|
|
|
|
|
if [ -n "$luks_new_Disk_Recovery_Key_desired" -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_new_Disk_Recovery_Key_desired" -a -z "$luks_new_Disk_Recovery_Key_passphrase_desired" ]; then
|
|
#Reencryption of disk was requested but not passphrase change
|
|
luks_reencrypt
|
|
elif [ -z "$luks_new_Disk_Recovery_Key_desired" -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"
|
|
{
|
|
echo $TPM_PASS
|
|
echo $TPM_PASS
|
|
} | /bin/tpm-reset >/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
|
|
rm /.gnupg/*.gpg 2>/dev/null
|
|
rm /.gnupg/*.kbx 2>/dev/null
|
|
gpg --list-keys >/dev/null 2>&1
|
|
|
|
## reset the GPG Key
|
|
echo -e "\nResetting GPG Key...\n(this will take around 3 minutes...)\n"
|
|
gpg_key_reset
|
|
|
|
# parse name of generated key
|
|
GPG_GEN_KEY=`grep -A1 pub /tmp/gpg_card_edit_output | tail -n1 | sed -nr 's/^([ ])*//p'`
|
|
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
|
|
|
|
#Applying custom GPG PINs
|
|
if [ "$USER_PIN" != "" -o "$ADMIN_PIN" != "" ]; then
|
|
echo -e "\nChanging default GPG Admin PIN\n"
|
|
gpg_key_change_pin "3" "$ADMIN_PIN_DEF" "$ADMIN_PIN"
|
|
echo -e "\nChanging default GPG User PIN\n"
|
|
gpg_key_change_pin "1" "$USER_PIN_DEF" "$USER_PIN"
|
|
fi
|
|
|
|
# export pubkey to file
|
|
if ! gpg --export --armor $GPG_GEN_KEY > "${PUBKEY}" 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "GPG Key gpg export to file failed!\n\n$ERROR"
|
|
fi
|
|
|
|
## export pubkey to USB
|
|
if [ $GPG_EXPORT -ne 0 ]; then
|
|
echo -e "\nExporting generated key to USB...\n"
|
|
# copy to USB
|
|
if ! cp "${PUBKEY}" "/media/${GPG_GEN_KEY}.asc" 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Key export error: unable to copy ${GPG_GEN_KEY}.asc to /media:\n\n$ERROR"
|
|
fi
|
|
mount -o remount,ro /media 2>/dev/null
|
|
fi
|
|
|
|
## flash generated key to ROM
|
|
echo -e "\nReading current firmware...\n(this will take a minute or two)\n"
|
|
/bin/flash.sh -r /tmp/oem-setup.rom >/dev/null 2>/tmp/error
|
|
if [ ! -s /tmp/oem-setup.rom ]; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error reading current firmware:\n\n$ERROR"
|
|
fi
|
|
|
|
# ensure key imported locally
|
|
if ! cat "$PUBKEY" | gpg --import >/dev/null 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error importing GPG key:\n\n$ERROR"
|
|
fi
|
|
# update /.gnupg/trustdb.gpg to ultimately trust all user provided public keys
|
|
if ! gpg --list-keys --fingerprint --with-colons 2>/dev/null \
|
|
| sed -E -n -e 's/^fpr:::::::::([0-9A-F]+):$/\1:6:/p' \
|
|
| gpg --import-ownertrust >/dev/null 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error importing GPG ownertrust:\n\n$ERROR"
|
|
fi
|
|
if ! gpg --update-trust >/dev/null 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR"
|
|
fi
|
|
# clear any existing heads/gpg files from current firmware
|
|
for i in `cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"`; do
|
|
cbfs.sh -o /tmp/oem-setup.rom -d $i
|
|
done
|
|
# add heads/gpg files to current firmware
|
|
if [ -e /.gnupg/pubring.kbx ];then
|
|
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx
|
|
if [ -e /.gnupg/pubring.gpg ];then
|
|
rm /.gnupg/pubring.gpg
|
|
fi
|
|
elif [ -e /.gnupg/pubring.gpg ];then
|
|
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.gpg" -f /.gnupg/pubring.gpg
|
|
fi
|
|
if [ -e /.gnupg/trustdb.gpg ]; then
|
|
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg
|
|
fi
|
|
# persist user config changes (boot device)
|
|
if [ -e /etc/config.user ]; then
|
|
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user
|
|
fi
|
|
# flash updated firmware image
|
|
echo -e "\nAdding generated key to current firmware and re-flashing...\n"
|
|
if ! /bin/flash.sh /tmp/oem-setup.rom >/dev/null 2>/tmp/error ; then
|
|
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
|
whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR"
|
|
fi
|
|
|
|
## sign files in /boot and generate checksums
|
|
if [[ "$SKIP_BOOT" == "n" ]]; then
|
|
echo -e "\nSigning boot files and generating checksums...\n"
|
|
generate_checksums
|
|
fi
|
|
|
|
# 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 "
|
|
$luks_passphrase_changed
|
|
$tpm_password_changed
|
|
GPG Admin PIN: $ADMIN_PIN\n
|
|
GPG User PIN: $USER_PIN\n\n" \
|
|
$HEIGHT $WIDTH --title "Provisioned secrets"
|
|
|
|
## all done -- reboot
|
|
whiptail --msgbox "
|
|
OEM Factory Reset / Re-Ownership has completed successfully\n\n
|
|
After rebooting, you will need to generate new TOTP/HOTP secrets\n
|
|
when prompted in order to complete the setup process.\n\n
|
|
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
|