From 04effa9c0c1ca3d0a2c11026cc8dc605c8fd5049 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Jan=20R=C4=99korajski?= Date: Thu, 26 Feb 2009 14:29:05 +0000 Subject: [PATCH] - support for initramfs-tools Changed files: initramfs-README -> 1.1 initramfs-passdev-hook -> 1.1 initramfs-root-conf -> 1.1 initramfs-root-hook -> 1.1 initramfs-root-local-top -> 1.1 --- initramfs-README | 194 ++++++++++++++++ initramfs-passdev-hook | 40 ++++ initramfs-root-conf | 6 + initramfs-root-hook | 487 +++++++++++++++++++++++++++++++++++++++ initramfs-root-local-top | 289 +++++++++++++++++++++++ 5 files changed, 1016 insertions(+) create mode 100644 initramfs-README create mode 100644 initramfs-passdev-hook create mode 100644 initramfs-root-conf create mode 100644 initramfs-root-hook create mode 100644 initramfs-root-local-top diff --git a/initramfs-README b/initramfs-README new file mode 100644 index 0000000..245fe36 --- /dev/null +++ b/initramfs-README @@ -0,0 +1,194 @@ +1. Introduction +--------------- +Kernels more recent than 2.6.12 have dropped support for devfs, which +means that initrd-tools can no longer be used to boot into an encrypted +root partition. Instead, a similar functionality has been developed for +use with an initramfs-image. + + +2. A fresh installation +----------------------- +If you plan to perform a completely new installation of Debian onto a +machine and to do so using an encrypted root partition, you might want +to consider using a version of Debian Installer with partman-crypto +(see http://wiki.debian.org/DebianInstaller/PartmanCrypto). + +The installation will then take care of all the details and perform the +necessary configuration for you, meaning that you should not have to +read the rest of this document to get a machine with an encrypted +root fs up and running. + +However, if you are not planning to perform a new installation from scratch, +the following information might be useful to you. + + +3. Requirements +--------------- +In order to boot from an encrypted root fs, you need an initramfs-image +which includes the necessary kernel modules and scripts to setup +the root device after the kernel has been initialized, but before the rest +of the operating system is booted. + +To do so, you need two partitions: +- an unencrypted /boot partition +- an encrypted / partition + +In addition, you need to have initramfs-tools installed. + +NOTE: You should make sure that your swap partition is either encrypted, or +that you are using a swap file on an encrypted partition, as crypto keys and +other sensitive information might otherwise be written out to the swap +partition in unencrypted form. + + +4. Setup (regular dm-crypt) +--------------------------- +First of all, you must edit /etc/crypttab and add a line describing your +root device, for example: + + cryptroot /dev/hda2 none cipher=aes-cbc-essiv:sha256,size=256,hash=sha256 + +This will allow cryptsetup to create /dev/mapper/cryptroot from the +encrypted partition /dev/hda2 during boot. + +In addition, you must also make sure that the root device is listed in +/etc/fstab, for example: + + /dev/mapper/cryptroot / ext3 defaults 0 1 + +This will allow the initramfs support scripts to know which of the devices +in the crypttab that is the root device. + +After doing these changes, you should regenerate the initramfs by running +"initramfs-update -u", then make sure that your boot loader is configured +to feed the initramfs to the kernel when booting. The kernel root argument +should also be changed to /dev/mapper/cryptroot. + +Now, reboot the machine, and if everything is correctly configured, you +should be given a prompt to type in the passphrase for the encrypted +root partition before the boot can continue. + +NOTE: the initramfs scripts default to using the sha256 hash function while +the plain cryptsetup binary defaults to using the ripemd160 hash function. +In order to ensure that the crypto setup works in a consistant manner, you +should make sure that the hash function is specified in the /etc/crypttab file +if you are using regular dm-crypt (with LUKS the hash function to use is stored +in the LUKS header). + + +5. Setup (using LUKS) +--------------------- +If you are using the LUKS feature of cryptsetup, the above setup recipe should +still apply, but since most options can be derived from the information stored +in the LUKS header on-disk, the line to add to /etc/crypttab should look +something like this: + + cryptroot /dev/sda2 none luks + + +6. Exotic key types +------------------- +The above examples assume that you use a regular passphrase as the key to the +encrypted filesystem. However, if you wish to make use of more complex setups +(such as root-key-on-usb-memory), you can create a script which does all the +steps necessary to retrieve the key and then prints it to stdout. + +Then add a keyscript=/path/to/your/script.sh to the options (fourth column) in +the above mentioned /etc/crypttab line, so that it looks something like this: + + cryptroot /dev/sda2 none luks,keyscript=/usr/local/sbin/cryptkey + +Next, regenerate your initramfs image. This will copy the script into the +initramfs image under the /keyscripts/ directory. + +NOTE: there is a limited set of tools available when the script is executing +as part of the initramfs bootup, you have to make sure that you do not use +any tools which are not available or your script, and therefore boot, will +fail. + + +7. "cryptopts" boot argument +---------------------------- +In general, you should use the above approach with a line describing your +root partition in /etc/crypttab and /etc/fstab. However, if for some reason +you wish to override the settings that are derived from these files and +stored in the initramfs image, you can use the "cryptopts" boot argument +(this *only* works for the root partition). + +The format of cryptopts is: +cryptopts==,=... + +Beside the "hash", "size", "cipher" and "lvm" options that correspond to the +same options in the fourth field of /etc/crypttab, the options "target", +"source" and "key" are also supported. They correspond to the first, second +and third field of /etc/crypttab, respectively. See the crypttab man page +for further details. + +Several "cryptopts" boot arguments can also be specified in case more than +one mapping needs to be setup in the initramfs stage of the boot. + +Example boot arguments: +root=/dev/mapper/crypt0 cryptopts=target=crypt0,source=/dev/hda1,cipher=twofish + +8. Resume device support +------------------------ +The initramfs scripts will also try to automatically determine the devices, +if any, that are used for software suspend (swsusp, suspend2 or uswsusp) and +to set them up during the initramfs stage in order to allow suspend and resume +in combination with encryption to keep the resume image safe from potential +attackers. + +If your resume device and your root partition use two different cryptsetup +mappings, you might want to use the "decrypt_derived" keyscript as described +below. + +9. The "decrypt_derived" keyscript +---------------------------------- +Assume that you have two entries in /etc/crypttab: + +cryptroot /dev/hda1 none luks +cryptswap /dev/hda2 none luks + +If cryptswap is used as your suspend/resume device, you'd normally need to +enter two different passphrases during the boot, but the "decrypt_derived" +script can generate the key for the second mapping using a hash of the key +for the first mapping. + +In short, you'll need to do something like the following to take advantage +of the decrypt_derived script: + +1) swapoff -a +2) cryptsetup luksClose cryptswap +3) edit /etc/crypttab and change the cryptswap line to e.g.: +cryptswap /dev/hda2 cryptroot cipher=aes-cbc-essiv:sha256,size=256,hash=sha256,keyscript=/lib/cryptsetup/scripts/decrypt_derived,swap +4) /etc/init.d/cryptdisks start +5) Make sure that /dev/mapper/cryptswap has been created +6) swapon -a +7) (optional) update-initramfs -u + +After you've followed the above steps, your swap device should be setup +automatically after the root device has been setup during the boot stage. + +Note: If you don't use suspend device support, it's better to use completely +random keys for your encrypted swap device. See the section '2. Encrypted +swap partition(s)' in /usr/share/doc/cryptsetup/README.Debian for information +on how to setup this. + +10. The "passdev" keyscript +---------------------------- +If you have a keyfile on a removable device (e.g. a USB-key), you can use the +passdev keyscript. It will wait for the device to appear, mount it read-only, +read the key and then unmount the device. + +The "key" part of /etc/crypttab will be interpreted as :, it is +strongly recommended that you use one of the persistent device names from +/dev/disk/*, e.g. /dev/disk/by-label/myusbkey. + +This is an example of a suitable line in cryptsetup: +cryptroot /dev/hda2 /dev/disk/by-label/myusbkey:/keys/root.key cipher=aes-cbc-essiv:sha256,size=256,hash=plain,keyscript=/lib/cryptsetup/scripts/passdev + +The above line would cause the boot to pause until /dev/disk/by-label/myusbkey +appears in the fs, then mount that device and use the file /keys/root.key +on the device as the key (without any hashing) as the key for the fs. + +-- David Härdeman diff --git a/initramfs-passdev-hook b/initramfs-passdev-hook new file mode 100644 index 0000000..ad6c484 --- /dev/null +++ b/initramfs-passdev-hook @@ -0,0 +1,40 @@ +#!/bin/sh + +set -e + +PREREQ="cryptroot" + +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + +# Hooks for adding filesystem modules to the initramfs when the passdev +# keyscript is used + +# Check whether the passdev script has been included +if [ ! -x "${DESTDIR}/keyscripts/passdev" ]; then + exit 0 +fi + +# The filesystem type of the removable device is probed at boot-time, so +# we add a generous list of filesystems to include. This also helps with +# recovery situation as including e.g. the vfat module might help a user +# who needs to create a new cryptkey (using a backup of a keyfile) on +# a windows-machine for example. + +# This list needs to be kept in sync with the one defined in passdev.c +for fs in ext3 ext2 vfat reiserfs xfs isofs udf; do + manual_add_modules "$fs" > /dev/null 2>&1 || true +done +exit 0 + diff --git a/initramfs-root-conf b/initramfs-root-conf new file mode 100644 index 0000000..0e5e64f --- /dev/null +++ b/initramfs-root-conf @@ -0,0 +1,6 @@ +# This will setup non-us keyboards in early userspace, +# necessary for punching in passphrases. +KEYMAP=y + +# force busybox on initramfs +BUSYBOX=y diff --git a/initramfs-root-hook b/initramfs-root-hook new file mode 100644 index 0000000..f237727 --- /dev/null +++ b/initramfs-root-hook @@ -0,0 +1,487 @@ +#!/bin/sh + +PREREQ="" + +prereqs() +{ + echo "$PREREQ" +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + +. /usr/share/initramfs-tools/hook-functions + +get_root_device() { + local device mount type options dump pass + + if [ ! -r /etc/fstab ]; then + return 1 + fi + + grep '^[^#]' /etc/fstab | \ + while read device mount type options dump pass; do + if [ "$mount" = "/" ]; then + echo "$device" + return + fi + done +} + +get_resume_devices() { + local device opt count dupe candidates devices + candidates="" + + # First, get a list of potential resume devices + + # uswsusp + if [ -e /etc/uswsusp.conf ]; then + device=$(sed -rn 's/^resume device[[:space:]]*[:=][[:space:]]*// p' /etc/uswsusp.conf) + if [ -n "$device" ]; then + candidates="$candidates $device" + fi + fi + + # uswsusp - again... + if [ -e /etc/suspend.conf ]; then + device=$(sed -rn 's/^resume device[[:space:]]*[:=][[:space:]]*// p' /etc/suspend.conf) + if [ -n "$device" ]; then + candidates="$candidates $device" + fi + fi + + # regular swsusp + for opt in $(cat /proc/cmdline); do + case $opt in + resume=*) + device="${opt#resume=}" + candidates="$candidates $device" + ;; + esac + done + + # initramfs-tools + if [ -e /etc/initramfs-tools/conf.d/resume ]; then + device=$(sed -rn 's/^RESUME[[:space:]]*=[[:space:]]*// p' /etc/initramfs-tools/conf.d/resume) + if [ -n "$device" ]; then + candidates="$candidates $device" + fi + fi + + # Now check the sanity of all candidates + devices="" + count=0 + for device in $candidates; do + # Weed out clever defaults + if [ "$device" = "" ]; then + continue + fi + + # Weed out duplicates + dupe=0 + for opt in $devices; do + if [ "$device" = "$opt" ]; then + dupe=1 + fi + done + if [ $dupe -eq 1 ]; then + continue + fi + + # This device seems ok + devices="$devices $device" + count=$(( $count + 1 )) + done + + if [ $count -gt 1 ]; then + echo "cryptsetup: WARNING: found more than one resume device candidate:" >&2 + for device in $devices; do + echo " $device" >&2 + done + fi + + if [ $count -gt 0 ]; then + echo $devices + fi + + return 0 +} + +node_is_in_crypttab() { + local node + node="$1" + + grep -q ^$node /etc/crypttab + return $? +} + +get_lvm_deps() { + local node deps maj min depnode + node="$1" + + if [ -z $node ]; then + echo "cryptsetup: WARNING: get_lvm_deps - invalid arguments" >&2 + return 1 + fi + + if ! deps=$(dmsetup deps "$node" 2> /dev/null | sed 's/[^:]*: *//;s/[ (]//g;s/)/ /g'); then + echo "cryptsetup: WARNING: failed to find deps for $node" >&2 + return 1 + fi + + # We should now have a list of major,minor pairs, e.g. "3,2 3,3" + for dep in $deps; do + maj=${dep%,*} + min=${dep#*,} + depnode=$(dmsetup ls | sed -n "s/\\([^ ]*\\) *($maj, $min)/\\1/p" | sed -e "s/[ \t]*$//") + if [ -z "$depnode" ]; then + continue + fi + if [ "$(dmsetup table "$depnode" 2> /dev/null | cut -d' ' -f3)" != "crypt" ]; then + get_lvm_deps "$depnode" + continue + fi + echo "$depnode" + done + + return 0 +} + +get_device_opts() { + local target source link extraopts rootopts opt + target="$1" + extraopts="$2" + KEYSCRIPT="" + OPTIONS="" + + if [ -z "$target" ]; then + echo "cryptsetup: WARNING: get_device_opts - invalid arguments" >&2 + return 1 + fi + + opt=$( grep ^$target /etc/crypttab | head -1 | sed 's/[[:space:]]\+/ /g' ) + source=$( echo $opt | cut -d " " -f2 ) + key=$( echo $opt | cut -d " " -f3 ) + rootopts=$( echo $opt | cut -d " " -f4- ) + + if [ -z "$opt" ] || [ -z "$source" ] || [ -z "$key" ] || [ -z "$rootopts" ]; then + echo "cryptsetup: WARNING: invalid line in /etc/crypttab - $opt" >&2 + return 1 + fi + + # Sanity checks for $source + if [ -h "$source" ]; then + link=$(readlink -nqe "$source") + if [ -z "$link" ]; then + echo "cryptsetup: WARNING: $source is a dangling symlink" >&2 + return 1 + fi + + if [ "$link" != "${link#/dev/mapper/}" ]; then + echo "cryptsetup: NOTE: using $link instead of $source for $target" >&2 + source="$link" + fi + fi + + # Sanity checks for $key + if [ "$key" = "/dev/random" ] || [ "$key" = "/dev/urandom" ]; then + echo "cryptsetup: WARNING: target $target has a random key, skipped" >&2 + return 1 + fi + + if [ -n "$extraopts" ]; then + rootopts="$extraopts,$rootopts" + fi + + # We have all the basic options, let's go trough them + OPTIONS="target=$target,source=$source,key=$key" + local IFS=", " + unset HASH_FOUND + unset LUKS_FOUND + for opt in $rootopts; do + case $opt in + cipher=*) + OPTIONS="$OPTIONS,$opt" + ;; + hash=*) + OPTIONS="$OPTIONS,$opt" + HASH_FOUND=1 + ;; + size=*) + OPTIONS="$OPTIONS,$opt" + ;; + lvm=*) + OPTIONS="$OPTIONS,$opt" + ;; + keyscript=*) + opt=${opt#keyscript=} + if [ ! -x "/lib/cryptsetup/scripts/$opt" ] && [ ! -x "$opt" ]; then + echo "cryptsetup: WARNING: target $target has an invalid keyscript, skipped" >&2 + return 1 + fi + KEYSCRIPT="$opt" + OPTIONS="$OPTIONS,keyscript=/keyscripts/$(basename "$opt")" + ;; + tries=*) + OPTIONS="$OPTIONS,$opt" + ;; + luks) + LUKS_FOUND=1 + ;; + *) + # Presumably a non-supported option + ;; + esac + done + + # Warn for missing hash option, unless we have a LUKS partition + if [ -z "$HASH_FOUND" ] && [ -z "$LUKS_FOUND" ]; then + echo "WARNING: Option hash missing in crypttab for target $target, assuming ripemd160." >&2 + echo " If this is wrong, this initramfs image will not boot." >&2 + echo " Please read /usr/share/doc/cryptsetup/README.initramfs.gz and add" >&2 + echo " the correct hash option to your /etc/crypttab." >&2 + fi + + # If keyscript is set, the "key" is just an argument to the script + if [ "$key" != "none" ] && [ -z "$KEYSCRIPT" ]; then + echo "cryptsetup: WARNING: target $target uses a key file, skipped" >&2 + return 1 + fi +} + +get_device_modules() { + local node value cipher blockcipher ivhash + node="$1" + + # Check the ciphers used by the active root mapping + value=$(dmsetup table "$node" | cut -d " " -f4) + cipher=$(echo "$value" | cut -d ":" -f1 | cut -d "-" -f1) + blockcipher=$(echo "$value" | cut -d ":" -f1 | cut -d "-" -f2) + ivhash=$(echo "$value" | cut -d ":" -s -f2) + + if [ -n "$cipher" ]; then + echo "$cipher" + else + return 1 + fi + + if [ -n "$blockcipher" ] && [ "$blockcipher" != "plain" ]; then + echo "$blockcipher" + fi + + if [ -n "$ivhash" ] && [ "$ivhash" != "plain" ]; then + echo "$ivhash" + fi + return 0 +} + +canonical_device() { + local dev altdev + dev="$1" + + altdev="${dev#LABEL=}" + if [ "$altdev" != "$dev" ]; then + dev="/dev/disk/by-label/$altdev" + fi + + altdev="${dev#UUID=}" + if [ "$altdev" != "$dev" ]; then + dev="/dev/disk/by-uuid/$altdev" + fi + + if [ -h "$dev" ]; then + dev=$(readlink -e "$dev") + fi + + altdev="${dev#/dev/mapper/}" + if [ "$altdev" != "$dev" ]; then + echo "$altdev" + return 0 + fi + + return 1 +} + +add_device() { + local node nodes opts lastopts i count + nodes="$1" + opts="" # Applied to all nodes + lastopts="" # Applied to last node + + if [ -z "$nodes" ]; then + return 0 + fi + + # Check that it is a node under /dev/mapper/ + nodes=$(canonical_device "$nodes") || return 0 + + # Can we find this node in crypttab + if ! node_is_in_crypttab "$nodes"; then + # dm node but not in crypttab, is it a lvm device backed by dm-crypt nodes? + lvmnodes=$(get_lvm_deps "$nodes") || return 1 + + # not backed by any dm-crypt nodes; stop here + if [ -z "$lvmnodes" ]; then + return 0 + fi + + # It is a lvm device! + lastopts="lvm=$nodes" + nodes="$lvmnodes" + fi + + # Prepare to setup each node + count=$(echo "$nodes" | wc -w) + i=1 + for node in $nodes; do + # Prepare the additional options + if [ $i -eq $count ]; then + if [ -z "$opts" ]; then + opts="$lastopts" + else + opts="$opts,$lastopts" + fi + fi + + # Get crypttab root options + if ! get_device_opts "$node" "$opts"; then + continue + fi + echo "$OPTIONS" >> "$DESTDIR/conf/conf.d/cryptroot" + + # If we have a keyscript, make sure it is included + if [ -n "$KEYSCRIPT" ]; then + if [ ! -d "$DESTDIR/keyscripts" ]; then + mkdir "$DESTDIR/keyscripts" + fi + + if [ -e "/lib/cryptsetup/scripts/$KEYSCRIPT" ]; then + copy_exec "/lib/cryptsetup/scripts/$KEYSCRIPT" /keyscripts + elif [ -e "$KEYSCRIPT" ]; then + copy_exec "$KEYSCRIPT" /keyscripts + else + echo "cryptsetup: WARNING: failed to find keyscript $KEYSCRIPT" >&2 + continue + fi + fi + + # Calculate needed modules + modules=$(get_device_modules $node | sort | uniq) + if [ -z "$modules" ]; then + echo "cryptsetup: WARNING: failed to determine cipher modules to load for $node" >&2 + continue + fi + echo dm_mod + echo dm_crypt + echo "$modules" + + i=$(( $i + 1 )) + done + + return 0 +} + +add_crypto_modules() { + local mod file altmod found genericfound + mod="$1" + found="" + genericfound="" + + if [ -z "$mod" ]; then + return 1 + fi + + # We have several potential sources of modules (in order of preference): + # + # a) /lib/modules/$VERSION/kernel/arch/$ARCH/crypto/$mod-$specific.ko + # b) /lib/modules/$VERSION/kernel/crypto/$mod_generic.ko + # c) /lib/modules/$VERSION/kernel/crypto/$mod.ko + # + # and (currently ignored): + # + # d) /lib/modules/$VERSION/kernel/drivers/crypto/$specific-$mod.ko + + for file in $(find "$MODULESDIR/kernel/arch/" -name "$mod-*.ko"); do + altmod="${file##*/}" + altmod="${altmod%.ko}" + manual_add_modules "$altmod" + found="yes" + done + + for file in $(find "$MODULESDIR/kernel/crypto/" -name "${mod}_generic.ko"); do + altmod="${file##*/}" + altmod="${altmod%.ko}" + manual_add_modules "$altmod" + found="yes" + genericfound="yes" + done + + if [ -z "$genericfound" ]; then + for file in $(find "$MODULESDIR/kernel/crypto/" -name "${mod}.ko"); do + altmod="${file##*/}" + altmod="${altmod%.ko}" + manual_add_modules "$altmod" + found="yes" + done + fi + + if [ -z "$found" ]; then + return 1 + fi + + return 0 +} + +# +# Begin real processing +# + +setup="no" +rootdev="" +resumedevs="" + +# Find the root and resume device(s) +if [ -r /etc/crypttab ]; then + rootdev=$(get_root_device) + if [ -z "$rootdev" ]; then + echo "cryptsetup: WARNING: could not determine root device from /etc/fstab" >&2 + fi + resumedevs=$(get_resume_devices) +fi + +# Load the config opts and modules for each device +for dev in $rootdev $resumedevs; do + if ! modules=$(add_device "$dev"); then + echo "cryptsetup: FAILURE: could not determine configuration for $dev" >&2 + continue + fi + + setup="yes" + for mod in $modules; do + add_crypto_modules $mod + done +done + +# With large initramfs, we always add a basic subset of modules +if [ "$MODULES" != "dep" ]; then + for mod in aes sha256 cbc; do + add_crypto_modules $mod + done +fi + +# See if we need to add the basic components +if [ "$MODULES" != "dep" ] || [ "$setup" = "yes" ]; then + for mod in dm_mod dm_crypt; do + manual_add_modules $mod + done + + copy_exec /sbin/cryptsetup + copy_exec /sbin/dmsetup +# copy_exec /lib/cryptsetup/askpass +fi + +exit 0 diff --git a/initramfs-root-local-top b/initramfs-root-local-top new file mode 100644 index 0000000..e89e607 --- /dev/null +++ b/initramfs-root-local-top @@ -0,0 +1,289 @@ +#!/bin/sh + +# +# Standard initramfs preamble +# +prereqs() +{ + # Make sure that cryptroot is run last in local-top + for req in /scripts/local-top/*; do + script=${req##*/} + if [ $script != cryptroot ]; then + echo $script + fi + done +} + +case $1 in +prereqs) + prereqs + exit 0 + ;; +esac + + +# +# Helper functions +# +message() +{ + if [ -p /dev/.initramfs/usplash_outfifo ] && [ -x /sbin/usplash_write ]; then + usplash_write "TEXT-URGENT $@" + else + echo "$@" >&2 + fi + return 0 +} + +parse_options() +{ + local cryptopts + cryptopts="$1" + + if [ -z "$cryptopts" ]; then + return 1 + fi + + # Defaults + cryptcipher=aes-cbc-essiv:sha256 + cryptsize=256 + crypthash=ripemd160 + crypttarget=cryptroot + cryptsource="" + cryptlvm="" + cryptkeyscript="" + cryptkey="" # This is only used as an argument to an eventual keyscript + crypttries=3 + + local IFS=" ," + for x in $cryptopts; do + case $x in + hash=*) + crypthash=${x#hash=} + ;; + size=*) + cryptsize=${x#size=} + ;; + cipher=*) + cryptcipher=${x#cipher=} + ;; + target=*) + crypttarget=${x#target=} + ;; + source=*) + cryptsource=${x#source=} + if [ ${cryptsource#UUID=} != $cryptsource ]; then + cryptsource="/dev/disk/by-uuid/${cryptsource#UUID=}" + elif [ ${cryptsource#LABEL=} != $cryptsource ]; then + cryptsource="/dev/disk/by-label/${cryptsource#LABEL=}" + fi + ;; + lvm=*) + cryptlvm=${x#lvm=} + ;; + keyscript=*) + cryptkeyscript=${x#keyscript=} + ;; + key=*) + if [ "${x#key=}" != "none" ]; then + cryptkey=${x#key=} + fi + ;; + tries=*) + crypttries="${x#tries=}" + case "$crypttries" in + *[![:digit:].]*) + crypttries=3 + ;; + esac + ;; + esac + done + + if [ -z "$cryptsource" ]; then + message "cryptsetup: source parameter missing" + return 1 + fi + return 0 +} + +activate_vg() +{ + local vg + vg="${1#/dev/mapper/}" + + # Sanity checks + if [ ! -x /sbin/lvm ] || [ "$vg" = "$1" ]; then + return 1 + fi + + # Make sure that the device contains at least one dash + if [ "${vg%%-*}" = "$vg" ]; then + return 1 + fi + + # Split volume group from logical volume. + vg=$(echo ${vg} | sed -e 's#\(.*\)\([^-]\)-[^-].*#\1\2#') + + # Reduce padded --'s to -'s + vg=$(echo ${vg} | sed -e 's#--#-#g') + + lvm vgchange -ay ${vg} + return $? +} + +activate_evms() +{ + local dev module + dev="${1#/dev/evms/}" + + # Sanity checks + if [ ! -x /sbin/evms_activate ] || [ "$dev" = "$1" ]; then + return 1 + fi + + # Load modules used by evms + for module in dm-mod linear raid0 raid1 raid10 raid5 raid6; do + /sbin/modprobe -q $module + done + + # Activate it + /sbin/evms_activate + return $? +} + +setup_mapping() +{ + local opts count cryptcreate cryptremove NEWROOT + opts="$1" + + if [ -z "$opts" ]; then + return 0 + fi + + parse_options "$opts" || return 1 + + if [ -n "$cryptkeyscript" ] && [ ! -x "$cryptkeyscript" ]; then + message "cryptsetup: error - script \"$cryptkeyscript\" missing" + return 1 + fi + + # The same target can be specified multiple times + # e.g. root and resume lvs-on-lvm-on-crypto + if [ -e "/dev/mapper/$crypttarget" ]; then + return 0 + fi + + /sbin/modprobe -q dm_crypt + + # Make sure the cryptsource device is available + if [ ! -e $cryptsource ]; then + activate_vg $cryptsource + activate_evms $cryptsource + fi + + /sbin/udevadm settle --timeout=30 + + if [ ! -e $cryptsource ]; then + message "cryptsetup: source device $cryptsource not found" + return 1 + fi + + # Prepare commands + if /sbin/cryptsetup isLuks $cryptsource > /dev/null 2>&1; then + cryptcreate="/sbin/cryptsetup -T 1 luksOpen $cryptsource $crypttarget" + else + cryptcreate="/sbin/cryptsetup -T 1 -c $cryptcipher -s $cryptsize -h $crypthash create $crypttarget $cryptsource" + fi + cryptremove="/sbin/cryptsetup remove $crypttarget" + NEWROOT="/dev/mapper/$crypttarget" + + # Try to get a satisfactory password $crypttries times + count=0 + while [ $crypttries -le 0 ] || [ $count -lt $crypttries ]; do + count=$(( $count + 1 )) + + if [ $count -gt 1 ]; then + sleep 3 + fi + + if [ $crypttries -gt 0 ] && [ $count -gt $crypttries ]; then + message "cryptsetup: maximum number of tries exceeded for $crypttarget" + return 1 + fi + + if [ -z "$cryptkeyscript" ]; then + cryptkeyscript="/lib/cryptsetup/askpass" + cryptkey="Enter passphrase to unlock the disk $cryptsource ($crypttarget): " + fi + + + if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \ + $cryptkeyscript "$cryptkey" | $cryptcreate --key-file=- ; then + message "cryptsetup: cryptsetup failed, bad password or options?" + continue + fi + + if [ ! -e "$NEWROOT" ]; then + message "cryptsetup: unknown error setting up device mapping" + return 1 + fi + + FSTYPE='' + eval $(fstype < "$NEWROOT") + + # See if we need to setup lvm on the crypto device + if [ "$FSTYPE" = "lvm" ] || [ "$FSTYPE" = "lvm2" ]; then + if [ -z "$cryptlvm" ]; then + message "cryptsetup: lvm fs found but no lvm configured" + return 1 + elif ! activate_vg "/dev/mapper/$cryptlvm"; then + message "cryptsetup: failed to setup lvm device" + return 1 + fi + + NEWROOT="/dev/mapper/$cryptlvm" + eval $(fstype < "$NEWROOT") + fi + + if [ -z "$FSTYPE" ] || [ "$FSTYPE" = "unknown" ]; then + message "cryptsetup: unknown fstype, bad password or options?" + $cryptremove + continue + fi + + message "cryptsetup: $crypttarget setup successfully" + break + done + + /sbin/udevadm settle --timeout=30 + return 0 +} + +# +# Begin real processing +# + +# Do we have any kernel boot arguments? +found='' +for opt in $(cat /proc/cmdline); do + case $opt in + cryptopts=*) + found=yes + setup_mapping "${opt#cryptopts=}" + ;; + esac +done + +if [ -n "$found" ]; then + exit 0 +fi + +# Do we have any settings from the /conf/conf.d/cryptroot file? +if [ -r /conf/conf.d/cryptroot ]; then + while read mapping <&3; do + setup_mapping "$mapping" + done 3< /conf/conf.d/cryptroot +fi + +exit 0 -- 2.44.0