diff --git a/src/luks/clevis-luks-common-functions b/src/luks/clevis-luks-common-functions index dea8c877..c52defdf 100644 --- a/src/luks/clevis-luks-common-functions +++ b/src/luks/clevis-luks-common-functions @@ -312,3 +312,66 @@ clevis_luks_unlock_device() { return 1 } + +# clevis_map_device() tries to map the device received as a parameter to a +# block device. As per crypttab(5), we support /path/to/encrypted/blockdev +# or UUID=. +clevis_map_device() { + local CDEV="${1}" + + if [[ "${CDEV}" == UUID=* ]]; then + CDEV=/dev/disk/by-uuid/${CDEV#UUID=} + fi + + if [[ "${CDEV}" == /* ]] && [ -b "${CDEV}" ]; then + echo "${CDEV}" + else + # Invalid crypttab entry. + return 1 + fi +} + +# clevis_is_luks_device_by_uuid_open() checks whether the LUKS device whose +# UUID was passed as a parameter is already open. +clevis_is_luks_device_by_uuid_open() { + local dev_luks_uuid="${1}" + [ -z "${dev_luks_uuid}" ] && return 1 + dev_luks_uuid="$(echo "${dev_luks_uuid}" | sed -e 's/-//g')" + test -b /dev/disk/by-id/dm-uuid-*"${dev_luks_uuid}"* +} + +# clevis_devices_to_unlock() returns a list of devices to be unlocked, as per +# the info from crypttab. +clevis_devices_to_unlock() { + [ ! -r /etc/crypttab ] && return 1 + + local dev clevis_devices crypt_device dev_uuid bindings + clevis_devices= + + # Build list of devices to unlock. + while read -r _ crypt_device _; do + if ! dev=$(clevis_map_device "${crypt_device}") \ + || [ -z "${dev}" ]; then + # Unable to get the device - maybe it's not available, e.g. a + # device on a volume group that has not been activated yet. + # Add it to the list anyway, since it's a pending device. + clevis_devices="${clevis_devices} ${dev}" + continue + fi + + # Check if this device has clevis bindings. + if ! bindings="$(clevis luks list -d "${dev}" 2>/dev/null)" \ + || [ -z "${bindings}" ]; then + continue + fi + + # Check if this device is already open. + dev_uuid="$(cryptsetup luksUUID "${dev}")" + if clevis_is_luks_device_by_uuid_open "${dev_uuid}"; then + continue + fi + + clevis_devices="${clevis_devices} ${dev}" + done < /etc/crypttab + echo "${clevis_devices}" | sed -e 's/^ //' +} diff --git a/src/luks/clevis-luks-unlockers.7.adoc b/src/luks/clevis-luks-unlockers.7.adoc index 161b73ab..f56c9c18 100644 --- a/src/luks/clevis-luks-unlockers.7.adoc +++ b/src/luks/clevis-luks-unlockers.7.adoc @@ -47,9 +47,8 @@ You can enable late boot unlocking by executing the following command: $ sudo systemctl enable clevis-luks-askpass.path -After a reboot, Clevis will attempt to unlock all *_netdev* devices listed in -*/etc/crypttab* when systemd prompts for their passwords. This implies that -systemd support for *_netdev* is required. +After a reboot, Clevis will attempt to unlock all devices listed in +*/etc/crypttab* that have clevis bindings when systemd prompts for their passwords. == DESKTOP UNLOCKING diff --git a/src/luks/systemd/clevis-luks-askpass b/src/luks/systemd/clevis-luks-askpass index e46ec7a9..d17d1224 100755 --- a/src/luks/systemd/clevis-luks-askpass +++ b/src/luks/systemd/clevis-luks-askpass @@ -1,4 +1,5 @@ -#!/bin/bash -e +#!/bin/bash +set -eu # vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: # # Copyright (c) 2016 Red Hat, Inc. @@ -21,25 +22,24 @@ . clevis-luks-common-functions -shopt -s nullglob - +loop= path=/run/systemd/ask-password while getopts ":lp:" o; do - case "$o" in + case "${o}" in l) loop=true;; - p) path="$OPTARG";; + p) path="${OPTARG}";; *) ;; esac done while true; do - todo=0 + for question in "${path}"/ask.*; do + # question will expand to itself, in case no files match, so we verify + # whether it actually exists, before proceeding. + [ ! -e "${question}" ] && continue - for question in "$path"/ask.*; do - unlocked=false d= s= - while read -r line; do case "$line" in Id=cryptsetup:*) d="${line##Id=cryptsetup:}";; @@ -50,16 +50,22 @@ while true; do [ -b "${d}" ] || continue [ -S "${s}" ] || continue - if pt="$(clevis_luks_unlock_device "${d}")"; then - echo -n "+${pt}" | ncat -U -u --send-only "${s}" - unlocked=true + if ! pt="$(clevis_luks_unlock_device "${d}")" || [ -z "${pt}" ]; then + continue + fi + + uuid="$(cryptsetup luksUUID "${d}")" + if ! printf '+%s' "${pt}" | ncat -U -u --send-only "${s}"; then + echo "Unable to unlock ${d} (UUID=${uuid}) with recovered passphrase" >&2 + continue fi - [ "$unlocked" == true ] && continue - ((todo++)) + echo "Unlocked ${d} (UUID=${uuid}) successfully" >&2 done - if [ "$todo" -eq 0 ] || [ "$loop" != true ]; then + [ "${loop}" != true ] && break + # Checking for pending devices to be unlocked. + if remaining=$(clevis_devices_to_unlock) && [ -z "${remaining}" ]; then break; fi diff --git a/src/luks/systemd/dracut/clevis/module-setup.sh.in b/src/luks/systemd/dracut/clevis/module-setup.sh.in index 77676aa0..36727244 100755 --- a/src/luks/systemd/dracut/clevis/module-setup.sh.in +++ b/src/luks/systemd/dracut/clevis/module-setup.sh.in @@ -38,8 +38,9 @@ install() { /etc/services \ @libexecdir@/clevis-luks-askpass \ clevis-luks-common-functions \ - head grep sed \ + head grep sed cut \ clevis-decrypt \ + clevis-luks-list \ cryptsetup \ luksmeta \ clevis \