debian: sync tests with packaging upstream

Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
This commit is contained in:
Thomas Lamprecht 2023-03-07 09:57:30 +01:00
parent 5b38a120b6
commit d7274593bf
5 changed files with 176 additions and 42 deletions

View File

@ -1,5 +1,5 @@
#
# Copyright 2019-2021 Canonical Ltd.
# Copyright 2019-2022 Canonical Ltd.
# Authors:
# - dann frazier <dann.frazier@canonical.com>
#
@ -87,35 +87,15 @@ class EfiBootableIsoImage:
class GrubShellBootableIsoImage(EfiBootableIsoImage):
def __init__(self, efi_arch, use_signed):
EfiArchToGrubArch = {
'X64': "x86_64",
'AA64': "arm64",
}
def __init__(self, efi_arch, shim_path, grub_path):
efi_img = FatFsImage(64)
efi_img.makedirs(os.path.join('EFI', 'BOOT'))
removable_media_path = os.path.join(
'EFI', 'BOOT', 'BOOT%s.EFI' % (efi_arch.upper())
)
efi_ext = 'efi'
grub_subdir = "%s-efi" % EfiArchToGrubArch[efi_arch.upper()]
if use_signed:
efi_ext = "%s.signed" % (efi_ext)
grub_subdir = "%s-signed" % (grub_subdir)
shim_src = os.path.join(
os.path.sep, 'usr', 'lib', 'shim',
'shim%s.%s' % (efi_arch.lower(), efi_ext)
)
grub_src = os.path.join(
os.path.sep, 'usr', 'lib', 'grub',
'%s' % (grub_subdir),
"" if use_signed else "monolithic",
'grub%s.%s' % (efi_arch.lower(), efi_ext)
)
grub_dest = os.path.join(
'EFI', 'BOOT', 'GRUB%s.EFI' % (efi_arch.upper())
)
efi_img.insert_file(shim_src, removable_media_path)
efi_img.insert_file(grub_src, grub_dest)
efi_img.insert_file(shim_path, removable_media_path)
efi_img.insert_file(grub_path, grub_dest)
super().__init__(efi_img)

View File

@ -43,11 +43,10 @@ class QemuEfiFlashSize(enum.Enum):
class QemuCommand:
# Based on the args used by ovmf-vars-generator
Qemu_Common_Params = [
'-no-user-config', '-nodefaults',
'-m', '256',
'-smp', '2,sockets=2,cores=1,threads=1',
'-smp', '1,sockets=1,cores=1,threads=1',
'-display', 'none',
'-serial', 'stdio',
]

52
debian/python/UEFI/SignedBinary.py vendored Normal file
View File

@ -0,0 +1,52 @@
#
# Copyright 2022 Canonical Ltd.
# Authors:
# - dann frazier <dann.frazier@canonical.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License version 3, as published
# by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranties of MERCHANTABILITY,
# SATISFACTORY QUALITY, 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
# this program. If not, see <http://www.gnu.org/licenses/>.
#
import os
import subprocess
import tempfile
class SignedBinary:
def __init__(self, binary_path, key_path, cert_path, password=None):
openssl_password_args = []
if password:
openssl_password_args = [
"-passin", f"pass:{password}"
]
with tempfile.NamedTemporaryFile() as keytmp:
subprocess.check_call(
[
"openssl", "rsa",
] + openssl_password_args + [
"-in", f"{key_path}",
"-out", f"{keytmp.name}",
]
)
with tempfile.NamedTemporaryFile(delete=False) as f:
self.path = f.name
subprocess.check_call(
[
"sbsign", "--key", f"{keytmp.name}",
"--cert", f"{cert_path}",
binary_path, "--output", f"{self.path}"
]
)
def __del__(self):
os.unlink(self.path)

View File

@ -5,6 +5,7 @@ Depends:
grub-efi-amd64-signed [amd64],
grub-efi-arm64-signed [arm64],
mtools [amd64 arm64],
openssl [amd64 arm64],
ovmf,
ovmf-ia32,
python3-pexpect,
@ -12,5 +13,6 @@ Depends:
qemu-efi-arm,
qemu-system-arm,
qemu-system-x86,
sbsigntool [amd64 arm64],
shim-signed [amd64 arm64],
xorriso [amd64 arm64],

