diff --git a/initrd/bin/mount-usb b/initrd/bin/mount-usb index 771bebab..22846a24 100755 --- a/initrd/bin/mount-usb +++ b/initrd/bin/mount-usb @@ -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 diff --git a/initrd/bin/oem-factory-reset b/initrd/bin/oem-factory-reset index 18ae18ad..bd26a795 100755 --- a/initrd/bin/oem-factory-reset +++ b/initrd/bin/oem-factory-reset @@ -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 diff --git a/initrd/etc/ash_functions b/initrd/etc/ash_functions index 7a32af09..d877382f 100644 --- a/initrd/etc/ash_functions +++ b/initrd/etc/ash_functions @@ -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" diff --git a/initrd/etc/functions b/initrd/etc/functions index 761efd96..23c8fc2b 100755 --- a/initrd/etc/functions +++ b/initrd/etc/functions @@ -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. diff --git a/initrd/etc/luks-functions b/initrd/etc/luks-functions index 8e90bc4d..1a96c77f 100644 --- a/initrd/etc/luks-functions +++ b/initrd/etc/luks-functions @@ -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