diff options
Diffstat (limited to 'debian/initramfs/scripts/local-top/cryptroot')
-rw-r--r-- | debian/initramfs/scripts/local-top/cryptroot | 239 |
1 files changed, 239 insertions, 0 deletions
diff --git a/debian/initramfs/scripts/local-top/cryptroot b/debian/initramfs/scripts/local-top/cryptroot new file mode 100644 index 0000000..90b521b --- /dev/null +++ b/debian/initramfs/scripts/local-top/cryptroot @@ -0,0 +1,239 @@ +#!/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/4 invocations with 1s sleep times (with + # a minimum of 20 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=5 + if [ "${CRYPTROOT_STAGE-}" = "local-block" ]; then + slumber=1 + fi + + cryptsetup_message "Waiting for encrypted source device $CRYPTTAB_SOURCE..." + + while [ $slumber -gt 0 ]; do + sleep 1 + + 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/4, with a minimum of 20. + + if [ -f "$CRYPTROOT_COUNT_FILE" ]; then + initrd_cnt="$(cat <"$CRYPTROOT_COUNT_FILE")" + else + initrd_cnt="${ROOTDELAY:-180}" + initrd_cnt=$(( initrd_cnt/4 )) + if [ $initrd_cnt -lt 20 ]; then + initrd_cnt=20 + fi + echo "$initrd_cnt" >"$CRYPTROOT_COUNT_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 -eq 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" >"$CRYPTROOT_COUNT_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 + + 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 + # unlock interactively or via keyscript + run_keyscript "$count" | unlock_mapping + 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 + 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" + 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 |