All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] 90crypt: key on rem. device enhancements
@ 2010-09-24 21:18 Amadeusz Żołnowski
       [not found] ` <4c9e7c0d.d39ccc0a.676a.684a-ATjtLOhZ0NVl57MIdRCFDg@public.gmane.org>
  0 siblings, 1 reply; 3+ messages in thread
From: Amadeusz Żołnowski @ 2010-09-24 21:18 UTC (permalink / raw)


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

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [RFC] 90crypt: key on rem. device enhancements
       [not found] ` <4c9e7c0d.d39ccc0a.676a.684a-ATjtLOhZ0NVl57MIdRCFDg@public.gmane.org>
@ 2010-09-30 23:28   ` Karel Zak
       [not found]     ` <20100930232719.GC6389-sHeGUpI7y9L/9pzu0YdTqQ@public.gmane.org>
  0 siblings, 1 reply; 3+ messages in thread
From: Karel Zak @ 2010-09-30 23:28 UTC (permalink / raw)
  To: Amadeusz Żołnowski; +Cc: initramfs-u79uwXL29TY76Z2rM5mHXA

On Fri, Sep 24, 2010 at 11:18:07PM +0200, Amadeusz Żołnowski wrote:
> +# 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=}" ] \

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

 ...

> +# 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" ;;

 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

-- 
 Karel Zak  <kzak-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
 http://karelzak.blogspot.com

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [RFC] 90crypt: key on rem. device enhancements
       [not found]     ` <20100930232719.GC6389-sHeGUpI7y9L/9pzu0YdTqQ@public.gmane.org>
@ 2010-10-03 20:04       ` Amadeusz Żołnowski
  0 siblings, 0 replies; 3+ messages in thread
From: Amadeusz Żołnowski @ 2010-10-03 20:04 UTC (permalink / raw)
  To: Karel Zak; +Cc: initramfs

[-- Attachment #1: Type: text/plain, Size: 2231 bytes --]

Excerpts from Karel Zak's message of Fri Oct 01 01:28:09 +0200 2010:
> On Fri, Sep 24, 2010 at 11:18:07PM +0200, Amadeusz Żołnowski wrote:
> > +# 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=}" ] \
> 
>  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. :-)

>  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,
-- 
Amadeusz Żołnowski

PGP key: 1024D/C284750D

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 198 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2010-10-03 20:04 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-09-24 21:18 [RFC] 90crypt: key on rem. device enhancements Amadeusz Żołnowski
     [not found] ` <4c9e7c0d.d39ccc0a.676a.684a-ATjtLOhZ0NVl57MIdRCFDg@public.gmane.org>
2010-09-30 23:28   ` Karel Zak
     [not found]     ` <20100930232719.GC6389-sHeGUpI7y9L/9pzu0YdTqQ@public.gmane.org>
2010-10-03 20:04       ` Amadeusz Żołnowski

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.