blob: ed39aab0817f20bb7ad80a69f3d43d6eed4b6bea [file] [log] [blame]
Masahisa Kojimac3b5af62022-11-20 09:21:18 +09001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Menu-driven UEFI Secure Boot Key Maintenance
4 *
5 * Copyright (c) 2022 Masahisa Kojima, Linaro Limited
6 */
7
8#include <ansi.h>
9#include <common.h>
10#include <charset.h>
11#include <hexdump.h>
12#include <log.h>
13#include <malloc.h>
14#include <menu.h>
15#include <efi_loader.h>
16#include <efi_config.h>
17#include <efi_variable.h>
18#include <crypto/pkcs7_parser.h>
19
Masahisa Kojimad0f9ae32022-11-20 09:21:19 +090020struct eficonfig_sig_data {
21 struct efi_signature_list *esl;
22 struct efi_signature_data *esd;
23 struct list_head list;
24 u16 *varname;
25};
26
Masahisa Kojimac3b5af62022-11-20 09:21:18 +090027enum efi_sbkey_signature_type {
28 SIG_TYPE_X509 = 0,
29 SIG_TYPE_HASH,
30 SIG_TYPE_CRL,
31 SIG_TYPE_RSA2048,
32};
33
34struct eficonfig_sigtype_to_str {
35 efi_guid_t sig_type;
36 char *str;
37 enum efi_sbkey_signature_type type;
38};
39
40static const struct eficonfig_sigtype_to_str sigtype_to_str[] = {
41 {EFI_CERT_X509_GUID, "X509", SIG_TYPE_X509},
42 {EFI_CERT_SHA256_GUID, "SHA256", SIG_TYPE_HASH},
43 {EFI_CERT_X509_SHA256_GUID, "X509_SHA256 CRL", SIG_TYPE_CRL},
44 {EFI_CERT_X509_SHA384_GUID, "X509_SHA384 CRL", SIG_TYPE_CRL},
45 {EFI_CERT_X509_SHA512_GUID, "X509_SHA512 CRL", SIG_TYPE_CRL},
46 /* U-Boot does not support the following signature types */
47/* {EFI_CERT_RSA2048_GUID, "RSA2048", SIG_TYPE_RSA2048}, */
48/* {EFI_CERT_RSA2048_SHA256_GUID, "RSA2048_SHA256", SIG_TYPE_RSA2048}, */
49/* {EFI_CERT_SHA1_GUID, "SHA1", SIG_TYPE_HASH}, */
50/* {EFI_CERT_RSA2048_SHA_GUID, "RSA2048_SHA", SIG_TYPE_RSA2048 }, */
51/* {EFI_CERT_SHA224_GUID, "SHA224", SIG_TYPE_HASH}, */
52/* {EFI_CERT_SHA384_GUID, "SHA384", SIG_TYPE_HASH}, */
53/* {EFI_CERT_SHA512_GUID, "SHA512", SIG_TYPE_HASH}, */
54};
55
56/**
57 * file_have_auth_header() - check file has EFI_VARIABLE_AUTHENTICATION_2 header
58 * @buf: pointer to file
59 * @size: file size
60 * Return: true if file has auth header, false otherwise
61 */
62static bool file_have_auth_header(void *buf, efi_uintn_t size)
63{
64 struct efi_variable_authentication_2 *auth = buf;
65
66 if (auth->auth_info.hdr.wCertificateType != WIN_CERT_TYPE_EFI_GUID)
67 return false;
68
69 if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7))
70 return false;
71
72 return true;
73}
74
75/**
Masahisa Kojimaad50ca52022-12-20 19:38:52 +090076 * file_is_null_key() - check the file is an authenticated and signed null key
77 *
78 * @auth: pointer to the file
79 * @size: file size
80 * @null_key: pointer to store the result
81 * Return: status code
82 */
83static efi_status_t file_is_null_key(struct efi_variable_authentication_2 *auth,
84 efi_uintn_t size, bool *null_key)
85{
86 efi_uintn_t auth_size =
87 sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength;
88
89 if (size < auth_size)
90 return EFI_INVALID_PARAMETER;
91
92 *null_key = (size == auth_size);
93
94 return EFI_SUCCESS;
95}
96
97/**
Masahisa Kojimac3b5af62022-11-20 09:21:18 +090098 * eficonfig_process_enroll_key() - enroll key into signature database
99 *
100 * @data: pointer to the data for each entry
101 * Return: status code
102 */
103static efi_status_t eficonfig_process_enroll_key(void *data)
104{
105 u32 attr;
106 char *buf = NULL;
107 efi_uintn_t size;
108 efi_status_t ret;
Masahisa Kojimaad50ca52022-12-20 19:38:52 +0900109 bool null_key = false;
Masahisa Kojimac3b5af62022-11-20 09:21:18 +0900110 struct efi_file_handle *f = NULL;
111 struct efi_device_path *full_dp = NULL;
112 struct eficonfig_select_file_info file_info;
113
114 file_info.current_path = calloc(1, EFICONFIG_FILE_PATH_BUF_SIZE);
115 if (!file_info.current_path) {
116 ret = EFI_OUT_OF_RESOURCES;
117 goto out;
118 }
119
120 ret = eficonfig_process_select_file(&file_info);
121 if (ret != EFI_SUCCESS)
122 goto out;
123
124 full_dp = eficonfig_create_device_path(file_info.dp_volume, file_info.current_path);
125 if (!full_dp) {
126 ret = EFI_OUT_OF_RESOURCES;
127 goto out;
128 }
129 f = efi_file_from_path(full_dp);
130 if (!f) {
131 ret = EFI_NOT_FOUND;
132 goto out;
133 }
134
135 size = 0;
136 ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, NULL));
137 if (ret != EFI_BUFFER_TOO_SMALL)
138 goto out;
139
140 buf = malloc(size);
141 if (!buf) {
142 ret = EFI_OUT_OF_RESOURCES;
143 goto out;
144 }
145 ret = EFI_CALL(f->getinfo(f, &efi_file_info_guid, &size, buf));
146 if (ret != EFI_SUCCESS)
147 goto out;
148
149 size = ((struct efi_file_info *)buf)->file_size;
150 free(buf);
151
152 if (!size) {
153 eficonfig_print_msg("ERROR! File is empty.");
154 ret = EFI_INVALID_PARAMETER;
155 goto out;
156 }
157
158 buf = malloc(size);
159 if (!buf) {
160 ret = EFI_OUT_OF_RESOURCES;
161 goto out;
162 }
163
164 ret = EFI_CALL(f->read(f, &size, buf));
165 if (ret != EFI_SUCCESS) {
166 eficonfig_print_msg("ERROR! Failed to read file.");
167 goto out;
168 }
169 if (!file_have_auth_header(buf, size)) {
170 eficonfig_print_msg("ERROR! Invalid file format. Only .auth variables is allowed.");
171 ret = EFI_INVALID_PARAMETER;
172 goto out;
173 }
174
Masahisa Kojimaad50ca52022-12-20 19:38:52 +0900175 ret = file_is_null_key((struct efi_variable_authentication_2 *)buf,
176 size, &null_key);
177 if (ret != EFI_SUCCESS) {
178 eficonfig_print_msg("ERROR! Invalid file format.");
179 goto out;
180 }
181
Masahisa Kojimac3b5af62022-11-20 09:21:18 +0900182 attr = EFI_VARIABLE_NON_VOLATILE |
183 EFI_VARIABLE_BOOTSERVICE_ACCESS |
184 EFI_VARIABLE_RUNTIME_ACCESS |
185 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS;
186
Masahisa Kojimaad50ca52022-12-20 19:38:52 +0900187 /*
188 * PK can enroll only one certificate.
189 * The signed null key is used to clear KEK, db and dbx.
190 * EFI_VARIABLE_APPEND_WRITE attribute must not be set in these cases.
191 */
192 if (u16_strcmp(data, u"PK") && !null_key) {
Masahisa Kojimac3b5af62022-11-20 09:21:18 +0900193 efi_uintn_t db_size = 0;
194
195 /* check the variable exists. If exists, add APPEND_WRITE attribute */
196 ret = efi_get_variable_int(data, efi_auth_var_get_guid(data), NULL,
197 &db_size, NULL, NULL);
198 if (ret == EFI_BUFFER_TOO_SMALL)
199 attr |= EFI_VARIABLE_APPEND_WRITE;
200 }
201
202 ret = efi_set_variable_int((u16 *)data, efi_auth_var_get_guid((u16 *)data),
203 attr, size, buf, false);
204 if (ret != EFI_SUCCESS)
205 eficonfig_print_msg("ERROR! Failed to update signature database");
206
207out:
208 free(file_info.current_path);
209 free(buf);
210 efi_free_pool(full_dp);
211 if (f)
212 EFI_CALL(f->close(f));
213
214 /* return to the parent menu */
215 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
216
217 return ret;
218}
219
Masahisa Kojimad0f9ae32022-11-20 09:21:19 +0900220/**
221 * eficonfig_process_show_siglist() - show signature list content
222 *
223 * @data: pointer to the data for each entry
224 * Return: status code
225 */
226static efi_status_t eficonfig_process_show_siglist(void *data)
227{
228 u32 i;
229 struct eficonfig_sig_data *sg = data;
230
231 puts(ANSI_CURSOR_HIDE);
232 puts(ANSI_CLEAR_CONSOLE);
233 printf(ANSI_CURSOR_POSITION, 1, 1);
234
235 printf("\n ** Show Signature Database (%ls) **\n\n"
236 " Owner GUID:\n"
237 " %pUL\n",
238 sg->varname, sg->esd->signature_owner.b);
239
240 for (i = 0; i < ARRAY_SIZE(sigtype_to_str); i++) {
241 if (!guidcmp(&sg->esl->signature_type, &sigtype_to_str[i].sig_type)) {
242 printf(" Signature Type:\n"
243 " %s\n", sigtype_to_str[i].str);
244
245 switch (sigtype_to_str[i].type) {
246 case SIG_TYPE_X509:
247 {
248 struct x509_certificate *cert_tmp;
249
250 cert_tmp = x509_cert_parse(sg->esd->signature_data,
251 sg->esl->signature_size);
252 printf(" Subject:\n"
253 " %s\n"
254 " Issuer:\n"
255 " %s\n",
256 cert_tmp->subject, cert_tmp->issuer);
257 break;
258 }
259 case SIG_TYPE_CRL:
260 {
261 u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t) -
262 sizeof(struct efi_time);
263 struct efi_time *time =
264 (struct efi_time *)((u8 *)sg->esd->signature_data +
265 hash_size);
266
267 printf(" ToBeSignedHash:\n");
268 print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1,
269 sg->esd->signature_data, hash_size, false);
270 printf(" TimeOfRevocation:\n"
271 " %d-%d-%d %02d:%02d:%02d\n",
272 time->year, time->month, time->day,
273 time->hour, time->minute, time->second);
274 break;
275 }
276 case SIG_TYPE_HASH:
277 {
278 u32 hash_size = sg->esl->signature_size - sizeof(efi_guid_t);
279
280 printf(" Hash:\n");
281 print_hex_dump(" ", DUMP_PREFIX_NONE, 16, 1,
282 sg->esd->signature_data, hash_size, false);
283 break;
284 }
285 default:
286 eficonfig_print_msg("ERROR! Unsupported format.");
287 return EFI_INVALID_PARAMETER;
288 }
289 }
290 }
291
292 while (tstc())
293 getchar();
294
295 printf("\n\n Press any key to continue");
296 getchar();
297
298 return EFI_SUCCESS;
299}
300
301/**
302 * prepare_signature_list_menu() - create the signature list menu entry
303 *
304 * @efimenu: pointer to the efimenu structure
305 * @varname: pointer to the variable name
306 * @db: pointer to the variable raw data
307 * @db_size: variable data size
308 * @func: callback of each entry
309 * Return: status code
310 */
311static efi_status_t prepare_signature_list_menu(struct efimenu *efi_menu, void *varname,
312 void *db, efi_uintn_t db_size,
313 eficonfig_entry_func func)
314{
315 u32 num = 0;
316 efi_uintn_t size;
317 struct eficonfig_sig_data *sg;
318 struct efi_signature_list *esl;
319 struct efi_signature_data *esd;
320 efi_status_t ret = EFI_SUCCESS;
321
322 INIT_LIST_HEAD(&efi_menu->list);
323
324 esl = db;
325 size = db_size;
326 while (size > 0) {
327 u32 remain;
328
329 esd = (struct efi_signature_data *)((u8 *)esl +
330 (sizeof(struct efi_signature_list) +
331 esl->signature_header_size));
332 remain = esl->signature_list_size - sizeof(struct efi_signature_list) -
333 esl->signature_header_size;
334 for (; remain > 0; remain -= esl->signature_size) {
335 char buf[37];
336 char *title;
337
338 if (num >= EFICONFIG_ENTRY_NUM_MAX - 1) {
339 ret = EFI_OUT_OF_RESOURCES;
340 goto out;
341 }
342
343 sg = calloc(1, sizeof(struct eficonfig_sig_data));
344 if (!sg) {
345 ret = EFI_OUT_OF_RESOURCES;
346 goto err;
347 }
348
349 snprintf(buf, sizeof(buf), "%pUL", &esd->signature_owner);
350 title = strdup(buf);
351 if (!title) {
352 free(sg);
353 ret = EFI_OUT_OF_RESOURCES;
354 goto err;
355 }
356
357 sg->esl = esl;
358 sg->esd = esd;
359 sg->varname = varname;
360 ret = eficonfig_append_menu_entry(efi_menu, title, func, sg);
361 if (ret != EFI_SUCCESS) {
362 free(sg);
363 free(title);
364 goto err;
365 }
366 esd = (struct efi_signature_data *)((u8 *)esd + esl->signature_size);
367 num++;
368 }
369
370 size -= esl->signature_list_size;
371 esl = (struct efi_signature_list *)((u8 *)esl + esl->signature_list_size);
372 }
373out:
374 ret = eficonfig_append_quit_entry(efi_menu);
375err:
376 return ret;
377}
378
379/**
380 * enumerate_and_show_signature_database() - enumerate and show the signature database
381 *
382 * @data: pointer to the data for each entry
383 * Return: status code
384 */
385static efi_status_t enumerate_and_show_signature_database(void *varname)
386{
387 void *db;
388 char buf[50];
389 efi_status_t ret;
390 efi_uintn_t db_size;
391 struct efimenu *efi_menu;
392 struct list_head *pos, *n;
393 struct eficonfig_entry *entry;
394
395 db = efi_get_var(varname, efi_auth_var_get_guid(varname), &db_size);
396 if (!db) {
397 eficonfig_print_msg("There is no entry in the signature database.");
398 return EFI_NOT_FOUND;
399 }
400
401 efi_menu = calloc(1, sizeof(struct efimenu));
402 if (!efi_menu) {
403 free(db);
404 return EFI_OUT_OF_RESOURCES;
405 }
406
407 ret = prepare_signature_list_menu(efi_menu, varname, db, db_size,
408 eficonfig_process_show_siglist);
409 if (ret != EFI_SUCCESS)
410 goto out;
411
412 snprintf(buf, sizeof(buf), " ** Show Signature Database (%ls) **", (u16 *)varname);
413 ret = eficonfig_process_common(efi_menu, buf);
414out:
415 list_for_each_safe(pos, n, &efi_menu->list) {
416 entry = list_entry(pos, struct eficonfig_entry, list);
417 free(entry->data);
418 }
419 eficonfig_destroy(efi_menu);
420 free(db);
421
422 return ret;
423}
424
425/**
426 * eficonfig_process_show_signature_database() - process show signature database
427 *
428 * @data: pointer to the data for each entry
429 * Return: status code
430 */
431static efi_status_t eficonfig_process_show_signature_database(void *data)
432{
433 efi_status_t ret;
434
435 while (1) {
436 ret = enumerate_and_show_signature_database(data);
437 if (ret != EFI_SUCCESS && ret != EFI_NOT_READY)
438 break;
439 }
440
441 /* return to the parent menu */
442 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
443
444 return ret;
445}
446
Masahisa Kojimac3b5af62022-11-20 09:21:18 +0900447static struct eficonfig_item key_config_menu_items[] = {
448 {"Enroll New Key", eficonfig_process_enroll_key},
Masahisa Kojimad0f9ae32022-11-20 09:21:19 +0900449 {"Show Signature Database", eficonfig_process_show_signature_database},
Masahisa Kojimac3b5af62022-11-20 09:21:18 +0900450 {"Quit", eficonfig_process_quit},
451};
452
453/**
454 * eficonfig_process_set_secure_boot_key() - display the key configuration menu
455 *
456 * @data: pointer to the data for each entry
457 * Return: status code
458 */
459static efi_status_t eficonfig_process_set_secure_boot_key(void *data)
460{
461 u32 i;
462 efi_status_t ret;
463 char header_str[32];
464 struct efimenu *efi_menu;
465
466 for (i = 0; i < ARRAY_SIZE(key_config_menu_items); i++)
467 key_config_menu_items[i].data = data;
468
469 snprintf(header_str, sizeof(header_str), " ** Configure %ls **", (u16 *)data);
470
471 while (1) {
472 efi_menu = eficonfig_create_fixed_menu(key_config_menu_items,
473 ARRAY_SIZE(key_config_menu_items));
474
475 ret = eficonfig_process_common(efi_menu, header_str);
476 eficonfig_destroy(efi_menu);
477
478 if (ret == EFI_ABORTED)
479 break;
480 }
481
482 /* return to the parent menu */
483 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
484
485 return ret;
486}
487
488static const struct eficonfig_item secure_boot_menu_items[] = {
489 {"PK", eficonfig_process_set_secure_boot_key, u"PK"},
490 {"KEK", eficonfig_process_set_secure_boot_key, u"KEK"},
491 {"db", eficonfig_process_set_secure_boot_key, u"db"},
492 {"dbx", eficonfig_process_set_secure_boot_key, u"dbx"},
493 {"Quit", eficonfig_process_quit},
494};
495
496/**
497 * eficonfig_process_secure_boot_config() - display the key list menu
498 *
499 * @data: pointer to the data for each entry
500 * Return: status code
501 */
502efi_status_t eficonfig_process_secure_boot_config(void *data)
503{
504 efi_status_t ret;
505 struct efimenu *efi_menu;
506
507 while (1) {
508 char header_str[64];
509
510 snprintf(header_str, sizeof(header_str),
511 " ** UEFI Secure Boot Key Configuration (SecureBoot : %s) **",
512 (efi_secure_boot_enabled() ? "ON" : "OFF"));
513
514 efi_menu = eficonfig_create_fixed_menu(secure_boot_menu_items,
515 ARRAY_SIZE(secure_boot_menu_items));
516 if (!efi_menu) {
517 ret = EFI_OUT_OF_RESOURCES;
518 break;
519 }
520
521 ret = eficonfig_process_common(efi_menu, header_str);
522 eficonfig_destroy(efi_menu);
523
524 if (ret == EFI_ABORTED)
525 break;
526 }
527
528 /* return to the parent menu */
529 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret;
530
531 return ret;
532}