Skip to contentSkip to conversation

90crypt: key on rem. device enhancements

RFC In review Amadeusz Żołnowski 2 participants 3 messages Sep 24, 2010 – Oct 3, 2010 2 messages not in the archive

lore
Share & tools
lore
#0 lore raw

RFC 1/1 90crypt: key on rem. device enhancements

Hi,

I'm doing some major modification of crypt module. Here's what I've done. If that's OK, I'll send cleaned up patch with docs update. Any remarks at this point are appreciated.

99base/dracut-lib.sh: new func.: do_mount, funiq, mkuniqdir

90crypt/cryptroot-ask.sh: new func.: search_key_for

The patch introduces support for labels and normal dev names for removable media storing key for LUKS. Previously only UUID was possible. rd_LUKS_KEYDEV_UUID changes to rd_LUKS_KEYDEV which takes dev names as in /etc/fstab (e.g.: rd_LUKS_KEYDEV=LABEL=boot).

It also fixes problem with devices not discovered on time by retrying (for default 3 times with incrementing sleep period) the scan.

probe_keydev informs which device it checks.

Moreover code (the rem. dev. part) is refactorized.

---
 modules.d/90crypt/cryptroot-ask.sh |   98 ++++++++++++++++++----------
 modules.d/90crypt/install          |    1 +
 modules.d/99base/dracut-lib.sh     |  123 ++++++++++++++++++++++++++++++++++--
 3 files changed, 182 insertions(+), 40 deletions(-)
modules.d/90crypt/cryptroot-ask.sh +63 -35
index 45b5fe7..1d89aff 100755
@@ -58,53 +58,82 @@ fi
 #
 
 # 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>",
+# the paths.  On success return 0 and print "<uuid>\t<first-existing-path>",
 # otherwise return 1.
-# Function leaves mount point created.
+#
+# $1 = dev
+# $2 = keypaths
 probe_keydev() {
-    local uuid="$1"; shift; local keypaths="$*"
-    local ret=1; local mount_point=/mnt/keydev
+    local dev="$1"; shift; local keypaths="$*"
+    local ret=1; local mount_point=$(mkuniqdir /mnt keydev)
     local path
 
-    [ -n "${uuid}" -a -n "${keypaths}" ] || return 1
-    [ -d ${mount_point} ] || mkdir -p "${mount_point}" || return 1
+    [ -n "${dev}" -a -n "${keypaths}" ] || return 1
+    [ -d "${mount_point}" ] || die 'Mount point does not exist!'
 
-    if mount -r -U "${uuid}" "${mount_point}" 2>/dev/null >/dev/null; then
+    info "cryptroot-ask: Probing ${dev}..."
+    if do_mount -q "${dev}" "${mount_point}"; then
         for path in ${keypaths}; do
             if [ -f "${mount_point}/${path}" ]; then
-                echo "${uuid} ${path}"
+                echo "${dev}	${path}"
                 ret=0
                 break
             fi
         done
+
+        umount "${mount_point}"
     fi
 
-    umount "${mount_point}" 2>/dev/null >/dev/null
+    rmdir "${mount_point}"
 
     return ${ret}
 }
 
