New kernel argument syntax for LUKS-keydev is introduced: rd.luks.key=<key_path>[:<key_dev>[:<luks_dev>]] Unfolding <key_dev> in BNF: <key_dev> ::= "UUID=" <uuid> | "LABEL=" <label> | <kname> Where <kname> matches following regular expression: ^/dev/.* <kname> need to be a character device and not a symlink for now. For every rd.luks.key argument udev rule is created. That rule runs test to check whether matching device contains <key_path>. If it does it's applied to matching <luks_dev>. --- modules.d/90crypt/crypt-lib.sh | 119 ++++++++++++++++++++++++++++++++++++ modules.d/90crypt/cryptroot-ask.sh | 79 ++++++------------------ modules.d/90crypt/install | 5 ++ modules.d/90crypt/parse-crypt.sh | 8 +-- modules.d/90crypt/parse-keydev.sh | 41 ++++++++++++ modules.d/90crypt/probe-keydev.sh | 15 +++++ 6 files changed, 201 insertions(+), 66 deletions(-) create mode 100644 modules.d/90crypt/crypt-lib.sh create mode 100644 modules.d/90crypt/parse-keydev.sh create mode 100755 modules.d/90crypt/probe-keydev.sh diff --git a/modules.d/90crypt/crypt-lib.sh b/modules.d/90crypt/crypt-lib.sh new file mode 100644 index 0000000..eee60fb --- /dev/null +++ b/modules.d/90crypt/crypt-lib.sh @@ -0,0 +1,119 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +. /lib/dracut-lib.sh + +# 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 +} + +# Get kernel name for given device. Device may be the name too (then the same +# is returned), a symlink (full path), UUID (prefixed with "UUID=") or label +# (prefixed with "LABEL="). If just a beginning of the UUID is specified or +# even an empty, function prints all device names which UUIDs match - every in +# single line. +# +# NOTICE: The name starts with "/dev/". +# +# Example: +# devnames UUID=123 +# May print: +# /dev/dm-1 +# /dev/sdb1 +# /dev/sdf3 +devnames() { + local dev="$1"; local d; local names + + case "$dev" in + UUID=*) + dev="$(foreach_uuid_until '! blkid -U $___' "${dev#UUID=}")" \ + && return 255 + [ -z "$dev" ] && return 255 + ;; + LABEL=*) dev="$(blkid -L "${dev#LABEL=}")" || return 255 ;; + /dev/?*) ;; + *) return 255 ;; + esac + + for d in $dev; do + names="$names +$(readlink -e -q "$d")" || return 255 + done + + echo "${names# +}" +} + +# 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 <keysfile> produced by probe-keydev and looks for first line to +# which device <for_dev> matches. The successful result is printed in format +# "<keydev>|<keypath>". 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 + + while IFS='|' read luks_dev key_dev key_path; do + if match_dev "$luks_dev" "$for_dev"; then + echo "${key_dev}|${key_path}" + return 0 + fi + done < "$keys_file" + + return 1 +} diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh index 6c3acb6..bb8e81e 100755 --- a/modules.d/90crypt/cryptroot-ask.sh +++ b/modules.d/90crypt/cryptroot-ask.sh @@ -14,7 +14,7 @@ # load dm_crypt if it is not already loaded [ -d /sys/module/dm_crypt ] || modprobe dm_crypt -. /lib/dracut-lib.sh +. /lib/dracut-crypt-lib.sh # default luksname - luks-UUID luksname=$2 @@ -26,6 +26,7 @@ else device="$1" fi +# TODO: improve to support what cmdline does if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -n rd_NO_CRYPTTAB; then while read name dev rest; do # ignore blank lines and comments @@ -54,72 +55,30 @@ if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -n rd_NO_CRYPTTAB; then fi # -# Search key on external devices -# - -# Try to mount device specified by UUID and probe for existence of any of -# the paths. On success return 0 and print "<uuid> <first-existing-path>", -# otherwise return 1. -# Function leaves mount point created. -probe_keydev() { - local uuid="$1"; shift; local keypaths="$*" - local ret=1; local mount_point=/mnt/keydev - local path - - [ -n "${uuid}" -a -n "${keypaths}" ] || return 1 - [ -d ${mount_point} ] || mkdir -p "${mount_point}" || return 1 - - if mount -r -U "${uuid}" "${mount_point}" 2>/dev/null >/dev/null; then - for path in ${keypaths}; do - if [ -f "${mount_point}/${path}" ]; then - echo "${uuid} ${path}" - ret=0 - break - fi - done - fi - - umount "${mount_point}" 2>/dev/null >/dev/null - - return ${ret} -} - -keypaths="$(getargs rd.luks.keypath rd_LUKS_KEYPATH)" -unset keydev_uuid keypath - -if [ -n "$keypaths" ]; then - keydev_uuids="$(getargs rd.luks.keydev.uuid rd_LUKS_KEYDEV_UUID)" - [ -n "$keydev_uuids" ] || { - warn 'No UUID of device storing LUKS key specified.' - warn 'It is recommended to set rd_LUKS_KEYDEV_UUID.' - warn 'Performing scan of *all* devices accessible by UUID...' - } - tmp=$(foreach_uuid_until "probe_keydev \$full_uuid $keypaths" \ - $keydev_uuids) && { - keydev_uuid="${tmp%% *}" - keypath="${tmp#* }" - } || { - warn "Key for $device not found." - } - unset tmp keydev_uuids -fi - -unset keypaths - -# # Open LUKS device # info "luksOpen $device $luksname" -if [ -n "$keydev_uuid" ]; then - mntp=/mnt/keydev - mkdir -p "$mntp" - mount -r -U "$keydev_uuid" "$mntp" +if [ -n "$(getarg rd.luks.key)" ]; then + if tmp=$(getkey /tmp/luks.keys $device); then + keydev="${tmp%%|*}" + keypath="${tmp#*|}" + else + info "No key found for $device. Will try later." + /sbin/initqueue --unique --onetime --settled \ + --name cryptroot-ask-$luksname \ + /sbin/cryptroot-ask "$@" + exit 0 + fi + unset tmp + + mntp=$(mkuniqdir /mnt keydev) + mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' cryptsetup -d "$mntp/$keypath" luksOpen "$device" "$luksname" umount "$mntp" - rmdir -p "$mntp" 2>/dev/null - unset mntp keypath keydev_uuid + rmdir "$mntp" + unset mntp keypath keydev else # Prompt for password with plymouth, if installed. # Should we check if plymouthd is running? diff --git a/modules.d/90crypt/install b/modules.d/90crypt/install index a518bc3..12b3555 100755 --- a/modules.d/90crypt/install +++ b/modules.d/90crypt/install @@ -2,7 +2,12 @@ # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh inst cryptsetup +inst rmdir +inst readlink inst "$moddir"/cryptroot-ask.sh /sbin/cryptroot-ask +inst "$moddir"/probe-keydev.sh /sbin/probe-keydev +inst_hook cmdline 10 "$moddir/parse-keydev.sh" inst_hook cmdline 30 "$moddir/parse-crypt.sh" inst_hook pre-pivot 30 "$moddir/crypt-cleanup.sh" inst /etc/crypttab +inst "$moddir/crypt-lib.sh" "/lib/dracut-crypt-lib.sh" diff --git a/modules.d/90crypt/parse-crypt.sh b/modules.d/90crypt/parse-crypt.sh index 3baad5f..0dca0d3 100755 --- a/modules.d/90crypt/parse-crypt.sh +++ b/modules.d/90crypt/parse-crypt.sh @@ -11,10 +11,6 @@ else } > /etc/udev/rules.d/70-luks.rules LUKS=$(getargs rd.luks.uuid rd_LUKS_UUID) - unset settled - [ -n "$(getargs rd.luks.keypath rd_LUKS_KEYPATH)" ] && \ - [ -z "$(getargs rd.luks.keydev.uuid rd_LUKS_KEYDEV_UUID)" ] && \ - settled='--settled' if [ -n "$LUKS" ]; then for luksid in $LUKS; do @@ -22,7 +18,7 @@ else { printf 'ENV{ID_FS_TYPE}=="crypto_LUKS", ' printf 'ENV{ID_FS_UUID}=="*%s*", ' $luksid - printf 'RUN+="/sbin/initqueue --unique --onetime %s ' "$settled" + printf 'RUN+="/sbin/initqueue --unique --onetime ' printf -- '--name cryptroot-ask-%%k /sbin/cryptroot-ask ' printf '$env{DEVNAME} luks-$env{ID_FS_UUID}"\n' } >> /etc/udev/rules.d/70-luks.rules @@ -35,7 +31,7 @@ else } >> /emergency/00-crypt.sh done else - echo 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="/sbin/initqueue' $settled \ + echo 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="/sbin/initqueue' \ '--unique --onetime --name cryptroot-ask-%k' \ '/sbin/cryptroot-ask $env{DEVNAME} luks-$env{ID_FS_UUID}"' \ >> /etc/udev/rules.d/70-luks.rules diff --git a/modules.d/90crypt/parse-keydev.sh b/modules.d/90crypt/parse-keydev.sh new file mode 100644 index 0000000..8712a46 --- /dev/null +++ b/modules.d/90crypt/parse-keydev.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +if getargbool 1 rd.luks -n rd_NO_LUKS && \ + [ -n "$(getarg rd.luks.key)" ]; then + exec 7>/etc/udev/rules.d/65-luks-keydev.rules + echo 'SUBSYSTEM!="block", GOTO="luks_keydev_end"' >&7 + echo 'ACTION!="add|change", GOTO="luks_keydev_end"' >&7 + + for arg in $(getargs rd.luks.key); do + unset keypath keydev luksdev + splitsep : "$arg" keypath keydev luksdev + + info "rd.luks.key: keypath='$keypath' keydev='$keydev' luksdev='$luksdev'" + + if [ -z "$keypath" ]; then + warn 'keypath required!' + continue + fi + + if [ -n "$keydev" ]; then + udevmatch "$keydev" >&7 || { + warn 'keydev incorrect!' + continue + } + printf ', ' >&7 + fi + + { + printf 'RUN+="/sbin/initqueue --unique --onetime ' + printf -- '--name probe-keydev-%%k ' + printf '/sbin/probe-keydev /dev/%%k %s %s"\n' \ + "${keypath}" "${luksdev}" + } >&7 + done + unset arg keypath keydev luksdev + + echo 'LABEL="luks_keydev_end"' >&7 + exec 7>&- +fi diff --git a/modules.d/90crypt/probe-keydev.sh b/modules.d/90crypt/probe-keydev.sh new file mode 100755 index 0000000..8fdbc1a --- /dev/null +++ b/modules.d/90crypt/probe-keydev.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +. /lib/dracut-crypt-lib.sh + + +real_keydev="$1"; keypath="$2"; luksdev="$3" + +[ -z "$real_keydev" -o -z "$keypath" ] && die 'probe-keydev: wrong usage!' +[ -z "$luksdev" ] && luksdev='*' + +info "Probing $real_keydev for $keypath..." +test_dev -f "$real_keydev" "$keypath" || exit 1 + +info "Found $keypath on $real_keydev" +echo "$luksdev|$real_keydev|$keypath" >> /tmp/luks.keys -- 1.7.3.2 -- To unsubscribe from this list: send the line "unsubscribe initramfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html