2017-04-12 10:48:38 +00:00
#!/bin/sh
# Shell functions for most initialization scripts
die() {
echo >&2 "$*";
2022-11-14 23:04:04 +00:00
sleep 2;
2017-04-12 10:48:38 +00:00
exit 1;
}
warn() {
echo >&2 "$*";
2022-11-14 23:04:04 +00:00
sleep 1;
2017-04-12 10:48:38 +00:00
}
recovery() {
echo >&2 "!!!!! $*"
# Remove any temporary secret files that might be hanging around
# but recreate the directory so that new tools can use it.
2023-01-12 23:04:27 +00:00
#safe to always be true. Otherwise "set -e" would make it exit here
shred -n 10 -z -u /tmp/secret/* 2> /dev/null || true
2017-04-12 10:48:38 +00:00
rm -rf /tmp/secret
mkdir -p /tmp/secret
2019-02-08 18:25:12 +00:00
# ensure /tmp/config exists for recovery scripts that depend on it
touch /tmp/config
2017-12-05 08:29:07 +00:00
if [ "$CONFIG_TPM" = y ]; then
tpm extend -ix 4 -ic recovery
fi
2018-04-10 19:39:05 +00:00
2020-09-16 21:46:22 +00:00
while [ true ]
do
echo >&2 "!!!!! Starting recovery shell"
sleep 1
if [ -x /bin/setsid ]; then
/bin/setsid -c /bin/ash
else
/bin/ash
fi
done
2017-04-12 10:48:38 +00:00
}
2018-03-10 23:40:07 +00:00
pause_recovery() {
read -p 'Hit enter to proceed to recovery shell:'
recovery $*
}
2017-04-12 10:48:38 +00:00
pcrs() {
2018-03-12 01:27:19 +00:00
head -8 /sys/class/tpm/tpm0/pcrs
2017-04-12 10:48:38 +00:00
}
2017-04-29 17:40:34 +00:00
confirm_totp()
{
2017-07-18 17:44:02 +00:00
prompt="$1"
2017-04-29 17:40:34 +00:00
last_half=X
2017-07-18 17:44:02 +00:00
unset totp_confirm
2017-04-29 17:40:34 +00:00
while true; do
# update the TOTP code every thirty seconds
date=`date "+%Y-%m-%d %H:%M:%S"`
seconds=`date "+%s"`
half=`expr \( $seconds % 60 \) / 30`
2017-12-05 08:29:07 +00:00
if [ "$CONFIG_TPM" != y ]; then
2017-07-18 17:44:02 +00:00
TOTP="NO TPM"
elif [ "$half" != "$last_half" ]; then
2017-04-29 17:40:34 +00:00
last_half=$half;
TOTP=`unseal-totp` \
|| recovery "TOTP code generation failed"
fi
echo -n "$date $TOTP: "
# read the first character, non-blocking
read \
-t 1 \
-n 1 \
-s \
2017-07-18 17:44:02 +00:00
-p "$prompt" \
2017-04-29 17:40:34 +00:00
totp_confirm \
&& break
# nothing typed, redraw the line
echo -ne '\r'
done
# clean up with a newline
echo
}
2017-07-04 23:49:14 +00:00
2017-12-06 08:04:27 +00:00
enable_usb()
2017-07-04 23:49:14 +00:00
{
2023-01-16 20:18:05 +00:00
#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
2017-12-06 08:04:27 +00:00
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
2017-07-22 20:32:10 +00:00
fi
2017-07-04 23:49:14 +00:00
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 \
2017-12-06 08:04:27 +00:00
|| die "xhci_hcd: module load failed"
2017-07-04 23:49:14 +00:00
fi
if ! lsmod | grep -q xhci_pci; then
insmod /lib/modules/xhci-pci.ko \
2017-12-06 08:04:27 +00:00
|| die "xhci_pci: module load failed"
2017-07-04 23:49:14 +00:00
sleep 2
fi
2022-04-05 17:53:09 +00:00
if [ "$CONFIG_USB_KEYBOARD" = y ]; then
if ! lsmod | grep -q usbhid; then
insmod /lib/modules/usbhid.ko \
|| die "usbhid: module load failed"
fi
fi
2017-12-06 08:04:27 +00:00
}
2022-11-02 15:03:30 +00:00
list_usb_storage()
{
stat -c %N /sys/block/sd* 2>/dev/null | grep usb |
cut -f1 -d ' ' |
sed "s/[']//g" |
while read b; do
# Ignore devices of size 0, such as empty SD card
# readers on laptops attached via USB.
if [ "$(cat "$b/size")" -gt 0 ]; then
echo "$b"
fi
done |
sed "s|/sys/block|/dev|" |
while read b; do
# If the device has a partition table, ignore it and
# include the partitions instead - even if the kernel
# hasn't detected the partitions yet. Such a device is
# never usable directly, and this allows the "wait for
# disks" loop in mount-usb to correctly wait for the
# partitions.
if fdisk -l "$b" | grep -q "doesn't contain a valid partition table"; then
# No partition table, include this device
echo "$b"
else
# Has a partition table, include partitions
ls -1 "$b"* | awk 'NR!=1 {print $0}'
fi
done
}
2017-12-06 08:04:27 +00:00
confirm_gpg_card()
{
read \
-n 1 \
-p "Please confirm that your GPG card is inserted [Y/n]: " \
card_confirm
echo
if [ "$card_confirm" != "y" \
-a "$card_confirm" != "Y" \
-a -n "$card_confirm" ] \
; then
die "gpg card not confirmed"
fi
# setup the USB so we can reach the GPG card
enable_usb
2017-07-04 23:49:14 +00:00
2019-07-09 21:46:14 +00:00
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
2017-07-04 23:49:14 +00:00
}
2017-07-08 20:59:37 +00:00
check_tpm_counter()
{
2018-06-19 19:27:27 +00:00
LABEL=${2:-3135106223}
2017-07-08 20:59:37 +00:00
# if the /boot.hashes file already exists, read the TPM counter ID
# from it.
if [ -r "$1" ]; then
TPM_COUNTER=`grep counter- "$1" | cut -d- -f2`
else
2018-06-19 19:27:27 +00:00
warn "$1 does not exist; creating new TPM counter"
2017-07-08 20:59:37 +00:00
read -s -p "TPM Owner password: " tpm_password
echo
tpm counter_create \
-pwdo "$tpm_password" \
-pwdc '' \
2018-05-09 21:25:43 +00:00
-la $LABEL \
2017-07-08 20:59:37 +00:00
| tee /tmp/counter \
|| die "Unable to create TPM counter"
TPM_COUNTER=`cut -d: -f1 < /tmp/counter`
fi
if [ -z "$TPM_COUNTER" ]; then
die "$1: TPM Counter not found?"
fi
}
read_tpm_counter()
{
tpm counter_read -ix "$1" | tee "/tmp/counter-$1" \
|| die "Counter read failed"
}
increment_tpm_counter()
{
tpm counter_increment -ix "$1" -pwdc '' \
| tee /tmp/counter-$1 \
|| die "Counter increment failed"
}
check_config() {
if [ ! -d /tmp/kexec ]; then
mkdir /tmp/kexec \
|| die 'Failed to make kexec tmp dir'
else
rm -rf /tmp/kexec/* \
|| die 'Failed to empty kexec tmp dir'
fi
if [ ! -r $1/kexec.sig ]; then
return
fi
if [ `find $1/kexec*.txt | wc -l` -eq 0 ]; then
return
fi
2018-03-14 17:24:14 +00:00
if [ "$2" != "force" ]; then
if ! sha256sum `find $1/kexec*.txt` | gpgv $1/kexec.sig - ; then
die 'Invalid signature on kexec boot params'
fi
2017-07-08 20:59:37 +00:00
fi
echo "+++ Found verified kexec boot params"
cp $1/kexec*.txt /tmp/kexec \
|| die "Failed to copy kexec boot params to tmp"
}
2018-04-22 00:21:37 +00:00
preserve_rom() {
new_rom="$1"
old_files=`cbfs -t 50 -l 2>/dev/null | grep "^heads/"`
for old_file in `echo $old_files`; do
2022-10-09 21:24:16 +00:00
new_file=`cbfs.sh -o $1 -l | grep -x $old_file`
2018-04-22 00:21:37 +00:00
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"
2022-10-09 21:24:16 +00:00
cbfs.sh -o $1 -a $old_file -f /tmp/rom.$$ \
2018-04-22 00:21:37 +00:00
|| die "Failed to write cbfs file to new ROM file"
fi
done
}
2018-12-06 23:24:28 +00:00
replace_config() {
2018-12-06 23:41:20 +00:00
CONFIG_FILE=$1
CONFIG_OPTION=$2
NEW_SETTING=$3
2018-12-06 23:24:28 +00:00
2018-12-06 23:41:20 +00:00
touch $CONFIG_FILE
2018-12-06 23:24:28 +00:00
# first pull out the existing option from the global config and place in a tmp file
2018-12-06 23:41:20 +00:00
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
2018-12-06 23:24:28 +00:00
# then copy any remaining settings from the existing config file, minus the option you changed
2018-12-07 00:10:10 +00:00
grep -v "^export ${CONFIG_OPTION}=" ${CONFIG_FILE} | grep -v "^${CONFIG_OPTION}=" >> ${CONFIG_FILE}.tmp || true
2018-12-07 00:51:43 +00:00
sort ${CONFIG_FILE}.tmp | uniq > ${CONFIG_FILE}
2019-02-22 01:17:16 +00:00
rm -f ${CONFIG_FILE}.tmp
2018-12-06 23:24:28 +00:00
}
combine_configs() {
2018-12-07 00:29:09 +00:00
cat /etc/config* > /tmp/config
2018-12-06 23:24:28 +00:00
}
2019-07-05 22:04:00 +00:00
update_checksums()
{
# ensure /boot mounted
if ! grep -q /boot /proc/mounts ; then
mount -o ro /boot \
|| recovery "Unable to mount /boot"
fi
2020-10-08 13:16:08 +00:00
2019-07-05 22:04:00 +00:00
# remount RW
2020-07-13 22:22:40 +00:00
mount -o rw,remount /boot
2019-07-05 22:04:00 +00:00
# sign and auto-roll config counter
extparam=
if [ "$CONFIG_TPM" = "y" ]; then
2020-10-08 13:16:08 +00:00
extparam=-r
2019-07-05 22:04:00 +00:00
fi
2020-10-08 13:16:08 +00:00
if ! kexec-sign-config -p /boot -u $extparam ; then
2021-09-24 20:05:14 +00:00
rv=1
else
rv=0
2019-11-13 23:28:12 +00:00
fi
2019-07-05 22:04:00 +00:00
# switch back to ro mode
mount -o ro,remount /boot
2021-09-24 20:05:14 +00:00
return $rv
2019-07-05 22:04:00 +00:00
}
2019-08-19 22:07:22 +00:00
2022-12-31 17:41:24 +00:00
print_tree() {
2023-01-08 13:02:13 +00:00
find ./ ! -path './kexec*' -print0 | sort -z
}
2023-01-14 12:14:09 +00:00
# Escape zero-delimited standard input to safely display it to the user in e.g.
# `whiptail`, `less`, `echo`, `cat`. Doesn't produce shell-escaped output.
# Most printable characters are passed verbatim (exception: \).
# These escapes are used to replace their corresponding characters: #n#r#t#v#b
# Other characters are rendered as hexadecimal escapes.
2023-01-08 13:02:13 +00:00
# escape_zero [prefix] [escape character]
# prefix: \0 in the input will result in \n[prefix]
# escape character: character to use for escapes (default: #); \ may be interpreted by `whiptail`
escape_zero() {
local prefix="$1"
local echar="${2:-#}"
local todo=""
2023-01-14 12:14:09 +00:00
local echar_hex="$(echo -n "$echar" | xxd -p -c1)"
[ ${#echar_hex} -eq 2 ] || die "Invalid escape character $echar passed to escape_zero(). Programming error?!"
2023-01-08 13:02:13 +00:00
echo -e -n "$prefix"
2023-01-14 12:14:09 +00:00
xxd -p -c1 | tr -d '\n' |
2023-01-08 13:02:13 +00:00
{
while IFS= read -r -n2 -d '' ; do
if [ -n "$todo" ] ; then
#REPLY == " " is EOF
[[ "$REPLY" == " " ]] && echo '' || echo -e -n "$todo"
todo=""
fi
case "$REPLY" in
00)
todo="\n$prefix"
;;
08)
echo -n "${echar}b"
;;
09)
echo -n "${echar}t"
;;
0a)
echo -n "${echar}n"
;;
0b)
echo -n "${echar}v"
;;
0d)
echo -n "${echar}r"
;;
2023-01-14 12:14:09 +00:00
"$echar_hex")
echo -n "$echar$echar"
2023-01-08 13:02:13 +00:00
;;
2023-01-14 12:14:09 +00:00
#interpreted characters:
2[0-9a-f]|3[0-9a-f]|4[0-9a-f]|5[0-9abd-f]|6[0-9a-f]|7[0-9a-e])
2023-01-08 13:02:13 +00:00
echo -e -n '\x'"$REPLY"
;;
# All others are escaped
2023-01-14 12:14:09 +00:00
*)
2023-01-08 13:02:13 +00:00
echo -n "${echar}x$REPLY"
;;
esac
done
}
2022-12-31 17:41:24 +00:00
}
2023-01-12 16:31:31 +00:00
# Currently heads doesn't support signing file names with certain characters
# due to https://bugs.busybox.net/show_bug.cgi?id=14226. Also, certain characters
# may be intepreted by `whiptail`, `less` et al (e.g. \n, \b, ...).
assert_signable() {
# ensure /boot mounted
if ! grep -q /boot /proc/mounts ; then
mount -o ro /boot || die "Unable to mount /boot"
fi
find /boot -print0 > /tmp/signable.ref
local del='\001-\037\134\177-\377'
LC_ALL=C tr -d "$del" < /tmp/signable.ref > /tmp/signable.del || die "Failed to execute tr."
if ! cmp -s "/tmp/signable.ref" "/tmp/signable.del" &> /dev/null ; then
2023-01-14 09:27:42 +00:00
local user_out="/tmp/hash_output_mismatches"
local add="Please investigate!"
[ -f "$user_out" ] && add="Please investigate the following relative paths to /boot (where # are sanitized invalid characters):"$'\n'"$(cat "$user_out")"
recovery "Some /boot file names contain characters that are currently not supported by heads: $del"$'\n'"$add"
2023-01-12 16:31:31 +00:00
fi
rm -f /tmp/signable.*
}
2022-12-31 17:41:24 +00:00
verify_checksums()
{
local boot_dir="$1"
2023-01-12 16:18:52 +00:00
local gui="${2:-y}"
2022-12-31 17:41:24 +00:00
(
set +e -o pipefail
local ret=0
cd "$boot_dir" || ret=1
sha256sum -c "$TMP_HASH_FILE" > /tmp/hash_output || ret=1
# also make sure that the file & directory structure didn't change
# (sha256sum won't detect added files)
print_tree > /tmp/tree_output || ret=1
2023-01-08 13:02:13 +00:00
if ! cmp -s "$TMP_TREE_FILE" /tmp/tree_output &> /dev/null ; then
ret=1
2023-01-12 16:18:52 +00:00
[[ "$gui" != "y" ]] && exit "$ret"
2023-01-08 13:02:13 +00:00
# produce a diff that can safely be presented to the user
# this is relatively hard as file names may e.g. contain backslashes etc.,
# which are interpreted by whiptail, less, ...
escape_zero "(new) " < "$TMP_TREE_FILE" > "${TMP_TREE_FILE}.user"
escape_zero "(new) " < /tmp/tree_output > /tmp/tree_output.user
diff "${TMP_TREE_FILE}.user" /tmp/tree_output.user | grep -E '^\+\(new\).*$' | sed -r 's/^\+\(new\)/(new)/g' >> /tmp/hash_output
rm -f "${TMP_TREE_FILE}.user"
rm -f /tmp/tree_output.user
fi
2022-12-31 17:41:24 +00:00
exit $ret
)
return $?
}
2019-08-19 22:07:22 +00:00
# detect and set /boot device
# mount /boot if successful
detect_boot_device()
{
# unmount /boot to be safe
2020-07-13 22:22:40 +00:00
cd / && umount /boot 2>/dev/null
2019-08-19 22:07:22 +00:00
# check $CONFIG_BOOT_DEV if set/valid
if [ -e "$CONFIG_BOOT_DEV" ]; then
2019-11-13 23:36:29 +00:00
if mount -o ro $CONFIG_BOOT_DEV /boot >/dev/null 2>&1; then
if ls -d /boot/grub* >/dev/null 2>&1; then
# CONFIG_BOOT_DEV is valid device and contains an installed OS
return 0
fi
2019-08-19 22:07:22 +00:00
fi
fi
# generate list of possible boot devices
2021-01-05 22:13:41 +00:00
fdisk -l | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" > /tmp/disklist
2020-10-18 18:46:57 +00:00
2019-08-19 22:07:22 +00:00
# filter out extraneous options
> /tmp/boot_device_list
for i in `cat /tmp/disklist`; do
# remove block device from list if numeric partitions exist, since not bootable
2020-02-27 20:39:13 +00:00
DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1))
2019-08-19 22:07:22 +00:00
if [ ${DEV_NUM_PARTITIONS} -eq 0 ]; then
echo $i >> /tmp/boot_device_list
else
ls $i* | tail -${DEV_NUM_PARTITIONS} >> /tmp/boot_device_list
fi
done
2020-10-18 18:46:57 +00:00
2019-08-19 22:07:22 +00:00
# iterate thru possible options and check for grub dir
for i in `cat /tmp/boot_device_list`; do
umount /boot 2>/dev/null
2019-11-13 23:36:29 +00:00
if mount -o ro $i /boot >/dev/null 2>&1; then
if ls -d /boot/grub* >/dev/null 2>&1; then
CONFIG_BOOT_DEV="$i"
return 0
fi
2019-08-19 22:07:22 +00:00
fi
done
# no valid boot device found
echo "Unable to locate /boot files on any mounted disk"
umount /boot 2>/dev/null
return 1
}
2021-10-19 18:48:03 +00:00
calc()
{
awk "BEGIN { print "$*" }";
}
print_battery_health()
{
2021-10-26 20:26:11 +00:00
if [ -d /sys/class/power_supply/BAT* ]; then
battery_health=$(calc $(cat /sys/class/power_supply/BAT*/charge_full)/$(cat /sys/class/power_supply/BAT*/charge_full_design)*100 | awk -F "." {'print $1'})
2021-10-19 18:48:03 +00:00
echo "$battery_health"
fi
}
print_battery_charge()
{
2021-10-26 20:26:11 +00:00
if [ -d /sys/class/power_supply/BAT* ]; then
battery_charge=$(calc $(cat /sys/class/power_supply/BAT*/charge_now)/$(cat /sys/class/power_supply/BAT*/charge_full)*100 | awk -F "." {'print $1'})
2021-10-19 18:48:03 +00:00
echo "$battery_charge"
fi
}
2022-04-29 14:24:02 +00:00
generate_random_mac_address()
{
#Borrowed from https://stackoverflow.com/questions/42660218/bash-generate-random-mac-address-unicast
hexdump -n 6 -ve '1/1 "%.2x "' /dev/urandom | awk -v a="2,6,a,e" -v r="$RANDOM" 'BEGIN{srand(r);}NR==1{split(a,b,",");r=int(rand()*4+1);printf "%s%s:%s:%s:%s:%s:%s\n",substr($1,0,1),b[r],$2,$3,$4,$5,$6}'
}