From fe1018842b3072de872a2d3e42cda9baf7a2ae25 Mon Sep 17 00:00:00 2001 From: Ole Tange Date: Sat, 24 Feb 2018 22:51:46 +0100 Subject: [PATCH] decrypt-root-with-usb: Patch for cryptroot. --- README | 2 + decrypt-root-with-usb/README | 49 ++ decrypt-root-with-usb/cryptroot.patch | 55 +++ .../scripts/local-top/cryptroot | 458 ++++++++++++++++++ 4 files changed, 564 insertions(+) create mode 100644 decrypt-root-with-usb/README create mode 100644 decrypt-root-with-usb/cryptroot.patch create mode 100755 decrypt-root-with-usb/usr/share/initramfs-tools/scripts/local-top/cryptroot diff --git a/README b/README index 23fef19..2890f1c 100644 --- a/README +++ b/README @@ -6,6 +6,8 @@ blink - blink disks in a disk enclosure bsearch - binary search through sorted text files. +decrypt-root-with-usb - Patch for cryptroot to decrypt root with key on USB. + duplicate-packets - Duplicate packets on an interface. Useful if wifi is bad. em - Force emacs to run in terminal. Use xemacs if installed. diff --git a/decrypt-root-with-usb/README b/decrypt-root-with-usb/README new file mode 100644 index 0000000..6b04dad --- /dev/null +++ b/decrypt-root-with-usb/README @@ -0,0 +1,49 @@ +It would be ideal to me if I could simply have a small USB stick +containing a passphrase that will unlock the disk. Not only would that +be handy for servers (where you could leave the USB stick in the +server - the goal is to be able to return broken harddisks without +having to worry about confidential data), it would also be great for +my laptop: Insert the USB stick when booting and remove it after +unlocking the cryptodisk. + +I have now written a patch that will search the root dir of all +devices for the file 'cryptkey.txt' and try decrypting with each line +as a key. If that fails: Revert to typing in the pass phrase. + +It does mean the key cannot contain \n, but that would apply to any +typed in key, too. The good part is that you can use the same USB disk +to store the key for multiple machines: You do not need a separate USB +disk for each. So if you have a USB drive in your physical key ring, +you can use the same drive for all the machines you boot when being +physically close. + +You add the key with: + + cryptsetup luksAddKey /dev/sda5 + +And then put the same key as a line in a file on the USB/MMC disk +called 'cryptkey.txt'. + +If the USB drivers, MMC drivers or the filesystems are not present in +your initramfs, you need to add them by adding to +/etc/initramfs-tools/modules: + + uhci_hcd + ehci_hcd + usb_storage + nls_utf8 + nls_cp437 + vfat + fat + sd_mod + mmc_block + tifm_sd + tifm_core + mmc_core + tifm_7xx1 + sdhci + sdhci_pci + +When all is done, update the initramfs: + + update-initramfs -u diff --git a/decrypt-root-with-usb/cryptroot.patch b/decrypt-root-with-usb/cryptroot.patch new file mode 100644 index 0000000..387d575 --- /dev/null +++ b/decrypt-root-with-usb/cryptroot.patch @@ -0,0 +1,55 @@ +--- /home/tange/tmp//cryptroot.orig 2018-02-24 22:16:39.920392548 +0100 ++++ usr/share/initramfs-tools/scripts/local-top/cryptroot 2018-02-24 22:30:03.527007065 +0100 +@@ -188,7 +188,21 @@ + + parse_options "$opts" || return 1 + ++ # Wait for USB to settle ++ /bin/sleep 3 ++ + if [ -z "$cryptkeyscript" ]; then ++ # Test all devices ++ mkdir /mnt ++ echo -n "Searching for cryptkey.txt on available disks... " ++ 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." ++ + if [ ${cryptsource#/dev/disk/by-uuid/} != $cryptsource ]; then + # UUIDs are not very helpful + diskname="$crypttarget" +@@ -305,11 +319,30 @@ + count=$(( $count + 1 )) + + if [ ! -e "$NEWROOT" ]; then ++ keyfound=0 ++ if [ -e /tmp/cryptkeys.txt ] ; then ++ echo Trying keys from cryptkey.txt ++ for key in `cat /tmp/cryptkeys.txt`; do ++ if crypttarget="$crypttarget" cryptsource="$cryptsource" \ ++ echo -n "$key" | $cryptopen; 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 ++ fi ++ if [ "$keyfound" = "0" ]; then ++ # Fall back to manual entry + if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ + $cryptkeyscript "$cryptkey" | $cryptopen; then + message "cryptsetup ($crypttarget): cryptsetup failed, bad password or options?" + continue + fi ++ fi + fi + + if [ ! -e "$NEWROOT" ]; then diff --git a/decrypt-root-with-usb/usr/share/initramfs-tools/scripts/local-top/cryptroot b/decrypt-root-with-usb/usr/share/initramfs-tools/scripts/local-top/cryptroot new file mode 100755 index 0000000..b473f82 --- /dev/null +++ b/decrypt-root-with-usb/usr/share/initramfs-tools/scripts/local-top/cryptroot @@ -0,0 +1,458 @@ +#!/bin/sh + +PREREQ="cryptroot-prepare" + +# +# Standard initramfs preamble +# +prereqs() +{ + # Make sure that cryptroot is run last in local-top + for req in $(dirname $0)/*; do + script=${req##*/} + if [ $script != cryptroot ]; then + echo $script + fi + done +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +# source for log_*_msg() functions, see LP: #272301 +. /scripts/functions + +# +# Helper functions +# +message() +{ + if [ -x /bin/plymouth ] && plymouth --ping; then + plymouth message --text="$@" + else + echo "$@" >&2 + fi + return 0 +} + +udev_settle() +{ + # Wait for udev to be ready, see https://launchpad.net/bugs/85640 + if command -v udevadm >/dev/null 2>&1; then + udevadm settle --timeout=30 + elif command -v udevsettle >/dev/null 2>&1; then + udevsettle --timeout=30 + fi + return 0 +} + +parse_options() +{ + local cryptopts + cryptopts="$1" + + if [ -z "$cryptopts" ]; then + return 1 + fi + + # Defaults + cryptcipher=aes-cbc-essiv:sha256 + cryptsize=256 + crypthash=ripemd160 + crypttarget=cryptroot + cryptsource="" + cryptheader="" + cryptlvm="" + cryptkeyscript="" + cryptkey="" # This is only used as an argument to an eventual keyscript + cryptkeyslot="" + crypttries=3 + crypttcrypt="" + cryptveracrypt="" + cryptrootdev="" + cryptdiscard="" + CRYPTTAB_OPTIONS="" + + local IFS=" ," + for x in $cryptopts; do + case $x in + hash=*) + crypthash=${x#hash=} + ;; + size=*) + cryptsize=${x#size=} + ;; + cipher=*) + cryptcipher=${x#cipher=} + ;; + target=*) + crypttarget=${x#target=} + export CRYPTTAB_NAME="$crypttarget" + ;; + source=*) + cryptsource=${x#source=} + if [ ${cryptsource#UUID=} != $cryptsource ]; then + cryptsource="/dev/disk/by-uuid/${cryptsource#UUID=}" + elif [ ${cryptsource#LABEL=} != $cryptsource ]; then + cryptsource="/dev/disk/by-label/$(printf '%s' "${cryptsource#LABEL=}" | sed 's,/,\\x2f,g')" + fi + export CRYPTTAB_SOURCE="$cryptsource" + ;; + header=*) + cryptheader=${x#header=} + if [ ! -e "$cryptheader" ] && [ -e "/conf/conf.d/cryptheader/$cryptheader" ]; then + cryptheader="/conf/conf.d/cryptheader/$cryptheader" + fi + export CRYPTTAB_HEADER="$cryptheader" + ;; + lvm=*) + cryptlvm=${x#lvm=} + ;; + keyscript=*) + cryptkeyscript=${x#keyscript=} + ;; + key=*) + if [ "${x#key=}" != "none" ]; then + cryptkey=${x#key=} + fi + export CRYPTTAB_KEY="$cryptkey" + ;; + keyslot=*) + cryptkeyslot=${x#keyslot=} + ;; + tries=*) + crypttries="${x#tries=}" + case "$crypttries" in + *[![:digit:].]*) + crypttries=3 + ;; + esac + ;; + tcrypt) + crypttcrypt="yes" + ;; + veracrypt) + cryptveracrypt="--veracrypt" + ;; + rootdev) + cryptrootdev="yes" + ;; + discard) + cryptdiscard="yes" + ;; + esac + PARAM="${x%=*}" + if [ "$PARAM" = "$x" ]; then + VALUE="yes" + else + VALUE="${x#*=}" + fi + CRYPTTAB_OPTIONS="$CRYPTTAB_OPTIONS $PARAM" + eval export CRYPTTAB_OPTION_$PARAM="\"$VALUE\"" + done + export CRYPTTAB_OPTIONS + + if [ -z "$cryptsource" ]; then + message "cryptsetup ($crypttarget): source parameter missing" + return 1 + fi + return 0 +} + +activate_vg() +{ + # Sanity checks + if [ ! -x /sbin/lvm ]; then + message "cryptsetup ($crypttarget): lvm is not available" + return 1 + fi + + # Detect and activate available volume groups + /sbin/lvm vgscan + /sbin/lvm vgchange -a y --sysinit + return $? +} + +setup_mapping() +{ + local opts count cryptopen cryptremove NEWROOT + opts="$1" + + if [ -z "$opts" ]; then + return 0 + fi + + parse_options "$opts" || return 1 + + # Wait for USB to settle + /bin/sleep 3 + + if [ -z "$cryptkeyscript" ]; then + # Test all devices + mkdir /mnt + echo -n "Searching for cryptkey.txt on available disks... " + 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." + + if [ ${cryptsource#/dev/disk/by-uuid/} != $cryptsource ]; then + # UUIDs are not very helpful + diskname="$crypttarget" + else + diskname="$cryptsource ($crypttarget)" + fi + cryptkeyscript="/lib/cryptsetup/askpass" + cryptkey="Please unlock disk $diskname: " + elif ! type "$cryptkeyscript" >/dev/null; then + message "cryptsetup ($crypttarget): error - script \"$cryptkeyscript\" missing" + return 1 + fi + + if [ "$cryptkeyscript" = "cat" ] && [ "${cryptkey#/root/}" != "$cryptkey" ]; then + # skip the mapping if the root FS is not mounted yet + sed -rn 's/^\s*[^#]\S*\s+(\S+)\s.*/\1/p' /proc/mounts | grep -Fxq "$rootmnt" || return 1 + # substitute the "/root" prefix by the real root FS mountpoint otherwise + cryptkey="${rootmnt}/${cryptkey#/root/}" + fi + + if [ -n "$cryptheader" ] && ! type "$cryptheader" >/dev/null; then + message "cryptsetup ($crypttarget): error - LUKS header \"$cryptheader\" missing" + return 1 + fi + + # The same target can be specified multiple times + # e.g. root and resume lvs-on-lvm-on-crypto + if [ -e "/dev/mapper/$crypttarget" ]; then + return 0 + fi + + modprobe -q dm_crypt + + # Make sure the cryptsource device is available + if [ ! -e $cryptsource ]; then + activate_vg + fi + + # If the encrypted source device hasn't shown up yet, give it a + # little while to deal with removable devices + + # the following lines below have been taken from + # /usr/share/initramfs-tools/scripts/local, as suggested per + # https://launchpad.net/bugs/164044 + if [ ! -e "$cryptsource" ]; then + log_begin_msg "Waiting for encrypted source device..." + + # Default delay is 180s + if [ -z "${ROOTDELAY}" ]; then + slumber=180 + else + slumber=${ROOTDELAY} + fi + + slumber=$(( ${slumber} * 10 )) + while [ ! -e "$cryptsource" ]; do + # retry for LVM devices every 10 seconds + if [ ${slumber} -eq $(( ${slumber}/100*100 )) ]; then + activate_vg + fi + + /bin/sleep 0.1 + slumber=$(( ${slumber} - 1 )) + [ ${slumber} -gt 0 ] || break + done + + if [ ${slumber} -gt 0 ]; then + log_end_msg 0 + else + log_end_msg 1 || true + fi + fi + udev_settle + + # We've given up, but we'll let the user fix matters if they can + if [ ! -e "${cryptsource}" ]; then + + echo " ALERT! ${cryptsource} does not exist." + echo " Check cryptopts=source= bootarg: cat /proc/cmdline" + echo " or missing modules, devices: cat /proc/modules; ls /dev" + panic -r "Dropping to a shell. Will skip ${cryptsource} if you can't fix." + fi + + if [ ! -e "${cryptsource}" ]; then + return 1 + fi + + + # Prepare commands + cryptopen="/sbin/cryptsetup -T 1" + if [ "$cryptdiscard" = "yes" ]; then + cryptopen="$cryptopen --allow-discards" + fi + if [ -n "$cryptheader" ]; then + cryptopen="$cryptopen --header=$cryptheader" + fi + if [ -n "$cryptkeyslot" ]; then + cryptopen="$cryptopen --key-slot=$cryptkeyslot" + fi + if /sbin/cryptsetup isLuks ${cryptheader:-$cryptsource} >/dev/null 2>&1; then + cryptopen="$cryptopen open --type luks $cryptsource $crypttarget --key-file=-" + elif [ "$crypttcrypt" = "yes" ]; then + cryptopen="$cryptopen open --type tcrypt $cryptveracrypt $cryptsource $crypttarget" + else + cryptopen="$cryptopen -c $cryptcipher -s $cryptsize -h $crypthash open --type plain $cryptsource $crypttarget --key-file=-" + fi + cryptremove="/sbin/cryptsetup remove $crypttarget" + NEWROOT="/dev/mapper/$crypttarget" + + # Try to get a satisfactory password $crypttries times + count=0 + while [ $crypttries -le 0 ] || [ $count -lt $crypttries ]; do + export CRYPTTAB_TRIED="$count" + count=$(( $count + 1 )) + + if [ ! -e "$NEWROOT" ]; then + keyfound=0 + if [ -e /tmp/cryptkeys.txt ] ; then + echo Trying keys from cryptkey.txt + for key in `cat /tmp/cryptkeys.txt`; do + if crypttarget="$crypttarget" cryptsource="$cryptsource" \ + echo -n "$key" | $cryptopen; 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 + fi + if [ "$keyfound" = "0" ]; then + # Fall back to manual entry + if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ + $cryptkeyscript "$cryptkey" | $cryptopen; then + message "cryptsetup ($crypttarget): cryptsetup failed, bad password or options?" + continue + fi + fi + fi + + if [ ! -e "$NEWROOT" ]; then + message "cryptsetup ($crypttarget): unknown error setting up device mapping" + return 1 + fi + + #FSTYPE='' + #eval $(fstype < "$NEWROOT") + FSTYPE="$(/sbin/blkid -s TYPE -o value "$NEWROOT")" + + # See if we need to setup lvm on the crypto device + #if [ "$FSTYPE" = "lvm" ] || [ "$FSTYPE" = "lvm2" ]; then + if [ "$FSTYPE" = "LVM_member" ] || [ "$FSTYPE" = "LVM2_member" ]; then + if [ -z "$cryptlvm" ]; then + message "cryptsetup ($crypttarget): lvm fs found but no lvm configured" + return 1 + elif ! activate_vg; then + # disable error message, LP: #151532 + #message "cryptsetup ($crypttarget): failed to setup lvm device" + return 1 + fi + + # Apparently ROOT is already set in /conf/param.conf for + # flashed kernels at least. See bugreport #759720. + if [ -f /conf/param.conf ] && grep -q "^ROOT=" /conf/param.conf; then + NEWROOT=$(sed -n 's/^ROOT=//p' /conf/param.conf) + else + NEWROOT=${cmdline_root:-/dev/mapper/$cryptlvm} + if [ "$cryptrootdev" = "yes" ]; then + # required for lilo to find the root device + echo "ROOT=$NEWROOT" >>/conf/param.conf + fi + fi + #eval $(fstype < "$NEWROOT") + FSTYPE="$(/sbin/blkid -s TYPE -o value "$NEWROOT")" + fi + + #if [ -z "$FSTYPE" ] || [ "$FSTYPE" = "unknown" ]; then + if [ -z "$FSTYPE" ]; then + message "cryptsetup ($crypttarget): unknown fstype, bad password or options?" + udev_settle + $cryptremove + continue + fi + + # decrease $count by 1, apparently last try was successful. + count=$(( $count - 1 )) + + message "cryptsetup ($crypttarget): set up successfully" + break + done + + failsleep=60 # make configurable later? + + if [ "$cryptrootdev" = "yes" ] && [ $crypttries -gt 0 ] && [ $count -ge $crypttries ]; then + message "cryptsetup ($crypttarget): maximum number of tries exceeded" + message "cryptsetup: going to sleep for $failsleep seconds..." + sleep $failsleep + exit 1 + fi + + udev_settle + return 0 +} + +# +# Begin real processing +# + +# Do we have any kernel boot arguments? +cmdline_cryptopts='' +unset cmdline_root +for opt in $(cat /proc/cmdline); do + case $opt in + cryptopts=*) + opt="${opt#cryptopts=}" + if [ -n "$opt" ]; then + if [ -n "$cmdline_cryptopts" ]; then + cmdline_cryptopts="$cmdline_cryptopts $opt" + else + cmdline_cryptopts="$opt" + fi + fi + ;; + root=*) + opt="${opt#root=}" + case $opt in + /*) # Absolute path given. Not lilo major/minor number. + cmdline_root=$opt + ;; + *) # lilo major/minor number (See #398957). Ignore + esac + ;; + esac +done + +if [ -n "$cmdline_cryptopts" ]; then + # Call setup_mapping separately for each possible cryptopts= setting + for cryptopt in $cmdline_cryptopts; do + setup_mapping "$cryptopt" + done + exit 0 +fi + +# Do we have any settings from the /conf/conf.d/cryptroot file? +if [ -r /conf/conf.d/cryptroot ]; then + while read mapping <&3; do + setup_mapping "$mapping" 3<&- + done 3< /conf/conf.d/cryptroot +fi + +exit 0