[RFC] 90crypt: key on rem. device enhancements

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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(-)

diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh
index 45b5fe7..1d89aff 100755
--- a/modules.d/90crypt/cryptroot-ask.sh
+++ b/modules.d/90crypt/cryptroot-ask.sh
@@ -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?
diff --git a/modules.d/90crypt/install b/modules.d/90crypt/install
index a518bc3..e4dfa47 100755
--- a/modules.d/90crypt/install
+++ b/modules.d/90crypt/install
@@ -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"
diff --git a/modules.d/99base/dracut-lib.sh b/modules.d/99base/dracut-lib.sh
index 627d2c5..786c7e5 100755
--- a/modules.d/99base/dracut-lib.sh
+++ b/modules.d/99base/dracut-lib.sh
@@ -6,6 +6,11 @@ strstr() {
     [ "${1#*$2*}" != "$1" ]
 }
 
+# returns OK if $1 contains $2 at the beginning
+strstarts() {
+    [ "${1#$2*}" != "$1" ]
+}
+
 getarg() {
     set +x 
     local o line val
@@ -286,32 +291,140 @@ ip_to_var() {
 # Evaluate command for UUIDs either given as arguments for this function or all
 # listed in /dev/disk/by-uuid.  UUIDs doesn't have to be fully specified.  If
 # beginning is given it is expanded to all matching UUIDs.  To pass full UUID
-# to your command use '${full_uuid}'.  Remember to escape '$'!
+# to your command use '$___' as a place holder.  Remember to escape '$'!
+#
+# foreach_uuid_until [ -p prefix ] command UUIDs
 #
-# $1 = command to be evaluated
-# $2 = list of UUIDs separated by space
+# prefix - string to put just before $___
+# command - command to be evaluated
+# UUIDs - list of UUIDs separated by space
 #
 # The function returns after *first successful evaluation* of the given command
 # with status 0.  If evaluation fails for every UUID function returns with
 # status 1.
 #
 # Example:
-# foreach_uuid_until "mount -U \${full_uuid} /mnt; echo OK; umount /mnt" \
+# foreach_uuid_until "mount -U \$___ /mnt; echo OK; umount /mnt" \
 #       "01234 f512 a235567f-12a3-c123-a1b1-01234567abcb"
 foreach_uuid_until() (
     cd /dev/disk/by-uuid
 
+    [ "$1" = -p ] && local prefix="$2" && shift 2
     local cmd="$1"; shift; local uuids_list="$*"
-    local uuid; local full_uuid
+    local uuid; local full_uuid; local ___
 
     [ -n "${cmd}" ] || return 1
 
     for uuid in ${uuids_list:-*}; do
         for full_uuid in ${uuid}*; do
             [ -e "${full_uuid}" ] || continue
+            ___="${prefix}${full_uuid}"
             eval ${cmd} && return 0
         done
     done
 
     return 1
 )
+
+# 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
+        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=}" ] \
+                        || continue
+                ___="${dev}"
+                eval ${cmd} && return 0
+            fi
+        done
+    else
+        foreach_uuid_until -p 'UUID=' "${cmd}" && return 0
+    fi
+
+    return 1
+}
+
+# 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" ;;
+        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" ;;
+    esac
+
+    [ -n "${dev}" -a -n "${mount_point}" ] || die 'do_mount - wrong usage!'
+
+    eval mount -r ${args} \'${dev}\' \'${mount_point}\' ${quiet}
+}
+
+funiq() {
+    local dir="$1"; local prefix="$2"
+    local i=0
+
+    [ -d "${dir}" ] || return 1
+
+    while [ -e "${dir}/${prefix}$i" ]; do
+        i=$(($i+1)) || return 1
+    done
+
+    echo "${dir}/${prefix}$i"
+}
+
+mkuniqdir() {
+    local dir="$1"; local prefix="$2"
+    local retdir
+
+    retdir=$(funiq "${dir}" "${prefix}") || return 1
+    until mkdir "${retdir}"; do
+        retdir=$(funiq "${dir}" "${prefix}") || return 1
+    done
+
+    echo "${retdir}"
+}
-- 
1.7.3

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


[Index of Archives]     [Linux Kernel]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux