1// 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 20struct 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 27enum 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/** 76 * 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/** 98 * 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; 109 bool null_key = false; 110 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 175 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 182 attr = EFI_VARIABLE_NON_VOLATILE | 183 EFI_VARIABLE_BOOTSERVICE_ACCESS | 184 EFI_VARIABLE_RUNTIME_ACCESS | 185 EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; 186 187 /* 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) { 193 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 220/** 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, eficonfig_menu_desc, 414 eficonfig_display_statusline, 415 eficonfig_print_entry, 416 eficonfig_choice_entry); 417out: 418 list_for_each_safe(pos, n, &efi_menu->list) { 419 entry = list_entry(pos, struct eficonfig_entry, list); 420 free(entry->data); 421 } 422 eficonfig_destroy(efi_menu); 423 free(db); 424 425 return ret; 426} 427 428/** 429 * eficonfig_process_show_signature_database() - process show signature database 430 * 431 * @data: pointer to the data for each entry 432 * Return: status code 433 */ 434static efi_status_t eficonfig_process_show_signature_database(void *data) 435{ 436 efi_status_t ret; 437 438 while (1) { 439 ret = enumerate_and_show_signature_database(data); 440 if (ret != EFI_SUCCESS && ret != EFI_NOT_READY) 441 break; 442 } 443 444 /* return to the parent menu */ 445 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; 446 447 return ret; 448} 449 450static struct eficonfig_item key_config_menu_items[] = { 451 {"Enroll New Key", eficonfig_process_enroll_key}, 452 {"Show Signature Database", eficonfig_process_show_signature_database}, 453 {"Quit", eficonfig_process_quit}, 454}; 455 456/** 457 * eficonfig_process_set_secure_boot_key() - display the key configuration menu 458 * 459 * @data: pointer to the data for each entry 460 * Return: status code 461 */ 462static efi_status_t eficonfig_process_set_secure_boot_key(void *data) 463{ 464 u32 i; 465 efi_status_t ret; 466 char header_str[32]; 467 struct efimenu *efi_menu; 468 469 for (i = 0; i < ARRAY_SIZE(key_config_menu_items); i++) 470 key_config_menu_items[i].data = data; 471 472 snprintf(header_str, sizeof(header_str), " ** Configure %ls **", (u16 *)data); 473 474 while (1) { 475 efi_menu = eficonfig_create_fixed_menu(key_config_menu_items, 476 ARRAY_SIZE(key_config_menu_items)); 477 478 ret = eficonfig_process_common(efi_menu, header_str, 479 eficonfig_menu_desc, 480 eficonfig_display_statusline, 481 eficonfig_print_entry, 482 eficonfig_choice_entry); 483 eficonfig_destroy(efi_menu); 484 485 if (ret == EFI_ABORTED) 486 break; 487 } 488 489 /* return to the parent menu */ 490 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; 491 492 return ret; 493} 494 495static const struct eficonfig_item secure_boot_menu_items[] = { 496 {"PK", eficonfig_process_set_secure_boot_key, u"PK"}, 497 {"KEK", eficonfig_process_set_secure_boot_key, u"KEK"}, 498 {"db", eficonfig_process_set_secure_boot_key, u"db"}, 499 {"dbx", eficonfig_process_set_secure_boot_key, u"dbx"}, 500 {"Quit", eficonfig_process_quit}, 501}; 502 503/** 504 * eficonfig_process_secure_boot_config() - display the key list menu 505 * 506 * @data: pointer to the data for each entry 507 * Return: status code 508 */ 509efi_status_t eficonfig_process_secure_boot_config(void *data) 510{ 511 efi_status_t ret; 512 struct efimenu *efi_menu; 513 514 while (1) { 515 char header_str[64]; 516 517 snprintf(header_str, sizeof(header_str), 518 " ** UEFI Secure Boot Key Configuration (SecureBoot : %s) **", 519 (efi_secure_boot_enabled() ? "ON" : "OFF")); 520 521 efi_menu = eficonfig_create_fixed_menu(secure_boot_menu_items, 522 ARRAY_SIZE(secure_boot_menu_items)); 523 if (!efi_menu) { 524 ret = EFI_OUT_OF_RESOURCES; 525 break; 526 } 527 528 ret = eficonfig_process_common(efi_menu, header_str, 529 eficonfig_menu_desc, 530 eficonfig_display_statusline, 531 eficonfig_print_entry, 532 eficonfig_choice_entry); 533 534 eficonfig_destroy(efi_menu); 535 536 if (ret == EFI_ABORTED) 537 break; 538 } 539 540 /* return to the parent menu */ 541 ret = (ret == EFI_ABORTED) ? EFI_NOT_READY : ret; 542 543 return ret; 544} 545