#! @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 . # 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 } . "${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 <>$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