shim/shim-install
Gary Ching-Pang Lin b90dab54cc Accepting request 1087321 from home:gary_lin:branches:devel:openSUSE:Factory
- Update shim-install to amend full disk encryption support
    b540061e041b  Adopt TPM 2.0 Key File for grub2 TPM 2.0 protector
    f2e8143ce831  Use the long name to specify the grub2 key protector
    72830120e5ea  cryptodisk: support TPM authorized policies
    49e7a0d307f3  Do not use tpm_record_pcrs unless the command is in command.lst

OBS-URL: https://build.opensuse.org/request/show/1087321
OBS-URL: https://build.opensuse.org/package/show/devel:openSUSE:Factory/shim?expand=0&rev=202
2023-05-25 12:41:58 +00:00

499 lines
15 KiB
Bash

#! /bin/bash -e
arch=`uname -m`
rootdir=
bootdir=
efidir=
install_device=
efibootdir=
ca_string=
no_nvram=no
removable=no
clean=no
sysconfdir="/etc"
libdir="/usr/lib64" # Beware, this is arch dependent!
datadir="/usr/share"
source_dir="${datadir}/efi/${arch}"
efibootmgr="/usr/sbin/efibootmgr"
grub_probe="/usr/sbin/grub2-probe"
grub_mkrelpath="/usr/bin/grub2-mkrelpath"
no_grub_install=no
grub_install="/usr/sbin/grub2-install"
grub_install_target=
self="`basename $0`"
grub_cfg="/boot/grub2/grub.cfg"
update_boot=no
def_grub_efi="${source_dir}/grub.efi"
def_boot_efi=
[ ! -r /usr/etc/default/shim ] || . /usr/etc/default/shim
[ ! -r /etc/default/shim ] || . /etc/default/shim
if [ -z "$def_shim_efi" -o ! -e ${source_dir}/${def_shim_efi} ] ; then
def_shim_efi="shim.efi"
fi
source_shim_efi="${source_dir}/${def_shim_efi}"
if [ x${arch} = xx86_64 ] ; then
grub_install_target="x86_64-efi"
def_boot_efi="bootx64.efi"
elif [ x${arch} = xaarch64 ] ; then
grub_install_target="arm64-efi"
def_boot_efi="bootaa64.efi"
else
echo "Unsupported architecture: ${arch}"
exit 1
fi
if [ ! -d "${source_dir}" -o ! -e "${def_grub_efi}" ] ; then
# for outdated packages fall back to previous behavior
source_dir="$libdir/efi"
def_grub_efi="${source_dir}/grub.efi"
fi
# Get GRUB_DISTRIBUTOR.
if test -f "${sysconfdir}/default/grub" ; then
. "${sysconfdir}/default/grub"
fi
if [ x"${GRUB_DISTRIBUTOR}" = x ] && [ -f "${sysconfdir}/os-release" ] ; then
. "${sysconfdir}/os-release"
GRUB_DISTRIBUTOR="${NAME} ${VERSION}"
fi
bootloader_id="$(echo "$GRUB_DISTRIBUTOR" | tr 'A-Z' 'a-z' | cut -d' ' -f1)"
if test -z "$bootloader_id"; then
bootloader_id=grub
fi
efi_distributor="$bootloader_id"
bootloader_id="${bootloader_id}-secureboot"
case "$bootloader_id" in
"sle"*)
ca_string='SUSE Linux Enterprise Secure Boot CA1';;
"opensuse"*)
ca_string='openSUSE Secure Boot CA1';;
*) ca_string="";;
esac
is_azure () {
local bios_vendor;
local product_name;
local sys_vendor;
local sysfs_dmi_id="/sys/class/dmi/id"
if test -e "${sysfs_dmi_id}/bios_vendor"; then
bios_vendor=$(cat "${sysfs_dmi_id}/bios_vendor")
fi
if test -e "${sysfs_dmi_id}/product_name"; then
product_name=$(cat "${sysfs_dmi_id}/product_name")
fi
if test -e "${sysfs_dmi_id}/sys_vendor"; then
sys_vendor=$(cat "${sysfs_dmi_id}/sys_vendor")
fi
if test "x${bios_vendor}" != "xMicrosoft Corporation"; then
# return false
return 1
fi
if test "x${product_name}" != "xVirtual Machine"; then
# return false
return 1
fi
if test "x${sys_vendor}" != "xMicrosoft Corporation"; then
# return false
return 1
fi
# return true
return 0
}
usage () {
echo "Usage: $self [OPTION] [INSTALL_DEVICE]"
echo
echo "Install Secure Boot Loaders on your drive."
echo
echo "--directory=DIR use images from DIR."
echo "--grub-probe=FILE use FILE as grub-probe."
echo "--removable the installation device is removable."
echo "--no-nvram don't update the NVRAM variable."
echo "--bootloader-id=ID the ID of bootloader."
echo "--efi-directory=DIR use DIR as the EFI System Partition root."
echo "--config-file=FILE use FILE as config file, default is $grub_cfg."
echo "--clean remove all installed files and configs."
echo "--suse-enable-tpm install grub.efi with TPM support."
echo "--no-grub-install Do not run grub2-install."
echo
echo "INSTALL_DEVICE must be system device filename."
}
argument () {
opt="$1"
shift
if test $# -eq 0; then
echo "$0: option requires an argument -- \`$opt'" 1>&2
exit 1
fi
echo "$1"
}
# Check the arguments.
while test $# -gt 0
do
option=$1
shift
case "$option" in
-h | --help)
usage
exit 0 ;;
--root-directory)
rootdir="`argument $option "$@"`"; shift;;
--root-directory=*)
rootdir="`echo "$option" | sed 's/--root-directory=//'`" ;;
--efi-directory)
efidir="`argument $option "$@"`"; shift;;
--efi-directory=*)
efidir="`echo "$option" | sed 's/--efi-directory=//'`" ;;
--directory | -d)
source_dir="`argument $option "$@"`"; shift;;
--directory=*)
source_dir="`echo "$option" | sed 's/--directory=//'`" ;;
--bootloader-id)
bootloader_id="`argument $option "$@"`"; shift;;
--bootloader-id=*)
bootloader_id="`echo "$option" | sed 's/--bootloader-id=//'`" ;;
--grub-probe)
grub_probe="`argument "$option" "$@"`"; shift;;
--grub-probe=*)
grub_probe="`echo "$option" | sed 's/--grub-probe=//'`" ;;
--config-file)
grub_cfg="`argument "$option" "$@"`"; shift;;
--config-file=*)
grub_cfg="`echo "$option" | sed 's/--config-file=//'`" ;;
--removable)
no_nvram=yes
removable=yes ;;
--no-nvram)
no_nvram=yes ;;
--suse-enable-tpm)
# bsc#1174320 shim-install uses wrong paths for EFI files
# There are 3 possible locations of grub-tpm.efi and we will check them
# one by one.
if [ -e "${source_dir}/grub-tpm.efi" ]; then
source_grub_efi="${source_dir}/grub-tpm.efi"
elif [ -e "${datadir}/grub2/${grub_install_target}/grub-tpm.efi" ] ; then
source_grub_efi="${datadir}/grub2/${grub_install_target}/grub-tpm.efi"
else
source_grub_efi="/usr/lib/grub2/${grub_install_target}/grub-tpm.efi"
fi
;;
--clean)
clean=yes ;;
--no-grub-install)
no_grub_install=yes ;;
-*)
echo "Unrecognized option \`$option'" 1>&2
usage
exit 1
;;
*)
if test "x$install_device" != x; then
echo "More than one install device?" 1>&2
usage
exit 1
fi
install_device="${option}" ;;
esac
done
if test -n "$efidir"; then
efi_fs=`"$grub_probe" --target=fs "${efidir}"`
if test "x$efi_fs" = xfat; then :; else
echo "$efidir doesn't look like an EFI partition." 1>&2
efidir=
fi
fi
if [ -z "$bootdir" ]; then
bootdir="/boot"
if [ -n "$rootdir" ] ; then
# Initialize bootdir if rootdir was initialized.
bootdir="${rootdir}/boot"
fi
fi
# Find the EFI System Partition.
if test -n "$efidir"; then
install_device="`"$grub_probe" --target=device --device-map= "${efidir}"`"
else
if test -d "${bootdir}/efi"; then
install_device="`"$grub_probe" --target=device --device-map= "${bootdir}/efi"`"
# Is it a mount point?
if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${bootdir}"`"; then
efidir="${bootdir}/efi"
fi
elif test -d "${bootdir}/EFI"; then
install_device="`"$grub_probe" --target=device --device-map= "${bootdir}/EFI"`"
# Is it a mount point?
if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${bootdir}"`"; then
efidir="${bootdir}/EFI"
fi
elif test -n "$rootdir" && test "x$rootdir" != "x/"; then
# The EFI System Partition may have been given directly using
# --root-directory.
install_device="`"$grub_probe" --target=device --device-map= "${rootdir}"`"
# Is it a mount point?
if test "x$install_device" != "x`"$grub_probe" --target=device --device-map= "${rootdir}/.."`"; then
efidir="${rootdir}"
fi
fi
if test -n "$efidir"; then
efi_fs=`"$grub_probe" --target=fs "${efidir}"`
if test "x$efi_fs" = xfat; then :; else
echo "$efidir doesn't look like an EFI partition." 1>&2
efidir=
fi
fi
fi
if test -n "$efidir"; then
efi_file=shim.efi
efibootdir="$efidir/EFI/boot"
mkdir -p "$efibootdir" || exit 1
if test "$removable" = "yes" ; then
efidir="$efibootdir"
else
efidir="$efidir/EFI/$efi_distributor"
mkdir -p "$efidir" || exit 1
fi
else
echo "No valid EFI partition" 1>&2
exit 1;
fi
if test "$removable" = "no" -a -f "$efibootdir/$def_boot_efi"; then
if test -n "$ca_string" && (grep -q "$ca_string" "$efibootdir/$def_boot_efi"); then
update_boot=yes
fi
else
update_boot=yes
fi
if test "$clean" = "yes"; then
rm -f "${efidir}/shim.efi"
rm -f "${efidir}/MokManager.efi"
rm -f "${efidir}/grub.efi"
rm -f "${efidir}/grub.cfg"
rm -f "${efidir}/boot.csv"
if test "$update_boot" = "yes"; then
rm -f "${efibootdir}/${def_boot_efi}"
rm -f "${efibootdir}/fallback.efi"
# bsc#1175626, bsc#1175656 also clean up MokManager
rm -f "${efibootdir}/MokManager.efi"
fi
if test "$no_nvram" = no && test -n "$bootloader_id"; then
# Delete old entries from the same distributor.
for bootnum in `$efibootmgr | grep '^Boot[0-9]' | \
fgrep -i " $bootloader_id" | cut -b5-8`; do
$efibootmgr -b "$bootnum" -B
done
fi
exit 0
fi
cp "${source_dir}/MokManager.efi" "${efidir}"
if test -n "$source_grub_efi" && ! test -f "$source_grub_efi"; then
echo "File $source_grub_efi doesn't exist, fallback to default one" 1>&2
source_grub_efi=""
fi
if test -z "$source_grub_efi"; then
source_grub_efi="$def_grub_efi"
fi
echo "copying $source_grub_efi to ${efidir}/grub.efi"
cp "$source_grub_efi" "${efidir}/grub.efi"
if test "$efidir" != "$efibootdir" ; then
cp "${source_shim_efi}" "${efidir}/shim.efi"
if test -n "$bootloader_id"; then
echo "shim.efi,${bootloader_id}" | iconv -f ascii -t ucs2 > "${efidir}/boot.csv"
fi
fi
if test "$update_boot" = "yes"; then
cp "$source_shim_efi" "${efibootdir}/${def_boot_efi}"
if test "$removable" = "no"; then
cp "${source_dir}/fallback.efi" "${efibootdir}"
# bsc#1175626, bsc#1175656 Since shim 15, loading MokManager becomes
# mandatory if a MOK request exists. Copy MokManager to \EFI\boot so
# that boot*.efi can load MokManager to process the request instead
# of shutting down the system immediately.
cp "${source_dir}/MokManager.efi" "${efibootdir}"
fi
fi
prepare_cryptodisk () {
uuid="$1"
if [ "x$GRUB_CRYPTODISK_PASSWORD" != x ]; then
echo "cryptomount -u $uuid -p \"$GRUB_CRYPTODISK_PASSWORD\""
return
fi
if [ "x$GRUB_TPM2_SEALED_KEY" = x ]; then
echo "cryptomount -u $uuid"
return
fi
tpm_sealed_key="${GRUB_TPM2_SEALED_KEY}"
declare -g TPM_PCR_SNAPSHOT_TAKEN
if [ -z "$TPM_PCR_SNAPSHOT_TAKEN" ]; then
TPM_PCR_SNAPSHOT_TAKEN=1
# Check if tpm_record_pcrs is available and set the command to
# grub.cfg.
if grep -q "tpm_record_pcrs" ${datadir}/grub2/${arch}-efi/command.lst ; then
echo "tpm_record_pcrs 0-9"
fi
fi
cat <<EOF
tpm2_key_protector_init -T \$prefix/$tpm_sealed_key
if ! cryptomount -u $uuid --protector tpm2; then
cryptomount -u $uuid
fi
EOF
}
make_grubcfg () {
grub_cfg_dirname=`dirname $grub_cfg`
grub_cfg_basename=`basename $grub_cfg`
cfg_fs_uuid=`"$grub_probe" --target=fs_uuid "$grub_cfg_dirname"`
# bsc#1153953 - Leap 42.3 boot error snapshot missing
# We have to check btrfs is used as root file system to enable relative path
# lookup for file to be on par with other utility which also accounts for it.
GRUB_FS="$(stat -f --printf=%T / || echo unknown)"
if test "x$SUSE_BTRFS_SNAPSHOT_BOOTING" = "xtrue" &&
[ "x${GRUB_FS}" = "xbtrfs" ] ; then
cat <<EOF
set btrfs_relative_path="yes"
EOF
if ${grub_mkrelpath} --usage | grep -q -e '--relative'; then
grub_mkrelpath="${grub_mkrelpath} -r"
fi
fi
if [ x$GRUB_ENABLE_CRYPTODISK = xy ]; then
for uuid in `"${grub_probe}" --target=cryptodisk_uuid --device-map= "${grub_cfg_dirname}"`; do
prepare_cryptodisk "$uuid"
done
fi
cat <<EOF
search --fs-uuid --set=root ${cfg_fs_uuid}
set prefix=(\${root})`${grub_mkrelpath} ${grub_cfg_dirname}`
source "\${prefix}/${grub_cfg_basename}"
EOF
}
# bnc#889765 GRUB shows broken letters at boot
# invoke grub_install to initialize /boot/grub2 directory with files needed by grub.cfg
# bsc#1118363 shim-install didn't specify the target for grub2-install
# set the target explicitly for some special cases
if test "$no_grub_install" != "yes"; then
${grub_install} --target=${grub_install_target} --no-nvram
fi
# Making sure grub.cfg not overwritten by grub-install above
make_grubcfg > "${efidir}/grub.cfg"
if test "$no_nvram" = no && test -n "$bootloader_id"; then
modprobe -q efivars 2>/dev/null || true
# Delete old entries from the same distributor.
for bootnum in `$efibootmgr | grep '^Boot[0-9]' | \
fgrep -i " $bootloader_id" | cut -b5-8`; do
$efibootmgr -b "$bootnum" -B
done
efidir_drive="$("$grub_probe" --target=drive --device-map= "$efidir")"
efidir_disk="$("$grub_probe" --target=disk --device-map= "$efidir")"
if test -z "$efidir_drive" || test -z "$efidir_disk"; then
echo "Can't find GRUB drive for $efidir; unable to create EFI Boot Manager entry." >&2
# bsc#1119762 If the MD device is partitioned, we just need to create one
# boot entry since the partitions are nested partitions and the mirrored
# partitions share the same UUID.
elif [[ "$efidir_drive" == \(mduuid/* && "$efidir_drive" != \(mduuid/*,* ]]; then
eval $(mdadm --detail --export "$efidir_disk" |
perl -ne 'print if m{^MD_LEVEL=}; push( @D, $1) if (m{^MD_DEVICE_\S+_DEV=(\S+)$});
sub END() {print "MD_DEVS=\"", join( " ", @D), "\"\n";};')
if [ "$MD_LEVEL" != "raid1" ]; then
echo "GRUB drive for $efidir not on RAID1; unable to create EFI Boot Manager entry." >&2
fi
for mddev in $MD_DEVS; do
efidir_drive="$("$grub_probe" --target=drive --device-map= -d "$mddev")"
efidir_disk="$("$grub_probe" --target=disk --device-map= -d "$mddev")"
efidir_part="$(echo "$efidir_drive" | sed 's/^([^,]*,[^0-9]*//; s/[^0-9].*//')"
efidir_d=${mddev#/dev/}
$efibootmgr -c -d "$efidir_disk" -p "$efidir_part" -w \
-L "$bootloader_id ($efidir_d)" -l "\\EFI\\$efi_distributor\\$efi_file"
done
else
efidir_part="$(echo "$efidir_drive" | sed 's/^([^,]*,[^0-9]*//; s/[^0-9].*//')"
$efibootmgr -c -d "$efidir_disk" -p "$efidir_part" -w \
-L "$bootloader_id" -l "\\EFI\\$efi_distributor\\$efi_file"
fi
fi
# bsc#1185464 bsc#1185961
# The Azure firmware sometimes doesn't respect the boot option created by
# either efibootmgr or fallback.efi so we have to remove fallback.efi to
# avoid the endless reset loop.
if is_azure; then
# Skip the workaround if we don't own \EFI\Boot or the removable
# option is used
if test "$update_boot" = "yes" && test "$removable" = "no"; then
# Remove fallback.efi which could cause the reset loop in Azure
rm -f "${efibootdir}/fallback.efi"
# Remove the older grub binary and config
rm -f "${efibootdir}/grub.efi"
rm -f "${efibootdir}/grub.cfg"
# Install new grub binary and config file to \EFI\Boot as
# the "removable" option
cp "${efidir}/grub.cfg" "${efibootdir}/grub.cfg"
cp "${efidir}/grub.efi" "${efibootdir}/grub.efi"
fi
fi