Merge branch 'mok' into netboot

Conflicts:
	shim.c
This commit is contained in:
Matthew Garrett 2012-11-01 15:29:23 -04:00
commit 821eca49e9

158
shim.c
View File

@ -276,6 +276,9 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
return rc; 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, static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
UINTN dbsize, UINT8 *data, UINTN dbsize, UINT8 *data,
int SignatureSize, EFI_GUID CertType) int SignatureSize, EFI_GUID CertType)
@ -314,6 +317,9 @@ static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
return DATA_NOT_FOUND; 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, static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
int SignatureSize, EFI_GUID CertType) int SignatureSize, EFI_GUID CertType)
{ {
@ -338,6 +344,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, static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash) UINT8 *sha256hash, UINT8 *sha1hash)
{ {
@ -367,6 +377,9 @@ static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
return EFI_SUCCESS; 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, static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
UINT8 *sha256hash, UINT8 *sha1hash) UINT8 *sha256hash, UINT8 *sha1hash)
{ {
@ -596,6 +609,9 @@ done:
return status; return status;
} }
/*
* Ensure that the MOK database hasn't been set or modified from an OS
*/
static EFI_STATUS verify_mok (void) { static EFI_STATUS verify_mok (void) {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
EFI_STATUS status = EFI_SUCCESS; EFI_STATUS status = EFI_SUCCESS;
@ -653,8 +669,14 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return status; return status;
/*
* Check that the MOK database hasn't been modified
*/
verify_mok(); verify_mok();
/*
* Ensure that the binary isn't blacklisted
*/
status = check_blacklist(cert, sha256hash, sha1hash); status = check_blacklist(cert, sha256hash, sha1hash);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
@ -662,6 +684,10 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status; return status;
} }
/*
* Check whether the binary is whitelisted in any of the firmware
* databases
*/
status = check_whitelist(cert, sha256hash, sha1hash); status = check_whitelist(cert, sha256hash, sha1hash);
if (status == EFI_SUCCESS) { if (status == EFI_SUCCESS) {
@ -669,6 +695,9 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
return status; return status;
} }
/*
* And finally, check against shim's built-in key
*/
if (AuthenticodeVerify(cert->CertData, if (AuthenticodeVerify(cert->CertData,
context->SecDir->Size - sizeof(cert->Hdr), context->SecDir->Size - sizeof(cert->Hdr),
vendor_cert, vendor_cert_size, sha256hash, vendor_cert, vendor_cert_size, sha256hash,
@ -762,12 +791,18 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
char *base, *end; char *base, *end;
PE_COFF_LOADER_IMAGE_CONTEXT context; PE_COFF_LOADER_IMAGE_CONTEXT context;
/*
* The binary header contains relevant context and section pointers
*/
efi_status = read_header(data, datasize, &context); efi_status = read_header(data, datasize, &context);
if (efi_status != EFI_SUCCESS) { if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read header\n"); Print(L"Failed to read header\n");
return efi_status; return efi_status;
} }
/*
* We only need to verify the binary if we're in secure mode
*/
if (secure_mode ()) { if (secure_mode ()) {
efi_status = verify_buffer(data, datasize, &context); 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); CopyMem(buffer, data, context.SizeOfHeaders);
/*
* Copy the executable's sections to their desired offsets
*/
Section = context.FirstSection; Section = context.FirstSection;
for (i = 0; i < context.NumberOfSections; i++) { for (i = 0; i < context.NumberOfSections; i++) {
size = Section->Misc.VirtualSize; size = Section->Misc.VirtualSize;
@ -810,6 +848,9 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
Section += 1; Section += 1;
} }
/*
* Run the relocation fixups
*/
efi_status = relocate_coff(&context, buffer); efi_status = relocate_coff(&context, buffer);
if (efi_status != EFI_SUCCESS) { 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); 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->ImageBase = buffer;
li->ImageSize = context.ImageSize; li->ImageSize = context.ImageSize;
@ -831,6 +876,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize,
return EFI_SUCCESS; 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, static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
EFI_DEVICE_PATH **grubpath, CHAR16 **PathName) 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, static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
int *datasize, CHAR16 *PathName) int *datasize, CHAR16 *PathName)
@ -893,6 +942,9 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
device = li->DeviceHandle; device = li->DeviceHandle;
/*
* Open the device
*/
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device, efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
&simple_file_system_protocol, &simple_file_system_protocol,
(void **)&drive); (void **)&drive);
@ -909,6 +961,9 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
goto error; goto error;
} }
/*
* And then open the file
*/
efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName, efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
EFI_FILE_MODE_READ, 0); EFI_FILE_MODE_READ, 0);
@ -925,6 +980,10 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
goto error; 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, efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
&buffersize, fileinfo); &buffersize, fileinfo);
@ -955,6 +1014,10 @@ static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
efi_status = EFI_OUT_OF_RESOURCES; efi_status = EFI_OUT_OF_RESOURCES;
goto error; goto error;
} }
/*
* Perform the actual read
*/
efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize, efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
*data); *data);
@ -986,6 +1049,10 @@ error:
return efi_status; 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 shim_verify (void *buffer, UINT32 size)
{ {
EFI_STATUS status; EFI_STATUS status;
@ -1004,6 +1071,9 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
return status; return status;
} }
/*
* Load and run an EFI executable
*/
EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath) EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
{ {
EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
@ -1016,6 +1086,10 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
void *data = NULL; void *data = NULL;
int datasize; 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, efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
&loaded_image_protocol, (void **)&li); &loaded_image_protocol, (void **)&li);
@ -1024,6 +1098,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
return efi_status; return efi_status;
} }
/*
* Build a new path from the existing one plus the executable name
*/
efi_status = generate_path(li, ImagePath, &path, &PathName); efi_status = generate_path(li, ImagePath, &path, &PathName);
if (efi_status != EFI_SUCCESS) { if (efi_status != EFI_SUCCESS) {
@ -1046,6 +1123,9 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
data = sourcebuffer; data = sourcebuffer;
datasize = sourcesize; datasize = sourcesize;
} else { } else {
/*
* Read the new executable off disk
*/
efi_status = load_image(li, &data, &datasize, PathName); efi_status = load_image(li, &data, &datasize, PathName);
if (efi_status != EFI_SUCCESS) { if (efi_status != EFI_SUCCESS) {
@ -1054,8 +1134,15 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
} }
} }
/*
* 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)); CopyMem(&li_bak, li, sizeof(li_bak));
/*
* Verify and, if appropriate, relocate and execute the executable
*/
efi_status = handle_image(data, datasize, li); efi_status = handle_image(data, datasize, li);
if (efi_status != EFI_SUCCESS) { if (efi_status != EFI_SUCCESS) {
@ -1064,8 +1151,14 @@ EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
goto done; goto done;
} }
/*
* The binary is trusted and relocated. Run it
*/
efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab); efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
/*
* Restore our original loaded image values
*/
CopyMem(li, &li_bak, sizeof(li_bak)); CopyMem(li, &li_bak, sizeof(li_bak));
done: done:
if (PathName) if (PathName)
@ -1077,6 +1170,10 @@ done:
return efi_status; 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 init_grub(EFI_HANDLE image_handle)
{ {
EFI_STATUS efi_status; EFI_STATUS efi_status;
@ -1094,6 +1191,10 @@ done:
return efi_status; 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_STATUS mirror_mok_list()
{ {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
@ -1122,6 +1223,9 @@ done:
return efi_status; return efi_status;
} }
/*
* Check if a variable exists
*/
static BOOLEAN check_var(CHAR16 *varname) static BOOLEAN check_var(CHAR16 *varname)
{ {
EFI_STATUS efi_status; EFI_STATUS efi_status;
@ -1140,6 +1244,10 @@ static BOOLEAN check_var(CHAR16 *varname)
return FALSE; 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 check_mok_request(EFI_HANDLE image_handle)
{ {
EFI_STATUS efi_status; EFI_STATUS efi_status;
@ -1157,6 +1265,10 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
return EFI_SUCCESS; return EFI_SUCCESS;
} }
/*
* Verify that MokSBState is valid, and if appropriate set insecure mode
*/
static EFI_STATUS check_mok_sb (void) static EFI_STATUS check_mok_sb (void)
{ {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
@ -1171,6 +1283,10 @@ static EFI_STATUS check_mok_sb (void)
if (status != EFI_SUCCESS) if (status != EFI_SUCCESS)
return EFI_ACCESS_DENIED; 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) { if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
Print(L"MokSBState is compromised! Clearing it\n"); Print(L"MokSBState is compromised! Clearing it\n");
if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) { if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
@ -1186,7 +1302,6 @@ static EFI_STATUS check_mok_sb (void)
return status; return status;
} }
EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
{ {
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
@ -1194,31 +1309,62 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
EFI_HANDLE handle = NULL; EFI_HANDLE handle = NULL;
EFI_STATUS efi_status; 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.Verify = shim_verify;
shim_lock_interface.Hash = generate_hash; shim_lock_interface.Hash = generate_hash;
shim_lock_interface.Context = read_header; shim_lock_interface.Context = read_header;
systab = passed_systab; systab = passed_systab;
/*
* Ensure that gnu-efi functions are available
*/
InitializeLib(image_handle, systab); InitializeLib(image_handle, systab);
/*
* Check whether the user has configured the system to run in
* insecure mode
*/
check_mok_sb(); check_mok_sb();
/*
* Tell the user that we're in insecure mode if necessary
*/
if (insecure_mode) { if (insecure_mode) {
Print(L"Booting in insecure mode\n"); Print(L"Booting in insecure mode\n");
uefi_call_wrapper(BS->Stall, 1, 2000000); uefi_call_wrapper(BS->Stall, 1, 2000000);
} }
efi_status = check_mok_request(image_handle); /*
* Install the protocol
efi_status = mirror_mok_list(); */
uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle, uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle,
&shim_lock_guid, EFI_NATIVE_INTERFACE, &shim_lock_guid, EFI_NATIVE_INTERFACE,
&shim_lock_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); efi_status = init_grub(image_handle);
/*
* If we're back here then clean everything up before exiting
*/
uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
&shim_lock_guid, &shim_lock_interface); &shim_lock_guid, &shim_lock_interface);