-keypaths="$(getargs rd_LUKS_KEYPATH)"
-unset keydev_uuid keypath
-
-if [ -n "$keypaths" ]; then
-    keydev_uuids="$(getargs 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
+# $1 = encrypted device
+# $2 = tries (optional; default is 3)
+# rd_LUKS_KEYPATH
+# rd_LUKS_KEYDEV
+#
+# returns 0 if search succeeded
+# returns 1 if search failed
+# returns 2 if search skipped (when rd_LUKS_KEYPATH not specified)
+search_key_for() {
+    local device="$1"; local tries="$2"
+    local keypaths="$(getargs rd_LUKS_KEYPATH)"; local keydevs
+
+    if [ -n "$keypaths" ]; then
+        keydevs="$(getargs rd_LUKS_KEYDEV)"
+        [ -n "$keydevs" ] || {
+            warn 'No device storing LUKS key specified.'
+            warn 'It is recommended to set rd_LUKS_KEYDEV (best by UUID).'
+            warn 'Performing scan of *all* devices accessible by UUID...'
+        }
+
+        { [ -z "$2" ] || [ $2 -lt 1 ]; } && tries=3
+        local i=0
+        while [ $i -lt 3 ]; do
+            sleep $i
+            # following outputs "$keydev\t$keypath" (on success only)
+            foreach_dev_until "probe_keydev \$___ $keypaths" $keydevs && \
+                    return 0
+            warn "Key for $device not found.  Trying again..."
+            i=$(($i+1))
+        done
+
+        return 1
+    fi
+
+    return 2
+}
+
+mkdir -p /mnt
+
+tmp=$(search_key_for $device) && {
+    keydev="${tmp%%	*}"
+    keypath="${tmp#*	}"
+}
+unset tmp
 
-unset keypaths
 
 #
 # Open LUKS device
@@ -112,14 +141,13 @@ unset keypaths
 
 info "luksOpen $device $luksname"
 
-if [ -n "$keydev_uuid" ]; then
-    mntp=/mnt/keydev
-    mkdir -p "$mntp"
-    mount -r -U "$keydev_uuid" "$mntp"
+if [ -n "$keydev" ]; then
+    mntp=$(mkuniqdir /mnt keydev)
+    do_mount "$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?
modules.d/90crypt/install +1 -0
index a518bc3..e4dfa47 100755
@@ -2,6 +2,7 @@
 # -*- 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 "$moddir"/cryptroot-ask.sh /sbin/cryptroot-ask
 inst_hook cmdline 30 "$moddir/parse-crypt.sh"
 inst_hook pre-pivot 30 "$moddir/crypt-cleanup.sh"
modules.d/99base/dracut-lib.sh +118 -5

156 lines not on this page · raw message

Signature · 1 line
1.7.3

A message that is not in the archive (sent off-list or not yet received): 4c9e7c0d.d39ccc0a.676a.684a-ATjtLOhZ0NVl57MIdRCFDg@public.gmane.org

#2 lore raw
Quoted 19 lines · Amadeusz Żołnowski
modules.d/99base/dracut-lib.sh +19 -0
+# Evaluate command for every given device.  Every single device must be
+# specified either by path, by label prefixed with 'LABEL=' or UUID prefixed
+# with 'UUID='.  UUIDs are processed by 'foreach_uuid_until'.  List elements'
+# order is preserved.
+#
+# foreach_dev_until command devices
+#
+# command - command to be evaluated
+# devices - list of devices separated by space
+#
+# The function returns after *first successful evaluation* of the given command
+# with status 0.  If evaluation fails for every device, function returns with
+# status 1.
+#
+# Example:
+# foreach_dev_until "echo \$___; false" "/dev/sda1 LABEL=boot UUID=123a"
+foreach_dev_until() {
+    local cmd="$1"; shift; local devs_list="$*"
+    local dev; local ___

modules.d/99base/dracut-lib.sh ip_to_var() { · line 357

Quoted 10 lines
modules.d/99base/dracut-lib.sh +10 -0
+
+    [ -n "${cmd}" ] || return 1
+
+    if [ -n "${devs_list}" ]; then
+        for dev in ${devs_list}; do
+            if strstarts "${dev}" 'UUID='; then
+                foreach_uuid_until -p 'UUID=' "${cmd}" "${dev#UUID=*}" && \
+                        return 0
+            else
+                [ -e "${dev}" ] || [ -e "/dev/disk/by-label/${dev#LABEL=}" ] \

hmm.. how does it work with blank or non-ascii chars in LABELs?

...

Quoted 20 lines
modules.d/99base/dracut-lib.sh +20 -0
+# It's a wrapper around 'mount' command.  In addition to 'mount' you can specify
+# device name like "UUID=01234567-89ab-cdef-0123-4567890abcde" and
+# "LABEL=fun_label".  'do_mount' mounts device as read-only for default.  To
+# mount as writable pass '-w' option.  'mount_point' argument is required.
+#
+# do_mount [options] dev mount_point
+#
+# Extra options:
+#   -q    Suppress any output.
+#
+# Example:
+# do_mount -q -w LABEL=blah /mnt/disk
+do_mount() {
+    local args; local quiet
+
+    while [ $# -gt 2 ]; do
+        case $1 in
+            -q) quiet='2>/dev/null >/dev/null' ;;
+            -v) quiet=''; args="${args} $1" ;;
+            *) args="${args} $1" ;;

modules.d/99base/dracut-lib.sh ip_to_var() { · line 399

Quoted 10 lines
modules.d/99base/dracut-lib.sh +10 -0
+        esac
+        shift
+    done
+
+    local dev_name="$1"; local dev="${dev_name#*=}"; local mount_point="$2"
+
+    case ${dev_name} in
+        -*) die 'do_mount - wrong usage!' ;;
+        UUID=*) args="${args} -U" ;;
+        LABEL=*) args="${args} -L" ;;

