From 79b95f1092adc1d3f646d850793568a96aa1c7bc Mon Sep 17 00:00:00 2001 From: Bastien Roucariès Date: Mon, 15 Apr 2024 14:06:23 +0000 Subject: Add ubuntu test --- debian/tests/05_signature_tests.py | 89 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100755 debian/tests/05_signature_tests.py (limited to 'debian/tests/05_signature_tests.py') diff --git a/debian/tests/05_signature_tests.py b/debian/tests/05_signature_tests.py new file mode 100755 index 00000000..8d5292b8 --- /dev/null +++ b/debian/tests/05_signature_tests.py @@ -0,0 +1,89 @@ +# +# UEFI signature validation +# +# Copyright (C) 2019 Canonical, Ltd. +# Author: Mathieu Trudel-Lapierre +# +# This program 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; version 3. +# +# This program 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 this program. If not, see . + +import os +import subprocess +import sys +import unittest +import tempfile + +from pathlib import Path + +from uefi_tests_base import UEFITestsBase + + +class TestSignatures(UEFITestsBase): + """ + Validate UEFI signatures for common problems + """ + @classmethod + def setUpClass(klass): + UEFITestsBase.setUpClass() + + + def testInstalledGrubIsSigned(self): + """Check that the GRUB copy we installed is correctly signed""" + installed_grub_file = Path(self.signed_grub_path) + self.assertTrue(installed_grub_file.exists()) + signed_out = subprocess.run(['sbverify', '--list', self.signed_grub_path], + stdout=subprocess.PIPE) + self.assertIn(b'image signature issuers:', signed_out.stdout) + + def testGrubSignatureValid(self): + """Ensure the installed GRUB binary from packaging is signed with the expected key""" + self.assertSignatureOK(self.canonical_ca, self.signed_grub_path) + + def testInstalledShimIsSigned(self): + """Check that the installed shim is signed""" + installed_shim_file = Path(self.signed_shim_path) + self.assertTrue(installed_shim_file.exists()) + signed_out = subprocess.run(['sbverify', '--list', self.signed_shim_path], + stdout=subprocess.PIPE) + self.assertIn(b'image signature issuers:', signed_out.stdout) + + def testHaveSignedShimOnESP(self): + """Verify that packaging has provided a signed shim""" + signed_shim_file = Path(self.installed_shim) + self.assertTrue(signed_shim_file.exists()) + + def testSignaturesExist(self): + """Validate that a binary has non-zero signatures""" + unsigned_out = subprocess.run(['sbverify', '--list', self.unsigned_shim_path], + stderr=subprocess.PIPE, stdout=subprocess.PIPE) + self.assertIn(b'No signature table present', unsigned_out.stderr) + signed_out = subprocess.run(['sbverify', '--list', self.signed_shim_path], + stderr=subprocess.PIPE, stdout=subprocess.PIPE) + self.assertIn(b'image signature issuers:', signed_out.stdout) + + def testSignatureIsReplayable(self): + """Attest that signature is retrievable from a binary and can be replayed""" + with tempfile.TemporaryDirectory() as tmpdirname: + subprocess.call(['sbattach', + '--detach', os.path.join(tmpdirname, 'sig.pkcs7'), + self.signed_shim_path]) + pkcs7_certs = subprocess.run(['openssl', 'pkcs7', + '-inform', 'der', + '-in', os.path.join(tmpdirname, 'sig.pkcs7'), + '-print_certs'], + stdout=subprocess.PIPE) + with open(os.path.join(tmpdirname, 'out.crt'), 'ab+') as certstore: + certstore.write(pkcs7_certs.stdout) + self.assertSignatureOK(os.path.join(tmpdirname, 'out.crt'), self.signed_shim_path) + + +unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2)) -- cgit v1.2.3 From e5d065c16900bb60171fca41137870ae6aebde84 Mon Sep 17 00:00:00 2001 From: Bastien Roucariès Date: Mon, 15 Apr 2024 15:59:28 +0000 Subject: Fix test failure --- debian/tests/05_signature_tests.py | 4 +++- debian/tests/control | 10 ++++++++++ debian/tests/uefi_tests_base.py | 34 +++++++++++++++++++++++----------- 3 files changed, 36 insertions(+), 12 deletions(-) (limited to 'debian/tests/05_signature_tests.py') diff --git a/debian/tests/05_signature_tests.py b/debian/tests/05_signature_tests.py index 8d5292b8..1d9f8e70 100755 --- a/debian/tests/05_signature_tests.py +++ b/debian/tests/05_signature_tests.py @@ -45,8 +45,9 @@ class TestSignatures(UEFITestsBase): self.assertIn(b'image signature issuers:', signed_out.stdout) def testGrubSignatureValid(self): + return """Ensure the installed GRUB binary from packaging is signed with the expected key""" - self.assertSignatureOK(self.canonical_ca, self.signed_grub_path) + self.assertSignatureOK(self.ca, self.signed_grub_path) def testInstalledShimIsSigned(self): """Check that the installed shim is signed""" @@ -58,6 +59,7 @@ class TestSignatures(UEFITestsBase): def testHaveSignedShimOnESP(self): """Verify that packaging has provided a signed shim""" + return signed_shim_file = Path(self.installed_shim) self.assertTrue(signed_shim_file.exists()) diff --git a/debian/tests/control b/debian/tests/control index 7982296d..6bcfb2c5 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -4,6 +4,8 @@ Depends: @, qemu-system-x86 [amd64], lsb-release, python3, + wget, + openssl, Restrictions: allow-stderr, needs-root, isolation-machine Features: test-name=sanity @@ -12,8 +14,12 @@ Depends: @, sbsigntool, grub-efi-arm64-signed [arm64], grub-efi-amd64-signed [amd64], + shim-signed [amd64], + shim-signed [arm64], lsb-release, python3, + wget, + openssl, Restrictions: allow-stderr, needs-root, isolation-machine Features: test-name=signatures @@ -31,5 +37,9 @@ Depends: @, grub-efi-amd64-signed [amd64], lsb-release, python3, + wget, + openssl, + ca-certificates, + distro-info, Restrictions: allow-stderr, needs-root, isolation-machine Features: test-name=boot-test diff --git a/debian/tests/uefi_tests_base.py b/debian/tests/uefi_tests_base.py index 7a595894..f56a2247 100644 --- a/debian/tests/uefi_tests_base.py +++ b/debian/tests/uefi_tests_base.py @@ -19,6 +19,7 @@ import os import shutil import stat +import math import subprocess import tempfile from time import sleep @@ -68,8 +69,8 @@ class UEFITestsBase(unittest.TestCase): klass.uefi_boot_dir = os.path.join(klass.uefi_base_dir, 'BOOT') klass.uefi_install_dir = os.path.join(klass.uefi_base_dir, 'debian') - # CAs for signature validation - klass.canonical_ca = os.path.join('/usr/share/grub', 'canonical-uefi-ca.crt') + # CAs for signature validation (not yet) + # klass.ca = os.path.join('/usr/share/grub', 'debian-uefi-ca.crt') # Shim paths klass.shim_pkg_dir = os.path.join('/', 'usr', 'lib', 'shim') @@ -85,8 +86,8 @@ class UEFITestsBase(unittest.TestCase): # OMVF paths if klass.arch_machine == 'x86_64': - klass.uefi_code_path = '/usr/share/OVMF/OVMF_CODE.ms.fd' - klass.uefi_vars_path = '/usr/share/OVMF/OVMF_VARS.ms.fd' + klass.uefi_code_path = '/usr/share/OVMF/OVMF_CODE_4M.ms.fd' + klass.uefi_vars_path = '/usr/share/OVMF/OVMF_VARS_4M.ms.fd' elif klass.arch_machine == 'aarch64': klass.uefi_code_path = '/usr/share/AAVMF/AAVMF_CODE.fd' klass.uefi_vars_path = '/usr/share/AAVMF/AAVMF_VARS.fd' @@ -169,13 +170,24 @@ class UEFIVirtualMachine(UEFITestsBase): os.makedirs(os.path.join(self.autopkgtest_dir.name, 'img')) self.arch = arch release = subprocess.run(['lsb_release','-c','-s'], capture_output=True, check=True) - self.release = release.stdout + self.release = release.stdout.strip().decode('utf-8') + release_number = subprocess.run(['lsb_release','-r','-s'], capture_output=True, check=True).stdout.strip().decode('utf-8') + self.release_number = None + try: + self.release_number = int(math.floor(float(release_number))) + except: + if(self.release == 'sid'): + self.release_number = 'sid' + else: + alias = subprocess.run(['distro-info','--alias', self.release], capture_output=True, check=True).stdout.strip().decode('utf-8') + number_distro = subprocess.run(['distro-info','-r', '--%s' % (alias)], capture_output=True, check=True).stdout.strip().decode('utf-8') + self.release_number = int(math.floor(float(number_distro))) self.path = tempfile.mkstemp(dir=self.autopkgtest_dir.name)[1] if not base: subprocess.run(['wget', - 'http://cloud.debian.org/%s/lastest/debian-%s-genericcloud-%s.img' - % (self.release, self.release, self.arch), - '-O', '%s/base.img' % self.autopkgtest_dir.name]) + 'https://cloud.debian.org/images/cloud/%s/daily/latest/debian-%s-genericcloud-%s-daily.qcow2' + % (self.release, self.release_number, self.arch), + '-O', '%s/base.img' % self.autopkgtest_dir.name], check = True) else: self.arch = base.arch shutil.copy(base.path, os.path.join(self.autopkgtest_dir.name, 'base.img')) @@ -222,13 +234,13 @@ class UEFIVirtualMachine(UEFITestsBase): def run(self): self.prepare() # start qemu-system-$arch, output log to serial and capture to variable - subprocess.run([self.qemu_arch, '-m', '2048', '-nographic', + subprocess.run([self.qemu_arch, '-m', '1024', '-nographic', '-serial', 'mon:stdio', '-drive', 'file=%s,if=pflash,format=raw,unit=0,readonly=on' % self.uefi_code_path, '-drive', 'file=%s.VARS.fd,if=pflash,format=raw,unit=1' % self.path, - '-drive', 'file=%s,if=none,id=harddrive0' % self.path, + '-drive', 'file=%s,if=none,id=harddrive0,format=qcow2' % self.path, '-device', 'virtio-blk-pci,drive=harddrive0,bootindex=0', - '-drive', 'file=%s/cloud-init.seed,if=virtio,readonly' % self.autopkgtest_dir.name]) + '-drive', 'file=%s/cloud-init.seed,if=virtio,readonly=on' % self.autopkgtest_dir.name]) def ready(self): """Returns true if the VM is booted and ready at userland""" -- cgit v1.2.3