From 20f6cde6799c899a893b12d85bdf3b3c1444ef2e Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 1 Nov 2012 15:26:36 -0400 Subject: [PATCH] Add comments --- shim.c | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 152 insertions(+), 6 deletions(-) diff --git a/shim.c b/shim.c index c038d8e..2a65627 100644 --- a/shim.c +++ b/shim.c @@ -275,6 +275,9 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid, return rc; } +/* + * Check a hash against an EFI_SIGNATURE_LIST in a buffer + */ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, UINTN dbsize, UINT8 *data, int SignatureSize, EFI_GUID CertType) @@ -313,6 +316,9 @@ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList, return DATA_NOT_FOUND; } +/* + * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable + */ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, int SignatureSize, EFI_GUID CertType) { @@ -337,6 +343,10 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data, } +/* + * Check whether the binary signature or hash are present in dbx or the + * built-in blacklist + */ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *sha256hash, UINT8 *sha1hash) { @@ -366,6 +376,9 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, return EFI_SUCCESS; } +/* + * Check whether the binary signature or hash are present in db or MokList + */ static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *sha256hash, UINT8 *sha1hash) { @@ -595,6 +608,9 @@ done: return status; } +/* + * Ensure that the MOK database hasn't been set or modified from an OS + */ static EFI_STATUS verify_mok (void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS status = EFI_SUCCESS; @@ -652,8 +668,14 @@ static EFI_STATUS verify_buffer (char *data, int datasize, if (status != EFI_SUCCESS) return status; + /* + * Check that the MOK database hasn't been modified + */ verify_mok(); + /* + * Ensure that the binary isn't blacklisted + */ status = check_blacklist(cert, sha256hash, sha1hash); if (status != EFI_SUCCESS) { @@ -661,6 +683,10 @@ static EFI_STATUS verify_buffer (char *data, int datasize, return status; } + /* + * Check whether the binary is whitelisted in any of the firmware + * databases + */ status = check_whitelist(cert, sha256hash, sha1hash); if (status == EFI_SUCCESS) { @@ -668,6 +694,9 @@ static EFI_STATUS verify_buffer (char *data, int datasize, return status; } + /* + * And finally, check against shim's built-in key + */ if (AuthenticodeVerify(cert->CertData, context->SecDir->Size - sizeof(cert->Hdr), vendor_cert, vendor_cert_size, sha256hash, @@ -762,12 +791,18 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, char *base, *end; PE_COFF_LOADER_IMAGE_CONTEXT context; + /* + * The binary header contains relevant context and section pointers + */ efi_status = read_header(data, datasize, &context); if (efi_status != EFI_SUCCESS) { Print(L"Failed to read header\n"); return efi_status; } + /* + * We only need to verify the binary if we're in secure mode + */ if (secure_mode ()) { efi_status = verify_buffer(data, datasize, &context); @@ -786,6 +821,9 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, CopyMem(buffer, data, context.SizeOfHeaders); + /* + * Copy the executable's sections to their desired offsets + */ Section = context.FirstSection; for (i = 0; i < context.NumberOfSections; i++) { size = Section->Misc.VirtualSize; @@ -810,6 +848,9 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, Section += 1; } + /* + * Run the relocation fixups + */ efi_status = relocate_coff(&context, buffer); if (efi_status != EFI_SUCCESS) { @@ -819,6 +860,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, } entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint); + /* + * grub needs to know its location and size in memory, so fix up + * the loaded image protocol values + */ li->ImageBase = buffer; li->ImageSize = context.ImageSize; @@ -831,6 +876,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, return EFI_SUCCESS; } +/* + * Generate the path of an executable given shim's path and the name + * of the executable + */ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, EFI_DEVICE_PATH **grubpath, CHAR16 **PathName) { @@ -877,7 +926,7 @@ error: } /* - * Locate the second stage bootloader and read it into a buffer + * Open the second stage bootloader and read it into a buffer */ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, int *datasize, CHAR16 *PathName) @@ -893,6 +942,9 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, device = li->DeviceHandle; + /* + * Open the device + */ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, &simple_file_system_protocol, (void **)&drive); @@ -909,6 +961,9 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, goto error; } + /* + * And then open the file + */ efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, EFI_FILE_MODE_READ, 0); @@ -925,6 +980,10 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, goto error; } + /* + * Find out how big the file is in order to allocate the storage + * buffer + */ efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id, &buffersize, fileinfo); @@ -955,6 +1014,10 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data, efi_status = EFI_OUT_OF_RESOURCES; goto error; } + + /* + * Perform the actual read + */ efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, *data); @@ -986,6 +1049,10 @@ error: return efi_status; } +/* + * Protocol entry point. If secure boot is enabled, verify that the provided + * buffer is signed with a trusted key. + */ EFI_STATUS shim_verify (void *buffer, UINT32 size) { EFI_STATUS status; @@ -1004,6 +1071,9 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size) return status; } +/* + * Load and run an EFI executable + */ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) { EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; @@ -1014,6 +1084,10 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) void *data = NULL; int datasize; + /* + * We need to refer to the loaded image protocol on the running + * binary in order to find our path + */ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &loaded_image_protocol, (void **)&li); @@ -1022,6 +1096,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) return efi_status; } + /* + * Build a new path from the existing one plus the executable name + */ efi_status = generate_path(li, ImagePath, &path, &PathName); if (efi_status != EFI_SUCCESS) { @@ -1029,6 +1106,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto done; } + /* + * Read the new executable off disk + */ efi_status = load_image(li, &data, &datasize, PathName); if (efi_status != EFI_SUCCESS) { @@ -1036,8 +1116,15 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto done; } + /* + * We need to modify the loaded image protocol entry before running + * the new binary, so back it up + */ CopyMem(&li_bak, li, sizeof(li_bak)); + /* + * Verify and, if appropriate, relocate and execute the executable + */ efi_status = handle_image(data, datasize, li); if (efi_status != EFI_SUCCESS) { @@ -1046,8 +1133,14 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) goto done; } + /* + * The binary is trusted and relocated. Run it + */ efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab); + /* + * Restore our original loaded image values + */ CopyMem(li, &li_bak, sizeof(li_bak)); done: if (PathName) @@ -1059,6 +1152,10 @@ done: return efi_status; } +/* + * Load and run grub. If that fails because grub isn't trusted, load and + * run MokManager. + */ EFI_STATUS init_grub(EFI_HANDLE image_handle) { EFI_STATUS efi_status; @@ -1076,6 +1173,10 @@ done: return efi_status; } +/* + * Copy the boot-services only MokList variable to the runtime-accessible + * MokListRT variable. It's not marked NV, so the OS can't modify it. + */ EFI_STATUS mirror_mok_list() { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -1104,6 +1205,9 @@ done: return efi_status; } +/* + * Check if a variable exists + */ static BOOLEAN check_var(CHAR16 *varname) { EFI_STATUS efi_status; @@ -1122,6 +1226,10 @@ static BOOLEAN check_var(CHAR16 *varname) return FALSE; } +/* + * If the OS has set any of these variables we need to drop into MOK and + * handle them appropriately + */ EFI_STATUS check_mok_request(EFI_HANDLE image_handle) { EFI_STATUS efi_status; @@ -1139,6 +1247,10 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle) return EFI_SUCCESS; } +/* + * Verify that MokSBState is valid, and if appropriate set insecure mode + */ + static EFI_STATUS check_mok_sb (void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -1153,6 +1265,10 @@ static EFI_STATUS check_mok_sb (void) if (status != EFI_SUCCESS) return EFI_ACCESS_DENIED; + /* + * Delete and ignore the variable if it's been set from or could be + * modified by the OS + */ if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { Print(L"MokSBState is compromised! Clearing it\n"); if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) { @@ -1168,7 +1284,6 @@ static EFI_STATUS check_mok_sb (void) return status; } - EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -1176,31 +1291,62 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) EFI_HANDLE handle = NULL; EFI_STATUS efi_status; + /* + * Set up the shim lock protocol so that grub and MokManager can + * call back in and use shim functions + */ shim_lock_interface.Verify = shim_verify; shim_lock_interface.Hash = generate_hash; shim_lock_interface.Context = read_header; systab = passed_systab; + /* + * Ensure that gnu-efi functions are available + */ InitializeLib(image_handle, systab); + /* + * Check whether the user has configured the system to run in + * insecure mode + */ check_mok_sb(); + /* + * Tell the user that we're in insecure mode if necessary + */ if (insecure_mode) { Print(L"Booting in insecure mode\n"); uefi_call_wrapper(BS->Stall, 1, 2000000); } - efi_status = check_mok_request(image_handle); - - efi_status = mirror_mok_list(); - + /* + * Install the protocol + */ uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle, &shim_lock_guid, EFI_NATIVE_INTERFACE, &shim_lock_interface); + /* + * Enter MokManager if necessary + */ + efi_status = check_mok_request(image_handle); + + /* + * Copy the MOK list to a runtime variable so the kernel can make + * use of it + */ + efi_status = mirror_mok_list(); + + /* + * Hand over control to the second stage bootloader + */ + efi_status = init_grub(image_handle); + /* + * If we're back here then clean everything up before exiting + */ uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, &shim_lock_guid, &shim_lock_interface);