Merge ~ubuntu-core-dev/shim/+git/shim-signed:xnox/dual-signed into ~ubuntu-core-dev/shim/+git/shim-signed:master

Proposed by Dimitri John Ledkov
Status: Merged
Merged at revision: a06853dfa77b35ea274fbd0e898c5cc83aa4f0d3
Proposed branch: ~ubuntu-core-dev/shim/+git/shim-signed:xnox/dual-signed
Merge into: ~ubuntu-core-dev/shim/+git/shim-signed:master
Diff against target: 299 lines (+241/-2)
6 files modified
CanonicalMasterCA.crt (+25/-0)
Makefile (+24/-1)
debian/changelog (+7/-0)
debian/control (+1/-1)
debian/shim-signed.install (+1/-0)
download-signed (+183/-0)
Reviewer Review Type Date Requested Status
Julian Andres Klode Approve
Ubuntu Core Development Team Pending
Review via email: mp+388661@code.launchpad.net

This proposal supersedes a proposal from 2020-08-04.

Commit message

Construct and ship dual-signed shim.

Currently using shim-canonical provided signed artefacts.

To post a comment you must log in.
Revision history for this message
Julian Andres Klode (juliank) wrote :

Looks OK I guess, but is there even a point in shipping a single-signed shim if we have the dual-signed one?

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/CanonicalMasterCA.crt b/CanonicalMasterCA.crt
2new file mode 100644
3index 0000000..55c06d5
4--- /dev/null
5+++ b/CanonicalMasterCA.crt
6@@ -0,0 +1,25 @@
7+-----BEGIN CERTIFICATE-----
8+MIIENDCCAxygAwIBAgIJALlBJKAYLJJnMA0GCSqGSIb3DQEBCwUAMIGEMQswCQYD
9+VQQGEwJHQjEUMBIGA1UECAwLSXNsZSBvZiBNYW4xEDAOBgNVBAcMB0RvdWdsYXMx
10+FzAVBgNVBAoMDkNhbm9uaWNhbCBMdGQuMTQwMgYDVQQDDCtDYW5vbmljYWwgTHRk
11+LiBNYXN0ZXIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEyMDQxMjExMTI1MVoX
12+DTQyMDQxMTExMTI1MVowgYQxCzAJBgNVBAYTAkdCMRQwEgYDVQQIDAtJc2xlIG9m
13+IE1hbjEQMA4GA1UEBwwHRG91Z2xhczEXMBUGA1UECgwOQ2Fub25pY2FsIEx0ZC4x
14+NDAyBgNVBAMMK0Nhbm9uaWNhbCBMdGQuIE1hc3RlciBDZXJ0aWZpY2F0ZSBBdXRo
15+b3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/WzoWdO4hXa5h
16+7Z1WrL3e3nLz3X4tTGIPrMBtSAgRz42L+2EfJ8wRbtlVPTlU60A7sbvihTR5yvd7
17+v7p6yBAtGX2tWc+m1OlOD9quUupMnpDOxpkNTmdleF350dU4Skp6j5OcfxqjhdvO
18++ov3wqIhLZtUQTUQVxONbLwpBlBKfuqZqWinO8cHGzKeoBmHDnm7aJktfpNS5fbr
19+yZv5K+24aEm82ZVQQFvFsnGq61xX3nH5QArdW6wehC1QGlLW4fNrbpBkT1u06yDk
20+YRDaWvDq5ELXAcT+IR/ZucBUlUKBUnIfSWR6yGwk8QhwC02loDLRoBxXqE3jr6WO
21+BQU+EEOhAgMBAAGjgaYwgaMwHQYDVR0OBBYEFK2RmQvCKrH1FwSMI7ZlWiaONFpj
22+MB8GA1UdIwQYMBaAFK2RmQvCKrH1FwSMI7ZlWiaONFpjMA8GA1UdEwEB/wQFMAMB
23+Af8wCwYDVR0PBAQDAgGGMEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly93d3cuY2Fu
24+b25pY2FsLmNvbS9zZWN1cmUtYm9vdC1tYXN0ZXItY2EuY3JsMA0GCSqGSIb3DQEB
25+CwUAA4IBAQA/ffZ2pbODtCt60G1SGgODxBKnUJxHkszAlHeC0q5Xs5kE9TI6xlUd
26+B9sSqVb62NR2IOvkw1Hbmlyckj8Yc9qUaqGZOIykiG3B/Dlx0HR2FgM+ViM11VVH
27+WxodQcLTEkzc/64KkpxiChcBnHPgXrH9vNa1GRF6fs0+A35m21uoyTlIUf9T4Zwx
28+U5EbOxB1Axe65oECgJRwTEa3lLA9Fc0fjgLgaAKP+/lHHX2iAcYHUcSazO3dz6Nd
29+7ZK7vtH95uwfM1FzBL48crB9CPgB/5h9y5zgaTl3JUdxiLGNJ6UuqPc/X4Bplz6p
30+9JkU284DDgtmxBxtvbgnd8FClL38agq8
31+-----END CERTIFICATE-----
32diff --git a/Makefile b/Makefile
33index 2a5395d..8ee5c6c 100644
34--- a/Makefile
35+++ b/Makefile
36@@ -1,3 +1,5 @@
37+SHIM_CANONICAL_VERSION=$(shell dpkg-query -W -f'$${Version}' shim-canonical-unsigned)
38+
39 check:
40 mkdir -p build
41 # Verifying that the image is signed with the correct key.
42@@ -8,6 +10,27 @@ check:
43 cp /usr/lib/shim/$(SHIM_BASE) build/$(SHIM_BASE).signed
44 sbattach --attach build/detached-sig build/$(SHIM_BASE).signed
45 cmp $(SHIM_BASE).signed build/$(SHIM_BASE).signed
46+ ####
47+ # Construct dual-signed shim
48+ ./download-signed shim-canonical-unsigned $(SHIM_CANONICAL_VERSION) shim-canonical signed
49+ # Verify that the downloaded binary has signatures chained to Canonical Master CA
50+ sbverify --cert CanonicalMasterCA.crt $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE).signed
51+ # Detach Canonical signature
52+ sbattach --detach $(SHIM_CANONICAL_VERSION)/detached-sig-canonical $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE).signed
53+ rm $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE).signed
54+ # Compare that shims are all the same now
55+ cmp /usr/lib/shim/$(SHIM_BASE) $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
56+ # Reattach Canonical signature
57+ sbattach --attach $(SHIM_CANONICAL_VERSION)/detached-sig-canonical $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
58+ # Verify that attachment worked
59+ sbverify --cert CanonicalMasterCA.crt $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
60+ # Attach Microsoft signature
61+ sbattach --attach build/detached-sig $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
62+ # Validate that this shim is now dualsigned
63+ sbverify --list $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
64+ sbverify --cert CanonicalMasterCA.crt $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
65+ sbverify --cert MicCorUEFCA2011_2011-06-27.crt $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE)
66+ cp $(SHIM_CANONICAL_VERSION)/$(SHIM_BASE) build/$(SHIM_BASE).dualsigned
67
68 clean:
69- rm -rf build boot.csv BOOT$(EFI_ARCH).CSV
70+ rm -rf build $(SHIM_CANONICAL_VERSION) $shim_boot.csv BOOT$(EFI_ARCH).CSV
71diff --git a/debian/changelog b/debian/changelog
72index 2df7ed5..f16a39d 100644
73--- a/debian/changelog
74+++ b/debian/changelog
75@@ -1,3 +1,10 @@
76+shim-signed (1.43) UNRELEASED; urgency=medium
77+
78+ * Add download-signed script from linux-signed package
79+ * Construct and ship dual-signed shim.
80+
81+ -- Dimitri John Ledkov <xnox@ubuntu.com> Tue, 04 Aug 2020 14:23:29 +0100
82+
83 shim-signed (1.42) groovy; urgency=medium
84
85 * Update to the signed 15+1552672080.a4a1fbe-0ubuntu2 binary from Microsoft.
86diff --git a/debian/control b/debian/control
87index 59dee6b..770764f 100644
88--- a/debian/control
89+++ b/debian/control
90@@ -2,7 +2,7 @@ Source: shim-signed
91 Section: utils
92 Priority: optional
93 Maintainer: Steve Langasek <steve.langasek@ubuntu.com>
94-Build-Depends: debhelper (>= 9), dh-exec, shim, sbsigntool (>= 0.6-0ubuntu4), po-debconf
95+Build-Depends: debhelper (>= 9), dh-exec, shim, sbsigntool (>= 0.6-0ubuntu4), po-debconf, python3, python3-apt, shim-canonical-unsigned (>= 2)
96 Standards-Version: 3.9.4
97 Vcs-Git: https://git.launchpad.net/~ubuntu-core-dev/shim/+git/shim-signed
98 Vcs-Browser: https://git.launchpad.net/~ubuntu-core-dev/shim/+git/shim-signed
99diff --git a/debian/shim-signed.install b/debian/shim-signed.install
100index 046d911..93d4e26 100755
101--- a/debian/shim-signed.install
102+++ b/debian/shim-signed.install
103@@ -1,6 +1,7 @@
104 #! /usr/bin/dh-exec
105
106 ${SHIM_BASE}.signed /usr/lib/shim
107+build/${SHIM_BASE}.dualsigned /usr/lib/shim
108 openssl.cnf /usr/lib/shim/mok
109 debian/source_shim-signed.py /usr/share/apport/package-hooks/
110 update-secureboot-policy /usr/sbin/
111diff --git a/download-signed b/download-signed
112new file mode 100755
113index 0000000..0793696
114--- /dev/null
115+++ b/download-signed
116@@ -0,0 +1,183 @@
117+#! /usr/bin/python3
118+
119+import hashlib
120+import argparse
121+import os
122+import re
123+import sys
124+import tarfile
125+from urllib import request
126+from urllib.error import HTTPError
127+from urllib.parse import (
128+ urlparse,
129+ urlunparse,
130+ )
131+
132+import apt
133+
134+# package_name: package containing the objects we signed
135+# package_version: package version containing the objects we signed
136+# src_package: source package name in dists
137+# signed_type: 'signed' or 'uefi' schema in the url
138+
139+parser = argparse.ArgumentParser()
140+parser.add_argument(
141+ "package_name",
142+ help="package containining the objects we signed")
143+parser.add_argument(
144+ "package_version",
145+ help="package version containing the objects we signed, or 'current'")
146+parser.add_argument(
147+ "src_package",
148+ help="source package name in dists")
149+parser.add_argument(
150+ "signed_type",
151+ nargs='?',
152+ default='signed',
153+ help="subdirectory type in the url, 'signed' or 'uefi'")
154+args = parser.parse_args()
155+
156+
157+class SignedDownloader:
158+ """Download a block of signed information from dists.
159+
160+ Find a block of signed information as published in dists/*/signed
161+ and download the contents. Use the contained checksum files to
162+ identify the members and to validate them once downloaded.
163+ """
164+
165+ def __init__(self, package_name, package_version, src_package, signed_type='signed'):
166+ self.package_name = package_name
167+ self.package_version = package_version
168+ self.src_package = src_package
169+
170+ # Find the package in the available archive repositories. Use a _binary_
171+ # package name and version to locate the appropriate archive. Then use the
172+ # URI there to look for and find the appropriate binary.
173+ cache = apt.Cache()
174+
175+ self.package = None
176+ if self.package_version == "current":
177+ self.package = cache[package_name].candidate
178+ else:
179+ for version in cache[package_name].versions:
180+ if version.version == self.package_version:
181+ self.package = version
182+ break
183+
184+ if not self.package:
185+ raise KeyError("{0}: package version not found".format(self.package_name))
186+
187+ origin = self.package.origins[0]
188+ pool_parsed = urlparse(self.package.uri)
189+ self.package_dir = "%s/%s/%s/%s-%s/%s/" % (
190+ origin.archive, 'main', signed_type,
191+ self.src_package, self.package.architecture, self.package_version)
192+
193+ # Prepare the master url stem and pull out any username/password. If present
194+ # replace the default opener with one which offers that password.
195+ dists_parsed_master = list(pool_parsed)
196+ if '@' in dists_parsed_master[1]:
197+ (username_password, host) = pool_parsed[1].split('@', 1)
198+ (username, password) = username_password.split(':', 1)
199+
200+ dists_parsed_master[1] = host
201+
202+ # Work out the authentication domain.
203+ domain_parsed = [ dists_parsed_master[0], dists_parsed_master[1], '/', None, None, None ]
204+ auth_uri = urlunparse(domain_parsed)
205+
206+ # create a password manager
207+ password_mgr = request.HTTPPasswordMgrWithDefaultRealm()
208+
209+ # Add the username and password.
210+ # If we knew the realm, we could use it instead of None.
211+ password_mgr.add_password(None, auth_uri, username, password)
212+
213+ handler = request.HTTPBasicAuthHandler(password_mgr)
214+
215+ # create "opener" (OpenerDirector instance)
216+ opener = request.build_opener(handler)
217+
218+ # Now all calls to urllib.request.urlopen use our opener.
219+ request.install_opener(opener)
220+
221+ self.dists_parsed = dists_parsed_master
222+
223+ def download_one(self, member, filename, hash_factory=None):
224+ directory = os.path.dirname(filename)
225+ if not os.path.exists(directory):
226+ os.makedirs(directory)
227+
228+ dists_parsed = list(self.dists_parsed)
229+ dists_parsed[2] = re.sub(r"/pool/.*", "/dists/" + self.package_dir + \
230+ member, dists_parsed[2])
231+ dists_uri = urlunparse(dists_parsed)
232+
233+ print("Downloading %s ... " % dists_uri, end='')
234+ sys.stdout.flush()
235+ try:
236+ with request.urlopen(dists_uri) as dists, open(filename, "wb") as out:
237+ hashobj = None
238+ if hash_factory:
239+ hashobj = hash_factory()
240+ for chunk in iter(lambda: dists.read(256 * 1024), b''):
241+ if hashobj:
242+ hashobj.update(chunk)
243+ out.write(chunk)
244+ checksum = True
245+ if hashobj:
246+ checksum = hashobj.hexdigest()
247+ except HTTPError as e:
248+ if e.code == 404:
249+ print("not found")
250+ else:
251+ raise
252+ else:
253+ print("found")
254+ return checksum
255+ return None
256+
257+ def download(self, base):
258+ """Download an entire signed result from dists."""
259+
260+ # Download the checksums and use that to download the contents.
261+ sums = 'SHA256SUMS'
262+ sums_local = os.path.join(base, self.package_version, sums)
263+ if not self.download_one(sums, sums_local):
264+ print('download-signed: {0}: not found'.format(sums))
265+ sys.exit(1)
266+
267+ # Read the checksum file and download the files it mentions.
268+ here = os.path.abspath(base)
269+ with open(sums_local) as sfd:
270+ for line in sfd:
271+ line = line.strip()
272+ (checksum_expected, member) = (line[0:64], line[66:])
273+ filename = os.path.abspath(os.path.join(base, self.package_version, member))
274+ if not filename.startswith(here):
275+ print('download-signed: {0}: member outside output directory'.format(member))
276+ sys.exit(1)
277+
278+ # Download and checksum this member.
279+ checksum_actual = self.download_one(member, filename, hashlib.sha256)
280+ if checksum_expected != checksum_actual:
281+ print('download-signed: {0}: member checksum invalid'.format(member))
282+ sys.exit(1)
283+
284+ # If this is a tarball result then extract it.
285+ here = os.path.abspath(os.path.join(base, self.package_version))
286+ tarball_filename = os.path.join(base, self.package_version, 'signed.tar.gz')
287+ if os.path.exists(tarball_filename):
288+ with tarfile.open(tarball_filename) as tarball:
289+ for tarinfo in tarball:
290+ if not filename.startswith(here):
291+ print('download-signed: {0}: tarball member outside output directory'.format(member))
292+ sys.exit(1)
293+ for tarinfo in tarball:
294+ print('Extracting {0} ...'.format(tarinfo.name))
295+ tarball.extract(tarinfo, base)
296+
297+
298+downloader = SignedDownloader(**vars(args))
299+downloader.download('.')

Subscribers

People subscribed via source and target branches