mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-19 21:17:55 +00:00
Wip: now supports both backup and copy to card and gpg_auth when backup exists. Might want to discuss that implementation. Some functions needed to be moved from functions to ash_functions so that gpg_auth can be called from recovery function. That might need to be discussed as well, recovery could be moved from ash_functions to functions instead.
Signed-off-by: Thierry Laurion <insurgo@riseup.net>
This commit is contained in:
parent
b1e5c638cd
commit
2c55338be5
@ -184,7 +184,7 @@ if cryptsetup isLuks "$USB_MOUNT_DEVICE"; then
|
||||
|| die "ERROR: Failed to open ${USB_MOUNT_DEVICE} LUKS device"
|
||||
fi
|
||||
|
||||
warn "Note that you cannot boot from a mounted encrypted device."
|
||||
warn "Note that you cannot boot from a mounted encrypted device"
|
||||
DEBUG "Setting USB_MOUNT_DEVICE=/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
|
||||
USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
|
||||
else
|
||||
|
@ -940,6 +940,14 @@ assert_signable
|
||||
|
||||
# Action time...
|
||||
|
||||
#TODO: Should we replace text from "Add a new GPG key" to "Replace current GPG key"? Should we wipe current keyring?
|
||||
#Current logic is for factory reset, where re-ownership adds key to the keyring which is then copied over cbfs.
|
||||
# In the all case, we should wipe the keyring since otherwise, USB security dongle is wiped but not the keyring which exposes past public keys
|
||||
# this seems wrong
|
||||
# clear local keyring
|
||||
rm /.gnupg/* | true
|
||||
|
||||
|
||||
# detect and set /boot device
|
||||
echo -e "\nDetecting and setting boot device...\n"
|
||||
if ! detect_boot_device; then
|
||||
@ -989,7 +997,7 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" == "1" ]; then
|
||||
#TODO seperate wiping and thumb drive functions with proper validation
|
||||
wipe_thumb_drive_and_copy_gpg_key_material
|
||||
#TODO seperate setting config
|
||||
set_user_config CONFIG_HAVE_GPG_KEY_BACKUP Y
|
||||
set_user_config CONFIG_HAVE_GPG_KEY_BACKUP y
|
||||
gpg_key_factory_reset
|
||||
keytocard_subkeys_to_smartcard
|
||||
else
|
||||
@ -1000,10 +1008,21 @@ else
|
||||
generate_OEM_gpg_keys
|
||||
fi
|
||||
|
||||
# parse name of generated key
|
||||
GPG_GEN_KEY=$(grep -A1 pub /tmp/gpg_card_edit_output | tail -n1 | sed -nr 's/^([ ])*//p')
|
||||
# Obtain GPG key ID
|
||||
GPG_GEN_KEY=$(gpg --list-keys --with-colons | grep "^fpr" | cut -d: -f10 | head -n1)
|
||||
#Where to export the public key
|
||||
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
|
||||
|
||||
DEBUG "GPG_GEN_KEY: $GPG_GEN_KEY"
|
||||
DEBUG "PUBKEY: $PUBKEY"
|
||||
|
||||
# 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
|
||||
|
||||
|
||||
#Applying custom GPG PINs if keys were not generated in memory
|
||||
if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then
|
||||
if [ "$USER_PIN" != "" -o "$ADMIN_PIN" != "" ]; then
|
||||
@ -1013,12 +1032,6 @@ if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then
|
||||
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"
|
||||
@ -1055,11 +1068,13 @@ 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
|
||||
@ -1071,13 +1086,15 @@ 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
|
||||
if ! /bin/flash.sh /tmp/oem-setup.rom 2>/tmp/error; then
|
||||
ERROR=$(tail -n 1 /tmp/error | fold -s)
|
||||
whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR"
|
||||
fi
|
||||
|
@ -53,9 +53,102 @@ preserve_rom() {
|
||||
done
|
||||
}
|
||||
|
||||
confirm_gpg_card() {
|
||||
TRACE "Under /etc/ash_functions:confirm_gpg_card"
|
||||
#Skip prompts if we are currently using a known GPG key material Thumb drive backup and keys are unlocked pinentry
|
||||
#TODO: probably export CONFIG_GPG_KEY_BACKUP_IN_USE but not under /etc/user.config?
|
||||
#Toggle to come in next PR, but currently we don't have a way to toggle it back to n if config.user flashed back in rom
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]]; then
|
||||
DEBUG "Using known GPG key material Thumb drive backup and keys are unlocked and useable through pinentry"
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" ]; then
|
||||
message="Please confirm that your GPG card is inserted(Y/n) or your GPG key material (b)backup thumbdrive is inserted [Y/n/b]: "
|
||||
else
|
||||
# Generic message if no known key material backup
|
||||
message="Please confirm that your GPG card is inserted [Y/n]: "
|
||||
fi
|
||||
|
||||
read \
|
||||
-n 1 \
|
||||
-p "$message" \
|
||||
card_confirm
|
||||
echo
|
||||
|
||||
if [ "$card_confirm" != "y" \
|
||||
-a "$card_confirm" != "Y" \
|
||||
-a "$card_confirm" != "b" \
|
||||
-a -n "$card_confirm" ] \
|
||||
; then
|
||||
die "gpg card not confirmed"
|
||||
fi
|
||||
|
||||
# If user has known GPG key material Thumb drive backup and asked to use it
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$card_confirm" == "b" ]]; then
|
||||
#Only mount and import GPG key material thumb drive backup once
|
||||
if [ ! "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]; then
|
||||
CR_NONCE="/tmp/secret/cr_nonce"
|
||||
CR_SIG="$CR_NONCE.sig"
|
||||
|
||||
#Wipe any previous CR_NONCE and CR_SIG
|
||||
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
|
||||
|
||||
#Prompt user for provisioned GPG Admin PIN that will be passed along to mount-usb and to import gpg subkeys
|
||||
echo
|
||||
read -s -p "Please enter GPG Admin PIN needed to use the GPG backup thumb drive: " gpg_admin_pin
|
||||
#prompt user to select the proper encrypted partition, which should the first one on next prompt
|
||||
echo -e "Please select encrypted LUKS container partition (not the public one)\n"
|
||||
mount-usb --pass "$gpg_admin_pin" || die "Unable to mount USB with GPG Admin PIN"
|
||||
warn "Testing detach-sign operation and verifiying against fused public key in ROM"
|
||||
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --import /media/subkeys.sec >/dev/null 2>&1 ||
|
||||
die "Unable to import GPG private subkeys"
|
||||
#Do a detach signature to ensure gpg material is usable and cache passphrase to sign /boot from caller functions
|
||||
dd if=/dev/urandom of="$CR_NONCE" bs=20 count=1 >/dev/null 2>&1 ||
|
||||
die "Unable to create $CR_NONCE to be signed with GPG private signing subkey"
|
||||
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --detach-sign "$CR_NONCE" >/dev/null 2>&1 ||
|
||||
die "Unable to sign $CR_NONCE with GPG private signing subkey using GPG Admin PIN"
|
||||
#verify detached signature against public key in rom
|
||||
gpg --verify "$CR_SIG" "$CR_NONCE" || die "Unable to verify $CR_SIG detached signature against public key in ROM"
|
||||
#Wipe any previous CR_NONCE and CR_SIG
|
||||
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
|
||||
#TODO: maybe just an export instead of setting /etc/user.config otherwise could be flashed in weird corner case situation
|
||||
set_user_config "CONFIG_GPG_KEY_BACKUP_IN_USE" "y"
|
||||
umount /media || die "Unable to unmount USB"
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# setup the USB so we can reach the GPG card
|
||||
enable_usb
|
||||
|
||||
echo -e "\nVerifying presence of GPG card...\n"
|
||||
# ensure we don't exit without retrying
|
||||
errexit=$(set -o | grep errexit | awk '{print $2}')
|
||||
set +e
|
||||
gpg --card-status >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
# prompt for reinsertion and try a second time
|
||||
read -n1 -r -p \
|
||||
"Can't access GPG key; remove and reinsert, then press Enter to retry. " \
|
||||
ignored
|
||||
# restore prev errexit state
|
||||
if [ "$errexit" = "on" ]; then
|
||||
set -e
|
||||
fi
|
||||
# retry card status
|
||||
gpg --card-status >/dev/null ||
|
||||
die "gpg card read failed"
|
||||
fi
|
||||
# restore prev errexit state
|
||||
if [ "$errexit" = "on" ]; then
|
||||
set -e
|
||||
fi
|
||||
}
|
||||
|
||||
gpg_auth() {
|
||||
TRACE "Under /etc/ash_functions:gpg_auth"
|
||||
if [ "$CONFIG_HAVE_GPG_KEY_BACKUP" = "y" ]; then
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" ]]; then
|
||||
TRACE "Under /etc/ash_functions:gpg_auth"
|
||||
# If we have a GPG key backup, we can use it to authenticate even if the card is lost
|
||||
echo >&2 "!!!!! Please authenticate with OpenPGP card/backup media to prove you are the owner of this machine !!!!!"
|
||||
|
||||
@ -88,9 +181,11 @@ gpg_auth() {
|
||||
&& gpgv "$CR_SIG" "$CR_NONCE" \
|
||||
; then
|
||||
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true
|
||||
DEBUG "Under /etc/ash_functions:gpg_auth: success"
|
||||
return 0
|
||||
else
|
||||
shred -n 10 -z -u "$CR_SIG" 2>/dev/null || true
|
||||
echo >&2 "!!!!! GPG authentication failed, please try again !!!!!"
|
||||
continue
|
||||
fi
|
||||
done
|
||||
@ -125,12 +220,12 @@ recovery() {
|
||||
fi
|
||||
while [ true ]
|
||||
do
|
||||
#Going to recovery shell should be authenticated if supported
|
||||
gpg_auth
|
||||
|
||||
echo >&2 "!!!!! Starting recovery shell"
|
||||
sleep 1
|
||||
|
||||
#Going to recovery shell should be authenticated if supported
|
||||
gpg_auth
|
||||
|
||||
if [ -x /bin/setsid ]; then
|
||||
/bin/setsid -c /bin/sh
|
||||
else
|
||||
@ -150,6 +245,57 @@ combine_configs() {
|
||||
cat /etc/config* > /tmp/config
|
||||
}
|
||||
|
||||
replace_config() {
|
||||
TRACE "Under /etc/functions:replace_config"
|
||||
CONFIG_FILE=$1
|
||||
CONFIG_OPTION=$2
|
||||
NEW_SETTING=$3
|
||||
|
||||
touch $CONFIG_FILE
|
||||
# first pull out the existing option from the global config and place in a tmp file
|
||||
awk "gsub(\"^export ${CONFIG_OPTION}=.*\",\"export ${CONFIG_OPTION}=\\\"${NEW_SETTING}\\\"\")" /tmp/config >${CONFIG_FILE}.tmp
|
||||
awk "gsub(\"^${CONFIG_OPTION}=.*\",\"${CONFIG_OPTION}=\\\"${NEW_SETTING}\\\"\")" /tmp/config >>${CONFIG_FILE}.tmp
|
||||
|
||||
# then copy any remaining settings from the existing config file, minus the option you changed
|
||||
grep -v "^export ${CONFIG_OPTION}=" ${CONFIG_FILE} | grep -v "^${CONFIG_OPTION}=" >>${CONFIG_FILE}.tmp || true
|
||||
sort ${CONFIG_FILE}.tmp | uniq >${CONFIG_FILE}
|
||||
rm -f ${CONFIG_FILE}.tmp
|
||||
}
|
||||
|
||||
# Set a config variable in a specific file to a given value - replace it if it
|
||||
# exists, or add it. If added, the variable will be exported.
|
||||
set_config() {
|
||||
CONFIG_FILE="$1"
|
||||
CONFIG_OPTION="$2"
|
||||
NEW_SETTING="$3"
|
||||
|
||||
if grep -q "$CONFIG_OPTION" "$CONFIG_FILE"; then
|
||||
replace_config "$CONFIG_FILE" "$CONFIG_OPTION" "$NEW_SETTING"
|
||||
else
|
||||
echo "export $CONFIG_OPTION=\"$NEW_SETTING\"" >>"$CONFIG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
# Set a value in config.user, re-combine configs, and update configs in the
|
||||
# environment.
|
||||
set_user_config() {
|
||||
CONFIG_OPTION="$1"
|
||||
NEW_SETTING="$2"
|
||||
|
||||
set_config /etc/config.user "$CONFIG_OPTION" "$NEW_SETTING"
|
||||
combine_configs
|
||||
. /tmp/config
|
||||
}
|
||||
|
||||
# Load a config value to a variable, defaulting to empty. Does not fail if the
|
||||
# config is not set (since it would expand to empty by default).
|
||||
load_config_value() {
|
||||
local config_name="$1"
|
||||
if grep -q "$config_name=" /tmp/config; then
|
||||
grep "$config_name=" /tmp/config | tail -n1 | cut -f2 -d '=' | tr -d '"'
|
||||
fi
|
||||
}
|
||||
|
||||
enable_usb()
|
||||
{
|
||||
TRACE "Under /etc/ash_functions:enable_usb"
|
||||
|
@ -189,102 +189,6 @@ list_usb_storage() {
|
||||
done
|
||||
}
|
||||
|
||||
confirm_gpg_card() {
|
||||
TRACE "Under /etc/functions:confirm_gpg_card"
|
||||
#Skip prompts if we are currently using a known GPG key material Thumb drive backup and keys are unlocked pinentry
|
||||
#TODO: probably export CONFIG_GPG_KEY_BACKUP_IN_USE but not under /etc/user.config?
|
||||
#Toggle to come in next PR, but currently we don't have a way to toggle it back to n if config.user flashed back in rom
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" ]; then
|
||||
message="Please confirm that your GPG card is inserted(Y/n) or your GPG key material (b)backup thumbdrive is inserted [Y/n/b]: "
|
||||
else
|
||||
# Generic message if no known key material backup
|
||||
message+="Please confirm that your GPG card is inserted [Y/n]: "
|
||||
fi
|
||||
|
||||
read \
|
||||
-n 1 \
|
||||
-p "$message" \
|
||||
card_confirm
|
||||
echo
|
||||
|
||||
if [ "$card_confirm" != "y" \
|
||||
-a "$card_confirm" != "Y" \
|
||||
-a "$card_confirm" != "b" \
|
||||
-a -n "$card_confirm" ] \
|
||||
; then
|
||||
die "gpg card not confirmed"
|
||||
fi
|
||||
|
||||
# If user has known GPG key material Thumb drive backup and asked to use it
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$card_confirm" == "b" ]]; then
|
||||
#Only mount and import GPG key material thumb drive backup once
|
||||
if [ ! "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]; then
|
||||
CR_NONCE="/tmp/secret/cr_nonce"
|
||||
CR_SIG="$CR_NONCE.sig"
|
||||
|
||||
#Wipe any previous CR_NONCE and CR_SIG
|
||||
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
|
||||
|
||||
#Prompt user for provisioned GPG Admin PIN that will be passed along to mount-usb and to import gpg subkeys
|
||||
echo
|
||||
read -s -p "Please enter GPG Admin PIN needed to use the GPG backup thumb drive: " gpg_admin_pin
|
||||
#prompt user to select the proper encrypted partition, which should the first one on next prompt
|
||||
echo -e "Please select encrypted LUKS container partition (not the public one)\n"
|
||||
mount-usb --pass "$gpg_admin_pin" || die "Unable to mount USB with GPG Admin PIN"
|
||||
warn "Testing detach-sign operation and verifiying against fused public key in ROM..."
|
||||
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --import /media/subkeys.sec >/dev/null 2>&1 ||
|
||||
die "Unable to import GPG private subkeys"
|
||||
#Do a detach signature to ensure gpg material is usable and cache passphrase to sign /boot from caller functions
|
||||
dd if=/dev/urandom of="$CR_NONCE" bs=20 count=1 >/dev/null 2>&1 ||
|
||||
die "Unable to create dummy file to sign"
|
||||
gpg --pinentry-mode=loopback --passphrase-file <(echo -n "${gpg_admin_pin}") --detach-sign "$CR_NONCE" >/dev/null 2>&1 ||
|
||||
die "Unable to sign dummy file with GPG private signing subkey"
|
||||
#verify detached signature against public key in rom
|
||||
gpg --verify "$CR_SIG" "$CR_NONCE" || die "Unable to verify dummy file with GPG public key in ROM: public key mismatch"
|
||||
#Wipe any previous CR_NONCE and CR_SIG
|
||||
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" >/dev/null 2>&1 || true
|
||||
#TODO: maybe just an export instead of setting /etc/user.config otherwise could be flashed in weird corner case situation
|
||||
set_user_config CONFIG_GPG_KEY_BACKUP_IN_USE y
|
||||
umount /media || die "Unable to unmount USB"
|
||||
return
|
||||
fi
|
||||
#Else if user has known GPG key material Thumb drive backup and already asked to use it
|
||||
if [[ "$CONFIG_HAVE_GPG_KEY_BACKUP" == "y" && "$CONFIG_GPG_KEY_BACKUP_IN_USE" == "y" ]]; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
# setup the USB so we can reach the GPG card
|
||||
enable_usb
|
||||
|
||||
echo -e "\nVerifying presence of GPG card...\n"
|
||||
# ensure we don't exit without retrying
|
||||
errexit=$(set -o | grep errexit | awk '{print $2}')
|
||||
set +e
|
||||
gpg --card-status >/dev/null
|
||||
if [ $? -ne 0 ]; then
|
||||
# prompt for reinsertion and try a second time
|
||||
read -n1 -r -p \
|
||||
"Can't access GPG key; remove and reinsert, then press Enter to retry. " \
|
||||
ignored
|
||||
# restore prev errexit state
|
||||
if [ "$errexit" = "on" ]; then
|
||||
set -e
|
||||
fi
|
||||
# retry card status
|
||||
gpg --card-status >/dev/null ||
|
||||
die "gpg card read failed"
|
||||
fi
|
||||
# restore prev errexit state
|
||||
if [ "$errexit" = "on" ]; then
|
||||
set -e
|
||||
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.
|
||||
|
@ -196,7 +196,8 @@ prepare_thumb_drive()
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "Preparing $DEVICE with $PERCENTAGE_MB MB for private LUKS container and rest of disk with exfat for public partition (This may take a while)..." | fold -s
|
||||
echo -e "Preparing $DEVICE with $PERCENTAGE_MB MB for private LUKS container while rest of device will be assigned to extfat public partition...\n"
|
||||
echo "Please wait..."
|
||||
DEBUG "Creating empty DOS partition table on device through fdisk to start clean"
|
||||
echo -e "o\nw\n" | fdisk $DEVICE >/dev/null 2>&1 || die "Error creating partition table"
|
||||
DEBUG "partition device with two partitions: first one being the percent applied and rest for second partition through fdisk"
|
||||
@ -240,7 +241,7 @@ select_luks_container()
|
||||
mount -o remount,ro /boot
|
||||
fi
|
||||
else
|
||||
warn "No encrypted device found."
|
||||
warn "No encrypted device found"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
Loading…
Reference in New Issue
Block a user