diff options
Diffstat (limited to '')
-rwxr-xr-x | modules.d/90crypt/crypt-cleanup.sh | 14 | ||||
-rwxr-xr-x | modules.d/90crypt/crypt-lib.sh | 278 | ||||
-rwxr-xr-x | modules.d/90crypt/crypt-run-generator.sh | 31 | ||||
-rwxr-xr-x | modules.d/90crypt/cryptroot-ask.sh | 207 | ||||
-rwxr-xr-x | modules.d/90crypt/module-setup.sh | 186 | ||||
-rwxr-xr-x | modules.d/90crypt/parse-crypt.sh | 197 | ||||
-rwxr-xr-x | modules.d/90crypt/parse-keydev.sh | 44 | ||||
-rwxr-xr-x | modules.d/90crypt/probe-keydev.sh | 16 |
8 files changed, 973 insertions, 0 deletions
diff --git a/modules.d/90crypt/crypt-cleanup.sh b/modules.d/90crypt/crypt-cleanup.sh new file mode 100755 index 0000000..94fa724 --- /dev/null +++ b/modules.d/90crypt/crypt-cleanup.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +# close everything which is not busy +rm -f -- /etc/udev/rules.d/70-luks.rules > /dev/null 2>&1 + +if ! getarg rd.luks.uuid -d rd_LUKS_UUID > /dev/null 2>&1 && getargbool 1 rd.luks -d -n rd_NO_LUKS > /dev/null 2>&1; then + while true; do + local do_break="y" + for i in /dev/mapper/luks-*; do + cryptsetup luksClose "$i" > /dev/null 2>&1 && do_break=n + done + [ "$do_break" = "y" ] && break + done +fi diff --git a/modules.d/90crypt/crypt-lib.sh b/modules.d/90crypt/crypt-lib.sh new file mode 100755 index 0000000..f3ba20d --- /dev/null +++ b/modules.d/90crypt/crypt-lib.sh @@ -0,0 +1,278 @@ +#!/bin/sh + +command -v getarg > /dev/null || . /lib/dracut-lib.sh + +# check if the crypttab contains an entry for a LUKS UUID +crypttab_contains() { + local luks="$1" + local dev="$2" + local l d rest + if [ -f /etc/crypttab ]; then + while read -r l d rest || [ -n "$l" ]; do + strstr "${l##luks-}" "${luks##luks-}" && return 0 + strstr "$d" "${luks##luks-}" && return 0 + if [ -n "$dev" ]; then + for _dev in $(devnames "$d"); do + [ "$dev" -ef "$_dev" ] && return 0 + done + fi + if [ -e /etc/block_uuid.map ]; then + # search for line starting with $d + _line=$(sed -n "\,^$d .*$,{p}" /etc/block_uuid.map) + [ -z "$_line" ] && continue + # get second column with uuid + _uuid="$(echo "$_line" | sed 's,^.* \(.*$\),\1,')" + strstr "$_uuid" "${luks##luks-}" && return 0 + fi + done < /etc/crypttab + fi + return 1 +} + +# ask_for_password +# +# Wraps around plymouth ask-for-password and adds fallback to tty password ask +# if plymouth is not present. +# +# --cmd command +# Command to execute. Required. +# --prompt prompt +# Password prompt. Note that function already adds ':' at the end. +# Recommended. +# --tries n +# How many times repeat command on its failure. Default is 3. +# --ply-[cmd|prompt|tries] +# Command/prompt/tries specific for plymouth password ask only. +# --tty-[cmd|prompt|tries] +# Command/prompt/tries specific for tty password ask only. +# --tty-echo-off +# Turn off input echo before tty command is executed and turn on after. +# It's useful when password is read from stdin. +ask_for_password() { + local ply_cmd + local ply_prompt + local ply_tries=3 + local tty_cmd + local tty_prompt + local tty_tries=3 + local ret + + while [ $# -gt 0 ]; do + case "$1" in + --cmd) + ply_cmd="$2" + tty_cmd="$2" + shift + ;; + --ply-cmd) + ply_cmd="$2" + shift + ;; + --tty-cmd) + tty_cmd="$2" + shift + ;; + --prompt) + ply_prompt="$2" + tty_prompt="$2" + shift + ;; + --ply-prompt) + ply_prompt="$2" + shift + ;; + --tty-prompt) + tty_prompt="$2" + shift + ;; + --tries) + ply_tries="$2" + tty_tries="$2" + shift + ;; + --ply-tries) + ply_tries="$2" + shift + ;; + --tty-tries) + tty_tries="$2" + shift + ;; + --tty-echo-off) tty_echo_off=yes ;; + esac + shift + done + + { + flock -s 9 + # Prompt for password with plymouth, if installed and running. + if type plymouth > /dev/null 2>&1 && plymouth --ping 2> /dev/null; then + plymouth ask-for-password \ + --prompt "$ply_prompt" --number-of-tries="$ply_tries" \ + --command="$ply_cmd" + ret=$? + else + if [ "$tty_echo_off" = yes ]; then + stty_orig="$(stty -g)" + stty -echo + fi + + local i=1 + while [ $i -le "$tty_tries" ]; do + [ -n "$tty_prompt" ] \ + && printf "%s" "$tty_prompt [$i/$tty_tries]:" >&2 + eval "$tty_cmd" && ret=0 && break + ret=$? + i=$((i + 1)) + [ -n "$tty_prompt" ] && printf '\n' >&2 + done + + [ "$tty_echo_off" = yes ] && stty "$stty_orig" + fi + } 9> /.console_lock + + [ $ret -ne 0 ] && echo "Wrong password" >&2 + return $ret +} + +# 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 + + mount_point=$(mkuniqdir /mnt testdev) + [ -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 +} + +# 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 -r luks_dev key_dev key_path _ || [ -n "$luks_dev" ]; do + if match_dev "$luks_dev" "$for_dev"; then + echo "${key_dev}:${key_path}" + return 0 + fi + done < "$keys_file" + + return 1 +} + +# readkey keypath keydev device +# +# Mounts <keydev>, reads key from file <keypath>, optionally processes it (e.g. +# if encrypted with GPG) and prints to standard output which is supposed to be +# read by cryptsetup. <device> is just passed to helper function for +# informational purpose. +readkey() { + local keypath="$1" + local keydev="$2" + local device="$3" + + # No mounting needed if the keyfile resides inside the initrd + if [ "/" = "$keydev" ]; then + local mntp=/ + else + # This creates a unique single mountpoint for *, or several for explicitly + # given LUKS devices. It accomplishes unlocking multiple LUKS devices with + # a single password entry. + local mntp + mntp="/mnt/$(str_replace "keydev-$keydev-$keypath" '/' '-')" + + if [ ! -d "$mntp" ]; then + mkdir -p "$mntp" + mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' + fi + fi + + case "${keypath##*.}" in + gpg) + if [ -f /lib/dracut-crypt-gpg-lib.sh ]; then + . /lib/dracut-crypt-gpg-lib.sh + gpg_decrypt "$mntp" "$keypath" "$keydev" "$device" + else + die "No GPG support to decrypt '$keypath' on '$keydev'." + fi + ;; + img) + if [ -f /lib/dracut-crypt-loop-lib.sh ]; then + . /lib/dracut-crypt-loop-lib.sh + loop_decrypt "$mntp" "$keypath" "$keydev" "$device" + printf "%s\n" "umount \"$mntp\"; rmdir \"$mntp\";" > "${hookdir}/cleanup/crypt-loop-cleanup-99-${mntp##*/}".sh + return 0 + else + die "No loop file support to decrypt '$keypath' on '$keydev'." + fi + ;; + *) cat "$mntp/$keypath" ;; + esac + + # No unmounting if the keyfile resides inside the initrd + if [ "/" != "$keydev" ]; then + # General unmounting mechanism, modules doing custom cleanup should return earlier + # and install a pre-pivot cleanup hook + umount "$mntp" + rmdir "$mntp" + fi +} diff --git a/modules.d/90crypt/crypt-run-generator.sh b/modules.d/90crypt/crypt-run-generator.sh new file mode 100755 index 0000000..3e78e6d --- /dev/null +++ b/modules.d/90crypt/crypt-run-generator.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +. /lib/dracut-lib.sh +type crypttab_contains > /dev/null 2>&1 || . /lib/dracut-crypt-lib.sh + +dev=$1 +luks=$2 + +crypttab_contains "$luks" "$dev" && exit 0 + +allowdiscards="-" + +# parse for allow-discards +if [ -n "$DRACUT_SYSTEMD" ] || strstr "$(cryptsetup --help)" "allow-discards"; then + if discarduuids=$(getargs "rd.luks.allow-discards"); then + discarduuids=$(str_replace "$discarduuids" 'luks-' '') + if strstr " $discarduuids " " ${luks##luks-}"; then + allowdiscards="discard" + fi + elif getargbool 0 rd.luks.allow-discards; then + allowdiscards="discard" + fi +fi + +echo "$luks $dev - timeout=0,$allowdiscards" >> /etc/crypttab + +if command -v systemctl > /dev/null; then + systemctl daemon-reload + systemctl start cryptsetup.target +fi +exit 0 diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh new file mode 100755 index 0000000..b1f8df8 --- /dev/null +++ b/modules.d/90crypt/cryptroot-ask.sh @@ -0,0 +1,207 @@ +#!/bin/sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +NEWROOT=${NEWROOT:-"/sysroot"} + +# do not ask, if we already have root +[ -f "$NEWROOT"/proc ] && exit 0 + +. /lib/dracut-lib.sh + +mkdir -p -m 0700 /run/cryptsetup + +# if device name is /dev/dm-X, convert to /dev/mapper/name +if [ "${1##/dev/dm-}" != "$1" ]; then + device="/dev/mapper/$(dmsetup info -c --noheadings -o name "$1")" +else + device="$1" +fi + +# default luksname - luks-UUID +luksname=$2 + +# is_keysource - ask for passphrase even if a rd.luks.key argument is set +is_keysource=${3:-0} + +# number of tries +numtries=${4:-10} + +# TODO: improve to support what cmdline does +if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -d -n rd_NO_CRYPTTAB; then + while read -r name dev luksfile luksoptions || [ -n "$name" ]; do + # ignore blank lines and comments + if [ -z "$name" -o "${name#\#}" != "$name" ]; then + continue + fi + + # PARTUUID used in crypttab + if [ "${dev%%=*}" = "PARTUUID" ]; then + if [ "luks-${dev##PARTUUID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # UUID used in crypttab + elif [ "${dev%%=*}" = "UUID" ]; then + if [ "luks-${dev##UUID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # ID used in crypttab + elif [ "${dev%%=*}" = "ID" ]; then + if [ "luks-${dev##ID=}" = "$luksname" ]; then + luksname="$name" + break + fi + + # path used in crypttab + else + cdev=$(readlink -f "$dev") + mdev=$(readlink -f "$device") + if [ "$cdev" = "$mdev" ]; then + luksname="$name" + break + fi + fi + done < /etc/crypttab + unset name dev +fi + +# check if destination already exists +[ -b /dev/mapper/"$luksname" ] && exit 0 + +# we already asked for this device +asked_file=/tmp/cryptroot-asked-$luksname +[ -f "$asked_file" ] && exit 0 + +# load dm_crypt if it is not already loaded +[ -d /sys/module/dm_crypt ] || modprobe dm_crypt + +. /lib/dracut-crypt-lib.sh + +# +# Open LUKS device +# + +info "luksOpen $device $luksname $luksfile $luksoptions" + +OLD_IFS="$IFS" +IFS=, +# shellcheck disable=SC2086 +set -- $luksoptions +IFS="$OLD_IFS" + +while [ $# -gt 0 ]; do + case $1 in + noauto) + # skip this + exit 0 + ;; + swap) + # skip this + exit 0 + ;; + tmp) + # skip this + exit 0 + ;; + allow-discards) + allowdiscards="--allow-discards" + ;; + header=*) + cryptsetupopts="${cryptsetupopts} --${1}" + ;; + esac + shift +done + +# parse for allow-discards +if strstr "$(cryptsetup --help)" "allow-discards"; then + if discarduuids=$(getargs "rd.luks.allow-discards"); then + discarduuids=$(str_replace "$discarduuids" 'luks-' '') + if strstr " $discarduuids " " ${luksdev##luks-}"; then + allowdiscards="--allow-discards" + fi + elif getargbool 0 rd.luks.allow-discards; then + allowdiscards="--allow-discards" + fi +fi + +if strstr "$(cryptsetup --help)" "allow-discards"; then + cryptsetupopts="$cryptsetupopts $allowdiscards" +fi + +unset allowdiscards + +# fallback to passphrase +ask_passphrase=1 + +if [ -n "$luksfile" -a "$luksfile" != "none" -a -e "$luksfile" ]; then + # shellcheck disable=SC2086 + if readkey "$luksfile" / "$device" \ + | cryptsetup -d - $cryptsetupopts luksOpen "$device" "$luksname"; then + ask_passphrase=0 + fi +elif [ "$is_keysource" -ne 0 ]; then + info "Asking for passphrase because $device is a keysource." +else + while [ -n "$(getarg rd.luks.key)" ]; do + if tmp=$(getkey /tmp/luks.keys "$device"); then + keydev="${tmp%%:*}" + keypath="${tmp#*:}" + else + if [ "$numtries" -eq 0 ]; then + warn "No key found for $device. Fallback to passphrase mode." + break + fi + sleep 1 + info "No key found for $device. Will try $numtries time(s) more later." + initqueue --unique --onetime --settled \ + --name cryptroot-ask-"$luksname" \ + "$(command -v cryptroot-ask)" "$device" "$luksname" "$is_keysource" "$((numtries - 1))" + exit 0 + fi + unset tmp + + info "Using '$keypath' on '$keydev'" + # shellcheck disable=SC2086 + readkey "$keypath" "$keydev" "$device" \ + | cryptsetup -d - $cryptsetupopts luksOpen "$device" "$luksname" \ + && ask_passphrase=0 + unset keypath keydev + break + done +fi + +if [ $ask_passphrase -ne 0 ]; then + luks_open="$(command -v cryptsetup) $cryptsetupopts luksOpen" + _timeout=$(getargs "rd.luks.timeout") + _timeout=${_timeout:-0} + ask_for_password --ply-tries 5 \ + --ply-cmd "$luks_open -T1 $device $luksname" \ + --ply-prompt "Password ($device)" \ + --tty-tries 1 \ + --tty-cmd "$luks_open -T5 -t $_timeout $device $luksname" + unset luks_open + unset _timeout +fi + +if [ "$is_keysource" -ne 0 -a "${luksname##luks-}" != "$luksname" ]; then + luks_close="$(command -v cryptsetup) close" + { + printf -- '[ -e /dev/mapper/%s ] && ' "$luksname" + printf -- '%s "%s"\n' "$luks_close" "$luksname" + } >> "$hookdir/cleanup/31-crypt-keysource.sh" + unset luks_close +fi + +unset device luksname luksfile + +# mark device as asked +: >> "$asked_file" + +need_shutdown +udevsettle + +exit 0 diff --git a/modules.d/90crypt/module-setup.sh b/modules.d/90crypt/module-setup.sh new file mode 100755 index 0000000..d5ac45b --- /dev/null +++ b/modules.d/90crypt/module-setup.sh @@ -0,0 +1,186 @@ +#!/bin/bash + +# called by dracut +check() { + local fs + # if cryptsetup is not installed, then we cannot support encrypted devices. + require_any_binary "$systemdutildir"/systemd-cryptsetup cryptsetup || return 1 + + [[ $hostonly ]] || [[ $mount_needs ]] && { + for fs in "${host_fs_types[@]}"; do + [[ $fs == "crypto_LUKS" ]] && return 0 + done + return 255 + } + + return 0 +} + +# called by dracut +depends() { + local deps + deps="dm rootfs-block" + if [[ $hostonly && -f "$dracutsysrootdir"/etc/crypttab ]]; then + if grep -q -e "fido2-device=" -e "fido2-cid=" "$dracutsysrootdir"/etc/crypttab; then + deps+=" fido2" + fi + if grep -q "pkcs11-uri" "$dracutsysrootdir"/etc/crypttab; then + deps+=" pkcs11" + fi + if grep -q "tpm2-device=" "$dracutsysrootdir"/etc/crypttab; then + deps+=" tpm2-tss" + fi + fi + echo "$deps" + return 0 +} + +# called by dracut +installkernel() { + hostonly="" instmods drbg + instmods dm_crypt + + # in case some of the crypto modules moved from compiled in + # to module based, try to install those modules + # best guess + if [[ $hostonly ]] || [[ $mount_needs ]]; then + # dmsetup returns s.th. like + # cryptvol: 0 2064384 crypt aes-xts-plain64 :64:logon:cryptsetup:.... + dmsetup table | while read -r name _ _ is_crypt cipher _; do + [[ $is_crypt == "crypt" ]] || continue + # get the device name + name=/dev/$(dmsetup info -c --noheadings -o blkdevname "${name%:}") + # check if the device exists as a key in our host_fs_types (even with null string) + # shellcheck disable=SC2030 # this is a shellcheck bug + if [[ ${host_fs_types[$name]+_} ]]; then + # split the cipher aes-xts-plain64 in pieces + IFS='-:' read -ra mods <<< "$cipher" + # try to load the cipher part with "crypto-" prepended + # in non-hostonly mode + hostonly='' instmods "${mods[@]/#/crypto-}" "crypto-$cipher" + fi + done + else + instmods "=crypto" + fi + return 0 +} + +# called by dracut +cmdline() { + local dev UUID + # shellcheck disable=SC2031 + for dev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$dev]} != "crypto_LUKS" ]] && continue + + UUID=$( + blkid -u crypto -o export "$dev" \ + | while read -r line || [ -n "$line" ]; do + [[ ${line#UUID} == "$line" ]] && continue + printf "%s" "${line#UUID=}" + break + done + ) + [[ ${UUID} ]] || continue + printf "%s" " rd.luks.uuid=luks-${UUID}" + done +} + +# called by dracut +install() { + + if [[ $hostonly_cmdline == "yes" ]]; then + local _cryptconf + _cryptconf=$(cmdline) + [[ $_cryptconf ]] && printf "%s\n" "$_cryptconf" >> "${initdir}/etc/cmdline.d/90crypt.conf" + fi + + inst_hook cmdline 30 "$moddir/parse-crypt.sh" + if ! dracut_module_included "systemd"; then + inst_multiple cryptsetup rmdir readlink umount + inst_script "$moddir"/cryptroot-ask.sh /sbin/cryptroot-ask + inst_script "$moddir"/probe-keydev.sh /sbin/probe-keydev + inst_hook cmdline 10 "$moddir/parse-keydev.sh" + inst_hook cleanup 30 "$moddir/crypt-cleanup.sh" + fi + + if [[ $hostonly ]] && [[ -f $dracutsysrootdir/etc/crypttab ]]; then + # filter /etc/crypttab for the devices we need + while read -r _mapper _dev _luksfile _luksoptions || [ -n "$_mapper" ]; do + [[ $_mapper == \#* ]] && continue + [[ $_dev ]] || continue + + [[ $_dev == PARTUUID=* ]] \ + && _dev="/dev/disk/by-partuuid/${_dev#PARTUUID=}" + + [[ $_dev == UUID=* ]] \ + && _dev="/dev/disk/by-uuid/${_dev#UUID=}" + + [[ $_dev == ID=* ]] \ + && _dev="/dev/disk/by-id/${_dev#ID=}" + + echo "$_dev $(blkid "$_dev" -s UUID -o value)" >> "${initdir}/etc/block_uuid.map" + + # loop through the options to check for the force option + luksoptions=${_luksoptions} + OLD_IFS="${IFS}" + IFS=, + # shellcheck disable=SC2086 + set -- ${luksoptions} + IFS="${OLD_IFS}" + + forceentry="" + while [ $# -gt 0 ]; do + case $1 in + force) + forceentry="yes" + break + ;; + esac + shift + done + + # include the entry regardless + if [ "${forceentry}" = "yes" ]; then + echo "$_mapper $_dev $_luksfile $_luksoptions" + else + # shellcheck disable=SC2031 + for _hdev in "${!host_fs_types[@]}"; do + [[ ${host_fs_types[$_hdev]} == "crypto_LUKS" ]] || continue + if [[ $_hdev -ef $_dev ]] || [[ /dev/block/$_hdev -ef $_dev ]]; then + echo "$_mapper $_dev $_luksfile $_luksoptions" + break + fi + done + fi + done < "$dracutsysrootdir"/etc/crypttab > "$initdir"/etc/crypttab + mark_hostonly /etc/crypttab + fi + + inst_simple "$moddir/crypt-lib.sh" "/lib/dracut-crypt-lib.sh" + inst_script "$moddir/crypt-run-generator.sh" "/sbin/crypt-run-generator" + + if dracut_module_included "systemd"; then + # the cryptsetup targets are already pulled in by 00systemd, but not + # the enablement symlinks + inst_multiple -o \ + "$tmpfilesdir"/cryptsetup.conf \ + "$systemdutildir"/system-generators/systemd-cryptsetup-generator \ + "$systemdutildir"/systemd-cryptsetup \ + "$systemdsystemunitdir"/systemd-ask-password-console.path \ + "$systemdsystemunitdir"/systemd-ask-password-console.service \ + "$systemdsystemunitdir"/cryptsetup.target \ + "$systemdsystemunitdir"/sysinit.target.wants/cryptsetup.target \ + "$systemdsystemunitdir"/remote-cryptsetup.target \ + "$systemdsystemunitdir"/initrd-root-device.target.wants/remote-cryptsetup.target \ + systemd-ask-password systemd-tty-ask-password-agent + fi + + # Install required libraries. + _arch=${DRACUT_ARCH:-$(uname -m)} + inst_libdir_file \ + {"tls/$_arch/",tls/,"$_arch/",}"/ossl-modules/fips.so" \ + {"tls/$_arch/",tls/,"$_arch/",}"/ossl-modules/legacy.so" + + dracut_need_initqueue +} diff --git a/modules.d/90crypt/parse-crypt.sh b/modules.d/90crypt/parse-crypt.sh new file mode 100755 index 0000000..e46e347 --- /dev/null +++ b/modules.d/90crypt/parse-crypt.sh @@ -0,0 +1,197 @@ +#!/bin/sh + +type crypttab_contains > /dev/null 2>&1 || . /lib/dracut-crypt-lib.sh + +_cryptgetargsname() { + debug_off + local _o _found _key + unset _o + unset _found + _key="$1" + set -- + for _o in $(getargs rd.luks.name); do + if [ "${_o%=*}" = "${_key%=}" ]; then + [ -n "${_o%=*}" ] && set -- "$@" "${_o#*=}" + _found=1 + fi + done + if [ -n "$_found" ]; then + [ $# -gt 0 ] && printf '%s' "$*" + return 0 + fi + return 1 +} + +if ! getargbool 1 rd.luks -d -n rd_NO_LUKS; then + info "rd.luks=0: removing cryptoluks activation" + rm -f -- /etc/udev/rules.d/70-luks.rules +else + { + echo 'SUBSYSTEM!="block", GOTO="luks_end"' + echo 'ACTION!="add|change", GOTO="luks_end"' + } > /etc/udev/rules.d/70-luks.rules.new + + PARTUUID=$(getargs rd.luks.partuuid -d rd_LUKS_PARTUUID) + SERIAL=$(getargs rd.luks.serial -d rd_LUKS_SERIAL) + LUKS=$(getargs rd.luks.uuid -d rd_LUKS_UUID) + tout=$(getarg rd.luks.key.tout) + + if [ -e /etc/crypttab ]; then + while read -r _ _dev _ || [ -n "$_dev" ]; do + set_systemd_timeout_for_dev "$_dev" + done < /etc/crypttab + fi + + if [ -n "$PARTUUID" ]; then + for uuid in $PARTUUID; do + + is_keysource=0 + _uuid=$uuid + uuid=${uuid#keysource:} + [ "$uuid" != "$_uuid" ] && is_keysource=1 + unset _uuid + + uuid=${uuid##luks-} + if luksname=$(_cryptgetargsname "$uuid="); then + luksname="${luksname#"$uuid"=}" + else + luksname="luks-$uuid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_PART_ENTRY_UUID}=="*%s*", ' "$uuid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$uuid"; then + { + printf -- 'ENV{ID_PART_ENTRY_UUID}=="*%s*", ' "$uuid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + done + + elif [ -n "$SERIAL" ]; then + for serialid in $SERIAL; do + + is_keysource=0 + _serialid=$serialid + serialid=${serialid#keysource:} + [ "$serialid" != "$_serialid" ] && is_keysource=1 + unset _serialid + + serialid=${serialid##luks-} + if luksname=$(_cryptgetargsname "$serialid="); then + luksname="${luksname#"$serialid"=}" + else + luksname="luks-$serialid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_SERIAL_SHORT}=="*%s*", ' "$serialid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$serialid"; then + { + printf -- 'ENV{ID_SERIAL_SHORT}=="*%s*", ' "$serialid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + done + + elif [ -n "$LUKS" ]; then + for luksid in $LUKS; do + + is_keysource=0 + _luksid=$luksid + luksid=${luksid#keysource:} + [ "$luksid" != "$_luksid" ] && is_keysource=1 + unset _luksid + + luksid=${luksid##luks-} + if luksname=$(_cryptgetargsname "$luksid="); then + luksname="${luksname#"$luksid"=}" + else + luksname="luks-$luksid" + fi + + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", ' + printf -- 'ENV{ID_FS_UUID}=="*%s*", ' "$luksid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name cryptroot-ask-%%k %s ' "$(command -v cryptroot-ask)" + # shellcheck disable=SC2016 + printf -- '$env{DEVNAME} %s %s %s"\n' "$luksname" "$is_keysource" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + luksname=$(dev_unit_name "$luksname") + # shellcheck disable=SC1003 + luksname="$(str_replace "$luksname" '\' '\\')" + + if ! crypttab_contains "$luksid"; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", ' + printf -- 'ENV{ID_FS_UUID}=="*%s*", ' "$luksid" + printf -- 'RUN+="%s --settled --unique --onetime ' "$(command -v initqueue)" + printf -- '--name systemd-cryptsetup-%%k %s start ' "$(command -v systemctl)" + printf -- 'systemd-cryptsetup@%s.service"\n' "$luksname" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + + if [ $is_keysource -eq 0 ]; then + uuid=$luksid + while [ "$uuid" != "${uuid#*-}" ]; do uuid=${uuid%%-*}${uuid#*-}; done + printf -- '[ -e /dev/disk/by-id/dm-uuid-CRYPT-LUKS?-*%s*-* ] || exit 1\n' "$uuid" \ + >> "$hookdir/initqueue/finished/90-crypt.sh" + { + printf -- '[ -e /dev/disk/by-uuid/*%s* ] || ' "$luksid" + printf -- 'warn "crypto LUKS UUID "%s" not found"\n' "$luksid" + } >> "$hookdir/emergency/90-crypt.sh" + fi + done + elif getargbool 0 rd.auto; then + if [ -z "$DRACUT_SYSTEMD" ]; then + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="%s ' "$(command -v initqueue)" + printf -- '--unique --settled --onetime --name cryptroot-ask-%%k ' + # shellcheck disable=SC2016 + printf -- '%s $env{DEVNAME} luks-$env{ID_FS_UUID} 0 %s"\n' "$(command -v cryptroot-ask)" "$tout" + } >> /etc/udev/rules.d/70-luks.rules.new + else + { + printf -- 'ENV{ID_FS_TYPE}=="crypto_LUKS", RUN+="%s ' "$(command -v initqueue)" + printf -- '--unique --settled --onetime --name crypt-run-generator-%%k ' + # shellcheck disable=SC2016 + printf -- '%s $env{DEVNAME} luks-$env{ID_FS_UUID}"\n' "$(command -v crypt-run-generator)" + } >> /etc/udev/rules.d/70-luks.rules.new + fi + fi + + echo 'LABEL="luks_end"' >> /etc/udev/rules.d/70-luks.rules.new + mv /etc/udev/rules.d/70-luks.rules.new /etc/udev/rules.d/70-luks.rules +fi diff --git a/modules.d/90crypt/parse-keydev.sh b/modules.d/90crypt/parse-keydev.sh new file mode 100755 index 0000000..467d892 --- /dev/null +++ b/modules.d/90crypt/parse-keydev.sh @@ -0,0 +1,44 @@ +#!/bin/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 + + # A keydev of '/' is treated as the initrd itself + if [ "/" = "$keydev" ]; then + [ -z "$luksdev" ] && luksdev='*' + echo "$luksdev:$keydev:$keypath" >> /tmp/luks.keys + continue + elif [ -n "$keydev" ]; then + udevmatch "$keydev" >&7 || { + warn 'keydev incorrect!' + continue + } + printf ', ' >&7 + fi + + { + printf -- 'RUN+="%s --unique --onetime ' "$(command -v initqueue)" + printf -- '--name probe-keydev-%%k ' + printf -- '%s /dev/%%k %s %s"\n' \ + "$(command -v probe-keydev)" "${keypath}" "${luksdev}" + } >&7 + done + unset arg keypath keydev luksdev + + echo 'LABEL="luks_keydev_end"' >&7 + exec 7>&- +fi diff --git a/modules.d/90crypt/probe-keydev.sh b/modules.d/90crypt/probe-keydev.sh new file mode 100755 index 0000000..e5a3f36 --- /dev/null +++ b/modules.d/90crypt/probe-keydev.sh @@ -0,0 +1,16 @@ +#!/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 |