#!/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' "$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