133
debian/tests/shell.py vendored
View File

@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
# Copyright 2019-2021 Canonical Ltd.
# Copyright 2019-2022 Canonical Ltd.
# Authors:
# - dann frazier <dann.frazier@canonical.com>
#
@ -18,12 +18,15 @@
#
import enum
import os
import pexpect
import subprocess
import sys
import time
import unittest
from UEFI.Filesystems import GrubShellBootableIsoImage
from UEFI.SignedBinary import SignedBinary
from UEFI.Qemu import QemuEfiMachine, QemuEfiVariant, QemuEfiFlashSize
from UEFI import Qemu
@ -31,15 +34,56 @@ DPKG_ARCH = subprocess.check_output(
['dpkg', '--print-architecture']
).decode().rstrip()
EfiArchToGrubArch = {
'X64': "x86_64",
'AA64': "arm64",
}
TEST_TIMEOUT = 120
def get_local_grub_path(efi_arch, signed=False):
grub_subdir = "%s-efi" % EfiArchToGrubArch[efi_arch.upper()]
ext = "efi"
if signed:
grub_subdir = f"{grub_subdir}-signed"
ext = f"{ext}.signed"
grub_path = os.path.join(
os.path.sep, 'usr', 'lib', 'grub',
'%s' % (grub_subdir),
"" if signed else "monolithic",
'grub%s.%s' % (efi_arch.lower(), ext)
)
return grub_path
def get_local_shim_path(efi_arch, signed=False):
ext = 'efi'
if signed:
ext = f"{ext}.signed"
shim_path = os.path.join(
os.path.sep, 'usr', 'lib', 'shim',
'shim%s.%s' % (efi_arch.lower(), ext)
)
return shim_path
class BootToShellTest(unittest.TestCase):
debug = True
def setUp(self):
self.startTime = time.time()
def tearDown(self):
t = time.time() - self.startTime
sys.stdout.write("%s runtime: %.3fs\n" % (self.id(), t))
def run_cmd_check_shell(self, cmd):
child = pexpect.spawn(' '.join(cmd))
child = pexpect.spawn(' '.join(cmd), encoding='UTF-8')
if self.debug:
child.logfile = sys.stdout.buffer
child.logfile = sys.stdout
try:
while True:
i = child.expect(
@ -47,7 +91,7 @@ class BootToShellTest(unittest.TestCase):
'Press .* or any other key to continue',
'Shell> '
],
timeout=60,
timeout=TEST_TIMEOUT,
)
if i == 0:
child.sendline('\x1b')
@ -56,7 +100,9 @@ class BootToShellTest(unittest.TestCase):
child.sendline('reset -s\r')
continue
except pexpect.EOF:
return
child.close()
if child.exitstatus != 0:
self.fail("ERROR: exit code %d\n" % (child.exitstatus))
except pexpect.TIMEOUT as err:
self.fail("%s\n" % (err))
@ -65,10 +111,10 @@ class BootToShellTest(unittest.TestCase):
PRE_EXEC = 1
POST_EXEC = 2
child = pexpect.spawn(' '.join(cmd))
child = pexpect.spawn(' '.join(cmd), encoding='UTF-8')
if self.debug:
child.logfile = sys.stdout.buffer
child.logfile = sys.stdout
try:
state = State.PRE_EXEC
while True:
@ -80,7 +126,7 @@ class BootToShellTest(unittest.TestCase):
'grub> ',
'Command Error Status: Access Denied',
],
timeout=60,
timeout=TEST_TIMEOUT,
)
if i == 0:
child.sendline('\x1b')
@ -102,10 +148,12 @@ class BootToShellTest(unittest.TestCase):
if i == 4:
verified = False
continue
except pexpect.EOF:
child.close()
if child.exitstatus != 0:
self.fail("ERROR: exit code %d\n" % (child.exitstatus))
except pexpect.TIMEOUT as err:
self.fail("%s\n" % (err))
except pexpect.EOF:
pass
self.assertEqual(should_verify, verified)
def test_aavmf(self):
@ -113,12 +161,20 @@ class BootToShellTest(unittest.TestCase):
self.run_cmd_check_shell(q.command)
@unittest.skipUnless(DPKG_ARCH == 'arm64', "Requires grub-efi-arm64")
@unittest.skipUnless(
subprocess.run(
['dpkg-vendor', '--derives-from', 'Ubuntu']
).returncode == 0,
"Debian does not provide a signed shim for arm64, see #992073"
)
def test_aavmf_ms_secure_boot_signed(self):
q = Qemu.QemuCommand(
QemuEfiMachine.AAVMF,
variant=QemuEfiVariant.MS,
)
iso = GrubShellBootableIsoImage('AA64', use_signed=True)
grub = get_local_grub_path('AA64', signed=True)
shim = get_local_shim_path('AA64', signed=True)
iso = GrubShellBootableIsoImage('AA64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'aa64', True)
@ -128,7 +184,9 @@ class BootToShellTest(unittest.TestCase):
QemuEfiMachine.AAVMF,
variant=QemuEfiVariant.MS,
)
iso = GrubShellBootableIsoImage('AA64', use_signed=False)
grub = get_local_grub_path('AA64', signed=False)
shim = get_local_shim_path('AA64', signed=False)
iso = GrubShellBootableIsoImage('AA64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'aa64', False)
@ -178,7 +236,9 @@ class BootToShellTest(unittest.TestCase):
variant=QemuEfiVariant.MS,
flash_size=QemuEfiFlashSize.SIZE_2MB,
)
iso = GrubShellBootableIsoImage('X64', use_signed=True)
grub = get_local_grub_path('X64', signed=True)
shim = get_local_shim_path('X64', signed=True)
iso = GrubShellBootableIsoImage('X64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', True)
@ -189,7 +249,9 @@ class BootToShellTest(unittest.TestCase):
variant=QemuEfiVariant.MS,
flash_size=QemuEfiFlashSize.SIZE_2MB,
)
iso = GrubShellBootableIsoImage('X64', use_signed=False)
grub = get_local_grub_path('X64', signed=False)
shim = get_local_shim_path('X64', signed=False)
iso = GrubShellBootableIsoImage('X64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', False)
@ -230,7 +292,9 @@ class BootToShellTest(unittest.TestCase):
variant=QemuEfiVariant.MS,
flash_size=QemuEfiFlashSize.SIZE_4MB,
)
iso = GrubShellBootableIsoImage('X64', use_signed=True)
grub = get_local_grub_path('X64', signed=True)
shim = get_local_shim_path('X64', signed=True)
iso = GrubShellBootableIsoImage('X64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', True)
@ -241,7 +305,44 @@ class BootToShellTest(unittest.TestCase):
variant=QemuEfiVariant.MS,
flash_size=QemuEfiFlashSize.SIZE_4MB,
)
iso = GrubShellBootableIsoImage('X64', use_signed=False)
grub = get_local_grub_path('X64', signed=False)
shim = get_local_shim_path('X64', signed=False)
iso = GrubShellBootableIsoImage('X64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', False)
@unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
def test_ovmf_snakeoil_secure_boot_signed(self):
q = Qemu.QemuCommand(
QemuEfiMachine.OVMF_Q35,
variant=QemuEfiVariant.SNAKEOIL,
)
shim = SignedBinary(
get_local_shim_path('X64', signed=False),
"/usr/share/ovmf/PkKek-1-snakeoil.key",
"/usr/share/ovmf/PkKek-1-snakeoil.pem",
"snakeoil",
)
grub = SignedBinary(
get_local_grub_path('X64', signed=False),
"/usr/share/ovmf/PkKek-1-snakeoil.key",
"/usr/share/ovmf/PkKek-1-snakeoil.pem",
"snakeoil",
)
iso = GrubShellBootableIsoImage('X64', shim.path, grub.path)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', True)
@unittest.skipUnless(DPKG_ARCH == 'amd64', "amd64-only")
def test_ovmf_snakeoil_secure_boot_unsigned(self):
q = Qemu.QemuCommand(
QemuEfiMachine.OVMF_Q35,
variant=QemuEfiVariant.SNAKEOIL,
flash_size=QemuEfiFlashSize.DEFAULT,
)
grub = get_local_grub_path('X64', signed=False)
shim = get_local_shim_path('X64', signed=False)
iso = GrubShellBootableIsoImage('X64', shim, grub)
q.add_disk(iso.path)
self.run_cmd_check_secure_boot(q.command, 'x64', False)