mount(8) supports LABEL= and UUID= tags, for example

mount LABEL=boot /mnt/test

so, you does not need to convert these tags to -L or -U options.

Karel

Signature · 2 lines
 Karel Zak  <[email protected]>
 http://karelzak.blogspot.com

A message that is not in the archive (sent off-list or not yet received): 20100930232719.GC6389-sHeGUpI7y9L/[email protected]

#4 lore raw

Excerpts from Karel Zak's message of Fri Oct 01 01:28:09 +0200 2010:

Quoted 24 lines · Amadeusz Żołnowski
Quoted 23 lines
modules.d/99base/dracut-lib.sh +23 -0
+# Evaluate command for every given device.  Every single device must be
+# specified either by path, by label prefixed with 'LABEL=' or UUID prefixed
+# with 'UUID='.  UUIDs are processed by 'foreach_uuid_until'.  List elements'
+# order is preserved.
+#
+# foreach_dev_until command devices
+#
+# command - command to be evaluated
+# devices - list of devices separated by space
+#
+# The function returns after *first successful evaluation* of the given command
+# with status 0.  If evaluation fails for every device, function returns with
+# status 1.
+#
+# Example:
+# foreach_dev_until "echo \$___; false" "/dev/sda1 LABEL=boot UUID=123a"
+foreach_dev_until() {
+    local cmd="$1"; shift; local devs_list="$*"
+    local dev; local ___
+
+    [ -n "${cmd}" ] || return 1
+
+    if [ -n "${devs_list}" ]; then
Quoted 8 lines

modules.d/99base/dracut-lib.sh ip_to_var() { · line 357

Quoted 6 lines
modules.d/99base/dracut-lib.sh +6 -0
+        for dev in ${devs_list}; do
+            if strstarts "${dev}" 'UUID='; then
+                foreach_uuid_until -p 'UUID=' "${cmd}" "${dev#UUID=*}" && \
+                        return 0
+            else
+                [ -e "${dev}" ] || [ -e "/dev/disk/by-label/${dev#LABEL=}" ] \

hmm.. how does it work with blank or non-ascii chars in LABELs?

Thank you for pointing this out!

It doesn't work with non-printable or spaces. Moreover 'getargs' doesn't expect spaces (even quoted with ""), so you cannot specify LABEL including those chars.

I've just fixed issues around 'foreach_dev_until'. (I'll send patch in a moment.) Label may contain spaces, tabs, greek letters or whatever now. I still left 'getopts' as is, so this new feature isn't easly available yet, but I'll fix that - don't worry. :-)

Quoted 5 lines

mount(8) supports LABEL= and UUID= tags, for example

mount LABEL=boot /mnt/test

so, you does not need to convert these tags to -L or -U options.

I'm amazed I've missed that feature. Thank you for highlighting it! I've removed the redundant 'do_mount'.

Cheers,

Signature · 3 lines
Amadeusz Żołnowski

PGP key: 1024D/C284750D