Merge lp:~jibel/ubuntu/trusty/sbsigntool/lp1257305_add_corrected_efivars_magic into lp:ubuntu/trusty/sbsigntool
- Trusty (14.04)
- lp1257305_add_corrected_efivars_magic
- Merge into trusty
Proposed by
Jean-Baptiste Lallement
Status: | Merged |
---|---|
Merge reported by: | Michael Terry |
Merged at revision: | not available |
Proposed branch: | lp:~jibel/ubuntu/trusty/sbsigntool/lp1257305_add_corrected_efivars_magic |
Merge into: | lp:ubuntu/trusty/sbsigntool |
Diff against target: |
1070 lines (+1014/-2) 6 files modified
.pc/add_corrected_efivars_magic.patch/src/sbkeysync.c (+979/-0) .pc/applied-patches (+1/-0) debian/changelog (+7/-0) debian/patches/add_corrected_efivars_magic.patch (+23/-0) debian/patches/series (+1/-0) src/sbkeysync.c (+3/-2) |
To merge this branch: | bzr merge lp:~jibel/ubuntu/trusty/sbsigntool/lp1257305_add_corrected_efivars_magic |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Terry | Approve | ||
Review via email: mp+197542@code.launchpad.net |
Commit message
Description of the change
* debian/
fix to add corrected efivars magic (LP: #1257305)
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 | === added directory '.pc/add_corrected_efivars_magic.patch' |
2 | === added file '.pc/add_corrected_efivars_magic.patch/.timestamp' |
3 | === added directory '.pc/add_corrected_efivars_magic.patch/src' |
4 | === added file '.pc/add_corrected_efivars_magic.patch/src/sbkeysync.c' |
5 | --- .pc/add_corrected_efivars_magic.patch/src/sbkeysync.c 1970-01-01 00:00:00 +0000 |
6 | +++ .pc/add_corrected_efivars_magic.patch/src/sbkeysync.c 2013-12-03 15:04:24 +0000 |
7 | @@ -0,0 +1,979 @@ |
8 | +/* |
9 | + * Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com> |
10 | + * |
11 | + * This program is free software; you can redistribute it and/or |
12 | + * modify it under the terms of the GNU General Public License |
13 | + * as published by the Free Software Foundation; either version 3 |
14 | + * of the License, or (at your option) any later version. |
15 | + * |
16 | + * This program is distributed in the hope that it will be useful, |
17 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | + * GNU General Public License for more details. |
20 | + * |
21 | + * You should have received a copy of the GNU General Public License |
22 | + * along with this program; if not, write to the Free Software |
23 | + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, |
24 | + * USA. |
25 | + * |
26 | + * In addition, as a special exception, the copyright holders give |
27 | + * permission to link the code of portions of this program with the OpenSSL |
28 | + * library under certain conditions as described in each individual source file, |
29 | + * and distribute linked combinations including the two. |
30 | + * |
31 | + * You must obey the GNU General Public License in all respects for all |
32 | + * of the code used other than OpenSSL. If you modify file(s) with this |
33 | + * exception, you may extend this exception to your version of the |
34 | + * file(s), but you are not obligated to do so. If you do not wish to do |
35 | + * so, delete this exception statement from your version. If you delete |
36 | + * this exception statement from all source files in the program, then |
37 | + * also delete it here. |
38 | + */ |
39 | +#define _GNU_SOURCE |
40 | + |
41 | +#include <stdint.h> |
42 | +#include <stdlib.h> |
43 | +#include <string.h> |
44 | +#include <dirent.h> |
45 | +#include <fcntl.h> |
46 | +#include <unistd.h> |
47 | +#include <sys/stat.h> |
48 | +#include <sys/statfs.h> |
49 | +#include <sys/types.h> |
50 | + |
51 | +#include <getopt.h> |
52 | + |
53 | +#include <efi.h> |
54 | + |
55 | +#include <ccan/list/list.h> |
56 | +#include <ccan/array_size/array_size.h> |
57 | +#include <ccan/talloc/talloc.h> |
58 | + |
59 | +#include <openssl/x509.h> |
60 | +#include <openssl/err.h> |
61 | + |
62 | +#include "fileio.h" |
63 | +#include "efivars.h" |
64 | + |
65 | +#define EFIVARS_MOUNTPOINT "/sys/firmware/efi/efivars" |
66 | +#define EFIVARS_FSTYPE 0x6165676C |
67 | + |
68 | +#define EFI_IMAGE_SECURITY_DATABASE_GUID \ |
69 | + { 0xd719b2cb, 0x3d3a, 0x4596, \ |
70 | + { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f } } |
71 | + |
72 | +static const char *toolname = "sbkeysync"; |
73 | + |
74 | +static const uint32_t sigdb_attrs = EFI_VARIABLE_NON_VOLATILE | |
75 | + EFI_VARIABLE_BOOTSERVICE_ACCESS | |
76 | + EFI_VARIABLE_RUNTIME_ACCESS | |
77 | + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | |
78 | + EFI_VARIABLE_APPEND_WRITE; |
79 | + |
80 | +struct key_database_type { |
81 | + const char *name; |
82 | + EFI_GUID guid; |
83 | +}; |
84 | + |
85 | +struct key_database_type keydb_types[] = { |
86 | + { "PK", EFI_GLOBAL_VARIABLE }, |
87 | + { "KEK", EFI_GLOBAL_VARIABLE }, |
88 | + { "db", EFI_IMAGE_SECURITY_DATABASE_GUID }, |
89 | + { "dbx", EFI_IMAGE_SECURITY_DATABASE_GUID }, |
90 | +}; |
91 | + |
92 | +enum keydb_type { |
93 | + KEYDB_PK = 0, |
94 | + KEYDB_KEK = 1, |
95 | + KEYDB_DB = 2, |
96 | + KEYDB_DBX = 3, |
97 | +}; |
98 | + |
99 | +static const char *default_keystore_dirs[] = { |
100 | + "/etc/secureboot/keys", |
101 | + "/usr/share/secureboot/keys", |
102 | +}; |
103 | + |
104 | + |
105 | +struct key { |
106 | + EFI_GUID type; |
107 | + int id_len; |
108 | + uint8_t *id; |
109 | + |
110 | + char *description; |
111 | + |
112 | + struct list_node list; |
113 | + |
114 | + /* set for keys loaded from a filesystem keystore */ |
115 | + struct fs_keystore_entry *keystore_entry; |
116 | +}; |
117 | + |
118 | +typedef int (*key_parse_func)(struct key *, uint8_t *, size_t); |
119 | + |
120 | +struct cert_type { |
121 | + EFI_GUID guid; |
122 | + key_parse_func parse; |
123 | +}; |
124 | + |
125 | +struct key_database { |
126 | + const struct key_database_type *type; |
127 | + struct list_head keys; |
128 | +}; |
129 | + |
130 | +struct keyset { |
131 | + struct key_database pk; |
132 | + struct key_database kek; |
133 | + struct key_database db; |
134 | + struct key_database dbx; |
135 | +}; |
136 | + |
137 | +struct fs_keystore_entry { |
138 | + const struct key_database_type *type; |
139 | + const char *root; |
140 | + const char *name; |
141 | + uint8_t *data; |
142 | + size_t len; |
143 | + struct list_node keystore_list; |
144 | + struct list_node new_list; |
145 | +}; |
146 | + |
147 | +struct fs_keystore { |
148 | + struct list_head keys; |
149 | +}; |
150 | + |
151 | +struct sync_context { |
152 | + const char *efivars_dir; |
153 | + struct keyset *filesystem_keys; |
154 | + struct keyset *firmware_keys; |
155 | + struct fs_keystore *fs_keystore; |
156 | + const char **keystore_dirs; |
157 | + unsigned int n_keystore_dirs; |
158 | + struct list_head new_keys; |
159 | + bool verbose; |
160 | + bool dry_run; |
161 | + bool set_pk; |
162 | +}; |
163 | + |
164 | + |
165 | +#define GUID_STRLEN (8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1) |
166 | +static void guid_to_str(const EFI_GUID *guid, char *str) |
167 | +{ |
168 | + snprintf(str, GUID_STRLEN, |
169 | + "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", |
170 | + guid->Data1, guid->Data2, guid->Data3, |
171 | + guid->Data4[0], guid->Data4[1], |
172 | + guid->Data4[2], guid->Data4[3], |
173 | + guid->Data4[4], guid->Data4[5], |
174 | + guid->Data4[6], guid->Data4[7]); |
175 | +} |
176 | + |
177 | +static int sha256_key_parse(struct key *key, uint8_t *data, size_t len) |
178 | +{ |
179 | + const unsigned int sha256_id_size = 256 / 8; |
180 | + unsigned int i; |
181 | + |
182 | + if (len != sha256_id_size) |
183 | + return -1; |
184 | + |
185 | + key->id = talloc_memdup(key, data, sha256_id_size); |
186 | + key->id_len = sha256_id_size; |
187 | + |
188 | + key->description = talloc_array(key, char, len * 2 + 1); |
189 | + for (i = 0; i < len; i++) |
190 | + snprintf(&key->description[i*2], 3, "%02x", data[i]); |
191 | + key->description[len*2] = '\0'; |
192 | + |
193 | + return 0; |
194 | +} |
195 | + |
196 | +static int x509_key_parse(struct key *key, uint8_t *data, size_t len) |
197 | +{ |
198 | + const int description_len = 160; |
199 | + ASN1_INTEGER *serial; |
200 | + const uint8_t *tmp; |
201 | + X509 *x509; |
202 | + int rc; |
203 | + |
204 | + rc = -1; |
205 | + |
206 | + tmp = data; |
207 | + |
208 | + x509 = d2i_X509(NULL, &tmp, len); |
209 | + if (!x509) |
210 | + return -1; |
211 | + |
212 | + /* we use the X509 serial number as the key ID */ |
213 | + if (!x509->cert_info || !x509->cert_info->serialNumber) |
214 | + goto out; |
215 | + |
216 | + serial = x509->cert_info->serialNumber; |
217 | + |
218 | + key->id_len = ASN1_STRING_length(serial); |
219 | + key->id = talloc_memdup(key, ASN1_STRING_data(serial), key->id_len); |
220 | + |
221 | + key->description = talloc_array(key, char, description_len); |
222 | + X509_NAME_oneline(x509->cert_info->subject, |
223 | + key->description, description_len); |
224 | + |
225 | + rc = 0; |
226 | + |
227 | +out: |
228 | + X509_free(x509); |
229 | + return rc; |
230 | +} |
231 | + |
232 | +struct cert_type cert_types[] = { |
233 | + { EFI_CERT_SHA256_GUID, sha256_key_parse }, |
234 | + { EFI_CERT_X509_GUID, x509_key_parse }, |
235 | +}; |
236 | + |
237 | +static int guidcmp(const EFI_GUID *a, const EFI_GUID *b) |
238 | +{ |
239 | + return memcmp(a, b, sizeof(EFI_GUID)); |
240 | +} |
241 | + |
242 | +static int key_parse(struct key *key, const EFI_GUID *type, |
243 | + uint8_t *data, size_t len) |
244 | +{ |
245 | + char guid_str[GUID_STRLEN]; |
246 | + unsigned int i; |
247 | + |
248 | + for (i = 0; i < ARRAY_SIZE(cert_types); i++) { |
249 | + if (guidcmp(&cert_types[i].guid, type)) |
250 | + continue; |
251 | + |
252 | + return cert_types[i].parse(key, data, len); |
253 | + } |
254 | + |
255 | + guid_to_str(type, guid_str); |
256 | + printf("warning: unknown signature type found:\n %s\n", |
257 | + guid_str); |
258 | + return -1; |
259 | + |
260 | +} |
261 | + |
262 | +typedef int (*sigdata_fn)(EFI_SIGNATURE_DATA *, int, const EFI_GUID *, void *); |
263 | + |
264 | +/** |
265 | + * Iterates an buffer of EFI_SIGNATURE_LISTs (at db_data, of length len), |
266 | + * and calls fn on each EFI_SIGNATURE_DATA item found. |
267 | + * |
268 | + * fn is passed the EFI_SIGNATURE_DATA pointer, and the length of the |
269 | + * signature data (including GUID header), the type of the signature list, |
270 | + * and a context pointer. |
271 | + */ |
272 | +static int sigdb_iterate(void *db_data, size_t len, |
273 | + sigdata_fn fn, void *arg) |
274 | +{ |
275 | + EFI_SIGNATURE_LIST *siglist; |
276 | + EFI_SIGNATURE_DATA *sigdata; |
277 | + unsigned int i, j; |
278 | + int rc = 0; |
279 | + |
280 | + if (len == 0) |
281 | + return 0; |
282 | + |
283 | + if (len < sizeof(*siglist)) |
284 | + return -1; |
285 | + |
286 | + for (i = 0, siglist = db_data + i; |
287 | + i + sizeof(*siglist) <= len && |
288 | + i + siglist->SignatureListSize > i && |
289 | + i + siglist->SignatureListSize <= len && !rc; |
290 | + i += siglist->SignatureListSize, |
291 | + siglist = db_data + i) { |
292 | + |
293 | + /* ensure that the header & sig sizes are sensible */ |
294 | + if (siglist->SignatureHeaderSize > siglist->SignatureListSize) |
295 | + continue; |
296 | + |
297 | + if (siglist->SignatureSize > siglist->SignatureListSize) |
298 | + continue; |
299 | + |
300 | + if (siglist->SignatureSize < sizeof(*sigdata)) |
301 | + continue; |
302 | + |
303 | + /* iterate through the (constant-sized) signature data blocks */ |
304 | + for (j = sizeof(*siglist) + siglist->SignatureHeaderSize; |
305 | + j < siglist->SignatureListSize && !rc; |
306 | + j += siglist->SignatureSize) |
307 | + { |
308 | + sigdata = (void *)(siglist) + j; |
309 | + |
310 | + rc = fn(sigdata, siglist->SignatureSize, |
311 | + &siglist->SignatureType, arg); |
312 | + |
313 | + } |
314 | + |
315 | + } |
316 | + |
317 | + return rc; |
318 | +} |
319 | + |
320 | +struct keydb_add_ctx { |
321 | + struct fs_keystore_entry *ke; |
322 | + struct key_database *kdb; |
323 | + struct keyset *keyset; |
324 | +}; |
325 | + |
326 | +static int keydb_add_key(EFI_SIGNATURE_DATA *sigdata, int len, |
327 | + const EFI_GUID *type, void *arg) |
328 | +{ |
329 | + struct keydb_add_ctx *add_ctx = arg; |
330 | + struct key *key; |
331 | + int rc; |
332 | + |
333 | + key = talloc(add_ctx->keyset, struct key); |
334 | + |
335 | + rc = key_parse(key, type, sigdata->SignatureData, |
336 | + len - sizeof(*sigdata)); |
337 | + |
338 | + if (rc) { |
339 | + talloc_free(key); |
340 | + return 0; |
341 | + } |
342 | + key->keystore_entry = add_ctx->ke; |
343 | + key->type = *type; |
344 | + |
345 | + /* add a reference to the keystore entry: we don't want it to be |
346 | + * deallocated if the keystore is deallocated before the |
347 | + * struct key. */ |
348 | + if (key->keystore_entry) |
349 | + talloc_reference(key, key->keystore_entry); |
350 | + |
351 | + list_add(&add_ctx->kdb->keys, &key->list); |
352 | + |
353 | + return 0; |
354 | +} |
355 | + |
356 | +static int read_firmware_keydb(struct sync_context *ctx, |
357 | + struct key_database *kdb) |
358 | +{ |
359 | + struct keydb_add_ctx add_ctx; |
360 | + char guid_str[GUID_STRLEN]; |
361 | + char *filename; |
362 | + uint8_t *buf; |
363 | + int rc = -1; |
364 | + size_t len; |
365 | + |
366 | + add_ctx.keyset = ctx->firmware_keys; |
367 | + add_ctx.kdb = kdb; |
368 | + add_ctx.ke = NULL; |
369 | + |
370 | + guid_to_str(&kdb->type->guid, guid_str); |
371 | + |
372 | + filename = talloc_asprintf(ctx->firmware_keys, "%s/%s-%s", |
373 | + ctx->efivars_dir, kdb->type->name, guid_str); |
374 | + |
375 | + buf = NULL; |
376 | + rc = fileio_read_file_noerror(ctx->firmware_keys, filename, &buf, &len); |
377 | + if (rc) |
378 | + goto out; |
379 | + |
380 | + /* efivars files start with a 32-bit attribute block */ |
381 | + if (len < sizeof(uint32_t)) |
382 | + goto out; |
383 | + |
384 | + buf += sizeof(uint32_t); |
385 | + len -= sizeof(uint32_t); |
386 | + |
387 | + rc = 0; |
388 | + sigdb_iterate(buf, len, keydb_add_key, &add_ctx); |
389 | + |
390 | +out: |
391 | + if (rc) |
392 | + talloc_free(buf); |
393 | + talloc_free(filename); |
394 | + |
395 | + return rc; |
396 | +} |
397 | + |
398 | +static void __attribute__((format(printf, 2, 3))) print_keystore_key_error( |
399 | + struct fs_keystore_entry *ke, const char *fmt, ...) |
400 | +{ |
401 | + char *errstr; |
402 | + va_list ap; |
403 | + |
404 | + va_start(ap, fmt); |
405 | + errstr = talloc_vasprintf(ke, fmt, ap); |
406 | + |
407 | + fprintf(stderr, "Invalid key %s/%s\n - %s\n", ke->root, ke->name, |
408 | + errstr); |
409 | + |
410 | + talloc_free(errstr); |
411 | + va_end(ap); |
412 | +} |
413 | + |
414 | +static int read_filesystem_keydb(struct sync_context *ctx, |
415 | + struct key_database *kdb) |
416 | +{ |
417 | + EFI_GUID cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; |
418 | + EFI_VARIABLE_AUTHENTICATION_2 *auth; |
419 | + struct keydb_add_ctx add_ctx; |
420 | + struct fs_keystore_entry *ke; |
421 | + int rc; |
422 | + |
423 | + add_ctx.keyset = ctx->filesystem_keys; |
424 | + add_ctx.kdb = kdb; |
425 | + |
426 | + list_for_each(&ctx->fs_keystore->keys, ke, keystore_list) { |
427 | + unsigned int len; |
428 | + void *buf; |
429 | + |
430 | + if (ke->len == 0) |
431 | + continue; |
432 | + |
433 | + if (ke->type != kdb->type) |
434 | + continue; |
435 | + |
436 | + /* parse the three data structures: |
437 | + * EFI_VARIABLE_AUTHENTICATION_2 token |
438 | + * EFI_SIGNATURE_LIST |
439 | + * EFI_SIGNATURE_DATA |
440 | + * ensuring that we have enough data for each |
441 | + */ |
442 | + |
443 | + buf = ke->data; |
444 | + len = ke->len; |
445 | + |
446 | + if (len < sizeof(*auth)) { |
447 | + print_keystore_key_error(ke, "does not contain an " |
448 | + "EFI_VARIABLE_AUTHENTICATION_2 descriptor"); |
449 | + continue; |
450 | + } |
451 | + |
452 | + auth = buf; |
453 | + |
454 | + if (guidcmp(&auth->AuthInfo.CertType, &cert_type_pkcs7)) { |
455 | + print_keystore_key_error(ke, "unknown cert type"); |
456 | + continue; |
457 | + } |
458 | + |
459 | + if (auth->AuthInfo.Hdr.dwLength > len) { |
460 | + print_keystore_key_error(ke, |
461 | + "invalid WIN_CERTIFICATE length"); |
462 | + continue; |
463 | + } |
464 | + |
465 | + /* the dwLength field includes the size of the WIN_CERTIFICATE, |
466 | + * but not the other data in the EFI_VARIABLE_AUTHENTICATION_2 |
467 | + * descriptor */ |
468 | + buf += sizeof(*auth) - sizeof(auth->AuthInfo) + |
469 | + auth->AuthInfo.Hdr.dwLength; |
470 | + len -= sizeof(*auth) - sizeof(auth->AuthInfo) + |
471 | + auth->AuthInfo.Hdr.dwLength; |
472 | + |
473 | + add_ctx.ke = ke; |
474 | + rc = sigdb_iterate(buf, len, keydb_add_key, &add_ctx); |
475 | + if (rc) { |
476 | + print_keystore_key_error(ke, "error parsing " |
477 | + "EFI_SIGNATURE_LIST"); |
478 | + continue; |
479 | + } |
480 | + |
481 | + } |
482 | + |
483 | + return 0; |
484 | +} |
485 | + |
486 | +static int read_keysets(struct sync_context *ctx) |
487 | +{ |
488 | + read_firmware_keydb(ctx, &ctx->firmware_keys->pk); |
489 | + read_firmware_keydb(ctx, &ctx->firmware_keys->kek); |
490 | + read_firmware_keydb(ctx, &ctx->firmware_keys->db); |
491 | + read_firmware_keydb(ctx, &ctx->firmware_keys->dbx); |
492 | + |
493 | + read_filesystem_keydb(ctx, &ctx->filesystem_keys->pk); |
494 | + read_filesystem_keydb(ctx, &ctx->filesystem_keys->kek); |
495 | + read_filesystem_keydb(ctx, &ctx->filesystem_keys->db); |
496 | + read_filesystem_keydb(ctx, &ctx->filesystem_keys->dbx); |
497 | + |
498 | + return 0; |
499 | +} |
500 | + |
501 | +static int check_pk(struct sync_context *ctx) |
502 | +{ |
503 | + struct key *key; |
504 | + int i = 0; |
505 | + |
506 | + list_for_each(&ctx->filesystem_keys->pk.keys, key, list) |
507 | + i++; |
508 | + |
509 | + return (i <= 1) ? 0 : 1; |
510 | +} |
511 | + |
512 | +static void print_keyset(struct keyset *keyset, const char *name) |
513 | +{ |
514 | + struct key_database *kdbs[] = |
515 | + { &keyset->pk, &keyset->kek, &keyset->db, &keyset->dbx }; |
516 | + struct key *key; |
517 | + unsigned int i; |
518 | + |
519 | + printf("%s keys:\n", name); |
520 | + |
521 | + for (i = 0; i < ARRAY_SIZE(kdbs); i++) { |
522 | + printf(" %s:\n", kdbs[i]->type->name); |
523 | + |
524 | + list_for_each(&kdbs[i]->keys, key, list) { |
525 | + printf(" %s\n", key->description); |
526 | + if (key->keystore_entry) |
527 | + printf(" from %s/%s\n", |
528 | + key->keystore_entry->root, |
529 | + key->keystore_entry->name); |
530 | + } |
531 | + } |
532 | +} |
533 | + |
534 | +static int check_efivars_mount(const char *mountpoint) |
535 | +{ |
536 | + struct statfs statbuf; |
537 | + int rc; |
538 | + |
539 | + rc = statfs(mountpoint, &statbuf); |
540 | + if (rc) |
541 | + return -1; |
542 | + |
543 | + if (statbuf.f_type != EFIVARS_FSTYPE) |
544 | + return -1; |
545 | + |
546 | + return 0; |
547 | +} |
548 | + |
549 | +static int keystore_entry_read(struct fs_keystore_entry *ke) |
550 | +{ |
551 | + const char *path; |
552 | + int rc; |
553 | + |
554 | + path = talloc_asprintf(ke, "%s/%s", ke->root, ke->name); |
555 | + |
556 | + rc = fileio_read_file(ke, path, &ke->data, &ke->len); |
557 | + |
558 | + talloc_free(path); |
559 | + |
560 | + return rc; |
561 | +} |
562 | + |
563 | +static bool keystore_contains_file(struct fs_keystore *keystore, |
564 | + const char *filename) |
565 | +{ |
566 | + struct fs_keystore_entry *ke; |
567 | + |
568 | + list_for_each(&keystore->keys, ke, keystore_list) { |
569 | + if (!strcmp(ke->name, filename)) |
570 | + return true; |
571 | + } |
572 | + |
573 | + return false; |
574 | +} |
575 | + |
576 | +static int update_keystore(struct fs_keystore *keystore, const char *root) |
577 | +{ |
578 | + struct fs_keystore_entry *ke; |
579 | + unsigned int i; |
580 | + |
581 | + for (i = 0; i < ARRAY_SIZE(keydb_types); i++) { |
582 | + const char *filename, *dirname; |
583 | + struct dirent *dirent; |
584 | + DIR *dir; |
585 | + |
586 | + dirname = talloc_asprintf(keystore, "%s/%s", root, |
587 | + keydb_types[i].name); |
588 | + |
589 | + dir = opendir(dirname); |
590 | + if (!dir) |
591 | + continue; |
592 | + |
593 | + for (dirent = readdir(dir); dirent; dirent = readdir(dir)) { |
594 | + |
595 | + if (dirent->d_name[0] == '.') |
596 | + continue; |
597 | + |
598 | + filename = talloc_asprintf(dirname, "%s/%s", |
599 | + keydb_types[i].name, |
600 | + dirent->d_name); |
601 | + |
602 | + if (keystore_contains_file(keystore, filename)) |
603 | + continue; |
604 | + |
605 | + ke = talloc(keystore, struct fs_keystore_entry); |
606 | + ke->name = filename; |
607 | + ke->root = root; |
608 | + ke->type = &keydb_types[i]; |
609 | + talloc_steal(ke, ke->name); |
610 | + |
611 | + if (keystore_entry_read(ke)) |
612 | + talloc_free(ke); |
613 | + else |
614 | + list_add(&keystore->keys, &ke->keystore_list); |
615 | + } |
616 | + |
617 | + closedir(dir); |
618 | + talloc_free(dirname); |
619 | + } |
620 | + |
621 | + return 0; |
622 | +} |
623 | + |
624 | +static int read_keystore(struct sync_context *ctx) |
625 | +{ |
626 | + struct fs_keystore *keystore; |
627 | + unsigned int i; |
628 | + |
629 | + keystore = talloc(ctx, struct fs_keystore); |
630 | + list_head_init(&keystore->keys); |
631 | + |
632 | + for (i = 0; i < ctx->n_keystore_dirs; i++) { |
633 | + update_keystore(keystore, ctx->keystore_dirs[i]); |
634 | + } |
635 | + |
636 | + ctx->fs_keystore = keystore; |
637 | + |
638 | + return 0; |
639 | +} |
640 | + |
641 | +static void print_keystore(struct fs_keystore *keystore) |
642 | +{ |
643 | + struct fs_keystore_entry *ke; |
644 | + |
645 | + printf("Filesystem keystore:\n"); |
646 | + |
647 | + list_for_each(&keystore->keys, ke, keystore_list) |
648 | + printf(" %s/%s [%zd bytes]\n", ke->root, ke->name, ke->len); |
649 | +} |
650 | + |
651 | +static int key_cmp(struct key *a, struct key *b) |
652 | +{ |
653 | + if (a->id_len != b->id_len) |
654 | + return a->id_len - b->id_len; |
655 | + |
656 | + return memcmp(a->id, b->id, a->id_len); |
657 | +} |
658 | + |
659 | +/** |
660 | + * Finds the set-difference of the filesystem and firmware keys, and |
661 | + * populates ctx->new_keys with the keystore_entries that should be |
662 | + * inserted into firmware |
663 | + */ |
664 | +static int find_new_keys(struct sync_context *ctx) |
665 | +{ |
666 | + struct { |
667 | + struct key_database *fs_kdb, *fw_kdb; |
668 | + } kdbs[] = { |
669 | + { &ctx->filesystem_keys->pk, &ctx->firmware_keys->pk }, |
670 | + { &ctx->filesystem_keys->kek, &ctx->firmware_keys->kek }, |
671 | + { &ctx->filesystem_keys->db, &ctx->firmware_keys->db }, |
672 | + { &ctx->filesystem_keys->dbx, &ctx->firmware_keys->dbx }, |
673 | + }; |
674 | + unsigned int i; |
675 | + int n = 0; |
676 | + |
677 | + for (i = 0; i < ARRAY_SIZE(kdbs); i++ ) { |
678 | + struct fs_keystore_entry *ke; |
679 | + struct key *fs_key, *fw_key; |
680 | + bool found; |
681 | + |
682 | + list_for_each(&kdbs[i].fs_kdb->keys, fs_key, list) { |
683 | + found = false; |
684 | + list_for_each(&kdbs[i].fw_kdb->keys, fw_key, list) { |
685 | + if (!key_cmp(fs_key, fw_key)) { |
686 | + found = true; |
687 | + break; |
688 | + } |
689 | + } |
690 | + if (found) |
691 | + continue; |
692 | + |
693 | + /* add the keystore entry if it's not already present */ |
694 | + found = false; |
695 | + list_for_each(&ctx->new_keys, ke, new_list) { |
696 | + if (fs_key->keystore_entry == ke) { |
697 | + found = true; |
698 | + break; |
699 | + } |
700 | + } |
701 | + |
702 | + if (found) |
703 | + continue; |
704 | + |
705 | + list_add(&ctx->new_keys, |
706 | + &fs_key->keystore_entry->new_list); |
707 | + n++; |
708 | + } |
709 | + } |
710 | + |
711 | + return n; |
712 | +} |
713 | + |
714 | +static void print_new_keys(struct sync_context *ctx) |
715 | +{ |
716 | + struct fs_keystore_entry *ke; |
717 | + |
718 | + printf("New keys in filesystem:\n"); |
719 | + |
720 | + list_for_each(&ctx->new_keys, ke, new_list) |
721 | + printf(" %s/%s\n", ke->root, ke->name); |
722 | +} |
723 | + |
724 | +static int insert_key(struct sync_context *ctx, struct fs_keystore_entry *ke) |
725 | +{ |
726 | + char guid_str[GUID_STRLEN]; |
727 | + char *efivars_filename; |
728 | + unsigned int buf_len; |
729 | + uint8_t *buf; |
730 | + int fd, rc; |
731 | + |
732 | + fd = -1; |
733 | + rc = -1; |
734 | + |
735 | + if (ctx->verbose) |
736 | + printf("Inserting key update %s/%s into %s\n", |
737 | + ke->root, ke->name, ke->type->name); |
738 | + |
739 | + /* we create a contiguous buffer of attributes & key data, so that |
740 | + * we write to the efivars file in a single syscall */ |
741 | + buf_len = sizeof(sigdb_attrs) + ke->len; |
742 | + buf = talloc_array(ke, uint8_t, buf_len); |
743 | + memcpy(buf, &sigdb_attrs, sizeof(sigdb_attrs)); |
744 | + memcpy(buf + sizeof(sigdb_attrs), ke->data, ke->len); |
745 | + |
746 | + guid_to_str(&ke->type->guid, guid_str); |
747 | + |
748 | + efivars_filename = talloc_asprintf(ke, "%s/%s-%s", ctx->efivars_dir, |
749 | + ke->type->name, guid_str); |
750 | + |
751 | + fd = open(efivars_filename, O_WRONLY | O_CREAT, 0600); |
752 | + if (fd < 0) { |
753 | + fprintf(stderr, "Can't create key file %s: %s\n", |
754 | + efivars_filename, strerror(errno)); |
755 | + goto out; |
756 | + } |
757 | + |
758 | + rc = write(fd, buf, buf_len); |
759 | + if (rc <= 0) { |
760 | + fprintf(stderr, "Error writing key update: %s\n", |
761 | + strerror(errno)); |
762 | + goto out; |
763 | + } |
764 | + |
765 | + if (rc != (int)buf_len) { |
766 | + fprintf(stderr, "Partial write during key update: " |
767 | + "wrote %d bytes, expecting %d\n", |
768 | + rc, buf_len); |
769 | + goto out; |
770 | + } |
771 | + |
772 | + rc = 0; |
773 | + |
774 | +out: |
775 | + if (fd >= 0) |
776 | + close(fd); |
777 | + talloc_free(efivars_filename); |
778 | + talloc_free(buf); |
779 | + if (rc) |
780 | + fprintf(stderr, "Error syncing keystore file %s/%s\n", |
781 | + ke->root, ke->name); |
782 | + return rc; |
783 | +} |
784 | + |
785 | +static int insert_new_keys(struct sync_context *ctx) |
786 | +{ |
787 | + struct fs_keystore_entry *ke, *ke_pk; |
788 | + int pks, rc; |
789 | + |
790 | + rc = 0; |
791 | + pks = 0; |
792 | + ke_pk = NULL; |
793 | + |
794 | + list_for_each(&ctx->new_keys, ke, new_list) { |
795 | + |
796 | + /* we handle PK last */ |
797 | + if (ke->type == &keydb_types[KEYDB_PK]) { |
798 | + ke_pk = ke; |
799 | + pks++; |
800 | + continue; |
801 | + } |
802 | + |
803 | + if (insert_key(ctx, ke)) |
804 | + rc = -1; |
805 | + } |
806 | + |
807 | + if (rc) |
808 | + return rc; |
809 | + |
810 | + if (pks == 0 || !ctx->set_pk) |
811 | + return 0; |
812 | + |
813 | + if (pks > 1) { |
814 | + fprintf(stderr, "Skipping PK update due to mutiple PKs\n"); |
815 | + return -1; |
816 | + } |
817 | + |
818 | + rc = insert_key(ctx, ke_pk); |
819 | + |
820 | + return rc; |
821 | +} |
822 | + |
823 | +static struct keyset *init_keyset(struct sync_context *ctx) |
824 | +{ |
825 | + struct keyset *keyset; |
826 | + |
827 | + keyset = talloc(ctx, struct keyset); |
828 | + |
829 | + list_head_init(&keyset->pk.keys); |
830 | + keyset->pk.type = &keydb_types[KEYDB_PK]; |
831 | + |
832 | + list_head_init(&keyset->kek.keys); |
833 | + keyset->kek.type = &keydb_types[KEYDB_KEK]; |
834 | + |
835 | + list_head_init(&keyset->db.keys); |
836 | + keyset->db.type = &keydb_types[KEYDB_DB]; |
837 | + |
838 | + list_head_init(&keyset->dbx.keys); |
839 | + keyset->dbx.type = &keydb_types[KEYDB_DBX]; |
840 | + |
841 | + return keyset; |
842 | +} |
843 | + |
844 | +static struct option options[] = { |
845 | + { "help", no_argument, NULL, 'h' }, |
846 | + { "version", no_argument, NULL, 'V' }, |
847 | + { "efivars-path", required_argument, NULL, 'e' }, |
848 | + { "verbose", no_argument, NULL, 'v' }, |
849 | + { "dry-run", no_argument, NULL, 'n' }, |
850 | + { "pk", no_argument, NULL, 'p' }, |
851 | + { "no-default-keystores", no_argument, NULL, 'd' }, |
852 | + { "keystore", required_argument, NULL, 'k' }, |
853 | + { NULL, 0, NULL, 0 }, |
854 | +}; |
855 | + |
856 | +static void usage(void) |
857 | +{ |
858 | + printf("Usage: %s [options]\n" |
859 | + "Update EFI key databases from the filesystem\n" |
860 | + "\n" |
861 | + "Options:\n" |
862 | + "\t--efivars-path <dir> Path to efivars mountpoint\n" |
863 | + "\t (or regular directory for testing)\n" |
864 | + "\t--verbose Print verbose progress information\n" |
865 | + "\t--dry-run Don't update firmware key databases\n" |
866 | + "\t--pk Set PK\n" |
867 | + "\t--keystore <dir> Read keys from <dir>/{db,dbx,KEK}/*\n" |
868 | + "\t (can be specified multiple times,\n" |
869 | + "\t first dir takes precedence)\n" |
870 | + "\t--no-default-keystores\n" |
871 | + "\t Don't read keys from the default\n" |
872 | + "\t keystore dirs\n", |
873 | + toolname); |
874 | +} |
875 | + |
876 | +static void version(void) |
877 | +{ |
878 | + printf("%s %s\n", toolname, VERSION); |
879 | +} |
880 | + |
881 | +static void add_keystore_dir(struct sync_context *ctx, const char *dir) |
882 | +{ |
883 | + ctx->keystore_dirs = talloc_realloc(ctx, ctx->keystore_dirs, |
884 | + const char *, ++ctx->n_keystore_dirs); |
885 | + |
886 | + ctx->keystore_dirs[ctx->n_keystore_dirs - 1] = |
887 | + talloc_strdup(ctx->keystore_dirs, dir); |
888 | +} |
889 | + |
890 | +int main(int argc, char **argv) |
891 | +{ |
892 | + bool use_default_keystore_dirs; |
893 | + struct sync_context *ctx; |
894 | + |
895 | + use_default_keystore_dirs = true; |
896 | + ctx = talloc_zero(NULL, struct sync_context); |
897 | + list_head_init(&ctx->new_keys); |
898 | + |
899 | + for (;;) { |
900 | + int idx, c; |
901 | + c = getopt_long(argc, argv, "e:dpkvhV", options, &idx); |
902 | + if (c == -1) |
903 | + break; |
904 | + |
905 | + switch (c) { |
906 | + case 'e': |
907 | + ctx->efivars_dir = optarg; |
908 | + break; |
909 | + case 'd': |
910 | + use_default_keystore_dirs = false; |
911 | + break; |
912 | + case 'k': |
913 | + add_keystore_dir(ctx, optarg); |
914 | + break; |
915 | + case 'p': |
916 | + ctx->set_pk = true; |
917 | + break; |
918 | + case 'v': |
919 | + ctx->verbose = true; |
920 | + break; |
921 | + case 'n': |
922 | + ctx->dry_run = true; |
923 | + break; |
924 | + case 'V': |
925 | + version(); |
926 | + return EXIT_SUCCESS; |
927 | + case 'h': |
928 | + usage(); |
929 | + return EXIT_SUCCESS; |
930 | + } |
931 | + } |
932 | + |
933 | + if (argc != optind) { |
934 | + usage(); |
935 | + return EXIT_FAILURE; |
936 | + } |
937 | + |
938 | + ERR_load_crypto_strings(); |
939 | + OpenSSL_add_all_digests(); |
940 | + OpenSSL_add_all_ciphers(); |
941 | + |
942 | + ctx->filesystem_keys = init_keyset(ctx); |
943 | + ctx->firmware_keys = init_keyset(ctx); |
944 | + |
945 | + if (!ctx->efivars_dir) { |
946 | + ctx->efivars_dir = EFIVARS_MOUNTPOINT; |
947 | + if (check_efivars_mount(ctx->efivars_dir)) { |
948 | + fprintf(stderr, "Can't access efivars filesystem " |
949 | + "at %s, aborting\n", ctx->efivars_dir); |
950 | + return EXIT_FAILURE; |
951 | + } |
952 | + } |
953 | + |
954 | + if (use_default_keystore_dirs) { |
955 | + unsigned int i; |
956 | + for (i = 0; i < ARRAY_SIZE(default_keystore_dirs); i++) |
957 | + add_keystore_dir(ctx, default_keystore_dirs[i]); |
958 | + } |
959 | + |
960 | + |
961 | + read_keystore(ctx); |
962 | + |
963 | + if (ctx->verbose) |
964 | + print_keystore(ctx->fs_keystore); |
965 | + |
966 | + read_keysets(ctx); |
967 | + if (ctx->verbose) { |
968 | + print_keyset(ctx->firmware_keys, "firmware"); |
969 | + print_keyset(ctx->filesystem_keys, "filesystem"); |
970 | + } |
971 | + |
972 | + if (check_pk(ctx)) |
973 | + fprintf(stderr, "WARNING: multiple PKs found in filesystem\n"); |
974 | + |
975 | + find_new_keys(ctx); |
976 | + |
977 | + if (ctx->verbose) |
978 | + print_new_keys(ctx); |
979 | + |
980 | + if (!ctx->dry_run) |
981 | + insert_new_keys(ctx); |
982 | + |
983 | + talloc_free(ctx); |
984 | + |
985 | + return EXIT_SUCCESS; |
986 | +} |
987 | |
988 | === modified file '.pc/applied-patches' |
989 | --- .pc/applied-patches 2013-10-04 01:43:03 +0000 |
990 | +++ .pc/applied-patches 2013-12-03 15:04:24 +0000 |
991 | @@ -3,3 +3,4 @@ |
992 | update_checksums.patch |
993 | fix-signature-padding.patch |
994 | ignore-certificate-expiries.patch |
995 | +add_corrected_efivars_magic.patch |
996 | |
997 | === modified file 'debian/changelog' |
998 | --- debian/changelog 2013-10-04 01:43:03 +0000 |
999 | +++ debian/changelog 2013-12-03 15:04:24 +0000 |
1000 | @@ -1,3 +1,10 @@ |
1001 | +sbsigntool (0.6-0ubuntu6) UNRELEASED; urgency=low |
1002 | + |
1003 | + * debian/patches/add_corrected_efivars_magic.patch: Cherry-picked upstream |
1004 | + fix to add corrected efivars magic (LP: #1257305) |
1005 | + |
1006 | + -- Jean-Baptiste Lallement <jean-baptiste.lallement@ubuntu.com> Tue, 03 Dec 2013 15:50:45 +0100 |
1007 | + |
1008 | sbsigntool (0.6-0ubuntu5) saucy; urgency=low |
1009 | |
1010 | * debian/patches/ignore-certificate-expiries.patch: ignore certificate |
1011 | |
1012 | === added file 'debian/patches/add_corrected_efivars_magic.patch' |
1013 | --- debian/patches/add_corrected_efivars_magic.patch 1970-01-01 00:00:00 +0000 |
1014 | +++ debian/patches/add_corrected_efivars_magic.patch 2013-12-03 15:04:24 +0000 |
1015 | @@ -0,0 +1,23 @@ |
1016 | +Index: sbsigntool/src/sbkeysync.c |
1017 | +=================================================================== |
1018 | +--- sbsigntool.orig/src/sbkeysync.c 2013-12-03 15:45:49.007312000 +0100 |
1019 | ++++ sbsigntool/src/sbkeysync.c 2013-12-03 15:47:47.396135699 +0100 |
1020 | +@@ -56,7 +56,8 @@ |
1021 | + #include "efivars.h" |
1022 | + |
1023 | + #define EFIVARS_MOUNTPOINT "/sys/firmware/efi/efivars" |
1024 | +-#define EFIVARS_FSTYPE 0x6165676C |
1025 | ++#define PSTORE_FSTYPE 0x6165676C |
1026 | ++#define EFIVARS_FSTYPE 0xde5e81e4 |
1027 | + |
1028 | + #define EFI_IMAGE_SECURITY_DATABASE_GUID \ |
1029 | + { 0xd719b2cb, 0x3d3a, 0x4596, \ |
1030 | +@@ -533,7 +534,7 @@ |
1031 | + if (rc) |
1032 | + return -1; |
1033 | + |
1034 | +- if (statbuf.f_type != EFIVARS_FSTYPE) |
1035 | ++ if (statbuf.f_type != EFIVARS_FSTYPE && statbuf.f_type != PSTORE_FSTYPE) |
1036 | + return -1; |
1037 | + |
1038 | + return 0; |
1039 | |
1040 | === modified file 'debian/patches/series' |
1041 | --- debian/patches/series 2013-10-04 01:43:03 +0000 |
1042 | +++ debian/patches/series 2013-12-03 15:04:24 +0000 |
1043 | @@ -3,3 +3,4 @@ |
1044 | update_checksums.patch |
1045 | fix-signature-padding.patch |
1046 | ignore-certificate-expiries.patch |
1047 | +add_corrected_efivars_magic.patch |
1048 | |
1049 | === modified file 'src/sbkeysync.c' |
1050 | --- src/sbkeysync.c 2012-10-12 00:19:26 +0000 |
1051 | +++ src/sbkeysync.c 2013-12-03 15:04:24 +0000 |
1052 | @@ -56,7 +56,8 @@ |
1053 | #include "efivars.h" |
1054 | |
1055 | #define EFIVARS_MOUNTPOINT "/sys/firmware/efi/efivars" |
1056 | -#define EFIVARS_FSTYPE 0x6165676C |
1057 | +#define PSTORE_FSTYPE 0x6165676C |
1058 | +#define EFIVARS_FSTYPE 0xde5e81e4 |
1059 | |
1060 | #define EFI_IMAGE_SECURITY_DATABASE_GUID \ |
1061 | { 0xd719b2cb, 0x3d3a, 0x4596, \ |
1062 | @@ -533,7 +534,7 @@ |
1063 | if (rc) |
1064 | return -1; |
1065 | |
1066 | - if (statbuf.f_type != EFIVARS_FSTYPE) |
1067 | + if (statbuf.f_type != EFIVARS_FSTYPE && statbuf.f_type != PSTORE_FSTYPE) |
1068 | return -1; |
1069 | |
1070 | return 0; |
Looks good! I uploaded to trusty.