Merge ~xnox/ubuntu/+source/shim-signed:dualsigned into ubuntu/+source/shim-signed:ubuntu/devel

Proposed by Dimitri John Ledkov
Status: Needs review
Proposed branch: ~xnox/ubuntu/+source/shim-signed:dualsigned
Merge into: ubuntu/+source/shim-signed:ubuntu/devel
Diff against target: 286 lines (+240/-1)
5 files modified
CanonicalMasterCA.crt (+25/-0)
Makefile (+24/-1)
debian/changelog (+7/-0)
debian/shim-signed.install (+1/-0)
download-signed (+183/-0)
Reviewer Review Type Date Requested Status
Canonical Signing Pending
Ubuntu Server Dev import team Pending
Review via email: mp+386190@code.launchpad.net

Commit message

* Add download-signed script from linux-signed package
* Construct and ship dual-signed shim.

This depends on src:shim-canonical, until src:shim itself submits things for signing.

Also to be used by UC20 signing archive.

To post a comment you must log in.
2119b24... by Dimitri John Ledkov

releasing package shim-signed version 1.40.4

Revision history for this message
Dimitri John Ledkov (xnox) wrote :

Tested against a shim-canonical uploaded into a ppa, and side-replacing the Canonical CA cert with the PPA cert.

Unmerged commits

2119b24... by Dimitri John Ledkov

releasing package shim-signed version 1.40.4

738affe... by Dimitri John Ledkov

Construct and ship dual-signed shim.

9d2c974... by Dimitri John Ledkov

Add download-signed script from linux-signed package

6c5eb87... by Julian Andres Klode

1.40.3 (patches unapplied)

Imported using git-ubuntu import.

aa52aa1... by Julian Andres Klode

1.40.2 (patches unapplied)

Imported using git-ubuntu import.

275ab49... by Julian Andres Klode

1.40.1 (patches unapplied)

