test: Add tests for the 'evil' vboot attacks

Add tests to check that these two attacks are mitigated by recent patches.

Signed-off-by: Simon Glass <sjg@chromium.org>
Reported-by: Bruce Monroe <bruce.monroe@intel.com>
Reported-by: Arie Haenel <arie.haenel@intel.com>
Reported-by: Julien Lenoir <julien.lenoir@intel.com>
This commit is contained in:
Simon Glass 2021-02-15 17:08:08 -07:00 committed by Tom Rini
parent fafafacb47
commit d5f3aadacb
1 changed files with 64 additions and 27 deletions

View File

@ -24,22 +24,26 @@ For configuration verification:
Tests run with both SHA1 and SHA256 hashing. Tests run with both SHA1 and SHA256 hashing.
""" """
import shutil
import struct import struct
import pytest import pytest
import u_boot_utils as util import u_boot_utils as util
import vboot_forge import vboot_forge
import vboot_evil
# Only run the full suite on a few combinations, since it doesn't add any more
# test coverage.
TESTDATA = [ TESTDATA = [
['sha1', '', None, False], ['sha1', '', None, False, True],
['sha1', '', '-E -p 0x10000', False], ['sha1', '', '-E -p 0x10000', False, False],
['sha1', '-pss', None, False], ['sha1', '-pss', None, False, False],
['sha1', '-pss', '-E -p 0x10000', False], ['sha1', '-pss', '-E -p 0x10000', False, False],
['sha256', '', None, False], ['sha256', '', None, False, False],
['sha256', '', '-E -p 0x10000', False], ['sha256', '', '-E -p 0x10000', False, False],
['sha256', '-pss', None, False], ['sha256', '-pss', None, False, False],
['sha256', '-pss', '-E -p 0x10000', False], ['sha256', '-pss', '-E -p 0x10000', False, False],
['sha256', '-pss', None, True], ['sha256', '-pss', None, True, False],
['sha256', '-pss', '-E -p 0x10000', True], ['sha256', '-pss', '-E -p 0x10000', True, True],
] ]
@pytest.mark.boardspec('sandbox') @pytest.mark.boardspec('sandbox')
@ -48,8 +52,10 @@ TESTDATA = [
@pytest.mark.requiredtool('fdtget') @pytest.mark.requiredtool('fdtget')
@pytest.mark.requiredtool('fdtput') @pytest.mark.requiredtool('fdtput')
@pytest.mark.requiredtool('openssl') @pytest.mark.requiredtool('openssl')
@pytest.mark.parametrize("sha_algo,padding,sign_options,required", TESTDATA) @pytest.mark.parametrize("sha_algo,padding,sign_options,required,full_test",
def test_vboot(u_boot_console, sha_algo, padding, sign_options, required): TESTDATA)
def test_vboot(u_boot_console, sha_algo, padding, sign_options, required,
full_test):
"""Test verified boot signing with mkimage and verification with 'bootm'. """Test verified boot signing with mkimage and verification with 'bootm'.
This works using sandbox only as it needs to update the device tree used This works using sandbox only as it needs to update the device tree used
@ -71,7 +77,7 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
util.run_and_log(cons, 'dtc %s %s%s -O dtb ' util.run_and_log(cons, 'dtc %s %s%s -O dtb '
'-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb)) '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
def run_bootm(sha_algo, test_type, expect_string, boots): def run_bootm(sha_algo, test_type, expect_string, boots, fit=None):
"""Run a 'bootm' command U-Boot. """Run a 'bootm' command U-Boot.
This always starts a fresh U-Boot instance since the device tree may This always starts a fresh U-Boot instance since the device tree may
@ -84,11 +90,14 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
use. use.
boots: A boolean that is True if Linux should boot and False if boots: A boolean that is True if Linux should boot and False if
we are expected to not boot we are expected to not boot
fit: FIT filename to load and verify
""" """
if not fit:
fit = '%stest.fit' % tmpdir
cons.restart_uboot() cons.restart_uboot()
with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)): with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)):
output = cons.run_command_list( output = cons.run_command_list(
['host load hostfs - 100 %stest.fit' % tmpdir, ['host load hostfs - 100 %s' % fit,
'fdt addr 100', 'fdt addr 100',
'bootm 100']) 'bootm 100'])
assert expect_string in ''.join(output) assert expect_string in ''.join(output)
@ -222,18 +231,41 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb]) util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
# Make sure that U-Boot checks that the config is in the list of hashed if full_test:
# nodes. If it isn't, a security bypass is possible. # Make sure that U-Boot checks that the config is in the list of hashed
with open(fit, 'rb') as fd: # nodes. If it isn't, a security bypass is possible.
root, strblock = vboot_forge.read_fdt(fd) ffit = '%stest.forged.fit' % tmpdir
root, strblock = vboot_forge.manipulate(root, strblock) shutil.copyfile(fit, ffit)
with open(fit, 'w+b') as fd: with open(ffit, 'rb') as fd:
vboot_forge.write_fdt(root, strblock, fd) root, strblock = vboot_forge.read_fdt(fd)
util.run_and_log_expect_exception( root, strblock = vboot_forge.manipulate(root, strblock)
cons, [fit_check_sign, '-f', fit, '-k', dtb], with open(ffit, 'w+b') as fd:
1, 'Failed to verify required signature') vboot_forge.write_fdt(root, strblock, fd)
util.run_and_log_expect_exception(
cons, [fit_check_sign, '-f', ffit, '-k', dtb],
1, 'Failed to verify required signature')
run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False) run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit)
# Try adding an evil root node. This should be detected.
efit = '%stest.evilf.fit' % tmpdir
shutil.copyfile(fit, efit)
vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot')
util.run_and_log_expect_exception(
cons, [fit_check_sign, '-f', efit, '-k', dtb],
1, 'Failed to verify required signature')
run_bootm(sha_algo, 'evil fakeroot', 'Bad Data Hash', False, efit)
# Try adding an @ to the kernel node name. This should be detected.
efit = '%stest.evilk.fit' % tmpdir
shutil.copyfile(fit, efit)
vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
util.run_and_log_expect_exception(
cons, [fit_check_sign, '-f', efit, '-k', dtb],
1, 'Node name contains @')
run_bootm(sha_algo, 'evil kernel@', 'Bad Data Hash', False, efit)
# Create a new properly signed fit and replace header bytes # Create a new properly signed fit and replace header bytes
make_fit('sign-configs-%s%s.its' % (sha_algo, padding)) make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
@ -344,8 +376,13 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required):
create_rsa_pair('prod') create_rsa_pair('prod')
# Create a number kernel image with zeroes # Create a number kernel image with zeroes
with open('%stest-kernel.bin' % tmpdir, 'w') as fd: with open('%stest-kernel.bin' % tmpdir, 'wb') as fd:
fd.write(500 * chr(0)) fd.write(500 * b'\0')
# Create a second kernel image with ones
evil_kernel = '%stest-kernel1.bin' % tmpdir
with open(evil_kernel, 'wb') as fd:
fd.write(500 * b'\x01')
try: try:
# We need to use our own device tree file. Remember to restore it # We need to use our own device tree file. Remember to restore it