summaryrefslogtreecommitdiff
diff options
authorPMR <pmr@pmr-lander>2018-06-28 16:35:41 +0000
committerPMR <pmr@pmr-lander>2018-06-28 16:35:41 +0000
commit28789c9f43d7da630e56d06abc33f4d57e7de16d (patch)
tree6bce14f4253a91630646be5516bf03488e88fa05
parent6392745efbe2b708fe7a9fdbedb530f321841894 (diff)
parent05a97f6894458eb6dfa86e6146976eee421f1581 (diff)
Merge #348581 from ~jocave/plainbox-provider-checkbox:secure-boot-check-snappy
-rwxr-xr-xbin/boot_mode_test_snappy.py125
-rw-r--r--units/miscellanea/jobs.pxu16
2 files changed, 141 insertions, 0 deletions
diff --git a/bin/boot_mode_test_snappy.py b/bin/boot_mode_test_snappy.py
new file mode 100755
index 00000000..46bed925
--- /dev/null
+++ b/bin/boot_mode_test_snappy.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+# Copyright 2018 Canonical Ltd.
+# Written by:
+# Jonathan Cave <jonathan.cave@canonical.com>
+
+import io
+import os
+import re
+import sys
+import subprocess as sp
+
+import yaml
+
+
+def fitdumpimage(filename):
+ cmd = 'dumpimage -l {}'.format(filename)
+ out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding)
+ buf = io.StringIO(out)
+
+ # first line should identify FIT file
+ if not buf.readline().startswith('FIT description'):
+ raise SystemExit('ERROR: expected FIT image description')
+
+ # second line contains some metadata, skip it
+ buf.readline()
+
+ # from then on should get blocks of text describing the objects that were
+ # combined in to the FIT image e.g. kernel, ramdisk, device tree
+ image_re = re.compile(r'(?:^\ Image)\ \d+\ \((\S+)\)$')
+ config_re = re.compile(r'^\ Default Configuration|^\ Configuration')
+ objects = {}
+ name = ''
+ while True:
+ line = buf.readline()
+ # stop at end
+ if line == '':
+ break
+ # interested in storing image information
+ match = image_re.search(line)
+ if match:
+ name = match.group(1)
+ objects[name] = {}
+ continue
+ # not interested in configurations
+ if config_re.search(line):
+ name = ''
+ continue
+ # while in an image section store the info
+ if name != '':
+ entries = [s.strip() for s in line.split(':', 1)]
+ objects[name][entries[0]] = entries[1]
+ return objects
+
+
+def main():
+ if len(sys.argv) != 3:
+ raise SystemExit('ERROR: please supply gadget & kernel name')
+ gadget = sys.argv[1]
+ kernel = sys.argv[2]
+
+ gadget_yaml = os.path.join('/snap', gadget, 'current/meta/gadget.yaml')
+
+ if not os.path.exists(gadget_yaml):
+ raise SystemExit(
+ 'ERROR: failed to find gadget.yaml at {}'.format(gadget_yaml))
+
+ with open(gadget_yaml) as f:
+ data = yaml.load(f)
+ for k in data['volumes'].keys():
+ bootloader = data['volumes'][k]['bootloader']
+ if not bootloader:
+ raise SystemExit('ERROR: could not find name of bootloader')
+
+ if bootloader not in ('u-boot', 'grub'):
+ raise SystemExit(
+ 'ERROR: Unexpected bootloader name {}'.format(bootloader))
+ print('Bootloader is {}\n'.format(bootloader))
+
+ if bootloader == 'u-boot':
+ print('Parsing FIT image information...\n')
+
+ kernel_rev = os.path.basename(
+ os.path.realpath('/snap/{}/current'.format(kernel)))
+ boot_kernel = '/boot/uboot/{}_{}.snap/kernel.img'.format(
+ kernel, kernel_rev)
+ boot_objects = fitdumpimage(boot_kernel)
+
+ for obj, attrs in boot_objects.items():
+ print('Checking object {}'.format(obj))
+ if 'Sign value' not in attrs:
+ raise SystemExit('ERROR: no sign value found for object')
+ print('Found "Sign value"')
+ if len(attrs['Sign value']) != 512:
+ raise SystemExit('ERROR: unexpected sign value size')
+ if all(s in attrs['Sign algo'] for s in ['sha256', 'rsa2048']):
+ print('Found expected signing algorithms')
+ else:
+ raise SystemExit(
+ 'ERROR: unexpected signing algorithms {}'.format(
+ attrs['Sign algo']))
+ print()
+
+ # check that all parts of the fit image have
+ snap_kernel = '/snap/{}/current/kernel.img'.format(kernel)
+ snap_objects = fitdumpimage(snap_kernel)
+ if snap_objects != boot_objects:
+ raise SystemExit(
+ 'ERROR: boot kernel and current snap kernel do not match')
+ print('Kernel images in current snap and u-boot match\n')
+
+ print('Secure Boot appears to be enabled on this system')
+
+ if bootloader == 'grub':
+ cmd = 'mokutil --sb-state'
+ print('+', cmd, flush=True)
+ out = sp.check_output(cmd, shell=True).decode(sys.stdout.encoding)
+ print(out, flush=True)
+ if out != 'SecureBoot enabled\n':
+ raise SystemExit('ERROR: mokutil reports Secure Boot not in use')
+
+ print('Secure Boot appears to be enabled on this system')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/units/miscellanea/jobs.pxu b/units/miscellanea/jobs.pxu
index 4a9980ba..a1d4f5a1 100644
--- a/units/miscellanea/jobs.pxu
+++ b/units/miscellanea/jobs.pxu
@@ -148,6 +148,22 @@ command: boot_mode_test secureboot
plugin: shell
category_id: com.canonical.plainbox::miscellanea
estimated_duration: 0.5
+unit: template
+template-resource: model_assertion
+template-unit: job
+requires:
+ executable.name == 'dumpimage'
+ executable.name == 'mokutil'
+id: miscellanea/secure_boot_mode_{gadget}
+_summary: Test that {gadget} Ubuntu Core system booted with Secure Boot active
+_description:
+ Test to verify that the system booted with Secure Boot active.
+command:
+ boot_mode_test_snappy.py {gadget} {kernel}
+
+plugin: shell
+category_id: com.canonical.plainbox::miscellanea
+estimated_duration: 0.5
user: root
id: miscellanea/efi_pxeboot
requires: