heads/initrd/etc/functions

438 lines
10 KiB
Plaintext
Raw Normal View History

#!/bin/sh
# Shell functions for most initialization scripts
die() {
echo >&2 "$*";
sleep 2;
exit 1;
}
warn() {
echo >&2 "$*";
sleep 1;
}
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
if [ "$CONFIG_TPM" = y ]; then
tpm extend -ix 4 -ic recovery
fi
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
}
pause_recovery() {
read -p 'Hit enter to proceed to recovery shell:'
recovery $*
}
pcrs() {
head -8 /sys/class/tpm/tpm0/pcrs
}
confirm_totp()
{
prompt="$1"
last_half=X
unset totp_confirm
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`
if [ "$CONFIG_TPM" != y ]; then
TOTP="NO TPM"
elif [ "$half" != "$last_half" ]; then
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 \
-p "$prompt" \
totp_confirm \
&& break
# nothing typed, redraw the line
echo -ne '\r'
done
# clean up with a newline
echo
}
enable_usb()
{
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_hcd; then
insmod /lib/modules/ehci-hcd.ko \
|| die "ehci_hcd: module load failed"
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
}
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
}
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
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
}
check_tpm_counter()
{
LABEL=${2:-3135106223}
# 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
warn "$1 does not exist; creating new TPM counter"
read -s -p "TPM Owner password: " tpm_password
echo
tpm counter_create \
-pwdo "$tpm_password" \
-pwdc '' \
-la $LABEL \
| 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
if [ "$2" != "force" ]; then
if ! sha256sum `find $1/kexec*.txt` | gpgv $1/kexec.sig - ; then
die 'Invalid signature on kexec boot params'
fi
fi
echo "+++ Found verified kexec boot params"
cp $1/kexec*.txt /tmp/kexec \
|| die "Failed to copy kexec boot params to tmp"
}
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
}
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
}
combine_configs() {
cat /etc/config* > /tmp/config
}
update_checksums()
{
# ensure /boot mounted
if ! grep -q /boot /proc/mounts ; then
mount -o ro /boot \
|| recovery "Unable to mount /boot"
fi
# remount RW
mount -o rw,remount /boot
# sign and auto-roll config counter
extparam=
if [ "$CONFIG_TPM" = "y" ]; then
extparam=-r
fi
if ! kexec-sign-config -p /boot -u $extparam ; then
rv=1
else
rv=0
fi
# switch back to ro mode
mount -o ro,remount /boot
return $rv
}
print_tree() {
#use \x0 as long as possible to avoid issues with newlines in file names
find ./ ! -path './kexec*' -print0 | sort -z | xargs -0 printf "%s\n"
}
verify_checksums()
{
local boot_dir="$1"
(
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
diff "$TMP_TREE_FILE" /tmp/tree_output > /tmp/tree_diff || ret=1
grep -E '^\+[^\+].*$' /tmp/tree_diff | sed -E 's/^\+(.*)/(new) \1/g' >> /tmp/hash_output
exit $ret
)
return $?
}
# detect and set /boot device
# mount /boot if successful
detect_boot_device()
{
# unmount /boot to be safe
cd / && umount /boot 2>/dev/null
# check $CONFIG_BOOT_DEV if set/valid
if [ -e "$CONFIG_BOOT_DEV" ]; then
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
fi
fi
# generate list of possible boot devices
fdisk -l | grep "Disk /dev/" | cut -f2 -d " " | cut -f1 -d ":" > /tmp/disklist
# 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
DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1))
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
# iterate thru possible options and check for grub dir
for i in `cat /tmp/boot_device_list`; do
umount /boot 2>/dev/null
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
fi
done
# no valid boot device found
echo "Unable to locate /boot files on any mounted disk"
umount /boot 2>/dev/null
return 1
}
calc()
{
awk "BEGIN { print "$*" }";
}
print_battery_health()
{
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'})
echo "$battery_health"
fi
}
print_battery_charge()
{
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'})
echo "$battery_charge"
fi
}
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}'
}