Merge ~juliank/grub/+git/ubuntu:juliank/check-signed-kernels into ~ubuntu-core-dev/grub/+git/ubuntu:ubuntu

Proposed by Julian Andres Klode
Status: Merged
Merged at revision: c42ceff265d3b79295c139e9e795d646c045f05a
Proposed branch: ~juliank/grub/+git/ubuntu:juliank/check-signed-kernels
Merge into: ~ubuntu-core-dev/grub/+git/ubuntu:ubuntu
Diff against target: 166 lines (+123/-0)
5 files modified
debian/changelog (+8/-0)
debian/grub-check-signatures (+94/-0)
debian/grub-common.install.in (+1/-0)
debian/postinst.in (+4/-0)
debian/templates.in (+16/-0)
Reviewer Review Type Date Requested Status
Steve Langasek Needs Fixing
Review via email: mp+345403@code.launchpad.net

Commit message

Check that kernels are signed

To post a comment you must log in.
Revision history for this message
Steve Langasek (vorlon) :
review: Needs Fixing
Revision history for this message
Steve Langasek (vorlon) wrote :

should actually check both /sys/firmware/efi/efivars/SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c != 0 && /sys/firmware/efi/efivars/MokSBStateRT-605dab50-e046-4300-abb6-3dd810dd8b23 != 1. mokutil unhelpfully gives no information about the latter, so you'll need to directly read the files. See /usr/sbin/update-secureboot-policy for examples.

*Ideally*, we would verify that the kernel is not just signed, but signed with a key that's trusted by the firmware (so: found in db, or in MokListRT). Requires a bit more code, but I believe it's warranted.

Revision history for this message
Julian Andres Klode (juliank) wrote :

It now looks at all kernels in /boot, rather then finding via grub.cfg (so we can run before update-grub), checks the policy in the postinst, and only if we are on secure boot.

I have not found any solution to check if it's signed with the right key, though. What I figured out is that sbverify needs a certificate, and mokutil can give me a list of keys.

Revision history for this message
Steve Langasek (vorlon) wrote :

Comments inline.

One additional concern: the grub maintainer script is not the only place that grub-install might be called. In particular, shim-signed will also call grub-install --target=x86_64-efi from its postinst - as will grub-efi-amd64-signed, which is from a different source package. And with the most recent adjustment of the dependencies (grub-efi-amd64-signed now depends on grub-efi-amd64 | grub-pc; which means some users in 18.04 and newer will actually have grub-pc installed, whose postinst /should not/ fail to configure due to the kernel secureboot question), grub-efi-amd64-signed may actually have its dependencies satisfied even though there are unsigned kernels.

So I think the right place for the grub-check-signatures code to run is as an inlined wrapper of grub-install. Do you agree?

review: Needs Fixing
Revision history for this message
Julian Andres Klode (juliank) wrote :

> Comments inline.
>
> One additional concern: the grub maintainer script is not the only place that
> grub-install might be called. In particular, shim-signed will also call grub-
> install --target=x86_64-efi from its postinst - as will grub-efi-amd64-signed,
> which is from a different source package. And with the most recent adjustment
> of the dependencies (grub-efi-amd64-signed now depends on grub-efi-amd64 |
> grub-pc; which means some users in 18.04 and newer will actually have grub-pc
> installed, whose postinst /should not/ fail to configure due to the kernel
> secureboot question), grub-efi-amd64-signed may actually have its dependencies
> satisfied even though there are unsigned kernels.
>
> So I think the right place for the grub-check-signatures code to run is as an
> inlined wrapper of grub-install. Do you agree?

Your review on the last diff said to only check when we are switching the secure boot policy in grub - the only place we can do this is from the grub maintainer scripts, as we need to have the grub version to check against.

We specifically can't do that in grub-install, since grub-install may be used for a lot of stuff other than installing stuff to the ESP. Hence the previous approach checked it in update-grub.

I don't see why grub-pc should not fail due to secure boot if it's on an EFI system in secure boot mode.

Revision history for this message
Julian Andres Klode (juliank) :
Revision history for this message
Steve Langasek (vorlon) wrote :

A few final comments inline, nothing that should block landing this. Please resolve these comments as you see fit and land this change.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/debian/changelog b/debian/changelog
2index e02ed3f..7c43119 100644
3--- a/debian/changelog
4+++ b/debian/changelog
5@@ -1,3 +1,11 @@
6+grub2 (2.02-2ubuntu11) UNRELEASED; urgency=medium
7+
8+ [ Julian Andres Klode ]
9+ * Verify that the current and newer kernels are signed when grub is updated, to
10+ make sure people do not accidentally shutdown without a signed kernel.
11+
12+ -- Julian Andres Klode <juliank@ubuntu.com> Wed, 20 Jun 2018 13:04:33 +0200
13+
14 grub2 (2.02-2ubuntu10) cosmic; urgency=medium
15
16 * debian/patches/grub-shell-test-helper-disable-seabios-sercon.patch: In the
17diff --git a/debian/grub-check-signatures b/debian/grub-check-signatures
18new file mode 100755
19index 0000000..3a466ec
20--- /dev/null
21+++ b/debian/grub-check-signatures
22@@ -0,0 +1,94 @@
23+#!/bin/sh
24+
25+set -e
26+
27+. /usr/share/debconf/confmodule
28+
29+# Check if we are on an EFI system
30+efivars=/sys/firmware/efi/efivars
31+secureboot_var=SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c
32+moksbstatert_var=MokSBStateRT-605dab50-e046-4300-abb6-3dd810dd8b23
33+
34+on_secure_boot() {
35+ # Validate any queued actions before we go try to do them.
36+ local moksbstatert=0
37+
38+ if ! [ -d $efivars ]; then
39+ return 1
40+ fi
41+
42+ if ! [ -f $efivars/$secureboot_var ] \
43+ || [ "$(od -An -t u1 $efivars/$secureboot_var | awk '{ print $NF }')" -ne 1 ]
44+ then
45+ return 1
46+ fi
47+
48+ if [ -f /proc/sys/kernel/moksbstate_disabled ]; then
49+ moksbstatert=$(cat /proc/sys/kernel/moksbstate_disabled 2>/dev/null || echo 0)
50+ elif [ -f $efivars/$moksbstatert_var ]; then
51+ # MokSBStateRT set to 1 means validation is disabled
52+ moksbstatert=$(od -An -t u1 $efivars/$moksbstatert_var | \
53+ awk '{ print $NF; }')
54+ fi
55+
56+ if [ $moksbstatert -eq 1 ]; then
57+ return 1
58+ fi
59+
60+ return 0
61+}
62+
63+# Check if a given kernel image is signed
64+is_signed() {
65+ tmp=$(mktemp)
66+ sbattach --detach $tmp $1 >/dev/null # that's ugly...
67+ test "$(wc -c < $tmp)" -ge 16 # Just _some_ minimum size
68+ result=$?
69+ rm $tmp
70+ return $result
71+}
72+
73+# Check that our current kernel and every newer one is signed
74+find_unsigned() {
75+ uname_r="$(uname -r)"
76+ for kernel in $(ls -1 /boot/vmlinuz-* | sort -V -r); do
77+ # no kernels :(
78+ if [ "$kernel" = "/boot/vmlinuz-*" ]; then
79+ break
80+ fi
81+ this_uname_r="$(echo "$kernel" | sed -r 's#^/boot/vmlinuz-(.*)#\1#; s#\.efi\.signed$##')"
82+ if dpkg --compare-versions "$this_uname_r" lt "$uname_r"; then
83+ continue
84+ fi
85+ if ! is_signed $kernel; then
86+ echo "$this_uname_r"
87+ fi
88+ done
89+}
90+
91+# Only reached from show_warning
92+error() {
93+ echo "E: Your kernels are unsigned. This system will fail to boot in a secure boot environment." >&2
94+ exit 1
95+}
96+
97+# Either shows a debconf note or prints an error with error() above if
98+# that fails
99+show_warning() {
100+ # kernels should be an indented list of one version per line
101+ escaped="$(printf "%s" "$unsigned" | sed "s#^# #" | debconf-escape -e )"
102+ db_capb escape
103+ db_settitle grub2/unsigned_kernels_title || error
104+ db_fset grub2/unsigned_kernels seen 0 || error
105+ db_subst grub2/unsigned_kernels unsigned_versions "$escaped" || error
106+ db_input critical grub2/unsigned_kernels || error
107+ db_go || error
108+ error
109+}
110+
111+if on_secure_boot; then
112+ unsigned="$(find_unsigned)"
113+ if [ -n "$unsigned" ]; then
114+ show_warning "$unsigned"
115+ fi
116+fi
117diff --git a/debian/grub-common.install.in b/debian/grub-common.install.in
118index f06320d..59d3462 100644
119--- a/debian/grub-common.install.in
120+++ b/debian/grub-common.install.in
121@@ -1,5 +1,6 @@
122 ../../debian/apport/source_grub2.py usr/share/apport/package-hooks/
123 ../../debian/grub.d etc
124+../../debian/grub-check-signatures usr/share/grub/
125
126 etc/bash_completion.d
127 etc/grub.d
128diff --git a/debian/postinst.in b/debian/postinst.in
129index b1435d3..5c8c93d 100644
130--- a/debian/postinst.in
131+++ b/debian/postinst.in
132@@ -320,6 +320,10 @@ case "$1" in
133
134 devicemap_regenerated=
135
136+ if [ @PACKAGE@ = "grub-efi-amd64" ] && dpkg --compare-versions "$2" lt-nl 2.02-2ubuntu11; then
137+ /usr/share/grub/grub-check-signatures
138+ fi
139+
140 if egrep -q '^[[:space:]]*post(inst|rm)_hook[[:space:]]*=[[:space:]]*(/sbin/|/usr/sbin/)?update-grub' /etc/kernel-img.conf 2>/dev/null; then
141 echo 'Removing update-grub hooks from /etc/kernel-img.conf in favour of' >&2
142 echo '/etc/kernel/ hooks.' >&2
143diff --git a/debian/templates.in b/debian/templates.in
144index e261bb3..4ae3b6e 100644
145--- a/debian/templates.in
146+++ b/debian/templates.in
147@@ -65,3 +65,19 @@ _Description: /boot/grub/device.map has been regenerated
148 .
149 If you do not understand this message, or if there are no custom
150 boot menu entries, you can ignore this message.
151+
152+Template: grub2/unsigned_kernels_title
153+Type: title
154+_Description: unsigned kernels
155+
156+Template: grub2/unsigned_kernels
157+Type: note
158+_Description: Cannot upgrade Secure Boot enforcement policy due to unsigned kernels
159+ Your system has UEFI Secure Boot enabled in firmware, and the following kernels
160+ present on your system are unsigned:
161+ .
162+ ${unsigned_versions}
163+ .
164+ These kernels cannot be verified under Secure Boot. To ensure your system
165+ remains bootable, GRUB will not be upgraded on your disk until these kernels are
166+ removed or replaced with signed kernels.

Subscribers

People subscribed via source and target branches