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