90crypt: probe for keydev asynchronously; changed kernel arg

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>.
master
Amadeusz Żołnowski 2010-11-12 10:21:49 +01:00 committed by Harald Hoyer
parent ccb0ab7348
commit 8844cd6b6c
6 changed files with 201 additions and 66 deletions

View File

@ -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
}

View File

@ -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
@ -53,73 +54,31 @@ if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -n rd_NO_CRYPTTAB; then
unset name dev rest
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?

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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