123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375 |
- #! @BUILD_SHEBANG@ -e
- # Test GRUBs ability to read various LUKS containers
- # Copyright (C) 2023 Free Software Foundation, Inc.
- #
- # GRUB is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # GRUB is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- # Initialize some variables.
- prefix="@prefix@"
- exec_prefix="@exec_prefix@"
- datarootdir="@datarootdir@"
- builddir="@builddir@"
- PACKAGE_NAME=@PACKAGE_NAME@
- PACKAGE_TARNAME=@PACKAGE_TARNAME@
- PACKAGE_VERSION=@PACKAGE_VERSION@
- # Force build directory components
- PATH="${builddir}:$PATH"
- export PATH
- grub_shell_opts=
- disksize=20M
- detached_header=
- keyfile=
- keyfile_offset=
- keyfile_size=
- KEYFILE_SIZE_MAX=4096
- debug="${GRUB_SHELL_LUKS_DEFAULT_DEBUG:-$GRUB_TEST_DEFAULT_DEBUG}"
- GRUB_SHELL_LUKS_TIMEOUT=${GRUB_SHELL_LUKS_TIMEOUT:-${GRUB_SHELL_DEFAULT_TIMEOUT:-600s}}
- # Usage: usage
- # Print the usage.
- usage () {
- cat <<EOF
- Usage: $0 [OPTION] [SOURCE]
- Create a LUKS disk with cryptsetup, then verify that it is accessible by grub
- running in a QEMU instance.
- -h, --help print this message and exit
- -v, --version print the version information and exit
- --modules=MODULES pre-load specified modules MODULES
- --qemu-opts=OPTIONS extra options to pass to Qemu instance
- --cs-opts=OPTIONS extra options to pass to cryptsetup instance
- --cs-script=FILE script of cryptsetup commands to be run after format
- --luks=1|2 Use LUKS1 or LUKS2 volumes
- --detached-header Use a detached header
- --keyfile[=FILE] Use a randomly generated key file of size $KEYFILE_SIZE_MAX if not
- given a FILE to use as the key file.
- $0 creates a LUKS disk with cryptsetup, then verify that it is accessible by
- grub running in a QEMU instance.
- Report bugs to <bug-grub@gnu.org>.
- EOF
- }
- . "${builddir}/grub-core/modinfo.sh"
- # TODO: We should be selecting the drive based on disk id, change this once
- # grub support searching by disk id.
- disk="hd0"
- case "${grub_modinfo_target_cpu}-${grub_modinfo_platform}" in
- i386-qemu)
- disk="ata0"
- ;;
- loongarch64-efi)
- disk="hd1"
- ;;
- esac
- # Check the arguments.
- for option in "$@"; do
- case "$option" in
- -h | --help)
- usage
- exit 0 ;;
- -v | --version)
- echo "$0 (GNU GRUB ${PACKAGE_VERSION})"
- exit 0 ;;
- -d | --debug)
- debug=$((${debug:-0}+1)) ;;
- --debug=*)
- debug=$((`echo "$option" | sed -e 's/--debug=//'`)) ;;
- --modules=*)
- ms=`echo "$option" | sed -e 's/--modules=//'`
- modules="$modules,$ms" ;;
- --qemu-opts=*)
- qs=`echo "$option" | sed -e 's/--qemu-opts=//'`
- qemuopts="$qemuopts $qs" ;;
- --cs-opts=*)
- qs=`echo "$option" | sed -e 's/--cs-opts=//'`
- csopts="$csopts $qs" ;;
- --cs-script=*)
- qs=`echo "$option" | sed -e 's/--cs-script=//'`
- csscripts="$csscripts $qs" ;;
- --luks=*)
- qs=`echo "$option" | sed -e 's/--luks=//'`
- csopts="$csopts --type luks$qs" ;;
- --detached-header)
- detached_header=1 ;;
- --keyfile=*)
- qs=`echo "$option" | sed -e 's/--keyfile=//'`
- keyfile="$qs" ;;
- --keyfile)
- keyfile=1 ;;
- --disksize=*)
- qs=`echo "$option" | sed -e 's/--disksize=//'`
- disksize="$qs" ;;
- -*)
- echo "Unrecognized option \`$option'" 1>&2
- usage
- exit 3
- ;;
- *)
- if [ "x${source}" != x ] ; then
- echo "too many parameters at the end" 1>&2
- usage
- exit 4
- fi
- source="${option}" ;;
- esac
- done
- [ "${debug:-0}" -gt 1 ] && set -x
- grub_shell_opts="$grub_shell_opts --timeout=${GRUB_SHELL_LUKS_TIMEOUT}"
- if [ "${debug:-0}" -gt 2 ]; then
- grub_shell_opts="$grub_shell_opts --qemu-opts=-nographic"
- fi
- # Make sure that the dm-crypto device is shutdown
- cleanup() {
- if [ -e "$luksdev" ]; then
- cryptsetup close "$luksdev"
- fi
- if [ -z "$debug" ] && [ "${RET:-1}" -eq 0 ]; then
- rm -rf "$lukstestdir" || :
- fi
- }
- trap cleanup EXIT INT TERM KILL QUIT
- get_random_bytes() {
- local NUM_BYTES=$1
- dd if=/dev/urandom bs=512 count=$((($NUM_BYTES / 512)+2)) 2>/dev/null \
- | tr -d '\0' | dd bs=1 count=$(($NUM_BYTES)) 2>/dev/null
- }
- # create a random directory to be hold generated files
- lukstestdir="`mktemp -d "${TMPDIR:-/tmp}/$(basename "$0").XXXXXXXXXX"`" || exit 20
- luksfile=$lukstestdir/luks.disk
- lukshdrfile=$lukstestdir/luks.header
- lukskeyfile=$lukstestdir/luks.key
- vfile=$lukstestdir/mnt/test.verify
- vtext="TEST VERIFIED"
- testvars=$lukstestdir/testvars
- testcase=$lukstestdir/testcase.cfg
- testoutput=$lukstestdir/testoutput
- password=testpass
- [ -n "$debug" ] && echo "LUKS TEST directory: $lukstestdir" >&2
- # If testing keyfiles, create a big one.
- if [ -e "$keyfile" ]; then
- password=`cat "$keyfile"`
- elif [ -n "$keyfile" ]; then
- password=`get_random_bytes $KEYFILE_SIZE_MAX`
- fi
- if [ -n "$detached_header" ]; then
- csopts="$csopts --header $lukshdrfile"
- fi
- # create the key file
- echo -n "$password" > $lukskeyfile
- # Create a very small LUKS container for the test
- truncate -s $disksize $luksfile || exit 21
- # Format the luks disk file
- cryptsetup luksFormat -q $csopts $luksfile $lukskeyfile || exit 22
- # Run any cryptsetup scripts
- export luksdiskfile=${detached_header:+$lukshdrfile}${detached_header:-$luksfile}
- export lukskeyfile
- for csscript in $csscripts; do
- [ -f "$csscript" ] && . $csscript
- done
- # Look for --keyfile-offset and --keyfile-size options in the cryptsetup
- # options, and process them specially.
- csopen_opts=
- get_args=0
- varname=
- for option in $csopts; do
- if [ "$get_args" -gt 0 ]; then
- csopen_opts=" $csopen_opts $option"
- get_args=$(($get_args - 1))
- eval ${varname}=$option
- continue
- fi
- case "$option" in
- --keyfile-offset)
- varname=keyfile_offset
- get_args=1 ;;
- --keyfile-offset=*)
- keyfile_offset=`echo "$option" | sed -e 's/--keyfile-offset=//'` ;;
- --keyfile-size | -l)
- varname=keyfile_size
- get_args=1 ;;
- --keyfile-size=*)
- keyfile_size=`echo "$option" | sed -e 's/--keyfile-size=//'` ;;
- *)
- continue ;;
- esac
- csopen_opts=" $csopen_opts $option"
- done
- # Open LUKS device
- luksdev=/dev/mapper/`basename $lukstestdir`
- cryptsetup open ${detached_header:+--header $lukshdrfile} $csopen_opts \
- --key-file $lukskeyfile $luksfile `basename $luksdev` || exit 23
- # Make filesystem on the luks disk
- mkfs.vfat $luksdev >/dev/null 2>&1 || exit 24
- # Add verification file to filesystem
- mkdir $lukstestdir/mnt
- mount $luksdev $lukstestdir/mnt || exit 25
- echo "$vtext" > $vfile
- # Unmount filesystem
- umount $lukstestdir/mnt || exit 26
- . "@builddir@/grub-core/modinfo.sh"
- if [ x"${grub_modinfo_platform}" = xemu ]; then
- grub_testvars="(host)$testvars"
- grub_key_file="(host)$lukskeyfile"
- grub_lukshdr_file="(host)$lukshdrfile"
- else
- grub_testvars="/testvars"
- grub_key_file="/keyfile"
- grub_lukshdr_file="/luks.header"
- fi
- # Can not use --disk with a raw LUKS container because it appears qemu
- # tries to convert the image to and is failing with:
- # "Parameter 'key-secret' is required for cipher"
- qemuopts="$qemuopts -drive file=$luksfile,index=0,media=disk,format=raw"
- # Add crypto modules
- modules="$modules cryptodisk luks luks2 fat"
- # Create randomly generated trim line
- trim_line=`mktemp -u XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
- # Create vars to import into grub script
- cat >$testvars <<EOF
- grub_debug="$debug"
- grub_lukshdr_file="$grub_lukshdr_file"
- grub_key_file="$grub_key_file"
- grub_keyfile_offset="$keyfile_offset"
- grub_keyfile_size="$keyfile_size"
- vfilename="`basename $vfile`"
- vtext="$vtext"
- trim_line="$trim_line"
- disk="$disk"
- EOF
- # If testing keyfiles, do not use password variable
- if [ -z "$keyfile" ]; then
- echo "grub_password=\"$password\"" >>$testvars
- fi
- # Create testcase script
- cat >$testcase <<'EOF'
- set debug=all
- search -n -f --set=testvarsdev /testvars
- if [ "$?" -ne 0 ]; then
- echo; echo "$trim_line"
- echo "Could not find testvars file."
- ${halt_cmd}
- fi
- set debug=
- . ($testvarsdev)/testvars
- # If key file exists, use it instead of password
- if [ -e "$grub_key_file" ]; then
- cryptomount_opts="$cryptomount_opts -k $grub_key_file"
- else
- cryptomount_opts="$cryptomount_opts -p $grub_password"
- fi
- if [ -n "$grub_keyfile_offset" ]; then
- cryptomount_opts="$cryptomount_opts -O $grub_keyfile_offset"
- fi
- if [ -n "$grub_keyfile_size" ]; then
- cryptomount_opts="$cryptomount_opts -S $grub_keyfile_size"
- fi
- if [ -e "$grub_lukshdr_file" ]; then
- cryptomount_opts="$cryptomount_opts -H $grub_lukshdr_file"
- fi
- cdisk=crypto0
- if test -n "$grub_debug" -a "$grub_debug" -gt 0; then
- echo cmd: cryptomount $cryptomount_opts ($disk)
- echo -n "devices: "
- ls
- fi
- if test -n "$grub_debug" -a "$grub_debug" -gt 1; then
- set debug=all
- fi
- cryptomount $cryptomount_opts ($disk)
- ret="$?"
- if test -n "$grub_debug" -a "$grub_debug" -eq 2; then
- set debug=
- fi
- echo; echo "$trim_line"
- if test $ret -eq 0; then
- cat ($cdisk)/$vfilename
- else
- echo "cryptomount failed: $ret"
- fi
- EOF
- grub_shell_opts="$grub_shell_opts --trim=${trim_line}"
- if [ -n "$keyfile" ]; then
- grub_shell_opts="$grub_shell_opts --files=${keyfile:+${grub_key_file}=${lukskeyfile}}"
- fi
- if [ -n "$detached_header" ]; then
- grub_shell_opts="$grub_shell_opts --files=${detached_header:+${grub_lukshdr_file}=${lukshdrfile}}"
- fi
- # Run the test in grub-shell
- @builddir@/grub-shell ${debug:+--debug=$debug} $grub_shell_opts \
- --modules="$modules" --qemu-opts="$qemuopts" \
- --files="${grub_testvars}=${testvars}" "$testcase" \
- >$testoutput
- ret=$?
- if [ "$ret" -eq 0 ]; then
- if ! grep -q "^${vtext}$" "$testoutput"; then
- echo "error: test not verified [`cat $testoutput`]" >&2
- exit 1
- fi
- else
- echo "grub-shell exited with error: $ret" >&2
- exit 27
- fi
- exit $ret
|