fwupd/plugins/uefi-capsule/efi/generate_binary.py
Javier Martinez Canillas 378b00018b uefi-capsule: Add SBAT metadata to the fwupd EFI binary
The Secure Boot Advanced Targeting (SBAT) [0] is a Generation Number Based
Revocation mechanism that is meant to replace the DBX revocation file list.

Binaries must contain a .sbat data section that has a set entries, each of
them consisting of UTF-8 strings as comma separated values. Allow to embed
this information into the fwupd EFI binary at build time.

The SBAT metadata must contain at least two entries. One that defines the
SBAT version used and another one that defines the component generation.

Downstream users can add additional entries if have changes that make them
diverge from the upstream code and potentially add other vulnerabilities.

[0]: https://github.com/rhboot/shim/blob/sbat/SBAT.md
2021-02-10 16:40:59 +00:00

196 lines
4.6 KiB
Python
Executable File

#!/usr/bin/python3
#
# Copyright (C) 2021 Javier Martinez Canillas <javierm@redhat.com>
# Copyright (C) 2021 Richard Hughes <richard@hughsie.com>
#
# SPDX-License-Identifier: LGPL-2.1+
#
# pylint: disable=missing-docstring, invalid-name
import subprocess
import sys
import argparse
import tempfile
def _run_objcopy_sbat(args):
""" append SBAT metadata """
FWUPD_SUMMARY = "Firmware update daemon"
FWUPD_URL = "https://github.com/fwupd/fwupd"
with tempfile.NamedTemporaryFile() as sfd:
# spec
sfd.write(
"{0},{1},{2},{0},{1},{3}\n".format(
"sbat",
args.sbat_version,
"UEFI shim",
"https://github.com/rhboot/shim/blob/main/SBAT.md",
).encode()
)
# fwupd
sfd.write(
"{0},{1},{2},{0},{3},{4}\n".format(
args.project_name,
args.sbat_generation,
"Firmware update daemon",
args.project_version,
FWUPD_URL,
).encode()
)
# distro specifics, falling back to the project defaults
if args.sbat_distro_id:
sfd.write(
"{0}-{1},{2},{3},{4},{5},{6}\n".format(
args.project_name,
args.sbat_distro_id,
args.sbat_distro_generation or args.sbat_generation,
args.sbat_distro_summary or FWUPD_SUMMARY,
args.sbat_distro_pkgname,
args.sbat_distro_version or args.project_version,
args.sbat_distro_url or FWUPD_URL,
).encode()
)
# all written
sfd.seek(0)
# add a section to the object; use `objdump -s -j .sbat` to verify
argv = [
args.objcopy,
"--add-section",
".sbat={}".format(sfd.name),
args.outfile,
]
subprocess.run(argv, check=True)
def _run_objcopy(args):
argv = [
args.objcopy,
"-j",
".text",
"-j",
".sdata",
"-j",
".dynamic",
"-j",
".rel*",
args.infile,
args.outfile,
]
# aarch64 and arm32 don't have an EFI capable objcopy
# Use 'binary' instead, and add required symbols manually
if args.arch in ["aarch64", "arm"]:
argv.extend(["-O", "binary"])
else:
argv.extend(["--target", "efi-app-{}".format(args.arch)])
try:
subprocess.run(argv, check=True)
except FileNotFoundError as e:
print(str(e))
sys.exit(1)
def _run_genpeimg(args):
# this is okay if it does not exist
argv = [
"genpeimg",
"-d",
"+d",
"+n",
"-d",
"+s",
args.outfile,
]
try:
subprocess.run(argv, check=True)
except FileNotFoundError as _:
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"--objcopy",
default="objcopy",
help="Binary file to use for objcopy",
)
parser.add_argument(
"--arch",
default="x86_64",
help="EFI architecture",
)
parser.add_argument(
"--project-name",
help="SBAT project name",
)
parser.add_argument(
"--project-version",
help="SBAT project version",
)
parser.add_argument(
"--sbat-version",
default=1,
type=int,
help="SBAT version",
)
parser.add_argument(
"--sbat-generation",
default=0,
type=int,
help="SBAT generation",
)
parser.add_argument(
"--sbat-distro-id",
default=None,
help="SBAT distribution ID"
)
parser.add_argument(
"--sbat-distro-generation",
default=None,
type=int,
help="SBAT distribution generation",
)
parser.add_argument(
"--sbat-distro-summary",
default=None,
help="SBAT distribution summary",
)
parser.add_argument(
"--sbat-distro-pkgname",
default=None,
help="SBAT distribution package name",
)
parser.add_argument(
"--sbat-distro-version",
default=None,
help="SBAT distribution version",
)
parser.add_argument(
"--sbat-distro-url",
default=None,
help="SBAT distribution URL",
)
parser.add_argument(
"infile",
help="Input file",
)
parser.add_argument(
"outfile",
help="Output file",
)
_args = parser.parse_args()
_run_objcopy(_args)
_run_objcopy_sbat(_args)
_run_genpeimg(_args)
sys.exit(0)