heads/initrd/etc/ash_functions

342 lines
11 KiB
Plaintext
Raw Normal View History

#!/bin/sh
#
# Core shell functions that do not require bash. These functions are used with
# busybox ash on legacy-flash boards, and with bash on all other boards.
die() {
if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then
echo " !!! ERROR: $* !!!" | tee -a /tmp/debug.log /dev/kmsg > /dev/null;
else
echo >&2 "!!! ERROR: $* !!!";
fi
sleep 2;
exit 1;
}
warn() {
if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then
echo " *** WARNING: $* ***" | tee -a /tmp/debug.log /dev/kmsg > /dev/null;
else
echo >&2 " *** WARNING: $* ***";
fi
sleep 1;
}
DEBUG() {
if [ "$CONFIG_DEBUG_OUTPUT" = "y" ];then
echo "DEBUG: $*" | while read line; do
echo "$line" | tee -a /tmp/debug.log /dev/kmsg >/dev/null
done
fi
}
TRACE() {
if [ "$CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT" = "y" ];then
echo "TRACE: $*" | tee -a /tmp/debug.log /dev/kmsg > /dev/null;
fi
}
preserve_rom() {
TRACE "Under /etc/ash_functions:preserve_rom"
new_rom="$1"
old_files=`cbfs -t 50 -l 2>/dev/null | grep "^heads/"`
for old_file in `echo $old_files`; do
new_file=`cbfs.sh -o $1 -l | grep -x $old_file`
if [ -z "$new_file" ]; then
echo "+++ Adding $old_file to $1"
cbfs -t 50 -r $old_file >/tmp/rom.$$ \
|| die "Failed to read cbfs file from ROM"
cbfs.sh -o $1 -a $old_file -f /tmp/rom.$$ \
|| die "Failed to write cbfs file to new ROM file"
fi
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() {
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 !!!!!"
# Wipe any existing nonce and signature
shred -n 10 -z -u "$CR_NONCE" "$CR_SIG" 2>/dev/null || true
confirm_gpg_card
# Perform a signing-based challenge-response,
# to authencate that the card plugged in holding
# the key to sign the list of boot files.
CR_NONCE="/tmp/secret/cr_nonce"
CR_SIG="$CR_NONCE.sig"
# Generate a random nonce
dd \
if=/dev/urandom \
of="$CR_NONCE" \
count=1 \
bs=20 \
2>/dev/null \
|| die "Unable to generate 20 random bytes"
# Sign the nonce
for tries in 1 2 3; do
if gpg --digest-algo SHA256 \
--detach-sign \
-o "$CR_SIG" \
"$CR_NONCE" \
&& 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
return 1
fi
}
recovery() {
TRACE "Under /etc/ash_functions:recovery"
echo >&2 "!!!!! $*"
# Remove any temporary secret files that might be hanging around
# but recreate the directory so that new tools can use it.
#safe to always be true. Otherwise "set -e" would make it exit here
shred -n 10 -z -u /tmp/secret/* 2> /dev/null || true
rm -rf /tmp/secret
mkdir -p /tmp/secret
# ensure /tmp/config exists for recovery scripts that depend on it
touch /tmp/config
. /tmp/config
if [ "$CONFIG_TPM" = "y" ]; then
tpmr extend -ix 4 -ic recovery
fi
if [ "$CONFIG_RESTRICTED_BOOT" = y ]; then
echo >&2 "Restricted Boot enabled, recovery console disabled, rebooting in 5 seconds"
sleep 5
/bin/reboot
fi
while [ true ]
do
#Going to recovery shell should be authenticated if supported
gpg_auth
echo >&2 "!!!!! Starting recovery shell"
sleep 1
if [ -x /bin/setsid ]; then
/bin/setsid -c /bin/sh
else
/bin/sh
fi
done
}
pause_recovery() {
TRACE "Under /etc/ash_functions:pause_recovery"
read -p $'!!! Hit enter to proceed to recovery shell !!!\n'
recovery $*
}
combine_configs() {
TRACE "Under /etc/ash_functions: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"
#insmod ehci_hcd prior of uhdc_hcd and ohci_hcd to suppress dmesg warning
if ! lsmod | grep -q ehci_hcd; then
insmod /lib/modules/ehci-hcd.ko \
|| die "ehci_hcd: module load failed"
fi
if [ "$CONFIG_LINUX_USB_COMPANION_CONTROLLER" = y ]; then
if ! lsmod | grep -q uhci_hcd; then
insmod /lib/modules/uhci-hcd.ko \
|| die "uhci_hcd: module load failed"
fi
if ! lsmod | grep -q ohci_hcd; then
insmod /lib/modules/ohci-hcd.ko \
|| die "ohci_hcd: module load failed"
fi
if ! lsmod | grep -q ohci_pci; then
insmod /lib/modules/ohci-pci.ko \
|| die "ohci_pci: module load failed"
fi
fi
if ! lsmod | grep -q ehci_pci; then
insmod /lib/modules/ehci-pci.ko \
|| die "ehci_pci: module load failed"
fi
if ! lsmod | grep -q xhci_hcd; then
insmod /lib/modules/xhci-hcd.ko \
|| die "xhci_hcd: module load failed"
fi
if ! lsmod | grep -q xhci_pci; then
insmod /lib/modules/xhci-pci.ko \
|| die "xhci_pci: module load failed"
sleep 2
fi
if [ "$CONFIG_USB_KEYBOARD" = y ]; then
if ! lsmod | grep -q usbhid; then
insmod /lib/modules/usbhid.ko \
|| die "usbhid: module load failed"
fi
fi
}