diff options
Diffstat (limited to 'modules.d/90crypt/crypt-lib.sh')
-rwxr-xr-x | modules.d/90crypt/crypt-lib.sh | 278 |
1 files changed, 278 insertions, 0 deletions
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 +} |