tangetools/decrypt-root-with-usb/ubuntu-20.04/usr/share/initramfs-tools/scripts/local-top/cryptroot
2020-11-23 22:35:42 +01:00

298 lines
9.7 KiB
Bash

#!/bin/sh
PREREQ="cryptroot-prepare"
#
# Standard initramfs preamble
#
prereqs()
{
# Make sure that cryptroot is run last in local-top
local req
for req in "${0%/*}"/*; do
script="${req##*/}"
if [ "$script" != "${0##*/}" ]; then
printf '%s\n' "$script"
fi
done
}
case $1 in
prereqs)
prereqs
exit 0
;;
esac
. /scripts/functions
[ -f /lib/cryptsetup/functions ] || return 0
. /lib/cryptsetup/functions
# wait_for_source()
# Wait for encrypted $CRYPTTAB_SOURCE . Set $CRYPTTAB_SOURCE
# to its normalized device name when it shows up;
# return 1 if timeout.
wait_for_source() {
wait_for_udev 10
if crypttab_resolve_source; then
# the device is here already, no need to loop
return 0
fi
# If the source device hasn't shown up yet, give it a little while
# to allow for asynchronous device discovery (e.g. USB).
#
# We also need to take into account RAID or other devices that may
# only be available on local-block stage. So, wait 5 seconds upfront,
# in local-top; if that fails, end execution relying on local-block
# invocations. Allow $ROOTDELAY/3 invocations with 1s sleep times (with
# a minimum of 30 invocations), and if after that we still fail, then it's
# really time to give-up. Variable $initrd_cnt tracks the re-invocations.
#
# Part of the lines below has been taken from initramfs-tools
# scripts/local's local_device_setup(), as suggested per
# https://launchpad.net/bugs/164044 .
local slumber=1
if [ ! -f "${CRYPTR_LOCAL_BLOCK}" ]; then # we are running on local-top
slumber=5
fi
cryptsetup_message "Waiting for encrypted source device $CRYPTTAB_SOURCE..."
while [ $slumber -gt 0 ]; do
sleep 1
if [ -x /scripts/local-block/lvm2 ]; then
# activate any VG that might hold $CRYPTTAB_SOURCE
/scripts/local-block/lvm2 "$CRYPTTAB_SOURCE"
fi
if crypttab_resolve_source; then
wait_for_udev 10
return 0
fi
slumber=$(( $slumber - 1 ))
done
return 1
}
# setup_mapping()
# Set up a crypttab(5) mapping defined by $CRYPTTAB_NAME,
# $CRYPTTAB_SOURCE, $CRYPTTAB_KEY, $CRYPTTAB_OPTIONS.
setup_mapping() {
local dev initrd_cnt
# We control here the number of re-invocations of this script from
# local-block - the heuristic is $ROOTDELAY/3, with a minimum of 30.
# This number is somewhat dictated by mdadm, we want to run more times
# than that script, to allow decrypting volumes on top of arrays.
if [ -f "${CRYPTR_CNT_FILE}" ]; then
initrd_cnt=$(cat ${CRYPTR_CNT_FILE})
else
initrd_cnt=${ROOTDELAY:-90}
initrd_cnt=$((initrd_cnt/3))
if [ "${initrd_cnt}" -lt 30 ]; then
initrd_cnt=30
fi
echo ${initrd_cnt} > "${CRYPTR_CNT_FILE}"
fi
# The same target can be specified multiple times
# e.g. root and resume lvs-on-lvm-on-crypto
if dm_blkdevname "$CRYPTTAB_NAME" >/dev/null; then
return 0
fi
crypttab_parse_options --export --missing-path=fail || return 1
if ! wait_for_source; then
if [ ${initrd_cnt} -le 0 ]; then
# we've given up
if [ -n "$panic" ]; then
panic "ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
else
# let the user fix matters if they can
echo " ALERT! encrypted source device $CRYPTTAB_SOURCE does not exist, can't unlock $CRYPTTAB_NAME."
echo " Check cryptopts=source= bootarg: cat /proc/cmdline"
echo " or missing modules, devices: cat /proc/modules; ls /dev"
panic "Dropping to a shell."
fi
return 1 # can't continue because environment is lost
else
initrd_cnt=$((initrd_cnt - 1))
echo ${initrd_cnt} > "${CRYPTR_CNT_FILE}"
return 0 # allow some attempts on local-block stage
fi
fi
# our `cryptroot-unlock` script searches for cryptsetup processes
# with a given CRYPTTAB_NAME it their environment
export CRYPTTAB_NAME
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ]; then
# no keyscript: interactive unlocking, or key file
if [ "${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}" != "$CRYPTTAB_KEY" ]; then
# skip the mapping for now if the root FS is not mounted yet
sed -rn 's/^\s*[^#[:blank:]]\S*\s+(\S+)\s.*/\1/p' /proc/mounts | grep -Fxq -- "$rootmnt" || return 1
# substitute the "/FIXME-initramfs-rootmnt/" prefix by the real root FS mountpoint otherwise
CRYPTTAB_KEY="$rootmnt/${CRYPTTAB_KEY#/FIXME-initramfs-rootmnt/}"
fi
if [ "$CRYPTTAB_KEY" != "none" ]; then
if [ ! -e "$CRYPTTAB_KEY" ]; then
cryptsetup_message "ERROR: Skipping target $CRYPTTAB_NAME: non-existing key file $CRYPTTAB_KEY"
return 1
fi
# try only once if we have a key file
CRYPTTAB_OPTION_tries=1
fi
fi
get_crypt_type # set CRYPTTAB_TYPE to the type of crypt device
local count=0 maxtries="${CRYPTTAB_OPTION_tries:-3}" fstype vg rv
while [ $maxtries -le 0 ] || [ $count -lt $maxtries ]; do
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ] && [ "$CRYPTTAB_KEY" != "none" ]; then
# unlock via keyfile
unlock_mapping "$CRYPTTAB_KEY"
else
if [ -z "${CRYPTTAB_OPTION_keyscript+x}" ]; then
# Wait for USB to settle
/bin/sleep 3
# Test all devices
mkdir /mnt
echo -n "Searching for cryptkey.txt on available disks... "
local partition
for partition in `cat /proc/partitions |awk '{print $4}'|tail -n +3`; do
if mount /dev/$partition /mnt 2>/dev/null; then
cat /mnt/cryptkey.txt >> /tmp/cryptkeys.txt 2>/dev/null
umount /dev/$partition
fi
done
echo "done."
fi
if [ -s /tmp/cryptkeys.txt ]; then
local keyfound
keyfound=0
echo Trying keys from cryptkey.txt
for key in `cat /tmp/cryptkeys.txt`; do
if echo -n "$key" | unlock_mapping; then
# Found the key
echo Key found in cryptkey.txt
keyfound=1
key=""
fi
done
# Remove traces of the key
rm /tmp/cryptkeys.txt
unset key
if [ "$keyfound" = "0" ]; then
# Fall back to manual entry
run_keyscript "$CRYPTTAB_KEY" "$count" | unlock_mapping
fi
else
# unlock interactively or via keyscript
run_keyscript "$CRYPTTAB_KEY" "$count" | unlock_mapping
fi
fi
rv=$?
count=$(( $count + 1 ))
if [ $rv -ne 0 ]; then
cryptsetup_message "ERROR: $CRYPTTAB_NAME: cryptsetup failed, bad password or options?"
sleep 1
continue
elif ! dev="$(dm_blkdevname "$CRYPTTAB_NAME")"; then
cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown error setting up device mapping"
return 1
fi
if ! fstype="$(get_fstype "$dev")" || [ "$fstype" = "unknown" ]; then
if [ "$CRYPTTAB_TYPE" != "luks" ]; then
# bad password for plain dm-crypt device? or mkfs not run yet?
cryptsetup_message "ERROR: $CRYPTTAB_NAME: unknown fstype, bad password or options?"
wait_for_udev 10
/sbin/cryptsetup remove -- "$CRYPTTAB_NAME"
sleep 1
continue
fi
elif [ "$fstype" = lvm2 ]; then
if [ ! -x /sbin/lvm ]; then
cryptsetup_message "WARNING: $CRYPTTAB_NAME: lvm is not available"
return 1
elif vg="$(lvm pvs --noheadings -o vg_name --config 'log{prefix=""}' -- "$dev")"; then
# activate the VG held by the PV we just unlocked
lvm lvchange -a y --sysinit --ignoreskippedcluster -- "$vg"
fi
fi
cryptsetup_message "$CRYPTTAB_NAME: set up successfully"
wait_for_udev 10
return 0
done
cryptsetup_message "ERROR: $CRYPTTAB_NAME: maximum number of tries exceeded"
if [ -f "${CRYPTR_CNT_FILE}" ]; then
echo 0 > "${CRYPTR_CNT_FILE}"
fi
exit 1
}
#######################################################################
# Begin real processing
mkdir -p /cryptroot # might not exist yet if the main system has no crypttab(5)
# Do we have any kernel boot arguments?
if ! grep -qE '^(.*\s)?cryptopts=' /proc/cmdline; then
# ensure $TABFILE exists and has a mtime greater than the boot time
# (existing $TABFILE is preserved)
touch -- "$TABFILE"
else
# let the read builtin unescape the '\' as GRUB substitutes '\' by '\\' in the cmdline
tr ' ' '\n' </proc/cmdline | sed -n 's/^cryptopts=//p' | while IFS= read cryptopts; do
# skip empty values (which can be used to disable the initramfs
# scripts for a particular boot, cf. #873840)
[ -n "$cryptopts" ] || continue
unset -v target source key options
IFS=","
for x in $cryptopts; do
case "$x" in
target=*) target="${x#target=}";;
source=*) source="${x#source=}";;
key=*) key="${x#key=}";;
*) options="${options+$options,}$x";;
esac
done
if [ -z "${source:+x}" ]; then
cryptsetup_message "ERROR: Missing source= value in kernel parameter cryptopts=$cryptopts"
else
# preserve mangling
printf '%s %s %s %s\n' "${target:-cryptroot}" "$source" "${key:-none}" "${options-}"
fi
done >"$TABFILE"
fi
# Do we have any settings from the $TABFILE?
if [ -s "$TABFILE" ]; then
# Create locking directory before invoking cryptsetup(8) to avoid warnings
mkdir -pm0700 /run/cryptsetup
modprobe -q dm_crypt
crypttab_foreach_entry setup_mapping
fi
exit 0