From 579851315aedacab9c9ce1eb6763f39f26a4f73f Mon Sep 17 00:00:00 2001 From: shimunn Date: Wed, 18 Sep 2019 19:34:49 +0200 Subject: [PATCH] integrate with dracut-crypt --- dracut/crypt-lib.sh | 245 +++++++++++++++++++++++++++++++ dracut/crypt-lib.sh.patch | 17 +++ dracut/dracut-crypt-fido2-lib.sh | 17 +++ dracut/fido2luks.sh | 28 ++-- dracut/module-setup.sh | 12 +- 5 files changed, 305 insertions(+), 14 deletions(-) create mode 100755 dracut/crypt-lib.sh create mode 100644 dracut/crypt-lib.sh.patch create mode 100644 dracut/dracut-crypt-fido2-lib.sh diff --git a/dracut/crypt-lib.sh b/dracut/crypt-lib.sh new file mode 100755 index 0000000..adc1518 --- /dev/null +++ b/dracut/crypt-lib.sh @@ -0,0 +1,245 @@ +#!/usr/bin/sh + +command -v getarg >/dev/null || . /lib/dracut-lib.sh + +# check if the crypttab contains an entry for a LUKS UUID +crypttab_contains() { + local luks="$1" + local dev="$2" + local l d rest + if [ -f /etc/crypttab ]; then + while read l d rest || [ -n "$l" ]; do + strstr "${l##luks-}" "${luks##luks-}" && return 0 + strstr "$d" "${luks##luks-}" && return 0 + if [ -n "$dev" ]; then + for _dev in $(devnames $d); do + [ "$dev" -ef "$_dev" ] && return 0 + done + fi + if [ -e /etc/block_uuid.map ]; then + # search for line starting with $d + _line=$(sed -n "\,^$d .*$,{p}" /etc/block_uuid.map) + [ -z "$_line" ] && continue + # get second column with uuid + _uuid="$(echo $_line | sed 's,^.* \(.*$\),\1,')" + strstr "$_uuid" "${luks##luks-}" && return 0 + fi + done < /etc/crypttab + fi + return 1 +} + +# ask_for_password +# +# Wraps around plymouth ask-for-password and adds fallback to tty password ask +# if plymouth is not present. +# +# --cmd command +# Command to execute. Required. +# --prompt prompt +# Password prompt. Note that function already adds ':' at the end. +# Recommended. +# --tries n +# How many times repeat command on its failure. Default is 3. +# --ply-[cmd|prompt|tries] +# Command/prompt/tries specific for plymouth password ask only. +# --tty-[cmd|prompt|tries] +# Command/prompt/tries specific for tty password ask only. +# --tty-echo-off +# Turn off input echo before tty command is executed and turn on after. +# It's useful when password is read from stdin. +ask_for_password() { + local cmd; local prompt; local tries=3 + local ply_cmd; local ply_prompt; local ply_tries=3 + local tty_cmd; local tty_prompt; local tty_tries=3 + local ret + + while [ $# -gt 0 ]; do + case "$1" in + --cmd) ply_cmd="$2"; tty_cmd="$2"; shift;; + --ply-cmd) ply_cmd="$2"; shift;; + --tty-cmd) tty_cmd="$2"; shift;; + --prompt) ply_prompt="$2"; tty_prompt="$2"; shift;; + --ply-prompt) ply_prompt="$2"; shift;; + --tty-prompt) tty_prompt="$2"; shift;; + --tries) ply_tries="$2"; tty_tries="$2"; shift;; + --ply-tries) ply_tries="$2"; shift;; + --tty-tries) tty_tries="$2"; shift;; + --tty-echo-off) tty_echo_off=yes;; + esac + shift + done + + { flock -s 9; + # Prompt for password with plymouth, if installed and running. + if type plymouth >/dev/null 2>&1 && plymouth --ping 2>/dev/null; then + plymouth ask-for-password \ + --prompt "$ply_prompt" --number-of-tries=$ply_tries \ + --command="$ply_cmd" + ret=$? + else + if [ "$tty_echo_off" = yes ]; then + stty_orig="$(stty -g)" + stty -echo + fi + + local i=1 + while [ $i -le $tty_tries ]; do + [ -n "$tty_prompt" ] && \ + printf "$tty_prompt [$i/$tty_tries]:" >&2 + eval "$tty_cmd" && ret=0 && break + ret=$? + i=$(($i+1)) + [ -n "$tty_prompt" ] && printf '\n' >&2 + done + + [ "$tty_echo_off" = yes ] && stty $stty_orig + fi + } 9>/.console_lock + + [ $ret -ne 0 ] && echo "Wrong password" >&2 + return $ret +} + +# Try to mount specified device (by path, by UUID or by label) and check +# the path with 'test'. +# +# example: +# test_dev -f LABEL="nice label" /some/file1 +test_dev() { + local test_op=$1; local dev="$2"; local f="$3" + local ret=1; local mount_point=$(mkuniqdir /mnt testdev) + local path + + [ -n "$dev" -a -n "$*" ] || return 1 + [ -d "$mount_point" ] || die 'Mount point does not exist!' + + if mount -r "$dev" "$mount_point" >/dev/null 2>&1; then + test $test_op "${mount_point}/${f}" + ret=$? + umount "$mount_point" + fi + + rmdir "$mount_point" + + return $ret +} + +# match_dev devpattern dev +# +# Returns true if 'dev' matches 'devpattern'. Both 'devpattern' and 'dev' are +# expanded to kernel names and then compared. If name of 'dev' is on list of +# names of devices matching 'devpattern', the test is positive. 'dev' and +# 'devpattern' may be anything which function 'devnames' recognizes. +# +# If 'devpattern' is empty or '*' then function just returns true. +# +# Example: +# match_dev UUID=123 /dev/dm-1 +# Returns true if /dev/dm-1 UUID starts with "123". +match_dev() { + [ -z "$1" -o "$1" = '*' ] && return 0 + local devlist; local dev + + devlist="$(devnames "$1")" || return 255 + dev="$(devnames "$2")" || return 255 + + strstr " +$devlist +" " +$dev +" +} + +# getkey keysfile for_dev +# +# Reads file produced by probe-keydev and looks for first line to +# which device matches. The successful result is printed in format +# ":". When nothing found, just false is returned. +# +# Example: +# getkey /tmp/luks.keys /dev/sdb1 +# May print: +# /dev/sdc1:/keys/some.key +getkey() { + local keys_file="$1"; local for_dev="$2" + local luks_dev; local key_dev; local key_path + + [ -z "$keys_file" -o -z "$for_dev" ] && die 'getkey: wrong usage!' + [ -f "$keys_file" ] || return 1 + + local IFS=: + while read luks_dev key_dev key_path || [ -n "$luks_dev" ]; do + if match_dev "$luks_dev" "$for_dev"; then + echo "${key_dev}:${key_path}" + return 0 + fi + done < "$keys_file" + + return 1 +} + +# readkey keypath keydev device +# +# Mounts , reads key from file , optionally processes it (e.g. +# if encrypted with GPG) and prints to standard output which is supposed to be +# read by cryptsetup. is just passed to helper function for +# informational purpose. +readkey() { + local keypath="$1" + local keydev="$2" + local device="$3" + + # No mounting needed if the keyfile resides inside the initrd + if [ "/" == "$keydev" ]; then + local mntp=/ + else + # This creates a unique single mountpoint for *, or several for explicitly + # given LUKS devices. It accomplishes unlocking multiple LUKS devices with + # a single password entry. + local mntp="/mnt/$(str_replace "keydev-$keydev-$keypath" '/' '-')" + + if [ ! -d "$mntp" ]; then + mkdir "$mntp" + mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' + fi + fi + + case "${keypath##*.}" in + gpg) + if [ -f /lib/dracut-crypt-gpg-lib.sh ]; then + . /lib/dracut-crypt-gpg-lib.sh + gpg_decrypt "$mntp" "$keypath" "$keydev" "$device" + else + die "No GPG support to decrypt '$keypath' on '$keydev'." + fi + ;; + img) + if [ -f /lib/dracut-crypt-loop-lib.sh ]; then + . /lib/dracut-crypt-loop-lib.sh + loop_decrypt "$mntp" "$keypath" "$keydev" "$device" + printf "%s\n" "umount \"$mntp\"; rmdir \"$mntp\";" > ${hookdir}/cleanup/"crypt-loop-cleanup-99-${mntp##*/}".sh + return 0 + else + die "No loop file support to decrypt '$keypath' on '$keydev'." + fi + ;; + fido2) + if [ -f /lib/dracut-crypt-fido2-lib.sh ]; then + . /lib/dracut-crypt-fido2-lib.sh + fido2_decrypt "$mntp" "$keypath" "$keydev" "$device" + else + die "No FIDO2 support to decrypt '$keypath' on '$keydev'." + fi + ;; + *) cat "$mntp/$keypath" ;; + esac + + # No unmounting if the keyfile resides inside the initrd + if [ "/" != "$keydev" ]; then + # General unmounting mechanism, modules doing custom cleanup should return earlier + # and install a pre-pivot cleanup hook + umount "$mntp" + rmdir "$mntp" + fi +} diff --git a/dracut/crypt-lib.sh.patch b/dracut/crypt-lib.sh.patch new file mode 100644 index 0000000..164f683 --- /dev/null +++ b/dracut/crypt-lib.sh.patch @@ -0,0 +1,17 @@ +--- crypt-lib.sh 2019-09-18 16:30:43.546776135 +0200 ++++ /usr/lib/dracut/modules.d/90crypt/crypt-lib.sh 2018-10-08 15:38:33.000000000 +0200 +@@ -224,14 +224,6 @@ + die "No loop file support to decrypt '$keypath' on '$keydev'." + fi + ;; +- fido2) +- if [ -f /lib/dracut-crypt-fido2-lib.sh ]; then +- . /lib/dracut-crypt-fido2-lib.sh +- fido2_decrypt "$mntp" "$keypath" "$keydev" "$device" +- else +- die "No FIDO2 support to decrypt '$keypath' on '$keydev'." +- fi +- ;; + *) cat "$mntp/$keypath" ;; + esac + diff --git a/dracut/dracut-crypt-fido2-lib.sh b/dracut/dracut-crypt-fido2-lib.sh new file mode 100644 index 0000000..6f19214 --- /dev/null +++ b/dracut/dracut-crypt-fido2-lib.sh @@ -0,0 +1,17 @@ +#!/usr/bin/sh + +command -v ask_for_password >/dev/null || . /lib/dracut-crypt-lib.sh + +fido2_decrypt() { + #Unused local mntp="$1" + local keypath="$2" + #Unused local keydev=$3 + #Unused local device=$4 + export FIDO2LUKS_CREDENTIAL_ID="${keypath%.*}" + export FIDO2LUKS_SALT="$(getargs rd.fido2luks.salt)" + export FIDO2LUKS_PASSWORD_HELPER="/usr/bin/systemd-ask-password 'Disk 2fa password'" + if [ -z "$FIDO2LUKS_SALT" ]; then + export FIDO2LUKS_SALT="Ask" + fi + /bin/fido2luks print-secret | xxd -r -p - +} diff --git a/dracut/fido2luks.sh b/dracut/fido2luks.sh index f16b326..cc891e5 100644 --- a/dracut/fido2luks.sh +++ b/dracut/fido2luks.sh @@ -1,6 +1,6 @@ #!/bin/sh -LUKS_DEVICES="$(getargs rd.fido2luks.devices | tr ' ' '\n'| cut -d '-' -f 2-)" +LUKS_DEVICES="$(getargs rd.fido2luks.devices | tr ' ' '\n')" CREDENTIAL_ID="$(getargs rd.fido2luks.credentialid)" SALT="$(getargs rd.fido2luks.salt)" @@ -34,16 +34,24 @@ handle_authenticator() { for DEV in $LUKS_DEVICES ; do export FIDO2LUKS_DEVICE="$DEV" export FIDO2LUKS_MAPPER_NAME="${MAPPER_NAME:-luks-$DEV}" - display_msg_timeout "Watch your authenicator" - ERR="$(/bin/f2l open -e 2>&1)" - if [ "$?" -eq 1 ]; then - display_msg_timeout "Failed to unlock: $ERR" - sleep 5 - else - exit 1 - fi + TRIES="0" + while true; do + ERR="$(/bin/f2l open -e 2>&1)" + if [ "$?" -eq 1 ]; then + display_msg_timeout "Failed to unlock: $ERR" + TRIES="$[$TRIES+1]" + if [ "$TRIES" -gt 5 ]; then + exit 1 + fi + sleep 5 + else + exit 0 + fi + done done } -handle_authenticator +if [ ! -z "$LUKS_DEVICES" ]; then + handle_authenticator +fi diff --git a/dracut/module-setup.sh b/dracut/module-setup.sh index 7da1483..5944e9d 100755 --- a/dracut/module-setup.sh +++ b/dracut/module-setup.sh @@ -12,8 +12,11 @@ depends() { } install() { + + patch + #inst_hook pre-trigger 91 "$moddir/ykluks.sh" - inst_hook initqueue 01 "$moddir/fido2luks.sh" + #inst_hook initqueue 01 "$moddir/fido2luks.sh" #inst_hook pre-mount 1 "$moddir/fix_crypttab.sh" #inst_hook pre-trigger 10 "$moddir/ykluks.sh" #inst_hook cmdline 5 "$moddir/ykluks.sh" @@ -36,8 +39,9 @@ install() { #inst_simple "/usr/bin/true" "/bin/true" #inst_simple "/usr/sbin/blkid" "/bin/blkid" #inst_simple "/usr/bin/ykchalresp" "/bin/ykchalresp" - inst_simple "/etc/fido2luks.json" "/etc/fido2luks.json" - inst_simple "$moddir/fido2luks" "/bin/f2l" + inst_simple "/usr/bin/xxd" "/usr/bin/xxd" + inst_simple "$moddir/fido2luks" "/usr/bin/fido2luks" + inst_simple "$moddir/dracut-crypt-fido2-lib.sh" "/lib/dracut-crypt-fido2-lib.sh" #inst_rules "$moddir/20-ykfde.rules" inst_rules "/usr/lib/udev/rules.d/60-u2f-hidraw.rules" #inst_simple "$moddir/ykluks.sh" "/bin/ykluks.sh" @@ -52,7 +56,7 @@ install() { $systemdsystemunitdir/systemd-ask-password-console.service \ systemd-ask-password systemd-tty-ask-password-agent - dracut_need_initqueue + #dracut_need_initqueue } installkernel() {