Imported using git-ubuntu import.

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 9e48f1c..d42af56 100644
73--- a/debian/changelog
74+++ b/debian/changelog
75@@ -1,3 +1,10 @@
76+shim-signed (1.40.4) groovy; 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> Mon, 22 Jun 2020 16:23:33 +0100
82+
83 shim-signed (1.40.3) focal; urgency=medium
84
85 * Depend on the correct version of grub-signed (LP: #1871895)
86diff --git a/debian/shim-signed.install b/debian/shim-signed.install
87index 046d911..93d4e26 100755
88--- a/debian/shim-signed.install
89+++ b/debian/shim-signed.install
90@@ -1,6 +1,7 @@
91 #! /usr/bin/dh-exec
92
93 ${SHIM_BASE}.signed /usr/lib/shim
94+build/${SHIM_BASE}.dualsigned /usr/lib/shim
95 openssl.cnf /usr/lib/shim/mok
96 debian/source_shim-signed.py /usr/share/apport/package-hooks/
97 update-secureboot-policy /usr/sbin/
98diff --git a/download-signed b/download-signed
99new file mode 100755
100index 0000000..0793696
101--- /dev/null
102+++ b/download-signed
103@@ -0,0 +1,183 @@
104+#! /usr/bin/python3
105+
106+import hashlib
107+import argparse
108+import os
109+import re
110+import sys
111+import tarfile
112+from urllib import request
113+from urllib.error import HTTPError
114+from urllib.parse import (
115+ urlparse,
116+ urlunparse,
117+ )
118+
119+import apt
120+
121+# package_name: package containing the objects we signed
122+# package_version: package version containing the objects we signed
123+# src_package: source package name in dists
124+# signed_type: 'signed' or 'uefi' schema in the url
125+
126+parser = argparse.ArgumentParser()
127+parser.add_argument(
128+ "package_name",
129+ help="package containining the objects we signed")
130+parser.add_argument(
131+ "package_version",
132+ help="package version containing the objects we signed, or 'current'")
133+parser.add_argument(
134+ "src_package",
135+ help="source package name in dists")
136+parser.add_argument(
137+ "signed_type",
138+ nargs='?',
139+ default='signed',
140+ help="subdirectory type in the url, 'signed' or 'uefi'")
141+args = parser.parse_args()
142+
143+
144+class SignedDownloader:
145+ """Download a block of signed information from dists.
146+
147+ Find a block of signed information as published in dists/*/signed
148+ and download the contents. Use the contained checksum files to
149+ identify the members and to validate them once downloaded.
150+ """
151+
152+ def __init__(self, package_name, package_version, src_package, signed_type='signed'):
153+ self.package_name = package_name
154+ self.package_version = package_version
155+ self.src_package = src_package
156+
157+ # Find the package in the available archive repositories. Use a _binary_
158+ # package name and version to locate the appropriate archive. Then use the
159+ # URI there to look for and find the appropriate binary.
160+ cache = apt.Cache()
161+
162+ self.package = None
163+ if self.package_version == "current":
164+ self.package = cache[package_name].candidate
165+ else:
166+ for version in cache[package_name].versions:
167+ if version.version == self.package_version:
168+ self.package = version
169+ break
170+
171+ if not self.package:
172+ raise KeyError("{0}: package version not found".format(self.package_name))
173+
174+ origin = self.package.origins[0]
175+ pool_parsed = urlparse(self.package.uri)
176+ self.package_dir = "%s/%s/%s/%s-%s/%s/" % (
177+ origin.archive, 'main', signed_type,
178+ self.src_package, self.package.architecture, self.package_version)
179+
180+ # Prepare the master url stem and pull out any username/password. If present
181+ # replace the default opener with one which offers that password.
182+ dists_parsed_master = list(pool_parsed)
183+ if '@' in dists_parsed_master[1]:
184+ (username_password, host) = pool_parsed[1].split('@', 1)
185+ (username, password) = username_password.split(':', 1)
186+
187+ dists_parsed_master[1] = host
188+
189+ # Work out the authentication domain.
190+ domain_parsed = [ dists_parsed_master[0], dists_parsed_master[1], '/', None, None, None ]
191+ auth_uri = urlunparse(domain_parsed)
192+
193+ # create a password manager
194+ password_mgr = request.HTTPPasswordMgrWithDefaultRealm()
195+
196+ # Add the username and password.
197+ # If we knew the realm, we could use it instead of None.
198+ password_mgr.add_password(None, auth_uri, username, password)
199+
200+ handler = request.HTTPBasicAuthHandler(password_mgr)
201+
202+ # create "opener" (OpenerDirector instance)
203+ opener = request.build_opener(handler)
204+
205+ # Now all calls to urllib.request.urlopen use our opener.
206+ request.install_opener(opener)
207+
208+ self.dists_parsed = dists_parsed_master
209+
210+ def download_one(self, member, filename, hash_factory=None):
211+ directory = os.path.dirname(filename)
212+ if not os.path.exists(directory):
213+ os.makedirs(directory)
214+
215+ dists_parsed = list(self.dists_parsed)
216+ dists_parsed[2] = re.sub(r"/pool/.*", "/dists/" + self.package_dir + \
217+ member, dists_parsed[2])
218+ dists_uri = urlunparse(dists_parsed)
219+
220+ print("Downloading %s ... " % dists_uri, end='')
221+ sys.stdout.flush()
222+ try:
223+ with request.urlopen(dists_uri) as dists, open(filename, "wb") as out:
224+ hashobj = None
225+ if hash_factory:
226+ hashobj = hash_factory()
227+ for chunk in iter(lambda: dists.read(256 * 1024), b''):
228+ if hashobj:
229+ hashobj.update(chunk)
230+ out.write(chunk)
231+ checksum = True
232+ if hashobj:
233+ checksum = hashobj.hexdigest()
234+ except HTTPError as e:
235+ if e.code == 404:
236+ print("not found")
237+ else:
238+ raise
239+ else:
240+ print("found")
241+ return checksum
242+ return None
243+
244+ def download(self, base):
245+ """Download an entire signed result from dists."""
246+
247+ # Download the checksums and use that to download the contents.
248+ sums = 'SHA256SUMS'
249+ sums_local = os.path.join(base, self.package_version, sums)
250+ if not self.download_one(sums, sums_local):
251+ print('download-signed: {0}: not found'.format(sums))
252+ sys.exit(1)
253+
254+ # Read the checksum file and download the files it mentions.
255+ here = os.path.abspath(base)
256+ with open(sums_local) as sfd:
257+ for line in sfd:
258+ line = line.strip()
259+ (checksum_expected, member) = (line[0:64], line[66:])
260+ filename = os.path.abspath(os.path.join(base, self.package_version, member))
261+ if not filename.startswith(here):
262+ print('download-signed: {0}: member outside output directory'.format(member))
263+ sys.exit(1)
264+
265+ # Download and checksum this member.
266+ checksum_actual = self.download_one(member, filename, hashlib.sha256)
267+ if checksum_expected != checksum_actual:
268+ print('download-signed: {0}: member checksum invalid'.format(member))
269+ sys.exit(1)
270+
271+ # If this is a tarball result then extract it.
272+ here = os.path.abspath(os.path.join(base, self.package_version))
273+ tarball_filename = os.path.join(base, self.package_version, 'signed.tar.gz')
274+ if os.path.exists(tarball_filename):
275+ with tarfile.open(tarball_filename) as tarball:
276+ for tarinfo in tarball:
277+ if not filename.startswith(here):
278+ print('download-signed: {0}: tarball member outside output directory'.format(member))
279+ sys.exit(1)
280+ for tarinfo in tarball:
281+ print('Extracting {0} ...'.format(tarinfo.name))
282+ tarball.extract(tarinfo, base)
283+
284+
285+downloader = SignedDownloader(**vars(args))
286+downloader.download('.')

Subscribers

People subscribed via source and target branches