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:
Thierry Laurion 2023-10-19 15:42:27 -04:00
parent b1e5c638cd
commit 2c55338be5
No known key found for this signature in database
GPG Key ID: E7B4A71658E36A93
5 changed files with 182 additions and 114 deletions

View File

@ -184,7 +184,7 @@ if cryptsetup isLuks "$USB_MOUNT_DEVICE"; then
|| die "ERROR: Failed to open ${USB_MOUNT_DEVICE} LUKS device" || die "ERROR: Failed to open ${USB_MOUNT_DEVICE} LUKS device"
fi 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")"" DEBUG "Setting USB_MOUNT_DEVICE=/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")"" USB_MOUNT_DEVICE="/dev/mapper/"usb_mount_$(basename "$USB_MOUNT_DEVICE")""
else else

View File

@ -940,6 +940,14 @@ assert_signable
# Action time... # 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 # detect and set /boot device
echo -e "\nDetecting and setting boot device...\n" echo -e "\nDetecting and setting boot device...\n"
if ! detect_boot_device; then 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 #TODO seperate wiping and thumb drive functions with proper validation
wipe_thumb_drive_and_copy_gpg_key_material wipe_thumb_drive_and_copy_gpg_key_material
#TODO seperate setting config #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 gpg_key_factory_reset
keytocard_subkeys_to_smartcard keytocard_subkeys_to_smartcard
else else
@ -1000,10 +1008,21 @@ else
generate_OEM_gpg_keys generate_OEM_gpg_keys
fi fi
# parse name of generated key # Obtain GPG key ID
GPG_GEN_KEY=$(grep -A1 pub /tmp/gpg_card_edit_output | tail -n1 | sed -nr 's/^([ ])*//p') 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" 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 #Applying custom GPG PINs if keys were not generated in memory
if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then if [ "$GPG_GEN_KEY_IN_MEMORY" == "0" ]; then
if [ "$USER_PIN" != "" -o "$ADMIN_PIN" != "" ]; 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" gpg_key_change_pin "1" "$USER_PIN_DEF" "$USER_PIN"
fi 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 ## export pubkey to USB
if [ $GPG_EXPORT -ne 0 ]; then if [ $GPG_EXPORT -ne 0 ]; then
echo -e "\nExporting generated key to USB...\n" 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) ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR" whiptail_error_die "Error updating GPG ownertrust:\n\n$ERROR"
fi fi
# clear any existing heads/gpg files from current firmware # clear any existing heads/gpg files from current firmware
for i in $(cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"); do for i in $(cbfs.sh -o /tmp/oem-setup.rom -l | grep -e "heads/"); do
cbfs.sh -o /tmp/oem-setup.rom -d "$i" cbfs.sh -o /tmp/oem-setup.rom -d "$i"
done done
# add heads/gpg files to current firmware # add heads/gpg files to current firmware
if [ -e /.gnupg/pubring.kbx ]; then if [ -e /.gnupg/pubring.kbx ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/pubring.kbx" -f /.gnupg/pubring.kbx
if [ -e /.gnupg/pubring.gpg ]; then if [ -e /.gnupg/pubring.gpg ]; then
@ -1071,13 +1086,15 @@ fi
if [ -e /.gnupg/trustdb.gpg ]; then if [ -e /.gnupg/trustdb.gpg ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/.gnupg/trustdb.gpg" -f /.gnupg/trustdb.gpg
fi fi
# persist user config changes (boot device) # persist user config changes (boot device)
if [ -e /etc/config.user ]; then if [ -e /etc/config.user ]; then
cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user cbfs.sh -o /tmp/oem-setup.rom -a "heads/initrd/etc/config.user" -f /etc/config.user
fi fi
# flash updated firmware image # flash updated firmware image
echo -e "\nAdding generated key to current firmware and re-flashing...\n" 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) ERROR=$(tail -n 1 /tmp/error | fold -s)
whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR" whiptail_error_die "Error flashing updated firmware image:\n\n$ERROR"
fi fi

View File

@ -53,9 +53,102 @@ preserve_rom() {
done 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() { 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 # 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 !!!!!" 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" \ && gpgv "$CR_SIG" "$CR_NONCE" \
; then ; then
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true
DEBUG "Under /etc/ash_functions:gpg_auth: success"
return 0 return 0
else else
shred -n 10 -z -u "$CR_SIG" 2>/dev/null || true shred -n 10 -z -u "$CR_SIG" 2>/dev/null || true
echo >&2 "!!!!! GPG authentication failed, please try again !!!!!"
continue continue
fi fi
done done
@ -125,12 +220,12 @@ recovery() {
fi fi
while [ true ] while [ true ]
do do
#Going to recovery shell should be authenticated if supported
gpg_auth
echo >&2 "!!!!! Starting recovery shell" echo >&2 "!!!!! Starting recovery shell"
sleep 1 sleep 1
#Going to recovery shell should be authenticated if supported
gpg_auth
if [ -x /bin/setsid ]; then if [ -x /bin/setsid ]; then
/bin/setsid -c /bin/sh /bin/setsid -c /bin/sh
else else
@ -150,6 +245,57 @@ combine_configs() {
cat /etc/config* > /tmp/config 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() enable_usb()
{ {
TRACE "Under /etc/ash_functions:enable_usb" TRACE "Under /etc/ash_functions:enable_usb"

View File

@ -189,102 +189,6 @@ list_usb_storage() {
done 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 # 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 # 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. # line, since some flows need it multiple times and only one prompt is ideal.

View File

@ -196,7 +196,8 @@ prepare_thumb_drive()
fi fi
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" 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" 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" 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 mount -o remount,ro /boot
fi fi
else else
warn "No encrypted device found." warn "No encrypted device found"
return 1 return 1
fi fi
fi fi