Add complete guide and all config variants

This commit is contained in:
renato97
2026-02-05 14:06:25 +00:00
parent 239ee0e593
commit b40c76762c
1053 changed files with 167761 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
## OpenCore + OpenLinuxBoot + Secure Boot
If you want to use OpenCore + OpenLinuxBoot + Secure Boot it is possible to sign everything
manually yourself, including any new Linux kernels after updates. This is possible since most
standard distros leave at least the previous kernel bootable (and OpenLinuxBoot exposes
this, via the Auxiliary menu), so you can boot into the old kernel, then sign the new
kernel yourself.
More convenient may be to trust the signing keys of the specific distros which you
want to boot, which are bundled into the `shimx64.efi` file installed with each distro.
You can extract these with `shim-to-cert.tool` distributed with OpenCore, then install
them in your system Secure Boot `db` variable. Best practice would be to install the deny
list (`vendor.dbx`) from `shimx64.efi`, if any, into your system `dbx` variable, as well.
(Otherwise you are ignoring any revocations which the vendor has made.)
Recently, Shim has added SBAT support, as a more efficient way to revoke unsafe
binaries. Unfortunately, the SBAT enforcement code is part of Shim, and is not
something you can extract and add to your system Secure Boot database.
To work round this, the new recommended way to boot OpenCore + OpenLinuxBoot +
Secure Boot is to make a user build of Shim. The vendor certificates
and revocation lists extracted from the distro `shimx64.efi` files are combined
and signed by you, into your own build of Shim; in this approach, these vendor
certificates should NOT also be included in the system Secure Boot database,
and should be removed if you added them previously. Including them in both places
will still boot under Secure Boot, but will effectively disable SBAT revocation.
> If you are signing everything yourself, including Linux kernels after updates, that
will still work as before and the below is not needed. Equally, if you are not
using Secure Boot the below is not needed.
The advantages of using a user build of Shim are:
- No need to sign every kernel after updates (same as previous method)
- Linux SBAT integration (new)
- Linux MOK integration (new)
- No need to include the Windows intermediate CA - you are trusting whichever distro
keys you choose to include in your own Shim, directly (new)
Disadvantages are:
- Need to update when distro keys or distro revocation lists within Shim are updated
(same as previous method)
- Need to udpate when Shim SBAT level is updated (new)
### Method
`Utilities/ShimUtils` includes a script `shim-make.tool` which will download the
current Shim source and build it for you, on macOS (using Ubuntu multipass) or on
Linux (Ubuntu and Fedora supported, others may work).
- Extract `vendor.db` and `vendor.dbx` files from the `shimx64.efi` file of each distro
which you want to load (using `shim-to-cert.tool`)
- For non-GRUB distros, the required public keys for this process cannot be extracted
from `shimx64.efi` and so must be found by additional user research
- Concatentate these (e.g. `cat fedora/vendor.db ubuntu/vendor.db > combined/vendor.db`
and `cat fedora/vendor.dbx ubuntu/vendor.dbx > combined/vendor.dbx`)
- Do not concatenate `.der` files directly, it will not work
- If you have a single distro with a single `.der` file, you can use `VENDOR_CERT_FILE`
instead of `VENDOR_DB_FILE` in the `make` options below; otherwise, you will need to use
`cert-to-efi-sig-list` from `efitools` to convert the `.der` file to a sig list - this
is done automatically by `shim-to-cert.tool` when `efitools` are available (in
Linux; or from within Ubuntu multipass on macOS, e.g. `multipass shell oc-shim`)
- Build a version of Shim which includes these concatenated signature lists (and
launches OpenCore.efi directly):
- `./shim-make.tool setup`
- `./shim-make.tool clean` (only needed if remaking after the initial make)
- `./shim-make.tool make VENDOR_DB_FILE={full-path-to}/vendor.db VENDOR_DBX_FILE={full-path-to}/vendor.dbx`
- On macOS, the paths to these files must either be within the multipass VM, or
within a subdirectory visible to macOS and the VM on the same path, such as
`/Users/{username}/shim_root` when using `shim-make.tool` default settings
- Copy the relevant files (`shimx64.efi` and `mmx64.efi` as well as `BOOTX64.CSV`) to your mounted ESP volume, e.g.:
- `./shim-make.tool install /Volumes/EFI` (macOS)
- `sudo ./shim-make.tool install /boot/efi` (Linux)
- Sign the newly built `shimx64.efi` and `mmx64.efi` with your own ISK (see e.g.
https://habr.com/en/articles/273497/ - Google translate is your friend)
- If you do not copy and sign `mmx64.efi` as well as `shimx64.efi`, your system will hang if any MOK operations are attempted
- `BOOTX64.CSV` is not required and is for information only
As before you need to sign `OpenCore.efi` and any drivers it loads with your ISK.
You now also need to add an empty SBAT section to `OpenCore.efi` before signing it.
> An empty SBAT section means: 'I'm not part of the system which allocates SBAT names
and signs them into boot files, and I don't want this boot file to be revoked by any
future SBAT revocations'. Of course, you can still revoke boot files you signed yourself
by rotating your own signing keys.
As noted [here](https://github.com/fwupd/fwupd/issues/2910) and
[here](https://github.com/rhboot/shim/issues/376),
the [documented](https://github.com/rhboot/shim/blob/main/SBAT.md) method for adding an
SBAT section to an already-linked `.efi` file does not work correctly (GNU `objcopy`
corrupts the executable). This
[third party python script](https://github.com/rhboot/shim/issues/376#issuecomment-1628004034)
does work. A suitable command is:
`pe-add-sections.py -s .sbat <(echo -n) -z .sbat -i OpenCore.efi -o OpenCore_empty_sbat.efi`
This file then needs to be signed and copied back into place, e.g.:
`sbsign --key {path-to}/ISK.key --cert {path-to}/ISK.pem OpenCore_empty_sbat.efi --output OpenCore.efi`
Finally, in order for OpenCore integration with Shim to work correctly
`UEFI/Quirks/ShimRetainProtocol` must be enabled in `config.plist`, and
`LauncherPath` should be set to `\EFI\OC\shimx64.efi`.
> Using Ubuntu multipass, it is now possible to operate entirely within macOS for signing,
key generation, etc. Note that the `~/shim_root` directory is already shared between
macOS and the `oc-shim` multipass VM (under its macOS path, e.g. `/Users/username/shim_root`),
and other macOS folders and volumes can be mounted if you wish, e.g.
`multipass mount /Volumes/EFI oc-shim:/Volumes/EFI`.

View File

@@ -0,0 +1,132 @@
#!/bin/bash
# sbat-info.tool - Dump SBAT information from .efi executable, with additional
# version and SBAT enforcement level information if the executable is Shim.
#
# Copyright (c) 2023, Michael Beaton. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-3-Clause
#
LIGHT_GREEN='\033[1;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Note: binutils can simply be placed last on the path in macOS, to provide objcopy but avoid hiding native objdump (this script parses LLVM or GNU objdump output).
# Alternatively BINUTILS_PREFIX environment variable can be set to enabled prefixed co-existing GNU tools.
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
usage () {
echo "Usage: $(basename "$0") <efifile>"
echo " Display SBAT information, if present, for any .efi executable."
echo " If the file is a version of Shim, also dislay Shim version info and SBAT enforcement level."
}
has_section () {
"${BINUTILS_PREFIX}"objdump -h "$1" | sed -nr 's/^ *[0-9]+ ([^ ]+).*/\1/p' | grep "$2" 1>/dev/null
}
if [ -z "$1" ]; then
usage
exit 1
fi
# https://unix.stackexchange.com/a/84980/340732
op_dir=$(mktemp -d 2>/dev/null || mktemp -d -t 'shim_info') || exit 1
# objcopy and objdump do not like writing to stdout when part of a pipe, so just write to temp files
# Note: these get created as empty files even if the section does not exist
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbat" "$1" "${op_dir}/sbat" || { rm -rf "${op_dir}"; exit 1; }
"${BINUTILS_PREFIX}"objcopy -O binary -j ".sbatlevel" "$1" "${op_dir}/sbatlevel" || { rm -rf "${op_dir}"; exit 1; }
"${BINUTILS_PREFIX}"objcopy -O binary -j ".data.ident" "$1" "${op_dir}/data.ident" || { rm -rf "${op_dir}"; exit 1; }
if ! has_section "$1" ".data.ident" ; then
IS_SHIM=0
echo -e "${YELLOW}Shim version info not present (probably not Shim)${NC}"
else
# Treat this section's presence as enough for the warning colours below
IS_SHIM=1
FILE_HEADER="$(head -n 1 "${op_dir}/data.ident")"
SHIM_HEADER="UEFI SHIM"
if [ ! "$FILE_HEADER" = "$SHIM_HEADER" ] ; then
echo -e "${RED}!!!Version header found '${FILE_HEADER}' expected '${SHIM_HEADER}'${NC}"
else
echo -e "${YELLOW}Found ${SHIM_HEADER} version header${NC}"
fi
# shellcheck disable=SC2016
VERSION="$(grep -a 'Version' "${op_dir}/data.ident" | sed -n 's/^\$Version: *\([^[:space:]]*\) *\$$/\1/p')"
if [ "$VERSION" = "" ] ; then
echo -e "${RED}Expected version number not present${NC}"
else
echo -e "Version: ${LIGHT_GREEN}${VERSION}${NC} (${VERSION}+, if user build)"
fi
# shellcheck disable=SC2016
COMMIT="$(grep -a 'Commit' "${op_dir}/data.ident" | sed -n 's/^\$Commit: *\([^[:space:]]*\) *\$$/\1/p')"
if [ "$COMMIT" = "" ] ; then
echo -e "${RED}Expected commit id not present${NC}"
else
echo "Commit: ${COMMIT}"
fi
fi
echo
echo -e "${YELLOW}SBAT (when tested by other files or by self):${NC}"
if ! has_section "$1" ".sbat" ; then
echo
echo -e "${RED}No .sbat section${NC}"
else
echo
echo -n -e "$LIGHT_GREEN"
cat "${op_dir}/sbat"
echo -n -e "$NC"
if ! cat "${op_dir}/sbat" | grep -E "[a-z]|[A-Z]" 1>/dev/null ; then
# Red for shim; yellow for other file
if [ "$IS_SHIM" -eq 1 ] ; then
echo -n -e "$RED"
else
echo -n -e "$LIGHT_GREEN"
fi
echo -e "Present but empty .sbat section"
fi
fi
echo
echo -e "${YELLOW}SBAT Level (when testing other files):${NC}"
if ! has_section "$1" ".sbatlevel" ; then
echo
# Yellow for shim (not present in earlier shims with .sbat, ~15.6); green for other file
if [ "$IS_SHIM" -eq 1 ] ; then
echo -n -e "$YELLOW"
else
echo -n -e "$LIGHT_GREEN"
fi
echo -n "No .sbatlevel section"
echo -e "$NC"
else
sbat_level_version=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
sbat_level_previous=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
sbat_level_latest=$(dd if="${op_dir}/sbatlevel" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm -rf "${op_dir}"; exit 1; }
if [ "$sbat_level_version" -ne 0 ] ; then
echo -e "${RED}!!! Unexpected .sbatlevel version $sbat_level_version != 0 !!!${NC}"
fi
echo
echo -e "${YELLOW}Previous (more permissive boot, default):${NC}"
echo -n -e "$LIGHT_GREEN"
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_previous + 4))" count="$(($sbat_level_latest - $sbat_level_previous))" 2>/dev/null
echo -e "$NC"
echo -e "${YELLOW}Latest (stricter boot, optional):${NC}"
echo -n -e "$LIGHT_GREEN"
dd if="${op_dir}/sbatlevel" ibs=1 skip="$(($sbat_level_latest + 4))" 2>/dev/null
echo -e "$NC"
fi
rm -rf "${op_dir}"
exit 0

View File

@@ -0,0 +1,339 @@
#!/bin/bash
#
# Copyright (c) 2023 Mike Beaton. All rights reserved.
# SPDX-License-Identifier: BSD-3-Clause
#
# Makes user build of shim which launches OpenCore.efi and includes specified vendor certificate lists.
# Builds on macOS using Ubuntu multipass or on Linux directly (Ubuntu & Fedora tested).
#
# To debug shim (e.g. within OVMF) make with
# ./shim-make.tool make OPTIMIZATIONS="-O0"
# and set 605dab50-e046-4300-abb6-3dd810dd8b23:SHIM_DEBUG to (UINT8)1 (<data>AQ==</data>) in NVRAM
#
ROOT=~/shim_root
SHIM=~/shim_source
OC_SHIM=oc-shim
unamer() {
NAME="$(uname)"
if [ "$(echo "${NAME}" | grep MINGW)" != "" ] || [ "$(echo "${NAME}" | grep MSYS)" != "" ]; then
echo "Windows"
else
echo "${NAME}"
fi
}
shim_command () {
DIR=$1
shift
if [ $DARWIN -eq 1 ] ; then
if [ $ECHO -eq 1 ] ; then
# shellcheck disable=SC2068
echo multipass exec ${OC_SHIM} --working-directory "'${DIR}'" -- $@ 1>/dev/stderr
fi
# shellcheck disable=SC2068,SC2294
eval multipass exec ${OC_SHIM} --working-directory "'${DIR}'" -- $@
else
if [ $ECHO -eq 1 ] ; then
# shellcheck disable=SC2068
echo "[${DIR}]" $@ 1>/dev/stderr
fi
pushd "${DIR}" 1>/dev/null || exit 1
# shellcheck disable=SC2068,SC2294
eval $@
retval=$?
popd 1>/dev/null || exit 1
return $retval
fi
}
shim_command_v () {
FOUND="$(shim_command . command -v "'${1}'" | wc -l)"
return $((1 - $FOUND))
}
usage() {
echo "Usage:"
echo -n " ./${SELFNAME} [args] [setup|clean|make [options]|install [esp-root-path]"
if [ $DARWIN -eq 1 ] ; then
echo -n "|mount [multipass-path]]"
fi
echo "]"
echo
echo "Args:"
echo -n " -r : Specify shim output root, default '${ROOT}'"
if [ $DARWIN -eq 1 ] ; then
echo -n " (shared)"
fi
echo
echo -n " -s : Specify shim source location, default '${SHIM}'"
if [ $DARWIN -eq 1 ] ; then
echo -n " (VM only, unless 'shim-make mount' used for development/debugging)"
fi
echo
echo "If used, -r/-s:"
echo " - Should be specified on every call, they are not remembered from 'setup'"
echo " - Should come before the chosen command"
echo
echo "Examples:"
echo " ./${SELFNAME} setup (sets up directories and installs rhboot/shim source)"
echo " ./${SELFNAME} clean (cleans after previous make)"
LOCATION="."
if [ $DARWIN -eq 1 ] ; then
LOCATION="${ROOT}"
fi
echo -n " ./${SELFNAME} make VENDOR_DB_FILE='${LOCATION}/combined/vendor.db' VENDOR_DBX_FILE='${LOCATION}/combined/vendor.dbx' (makes shim with db and dbx contents"
if [ $DARWIN -eq 1 ] ; then
echo -n "; note VENDOR_DB_FILE and VENDOR_DBX_FILE are inside a directory shared with VM"
fi
echo ")"
echo " ./${SELFNAME} install '${EXAMPLE_ESP}' (installs made files to ESP mounted at '${EXAMPLE_ESP}')"
echo
echo "After installation shimx64.efi and mmx64.efi must be signed by user ISK; OpenCore.efi must have an .sbat section added and be signed by user ISK."
}
mount_path () {
SRC=$1
if [ "${SRC}" = "" ] ; then
echo "Incorrect call to mount_path!"
exit 1
fi
DEST=$2
if [ "${DEST}" = "" ] ; then
DEST=${SRC}
fi
if [ ! -d "${SRC}" ] ; then
echo "Adding ${SRC}..."
mkdir -p "${SRC}" || exit 1
else
echo "${SRC} already present..."
fi
if [ $DARWIN -eq 1 ] ; then
if ! multipass info ${OC_SHIM} | grep "${DEST}" 1>/dev/null ; then
echo "Mounting ${SRC}..."
multipass mount "${SRC}" "${OC_SHIM}:${DEST}" || exit 1
else
echo "${SRC} already mounted..."
fi
fi
}
get_ready () {
if [ $DARWIN -eq 1 ] ; then
if ! command -v multipass 1>/dev/null ; then
echo "Installing Ubuntu multipass..."
brew install --cask multipass || exit 1
else
echo "Ubuntu multipass already installed..."
fi
if ! multipass info ${OC_SHIM} &>/dev/null ; then
echo "Launching ${OC_SHIM} multipass instance..."
multipass launch -n ${OC_SHIM} || exit 1
else
echo "${OC_SHIM} multipass instance already launched..."
fi
fi
mount_path "${ROOT}"
if ! shim_command_v gcc || ! shim_command_v make || ! shim_command_v git || ! shim_command_v sbsign || ! shim_command_v sbsiglist ; then
echo "Installing dependencies..."
if shim_command_v dnf ; then
shim_command . sudo dnf install -y gcc make git elfutils-libelf-devel sbsigntools efitools || exit 1
else
shim_command . sudo apt-get update || exit 1
shim_command . sudo apt install -y gcc make git libelf-dev sbsigntool efitools || exit 1
fi
else
echo "Dependencies already installed..."
fi
#
# For debug/develop purposes on Darwin it would be nicer to keep the source code in
# macOS, just mounted and built in multipass, but the build is about 1/3 the speed of
# building in a native multipass directory.
# For the purposes of having a fast build but code which can be opened e.g.
# within an IDE within macOS, sshfs can be used to mount out from multipass
# to macOS using ./shim-make mount:
# - https://github.com/canonical/multipass/issues/1070
# - https://osxfuse.github.io/
#
if ! shim_command . test -d "'${SHIM}'" ; then
# TODO: Once there is a stable version of Shim containing the commits we need
# - 1f38cb30a5e1dcea97b8d48915515b866ec13c32
# - a8b0b600ddcf02605da8582b4eac1932a3bb13fa
# (i.e. 15.8. hopefully) we should probably check out at a fixed, stable tag, starting
# at 15.8 and then manually updated in this script when a new stable version is released,
# but which can be overridden with any commit or branch with a script option.
echo "Cloning rhboot/shim..."
shim_command . git clone https://github.com/rhboot/shim.git "'${SHIM}'" || exit 1
shim_command "${SHIM}" git submodule update --init || exit 1
else
if ! shim_command "${SHIM}" git remote -v | grep "rhboot/shim" 1>/dev/null ; then
echo "FATAL: VM subdirectory ${SHIM} is already present, but does not contain rhboot/shim!"
exit 1
fi
echo "rhboot/shim already cloned..."
fi
echo "Make.defaults:"
# These two modifications to Make.defaults only required for debugging
FOUND=$(shim_command "${SHIM}" grep "gdwarf" Make.defaults | wc -l)
if [ "$FOUND" -eq 0 ] ; then
echo " Updating gdwarf flags..."
shim_command "${SHIM}" sed -i "'s^-ggdb \\\\^-ggdb -gdwarf-4 -gstrict-dwarf \\\\^g'" Make.defaults
else
echo " gdwarf flags already updated..."
fi
FOUND=$(shim_command "${SHIM}" grep "'${ROOT}'" Make.defaults | wc -l)
if [ "$FOUND" -eq 0 ] ; then
echo " Updating debug directory..."
shim_command "${SHIM}" sed -i "\"s^-DDEBUGDIR='L\\\"/usr/lib/debug/usr/share/shim/\\\$(ARCH_SUFFIX)-\\\$(VERSION)\\\$(DASHRELEASE)/\\\"'^-DDEBUGDIR='L\\\"${ROOT}/usr/lib/debug/boot/efi/EFI/OC/\\\"'^g\"" Make.defaults
else
echo " Debug directory already updated..."
fi
# Work-around for https://github.com/rhboot/shim/issues/596
FOUND=$(shim_command "${SHIM}" grep "'export DEFINES'" Make.defaults | wc -l)
if [ "$FOUND" -eq 0 ] ; then
echo " Updating exports..."
# var assignment to make output piping work normally
# shellcheck disable=SC2034
temp=$(echo "export DEFINES" | shim_command "${SHIM}" tee -a Make.defaults) 1>/dev/null
else
echo " Exports already updated..."
fi
}
SELFNAME="$(/usr/bin/basename "${0}")"
ECHO=0
if [ "$(unamer)" = "Darwin" ] ; then
DARWIN=1
EXAMPLE_ESP='/Volumes/EFI'
else
DARWIN=0
EXAMPLE_ESP='/boot/efi'
fi
OPTS=0
while [ "${1:0:1}" = "-" ] ; do
OPTS=1
if [ "$1" = "-r" ] ; then
shift
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
ROOT=$1
shift
else
echo "No root directory specified" && exit 1
fi
elif [ "$1" = "-s" ] ; then
shift
if [ "$1" != "" ] && ! [ "${1:0:1}" = "-" ] ; then
SHIM=$1
shift
else
echo "No shim directory specified" && exit 1
fi
elif [ "$1" = "--echo" ] ; then
ECHO=1
shift
else
echo "Unknown option: $1"
exit 1
fi
done
if [ "$1" = "setup" ] ; then
get_ready
echo "Installation complete."
exit 0
elif [ "$1" = "clean" ] ; then
echo "Cleaning..."
shim_command "${SHIM}" make clean
exit 0
elif [ "$1" = "make" ] ; then
echo "Making..."
shift
shim_command "${SHIM}" make DEFAULT_LOADER="\\\\\\\\OpenCore.efi" OVERRIDE_SECURITY_POLICY=1 "$@"
exit 0
elif [ "$1" = "install" ] ; then
echo "Installing..."
rm -rf "${ROOT:?}"/usr
shim_command "${SHIM}" DESTDIR="'${ROOT}'" EFIDIR='OC' OSLABEL='OpenCore' make install
if [ ! "$2" = "" ] ; then
echo "Installing to ESP ${2}..."
cp "${ROOT}"/boot/efi/EFI/OC/* "${2}"/EFI/OC || exit 1
fi
exit 0
elif [ $DARWIN -eq 1 ] && [ "$1" = "mount" ] ; then
MOUNT="$2"
if [ "${MOUNT}" = "" ] ; then
MOUNT=$SHIM
fi
#
# Useful for devel/debug only.
# Note: We are only mounting in the reverse direction because we get much faster build speeds.
#
if ! command -v sshfs 1>/dev/null ; then
echo "sshfs (https://osxfuse.github.io/) is required for mounting directories from multipass into macOS (https://github.com/canonical/multipass/issues/1070)"
exit 1
fi
if [ ! -d "${MOUNT}" ] ; then
echo "Making local directory ${MOUNT}..."
mkdir -p "${MOUNT}" || exit 1
fi
ls "${MOUNT}" 1>/dev/null
if [ $? -ne 0 ] ; then
echo "Directory may be mounted but not ready (no authorized key?)"
echo "Try: umount ${MOUNT}"
exit 1
fi
if mount | grep ":${MOUNT}" ; then
echo "Already mounted at ${MOUNT}"
exit 0
fi
# shellcheck disable=SC2012
if [ "$(ls -A -1 "${MOUNT}" | wc -l)" -ne 0 ] ; then
echo "Directory ${MOUNT} is not empty!"
exit 1
fi
IP=$(multipass info ${OC_SHIM} | grep IPv4 | cut -d ":" -f 2 | sed 's/ //g')
if [ "${IP}" = "" ] ; then
echo "Cannot obtain IPv4 for ${OC_SHIM}"
exit 1
fi
if sshfs "ubuntu@${IP}:$(realpath "${MOUNT}")" "${MOUNT}" ; then
echo "Mounted at ${MOUNT}"
exit 0
else
umount "${MOUNT}"
echo "Directory cannot be mounted, append your ssh public key to .ssh/authorized_keys in the VM and try again."
exit 1
fi
elif [ "$1" = "" ] ; then
if [ $OPTS -eq 0 ] ; then
usage
else
echo "No command specified."
fi
exit 1
else
echo "Unkown command: $1"
exit 1
fi

View File

@@ -0,0 +1,93 @@
#!/bin/bash
# shim-to-cert.tool - Extract OEM signing certificate public key (and full db, dbx if present) from GRUB shim file.
#
# Copyright (c) 2021-2023, Michael Beaton. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-3-Clause
#
LIGHT_GREEN='\033[1;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Note: binutils can be placed last on path in macOS, to provide objcopy but avoid overwriting native tools.
command -v "${BINUTILS_PREFIX}"objcopy >/dev/null 2>&1 || { echo >&2 "objcopy not found - please install binutils package or set BINUTILS_PREFIX environment variable."; exit 1; }
command -v openssl >/dev/null 2>&1 || { echo >&2 "openssl not found - please install openssl package."; exit 1; }
if [ -z "$1" ]; then
echo "Usage: $(basename "$0") <shimfile>"
exit 1
fi
sectfile=$(mktemp) || exit 1
# make certain we have output file name, as objcopy will trash input file without it
if [ "x$sectfile" = "x" ]; then
echo >&2 "Error creating tempfile!"
exit 1
fi
# extract .vendor_cert section
"${BINUTILS_PREFIX}"objcopy -O binary -j .vendor_cert "$1" "$sectfile" || exit 1
if [ ! -s "$sectfile" ] ; then
echo >&2 "No .vendor_cert section in $1."
rm "$sectfile"
exit 1
fi
# xargs trims white space
vendor_authorized_size=$(dd if="$sectfile" ibs=1 skip=0 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_deauthorized_size=$(dd if="$sectfile" ibs=1 skip=4 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_authorized_offset=$(dd if="$sectfile" ibs=1 skip=8 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
vendor_deauthorized_offset=$(dd if="$sectfile" ibs=1 skip=12 count=4 2>/dev/null | od -t u4 -An | xargs) || { rm "$sectfile"; exit 1; }
# extract cert or db
certfile=$(mktemp) || { rm "$sectfile"; exit 1; }
# extract db
if [ "$vendor_authorized_size" -ne "0" ]; then
dd if="$sectfile" ibs=1 skip="$vendor_authorized_offset" count="$vendor_authorized_size" 2>/dev/null > "$certfile" || { rm "$sectfile"; rm "$certfile"; exit 1; }
fi
# extract dbx
if [ "$vendor_deauthorized_size" -ne "0" ]; then
dd if="$sectfile" ibs=1 skip="$vendor_deauthorized_offset" count="$vendor_deauthorized_size" 2>/dev/null > "vendor.dbx" || { rm "$sectfile"; rm "$certfile"; exit 1; }
echo -e " - Secure Boot revocation list saved as ${LIGHT_GREEN}vendor.dbx${NC}"
else
echo " - No secure Boot revocation list"
fi
rm "$sectfile"
if [ "$vendor_authorized_size" -eq "0" ]; then
echo "!!! Empty vendor_authorized section (no secure boot signing certificates present)."
rm "$certfile"
exit 1
fi
# valid as single cert?
openssl x509 -noout -inform der -in "$certfile" 2>/dev/null
if [ $? -ne 0 ]; then
cp "$certfile" vendor.db
echo -e " - Secure Boot signing list saved as ${LIGHT_GREEN}vendor.db${NC}"
else
# outfile name from cert CN
certname=$(openssl x509 -noout -subject -inform der -in "$certfile" | sed 's/^subject=.*CN *=[ \"]*//' | sed 's/[,\/].*//' | sed 's/ *//g') || { rm "$certfile"; exit 1; }
outfile="${certname}.der"
cp "$certfile" "$outfile" || { rm "$certfile"; exit 1; }
echo -e " - Secure Boot certificate saved as ${LIGHT_GREEN}${outfile}${NC}"
if ! command -v cert-to-efi-sig-list >/dev/null ; then
echo -e " o To convert certificate to vendor.db use:"
echo -e " ${YELLOW}" 'cert-to-efi-sig-list <(openssl x509 -in' "'${outfile}'" '-outform PEM) vendor.db' "${NC}"
else
cert-to-efi-sig-list <(openssl x509 -in "${outfile}" -outform PEM) vendor.db || { rm "$certfile"; exit 1; }
echo -e " o Certificate has also been saved as single cert signing list ${LIGHT_GREEN}vendor.db${NC}"
fi
fi
rm "$certfile"

View File

@@ -0,0 +1,92 @@
#!/bin/bash
#
# unsign-efi-sig-list.tool
# Removes signing key from signed certificate list,
# also saves signing section and prints CNs found in,
# thereby helping to indicate who signed it originally.
#
# Copyright (c) 2023, Michael Beaton. All rights reserved.<BR>
# SPDX-License-Identifier: BSD-3-Clause
#
if [ $# -lt 1 ] || [ $# -gt 3 ] ; then
echo "Usage:"
echo " $(basename "$0") <signed-sig-list> [<outfile> [<sigfile>]]"
echo ""
echo " - Uses infile name with .unsigned appended if outfile name not specified"
echo " - Uses infile name with .pkcs7 appended if sigfile name not specified"
echo ""
exit 0
fi
if [ ! -f "$1" ] ; then
echo "File not found: $1"
exit 1
fi
infile="$1"
shift
if [ "$1" != "" ] ; then
outfile="$1"
shift
else
outfile="${infile}.unsigned"
fi
EfiTimeSize=16
WinCertHdrSize=8
GuidSize=16
ExpectedRevision=$((0x0200))
ExpectedCertificateType=$((0x0EF1))
WinCertificateSize=$(dd if="$infile" ibs=1 skip="$EfiTimeSize" count=4 2>/dev/null | od -t u4 -An | xargs)
Revision=$(dd if="$infile" ibs=1 skip="$(($EfiTimeSize + 4))" count=2 2>/dev/null | od -t u4 -An | xargs)
CertificateType=$(dd if="$infile" ibs=1 skip="$(($EfiTimeSize + 6))" count=2 2>/dev/null | od -t u4 -An | xargs)
if [ "$CertificateType" -ne "$ExpectedCertificateType" ] ; then
printf "Not a signed certificate file (signature 0x%04X found, 0x%04X expected) in: %s\n" "$CertificateType" "$ExpectedCertificateType" "$infile"
exit 1
fi
if [ "$Revision" -ne "$ExpectedRevision" ] ; then
printf "Unexpected revision (0x%04X found, 0x%04X expected) in signed certificate file: %s\n" "$Revision" "$ExpectedRevision" "$infile"
exit 1
fi
dd if="$infile" of="$outfile" ibs=1 skip=$(($EfiTimeSize + $WinCertificateSize)) 2>/dev/null
echo "Unsigned certificate list saved to '$outfile'"
# Total size is header+guid+sig
if [ "$WinCertificateSize" -le "$(($WinCertHdrSize + $GuidSize))" ] ; then
echo "!!! Certificate size smaller than expected header size"
exit 1
fi
# In little-endian byte order
EFI_CERT_TYPE_PKCS7_GUID="9dd2af4adf68ee498aa9347d375665a7"
# Expect pkcs7 sig (diff piping requires bash not sh)
if ! diff <(dd if="$infile" ibs=1 skip=$(($EfiTimeSize + $WinCertHdrSize)) count=$(($GuidSize)) 2>/dev/null | xxd -p) <(echo $EFI_CERT_TYPE_PKCS7_GUID) 1>/dev/null ; then
echo "!!! EFI_CERT_TYPE_PKCS7_GUID not found"
ext=".sig"
else
ext=".pkcs7"
fi
if [ "$1" != "" ] ; then
sigfile="$1"
shift
else
sigfile="${infile}${ext}"
fi
dd if="$infile" of="$sigfile" ibs=1 skip=$(($EfiTimeSize + $WinCertHdrSize + $GuidSize)) count=$(($WinCertificateSize - $WinCertHdrSize - $GuidSize)) 2>/dev/null
echo "Signing data saved to '$sigfile'"
# TODO: Figure out which openssl command parses this file at the correct level
echo "Signing CNs:"
openssl asn1parse -inform der -in "$sigfile" | grep -A1 commonName | grep STRING