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