Search
j0ke.net Open Build Service
>
Projects
>
home:jg
:
playground
>
dracut
> 0309-crypt-add-support-for-keyfiles-in-the-initramfs.patch
Sign Up
|
Log In
Username
Password
Cancel
Overview
Repositories
Revisions
Requests
Users
Advanced
Attributes
Meta
File 0309-crypt-add-support-for-keyfiles-in-the-initramfs.patch of Package dracut
From f2c3a039da2c9398375561a8fe52b77d54ca5e97 Mon Sep 17 00:00:00 2001 From: Harald Hoyer <harald@redhat.com> Date: Thu, 18 Jul 2013 11:53:08 +0200 Subject: [PATCH] crypt: add support for keyfiles in the initramfs https://bugzilla.redhat.com/show_bug.cgi?id=886194 --- modules.d/90crypt/crypt-lib.sh | 222 +++++++++++++++++++++++++++++++++++++ modules.d/90crypt/cryptroot-ask.sh | 121 ++++++++++++++------ modules.d/90crypt/install | 4 + 3 files changed, 315 insertions(+), 32 deletions(-) create mode 100644 modules.d/90crypt/crypt-lib.sh diff --git a/modules.d/90crypt/crypt-lib.sh b/modules.d/90crypt/crypt-lib.sh new file mode 100644 index 0000000..69f14d0 --- /dev/null +++ b/modules.d/90crypt/crypt-lib.sh @@ -0,0 +1,222 @@ +#!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +command -v getarg >/dev/null || . /lib/dracut-lib.sh + +# 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 cmd; local prompt; local tries=3 + 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 [ -x /bin/plymouth ] && /bin/plymouth --ping; then + /bin/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 "$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=$(mkuniqdir /mnt testdev) + local path + + [ -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 +} + +# Get kernel name for given device. Device may be the name too (then the same +# is returned), a symlink (full path), UUID (prefixed with "UUID=") or label +# (prefixed with "LABEL="). If just a beginning of the UUID is specified or +# even an empty, function prints all device names which UUIDs match - every in +# single line. +# +# NOTICE: The name starts with "/dev/". +# +# Example: +# devnames UUID=123 +# May print: +# /dev/dm-1 +# /dev/sdb1 +# /dev/sdf3 +devnames() { + local dev="$1"; local d; local names + + case "$dev" in + UUID=*) + dev="$(foreach_uuid_until '! blkid -U $___' "${dev#UUID=}")" \ + && return 255 + [ -z "$dev" ] && return 255 + ;; + LABEL=*) dev="$(blkid -L "${dev#LABEL=}")" || return 255 ;; + /dev/?*) ;; + *) return 255 ;; + esac + + for d in $dev; do + names="$names +$(readlink -e -q "$d")" || return 255 + done + + echo "${names# +}" +} + +# 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 + + local IFS=: + while read luks_dev key_dev key_path; 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" + + local mntp=$(mkuniqdir /mnt keydev) + mount -r "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' + + 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 + ;; + *) cat "$mntp/$keypath" ;; + esac + + umount "$mntp" + rmdir "$mntp" +} diff --git a/modules.d/90crypt/cryptroot-ask.sh b/modules.d/90crypt/cryptroot-ask.sh index a6a9af1..6a95dc2 100755 --- a/modules.d/90crypt/cryptroot-ask.sh +++ b/modules.d/90crypt/cryptroot-ask.sh @@ -1,7 +1,12 @@ #!/bin/sh +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +PATH=/usr/sbin:/usr/bin:/sbin:/bin +NEWROOT=${NEWROOT:-"/sysroot"} # do not ask, if we already have root -[ -f /sysroot/proc ] && exit 0 +[ -f $NEWROOT/proc ] && exit 0 # check if destination already exists [ -b /dev/mapper/$2 ] && exit 0 @@ -12,11 +17,14 @@ # load dm_crypt if it is not already loaded [ -d /sys/module/dm_crypt ] || modprobe dm_crypt -. /lib/dracut-lib.sh +. /lib/dracut-crypt-lib.sh # default luksname - luks-UUID luksname=$2 +# fallback to passphrase +ask_passphrase=1 + # 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")" @@ -24,42 +32,91 @@ else device="$1" fi -if [ -f /etc/crypttab ] && ! getargs rd_NO_CRYPTTAB; then - while read name dev rest; do - # ignore blank lines and comments - if [ -z "$name" -o "${name#\#}" != "$name" ]; then - continue - fi - - # UUID used in crypttab - if [ "${dev%%=*}" = "UUID" ]; then - if [ "luks-${dev##UUID=}" = "$2" ]; 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 +# TODO: improve to support what cmdline does +if [ -f /etc/crypttab ] && getargbool 1 rd.luks.crypttab -n rd_NO_CRYPTTAB; then + while read name dev luksfile rest; do + # ignore blank lines and comments + if [ -z "$name" -o "${name#\#}" != "$name" ]; then + continue + fi + + # UUID used in crypttab + if [ "${dev%%=*}" = "UUID" ]; then + if [ "luks-${dev##UUID=}" = "$2" ]; 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 rest fi -info "luksOpen $device $luksname" -# flock against other interactive activities -{ flock -s 9; - echo -n "$device ($luksname) is password protected" - cryptsetup luksOpen -T1 $1 $luksname -} 9>/.console.lock +# +# Open LUKS device +# + +info "luksOpen $device $luksname $luksfile" + +if [ -n "$luksfile" -a "$luksfile" != "none" -a -e "$luksfile" ]; then + if cryptsetup --key-file "$luksfile" luksOpen "$device" "$luksname"; then + ask_passphrase=0 + fi +else + while [ -n "$(getarg rd.luks.key)" ]; do + if tmp=$(getkey /tmp/luks.keys $device); then + keydev="${tmp%%:*}" + keypath="${tmp#*:}" + else + if [ $# -eq 3 ]; then + if [ $3 -eq 0 ]; then + info "No key found for $device. Fallback to passphrase mode." + break + fi + info "No key found for $device. Will try $3 time(s) more later." + set -- "$1" "$2" "$(($3 - 1))" + else + info "No key found for $device. Will try later." + fi + initqueue --unique --onetime --settled \ + --name cryptroot-ask-$luksname \ + $(command -v cryptroot-ask) "$@" + exit 0 + fi + unset tmp + + info "Using '$keypath' on '$keydev'" + readkey "$keypath" "$keydev" "$device" \ + | cryptsetup -d - luksOpen "$device" "$luksname" + unset keypath keydev + ask_passphrase=0 + break + done +fi + +if [ $ask_passphrase -ne 0 ]; then + luks_open="$(command -v cryptsetup) luksOpen" + 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 $device $luksname" + unset luks_open +fi + +unset device luksname luksfile # mark device as asked >> /tmp/cryptroot-asked-$2 +udevsettle + exit 0 -# vim:ts=8:sw=4:sts=4:et \ No newline at end of file diff --git a/modules.d/90crypt/install b/modules.d/90crypt/install index 7a7418d..fa25e34 100755 --- a/modules.d/90crypt/install +++ b/modules.d/90crypt/install @@ -1,7 +1,11 @@ #!/bin/bash inst cryptsetup +inst rmdir +inst readlink +inst umount 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" inst_hook pre-pivot 31 "$moddir/crypt-cleanup.sh" +inst "$moddir/crypt-lib.sh" "/lib/dracut-crypt-lib.sh" inst /etc/crypttab -- 1.8.3.1