From 09e2c939569800c4e0b7270fd4e5692bc9c5e3c6 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Mon, 10 Dec 2012 16:34:35 +0800 Subject: Get the second stage loader from the Load Options This commit replaces the 2nd stage loader path with the first argument in the Load Options and moves the rest arguments (if any) to the Load Options for the 2nd stage loader. For example, to make shim to load elilo.efi, just create a new boot entry with efibootmgr: # efibootmgr -c -L "shim elilo" -l "efi\\shim.efi" -u "elilo.efi" --- shim.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 2 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index c3aae9e0..44301dd1 100644 --- a/shim.c +++ b/shim.c @@ -42,12 +42,16 @@ #include "netboot.h" #include "shim_cert.h" -#define SECOND_STAGE L"\\grub.efi" +#define DEFAULT_LOADER L"\\grub.efi" #define MOK_MANAGER L"\\MokManager.efi" static EFI_SYSTEM_TABLE *systab; static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table); +static CHAR16 *second_stage; +static void *load_options; +static UINT32 load_options_size; + /* * The vendor certificate used for validating the second stage loader */ @@ -881,6 +885,10 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, li->ImageBase = buffer; li->ImageSize = context.ImageSize; + /* Pass the load options to the second stage loader */ + li->LoadOptions = load_options; + li->LoadOptionsSize = load_options_size; + if (!entry_point) { Print(L"Invalid entry point\n"); FreePool(buffer); @@ -1192,7 +1200,7 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) { EFI_STATUS efi_status; - efi_status = start_image(image_handle, SECOND_STAGE); + efi_status = start_image(image_handle, second_stage); if (efi_status != EFI_SUCCESS) efi_status = start_image(image_handle, MOK_MANAGER); @@ -1312,6 +1320,55 @@ static EFI_STATUS check_mok_sb (void) return status; } +/* + * Check the load options to specify the second stage loader + */ +EFI_STATUS set_second_stage (EFI_HANDLE image_handle) +{ + EFI_STATUS status; + EFI_LOADED_IMAGE *li; + CHAR16 *start = NULL, *c; + int i, remaining_size = 0; + + second_stage = DEFAULT_LOADER; + load_options = NULL; + load_options_size = 0; + + status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &LoadedImageProtocol, (void **) &li); + if (status != EFI_SUCCESS) { + Print (L"Failed to get load options\n"); + return status; + } + + /* Expect a CHAR16 string with at least one CHAR16 */ + if (li->LoadOptionsSize < 4 || li->LoadOptionsSize % 2 != 0) { + return EFI_BAD_BUFFER_SIZE; + } + c = (CHAR16 *)(li->LoadOptions + (li->LoadOptionsSize - 2)); + if (*c != L'\0') { + return EFI_BAD_BUFFER_SIZE; + } + + for (i = 0; i < li->LoadOptionsSize; i += 2) { + c = (CHAR16 *)(li->LoadOptions + i); + if (*c == L' ') { + *c = L'\0'; + start = c + 1; + remaining_size = li->LoadOptionsSize - i - 2; + break; + } + } + + second_stage = (CHAR16 *)li->LoadOptions; + if (start && remaining_size > 0) { + load_options = start; + load_options_size = remaining_size; + } + + return EFI_SUCCESS; +} + EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -1334,6 +1391,9 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) */ InitializeLib(image_handle, systab); + /* Set the second stage loader */ + set_second_stage (image_handle); + /* * Check whether the user has configured the system to run in * insecure mode -- cgit v1.2.3 From 92a136d823e211b60656872ee43ade8693057ee1 Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Thu, 3 Jan 2013 12:20:22 +0800 Subject: Add support for deleting specific keys --- MokManager.c | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- shim.c | 3 +- 2 files changed, 293 insertions(+), 46 deletions(-) (limited to 'shim.c') diff --git a/MokManager.c b/MokManager.c index ddf12deb..bfcbfd67 100644 --- a/MokManager.c +++ b/MokManager.c @@ -31,6 +31,7 @@ struct menu_item { typedef struct { UINT32 MokSize; UINT8 *Mok; + EFI_GUID Type; } __attribute__ ((packed)) MokListNode; typedef struct { @@ -39,6 +40,32 @@ typedef struct { CHAR16 Password[PASSWORD_MAX]; } __attribute__ ((packed)) MokSBvar; +static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes, + UINTN *size, void **buffer) +{ + EFI_STATUS efi_status; + char allocate = !(*size); + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, + attributes, size, buffer); + + if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { + return efi_status; + } + + *buffer = AllocatePool(*size); + + if (!*buffer) { + Print(L"Unable to allocate variable buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, + attributes, size, *buffer); + + return efi_status; +} + static EFI_INPUT_KEY get_keystroke (void) { EFI_INPUT_KEY key; @@ -88,6 +115,42 @@ done: return status; } +static UINT32 count_keys(void *Data, UINTN DataSize) +{ + EFI_SIGNATURE_LIST *CertList = Data; + EFI_GUID CertType = EfiCertX509Guid; + EFI_GUID HashType = EfiHashSha256Guid; + UINTN dbsize = DataSize; + UINT32 MokNum = 0; + + while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { + if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && + (CompareGuid (&CertList->SignatureType, &HashType) != 0)) { + Print(L"Doesn't look like a key or hash\n"); + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + + CertList->SignatureListSize); + continue; + } + + if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && + (CertList->SignatureSize != 48)) { + Print(L"Doesn't look like a valid hash\n"); + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + + CertList->SignatureListSize); + continue; + } + + MokNum++; + dbsize -= CertList->SignatureListSize; + CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + + CertList->SignatureListSize); + } + + return MokNum; +} + static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { MokListNode *list; EFI_SIGNATURE_LIST *CertList = Data; @@ -126,6 +189,7 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { list[count].MokSize = CertList->SignatureSize - sizeof(EFI_GUID); list[count].Mok = (void *)Cert->SignatureData; + list[count].Type = CertList->SignatureType; count++; dbsize -= CertList->SignatureListSize; @@ -391,61 +455,35 @@ static INTN get_number () return (INTN)Atoi(input); } -static UINT8 list_keys (void *MokNew, UINTN MokNewSize) +static UINT8 list_keys (void *KeyList, UINTN KeyListSize, CHAR16 *title) { UINT32 MokNum = 0; MokListNode *keys = NULL; INTN key_num = 0; UINT8 initial = 1; - EFI_SIGNATURE_LIST *CertList = MokNew; - EFI_GUID CertType = EfiCertX509Guid; - EFI_GUID HashType = EfiHashSha256Guid; - UINTN dbsize = MokNewSize; - if (MokNewSize < (sizeof(EFI_SIGNATURE_LIST) + - sizeof(EFI_SIGNATURE_DATA))) { + if (KeyListSize < (sizeof(EFI_SIGNATURE_LIST) + + sizeof(EFI_SIGNATURE_DATA))) { Print(L"No keys\n"); Pause(); return 0; } - while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) { - if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && - (CompareGuid (&CertList->SignatureType, &HashType) != 0)) { - Print(L"Doesn't look like a key or hash\n"); - dbsize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + - CertList->SignatureListSize); - continue; - } - - if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) && - (CertList->SignatureSize != 48)) { - Print(L"Doesn't look like a valid hash\n"); - dbsize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + - CertList->SignatureListSize); - continue; - } - - MokNum++; - dbsize -= CertList->SignatureListSize; - CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + - CertList->SignatureListSize); - } - - keys = build_mok_list(MokNum, MokNew, MokNewSize); + MokNum = count_keys(KeyList, KeyListSize); + keys = build_mok_list(MokNum, KeyList, KeyListSize); if (!keys) { - Print(L"Failed to construct key list in MokNew\n"); + Print(L"Failed to construct key list\n"); return 0; } do { uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); + if (title) + Print(L"%s\n", title); Print(L"Input the key number to show the details of the key or\n" L"type \'0\' to continue\n\n"); - Print(L"%d key(s) in the new key list\n\n", MokNum); + Print(L"%d key(s) in the key list\n\n", MokNum); if (key_num > MokNum) { Print(L"[Key %d]\n", key_num); @@ -663,7 +701,7 @@ static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) { EFI_STATUS efi_status; do { - if (!list_keys(MokNew, MokNewSize)) { + if (!list_keys(MokNew, MokNewSize, L"[Enroll MOK]")) { return 0; } @@ -704,7 +742,8 @@ static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2, return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE); } -static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) { +static INTN mok_reset_prompt (void *MokNew, void *data2, void *data3) +{ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; CHAR16 line[1]; UINT32 length; @@ -737,6 +776,180 @@ static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) { return 0; } +static EFI_STATUS write_back_mok_list (MokListNode *list, INTN key_num) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + EFI_SIGNATURE_LIST *CertList; + EFI_SIGNATURE_DATA *CertData; + void *Data = NULL, *ptr; + INTN DataSize = 0; + int i; + + for (i = 0; i < key_num; i++) { + if (list[i].Mok == NULL) + continue; + + DataSize += sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID); + DataSize += list[i].MokSize; + } + + Data = AllocatePool(DataSize); + if (Data == NULL && DataSize != 0) + return EFI_OUT_OF_RESOURCES; + + ptr = Data; + + for (i = 0; i < key_num; i++) { + if (list[i].Mok == NULL) + continue; + + CertList = (EFI_SIGNATURE_LIST *)ptr; + CertData = (EFI_SIGNATURE_DATA *)(((uint8_t *)ptr) + + sizeof(EFI_SIGNATURE_LIST)); + + CertList->SignatureType = list[i].Type; + CertList->SignatureListSize = list[i].MokSize + + sizeof(EFI_SIGNATURE_LIST) + + sizeof(EFI_SIGNATURE_DATA) - 1; + CertList->SignatureHeaderSize = 0; + CertList->SignatureSize = list[i].MokSize + sizeof(EFI_GUID); + + CertData->SignatureOwner = shim_lock_guid; + CopyMem(CertData->SignatureData, list[i].Mok, list[i].MokSize); + + ptr = (uint8_t *)ptr + sizeof(EFI_SIGNATURE_LIST) + + sizeof(EFI_GUID) + list[i].MokSize; + } + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList", + &shim_lock_guid, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, Data); + if (Data) + FreePool(Data); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to set variable %d\n", efi_status); + return efi_status; + } + + return EFI_SUCCESS; +} + +static EFI_STATUS delete_keys (void *MokDel, UINTN MokDelSize) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + UINT8 auth[SHA256_DIGEST_SIZE]; + UINTN auth_size = SHA256_DIGEST_SIZE; + UINT32 attributes; + void *MokListData = NULL; + UINTN MokListDataSize = 0; + MokListNode *mok, *del_key; + INTN mok_num, del_num; + int i, j; + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDelAuth", + &shim_lock_guid, + &attributes, &auth_size, auth); + + if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) { + Print(L"Failed to get MokDelAuth %d\n", efi_status); + return efi_status; + } + + efi_status = match_password(MokDel, MokDelSize, auth, NULL); + if (efi_status != EFI_SUCCESS) + return EFI_ACCESS_DENIED; + + efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, + &MokListDataSize, &MokListData); + + if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { + Print(L"MokList is compromised!\nErase all keys in MokList!\n"); + if (LibDeleteVariable(L"MokList", &shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to erase MokList\n"); + } + return EFI_ACCESS_DENIED; + } + + /* Nothing to do */ + if (!MokListData || MokListDataSize == 0) + return EFI_SUCCESS; + + /* Construct lists */ + mok_num = count_keys(MokListData, MokListDataSize); + mok = build_mok_list(mok_num, MokListData, MokListDataSize); + del_num = count_keys(MokDel, MokDelSize); + del_key = build_mok_list(del_num, MokDel, MokDelSize); + + /* Search and destroy */ + for (i = 0; i < del_num; i++) { + UINT32 key_size = del_key[i].MokSize; + void *key = del_key[i].Mok; + for (j = 0; j < mok_num; j++) { + if (mok[j].MokSize == key_size && + CompareMem(key, mok[j].Mok, key_size) == 0) { + /* Remove the key */ + mok[j].Mok = NULL; + mok[j].MokSize = 0; + } + } + } + + efi_status = write_back_mok_list(mok, mok_num); + + if (MokListData) + FreePool(MokListData); + if (mok) + FreePool(mok); + if (del_key) + FreePool(del_key); + + return efi_status; +} + +static INTN mok_deletion_prompt (void *MokDel, void *data2, void *data3) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + UINTN MokDelSize = (UINTN)data2; + CHAR16 line[1]; + UINT32 length; + EFI_STATUS efi_status; + + do { + if (!list_keys(MokDel, MokDelSize, L"[Delete MOK]")) { + return 0; + } + + Print(L"Delete the key(s)? (y/n): "); + + get_line (&length, line, 1, 1); + + if (line[0] == 'Y' || line[0] == 'y') { + efi_status = delete_keys(MokDel, MokDelSize); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to delete keys\n"); + return -1; + } + + LibDeleteVariable(L"MokDel", &shim_lock_guid); + LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); + + Print(L"\nPress a key to reboot system\n"); + Pause(); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, + EFI_SUCCESS, 0, NULL); + Print(L"Failed to reboot\n"); + return -1; + } + } while (line[0] != 'N' && line[0] != 'n'); + return -1; +} + static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; @@ -1505,12 +1718,15 @@ static BOOLEAN verify_pw(void) return TRUE; } -static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, - UINTN MokNewSize, void *MokSB, - UINTN MokSBSize, void *MokPW, UINTN MokPWSize) +static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, + void *MokNew, UINTN MokNewSize, + void *MokDel, UINTN MokDelSize, + void *MokSB, UINTN MokSBSize, + void *MokPW, UINTN MokPWSize) { struct menu_item *menu_item; UINT32 MokAuth = 0; + UINT32 MokDelAuth = 0; UINTN menucount = 3, i = 0; EFI_STATUS efi_status; EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; @@ -1528,9 +1744,19 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE)) MokAuth = 1; + efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokDelAuth", + &shim_lock_guid, + &attributes, &auth_size, auth); + + if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE)) + MokDelAuth = 1; + if (MokNew || MokAuth) menucount++; + if (MokDel || MokDelAuth) + menucount++; + if (MokSB) menucount++; @@ -1550,9 +1776,9 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, if (MokNew || MokAuth) { if (!MokNew) { - menu_item[i].text = StrDuplicate(L"Delete MOK"); + menu_item[i].text = StrDuplicate(L"Reset MOK"); menu_item[i].colour = EFI_WHITE; - menu_item[i].callback = mok_deletion_prompt; + menu_item[i].callback = mok_reset_prompt; } else { menu_item[i].text = StrDuplicate(L"Enroll MOK"); menu_item[i].colour = EFI_WHITE; @@ -1563,6 +1789,15 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, i++; } + if (MokDel || MokDelAuth) { + menu_item[i].text = StrDuplicate(L"Delete MOK"); + menu_item[i].colour = EFI_WHITE; + menu_item[i].data = MokDel; + menu_item[i].data2 = (void *)MokDelSize; + menu_item[i].callback = mok_deletion_prompt; + i++; + } + if (MokSB) { menu_item[i].text = StrDuplicate(L"Change Secure Boot state"); menu_item[i].colour = EFI_WHITE; @@ -1605,19 +1840,22 @@ static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew, static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; - UINTN MokNewSize = 0, MokSBSize = 0, MokPWSize = 0; + UINTN MokNewSize = 0, MokDelSize = 0, MokSBSize = 0, MokPWSize = 0; void *MokNew = NULL; + void *MokDel = NULL; void *MokSB = NULL; void *MokPW = NULL; MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize); + MokDel = LibGetVariableAndSize(L"MokDel", &shim_lock_guid, &MokDelSize); + MokSB = LibGetVariableAndSize(L"MokSB", &shim_lock_guid, &MokSBSize); MokPW = LibGetVariableAndSize(L"MokPW", &shim_lock_guid, &MokPWSize); - enter_mok_menu(image_handle, MokNew, MokNewSize, MokSB, MokSBSize, - MokPW, MokPWSize); + enter_mok_menu(image_handle, MokNew, MokNewSize, MokDel, MokDelSize, + MokSB, MokSBSize, MokPW, MokPWSize); if (MokNew) { if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) { @@ -1626,6 +1864,13 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) FreePool (MokNew); } + if (MokDel) { + if (LibDeleteVariable(L"MokDel", &shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to delete MokDel\n"); + } + FreePool (MokDel); + } + if (MokSB) { if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) { Print(L"Failed to delete MokSB\n"); @@ -1641,6 +1886,7 @@ static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) } LibDeleteVariable(L"MokAuth", &shim_lock_guid); + LibDeleteVariable(L"MokDelAuth", &shim_lock_guid); return EFI_SUCCESS; } diff --git a/shim.c b/shim.c index 44301dd1..dcf1c516 100644 --- a/shim.c +++ b/shim.c @@ -1271,7 +1271,8 @@ EFI_STATUS check_mok_request(EFI_HANDLE image_handle) EFI_STATUS efi_status; if (check_var(L"MokNew") || check_var(L"MokSB") || - check_var(L"MokPW") || check_var(L"MokAuth")) { + check_var(L"MokPW") || check_var(L"MokAuth") || + check_var(L"MokDel")) { efi_status = start_image(image_handle, MOK_MANAGER); if (efi_status != EFI_SUCCESS) { -- cgit v1.2.3 From 6e1bd3dcb70985e81f1f9d133219b708eb9beddd Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 26 Apr 2013 11:44:15 -0400 Subject: UEFI Shell sticks the UCS2 of li->FilePath in li->LoadOptions. Ignore it. If li->LoadOptions tells us to execute our own binary, it's clearly not what we want to do for the second stage. So simply ignore that case. Signed-off-by: Peter Jones --- shim.c | 7 ++++++- ucs2.h | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 ucs2.h (limited to 'shim.c') diff --git a/shim.c b/shim.c index dcf1c516..32b3ae95 100644 --- a/shim.c +++ b/shim.c @@ -41,6 +41,7 @@ #include "signature.h" #include "netboot.h" #include "shim_cert.h" +#include "ucs2.h" #define DEFAULT_LOADER L"\\grub.efi" #define MOK_MANAGER L"\\MokManager.efi" @@ -1328,6 +1329,7 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) { EFI_STATUS status; EFI_LOADED_IMAGE *li; + CHAR16 *bootpath = NULL; CHAR16 *start = NULL, *c; int i, remaining_size = 0; @@ -1361,7 +1363,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) } } - second_stage = (CHAR16 *)li->LoadOptions; + bootpath = DevicePathToStr(li->FilePath); + if (!StrCaseCmp(bootpath, (CHAR16 *)li->LoadOptions)) + second_stage = (CHAR16 *)li->LoadOptions; + if (start && remaining_size > 0) { load_options = start; load_options_size = remaining_size; diff --git a/ucs2.h b/ucs2.h new file mode 100644 index 00000000..7c49b09b --- /dev/null +++ b/ucs2.h @@ -0,0 +1,57 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2013 Red Hat, Inc + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Significant portions of this code are derived from Tianocore + * (http://tianocore.sf.net) and are Copyright 2009-2012 Intel + * Corporation. + */ + +#ifndef SHIM_UCS2_H +#define SHIM_UCS2_H + +static inline INTN +__attribute__((unused)) +StrCaseCmp(CHAR16 *s0, CHAR16 *s1) +{ + CHAR16 c0, c1; + while (1) { + if (*s0 == L'\0' || *s1 == L'\0') + return *s1 - *s0; + c0 = (*s0 >= L'a' && *s0 <= L'z') ? *s0 - 32 : *s0; + c1 = (*s1 >= L'a' && *s1 <= L'z') ? *s1 - 32 : *s1; + if (c0 != c1) + return c1 - c0; + s0++; + s1++; + } + return 0; +} + +#endif /* SHIM_UCS2_H */ -- cgit v1.2.3 From 155a76bb8657c6521be71ab3da609ae181376150 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 26 Apr 2013 11:44:28 -0400 Subject: Don't put the directory in the file path twice. Sometimes when we're creating paths, the ImagePath can contain the directory name already. If that happens, don't add it in again. Signed-off-by: Peter Jones --- shim.c | 3 ++- ucs2.h | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 32b3ae95..7abbe8b4 100644 --- a/shim.c +++ b/shim.c @@ -939,7 +939,8 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath, } *PathName[0] = '\0'; - StrCat(*PathName, bootpath); + if (StrnCaseCmp(bootpath, ImagePath, StrLen(bootpath))) + StrCat(*PathName, bootpath); StrCat(*PathName, ImagePath); *grubpath = FileDevicePath(device, *PathName); diff --git a/ucs2.h b/ucs2.h index 7c49b09b..03742847 100644 --- a/ucs2.h +++ b/ucs2.h @@ -54,4 +54,23 @@ StrCaseCmp(CHAR16 *s0, CHAR16 *s1) return 0; } +static inline INTN +__attribute__((unused)) +StrnCaseCmp(CHAR16 *s0, CHAR16 *s1, int n) +{ + CHAR16 c0, c1; + int x = 0; + while (n > x++) { + if (*s0 == L'\0' || *s1 == L'\0') + return *s1 - *s0; + c0 = (*s0 >= L'a' && *s0 <= L'z') ? *s0 - 32 : *s0; + c1 = (*s1 >= L'a' && *s1 <= L'z') ? *s1 - 32 : *s1; + if (c0 != c1) + return c1 - c0; + s0++; + s1++; + } + return 0; +} + #endif /* SHIM_UCS2_H */ -- cgit v1.2.3 From 4a3013c76c539997953af1e784224edb49ec233e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Fri, 26 Apr 2013 12:12:48 -0400 Subject: Get rid of extra label. Signed-off-by: Peter Jones --- shim.c | 1 - 1 file changed, 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 7abbe8b4..d1071d68 100644 --- a/shim.c +++ b/shim.c @@ -1206,7 +1206,6 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) if (efi_status != EFI_SUCCESS) efi_status = start_image(image_handle, MOK_MANAGER); -done: return efi_status; } -- cgit v1.2.3 From 0283024e0e861e2f933a5a6e22b0a7d0f869df5e Mon Sep 17 00:00:00 2001 From: Gary Ching-Pang Lin Date: Tue, 30 Apr 2013 09:45:45 -0400 Subject: Adopt the UEFI shell style LoadOptions The previous commit, 14d4b8e, caused shim failed to parse the name of the 2nd stage loader in UEFI shell. Amend parsing of the name the 2nd stage loader to be compatible with UEFI shell. To create an boot entry for elilo.efi: # efibootmgr -c -L "shim elilo" -l "efi\\shim.efi" -u "shim.efi elilo.efi" --- shim.c | 41 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 5 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index d1071d68..0113ed5e 100644 --- a/shim.c +++ b/shim.c @@ -1329,9 +1329,10 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) { EFI_STATUS status; EFI_LOADED_IMAGE *li; - CHAR16 *bootpath = NULL; CHAR16 *start = NULL, *c; int i, remaining_size = 0; + CHAR16 *loader_str = NULL; + int loader_len = 0; second_stage = DEFAULT_LOADER; load_options = NULL; @@ -1353,6 +1354,11 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) return EFI_BAD_BUFFER_SIZE; } + /* + * UEFI shell copies the whole line of the command into LoadOptions. + * We ignore the string before the first L' ', i.e. the name of this + * program. + */ for (i = 0; i < li->LoadOptionsSize; i += 2) { c = (CHAR16 *)(li->LoadOptions + i); if (*c == L' ') { @@ -1363,11 +1369,30 @@ EFI_STATUS set_second_stage (EFI_HANDLE image_handle) } } - bootpath = DevicePathToStr(li->FilePath); - if (!StrCaseCmp(bootpath, (CHAR16 *)li->LoadOptions)) - second_stage = (CHAR16 *)li->LoadOptions; + if (!start || remaining_size <= 0) + return EFI_SUCCESS; - if (start && remaining_size > 0) { + for (i = 0; start[i] != '\0'; i++) { + if (start[i] == L' ' || start[i] == L'\0') + break; + loader_len++; + } + + /* + * Setup the name of the alternative loader and the LoadOptions for + * the loader + */ + if (loader_len > 0) { + loader_str = AllocatePool((loader_len + 1) * sizeof(CHAR16)); + if (!loader_str) { + Print(L"Failed to allocate loader string\n"); + return EFI_OUT_OF_RESOURCES; + } + for (i = 0; i < loader_len; i++) + loader_str[i] = start[i]; + loader_str[loader_len] = L'\0'; + + second_stage = loader_str; load_options = start; load_options_size = remaining_size; } @@ -1444,5 +1469,11 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab) uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle, &shim_lock_guid, &shim_lock_interface); + /* + * Free the space allocated for the alternative 2nd stage loader + */ + if (load_options_size > 0) + FreePool(second_stage); + return efi_status; } -- cgit v1.2.3 From 6d6b0221693c46adae695e573b300b775d352be6 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 30 Apr 2013 09:46:22 -0400 Subject: Make shim use fallback when appropriate. If we're called as /BOOT/EFI/BOOT*.EFI, and /BOOT/EFI/FALLBACK.EFI exists, give it a shot. Signed-off-by: Peter Jones --- shim.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 0113ed5e..f2b8f1d1 100644 --- a/shim.c +++ b/shim.c @@ -44,6 +44,7 @@ #include "ucs2.h" #define DEFAULT_LOADER L"\\grub.efi" +#define FALLBACK L"\\fallback.efi" #define MOK_MANAGER L"\\MokManager.efi" static EFI_SYSTEM_TABLE *systab; @@ -899,6 +900,69 @@ static EFI_STATUS handle_image (void *data, unsigned int datasize, return EFI_SUCCESS; } +static int +should_use_fallback(EFI_HANDLE image_handle) +{ + EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; + EFI_LOADED_IMAGE *li; + EFI_DEVICE_PATH *devpath; + int i; + unsigned int pathlen = 0; + CHAR16 *bootpath; + EFI_FILE_IO_INTERFACE *fio = NULL; + EFI_FILE_HANDLE vh; + EFI_FILE_HANDLE fh; + EFI_STATUS rc; + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &loaded_image_protocol, (void **)&li); + if (EFI_ERROR(rc)) + return 0; + + devpath = li->FilePath; + + bootpath = DevicePathToStr(devpath); + + /* Check the beginning of the string and the end, to avoid + * caring about which arch this is. */ + /* I really don't know why, but sometimes bootpath gives us + * L"\\EFI\\BOOT\\/BOOTX64.EFI". So just handle that here... + */ + if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && + StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15)) + return 0; + pathlen = StrLen(bootpath); + if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) + return 0; + + for (i=pathlen; i>0; i--) { + if (bootpath[i] == '\\') + break; + } + + bootpath[i+1] = '\0'; + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, + &FileSystemProtocol, &fio); + if (EFI_ERROR(rc)) + return 0; + + rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh); + if (EFI_ERROR(rc)) + return 0; + + rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, + EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(rc)) { + uefi_call_wrapper(vh->Close, 1, vh); + return 0; + } + uefi_call_wrapper(fh->Close, 1, fh); + uefi_call_wrapper(vh->Close, 1, vh); + + return 1; +} + /* * Generate the path of an executable given shim's path and the name * of the executable @@ -1202,7 +1266,10 @@ EFI_STATUS init_grub(EFI_HANDLE image_handle) { EFI_STATUS efi_status; - efi_status = start_image(image_handle, second_stage); + if (should_use_fallback(image_handle)) + efi_status = start_image(image_handle, FALLBACK); + else + efi_status = start_image(image_handle, second_stage); if (efi_status != EFI_SUCCESS) efi_status = start_image(image_handle, MOK_MANAGER); -- cgit v1.2.3 From 35b0b55b3ee0f6d6771c7992b8fefffc54bda48e Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2013 13:37:15 -0400 Subject: Fix some minor type errors. Signed-off-by: Peter Jones --- fallback.c | 2 +- shim.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'shim.c') diff --git a/fallback.c b/fallback.c index cf1ebe46..82ddbf2f 100644 --- a/fallback.c +++ b/fallback.c @@ -501,7 +501,7 @@ find_boot_options(EFI_HANDLE device) EFI_FILE_IO_INTERFACE *fio = NULL; rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, - &FileSystemProtocol, &fio); + &FileSystemProtocol, (void **)&fio); if (EFI_ERROR(rc)) { Print(L"Couldn't find file system: %d\n", rc); return rc; diff --git a/shim.c b/shim.c index f2b8f1d1..c1cf17ae 100644 --- a/shim.c +++ b/shim.c @@ -910,8 +910,8 @@ should_use_fallback(EFI_HANDLE image_handle) unsigned int pathlen = 0; CHAR16 *bootpath; EFI_FILE_IO_INTERFACE *fio = NULL; - EFI_FILE_HANDLE vh; - EFI_FILE_HANDLE fh; + EFI_FILE *vh; + EFI_FILE *fh; EFI_STATUS rc; rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, @@ -943,7 +943,7 @@ should_use_fallback(EFI_HANDLE image_handle) bootpath[i+1] = '\0'; rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, - &FileSystemProtocol, &fio); + &FileSystemProtocol, (void **)&fio); if (EFI_ERROR(rc)) return 0; -- cgit v1.2.3 From 2e7fc28d928117447b60ad4a0014d1d496fcabc0 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2013 13:38:00 -0400 Subject: Remove some unnecessary code. Signed-off-by: Peter Jones --- shim.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index c1cf17ae..33d4396d 100644 --- a/shim.c +++ b/shim.c @@ -905,8 +905,6 @@ should_use_fallback(EFI_HANDLE image_handle) { EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL; EFI_LOADED_IMAGE *li; - EFI_DEVICE_PATH *devpath; - int i; unsigned int pathlen = 0; CHAR16 *bootpath; EFI_FILE_IO_INTERFACE *fio = NULL; @@ -919,9 +917,7 @@ should_use_fallback(EFI_HANDLE image_handle) if (EFI_ERROR(rc)) return 0; - devpath = li->FilePath; - - bootpath = DevicePathToStr(devpath); + bootpath = DevicePathToStr(li->FilePath); /* Check the beginning of the string and the end, to avoid * caring about which arch this is. */ @@ -931,17 +927,11 @@ should_use_fallback(EFI_HANDLE image_handle) if (StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\BOOT", 14) && StrnCaseCmp(bootpath, L"\\EFI\\BOOT\\/BOOT", 15)) return 0; + pathlen = StrLen(bootpath); if (pathlen < 5 || StrCaseCmp(bootpath + pathlen - 4, L".EFI")) return 0; - for (i=pathlen; i>0; i--) { - if (bootpath[i] == '\\') - break; - } - - bootpath[i+1] = '\0'; - rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, &FileSystemProtocol, (void **)&fio); if (EFI_ERROR(rc)) -- cgit v1.2.3 From c9d11306e4d6a4c4638735f15852b59f3585e792 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2013 13:38:27 -0400 Subject: Add some error messages when things don't work. Signed-off-by: Peter Jones --- shim.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 33d4396d..16b2faa9 100644 --- a/shim.c +++ b/shim.c @@ -914,8 +914,10 @@ should_use_fallback(EFI_HANDLE image_handle) rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &loaded_image_protocol, (void **)&li); - if (EFI_ERROR(rc)) + if (EFI_ERROR(rc)) { + Print(L"Could not get image for bootx64.efi: %d\n", rc); return 0; + } bootpath = DevicePathToStr(li->FilePath); @@ -934,16 +936,21 @@ should_use_fallback(EFI_HANDLE image_handle) rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, &FileSystemProtocol, (void **)&fio); - if (EFI_ERROR(rc)) + if (EFI_ERROR(rc)) { + Print(L"Could not get fio for li->DeviceHandle: %d\n", rc); return 0; + } rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &vh); - if (EFI_ERROR(rc)) + if (EFI_ERROR(rc)) { + Print(L"Could not open fio volume: %d\n", rc); return 0; + } rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, EFI_FILE_READ_ONLY, 0); if (EFI_ERROR(rc)) { + Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, rc); uefi_call_wrapper(vh->Close, 1, vh); return 0; } -- cgit v1.2.3 From 5bb3e64ed8584947d6d92371f261c12c2421c263 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Wed, 15 May 2013 13:38:44 -0400 Subject: Use the correct define on Open. The value here doesn't actually change any, but we should still use the right name. Signed-off-by: Peter Jones --- shim.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'shim.c') diff --git a/shim.c b/shim.c index 16b2faa9..94b97104 100644 --- a/shim.c +++ b/shim.c @@ -948,7 +948,7 @@ should_use_fallback(EFI_HANDLE image_handle) } rc = uefi_call_wrapper(vh->Open, 5, vh, &fh, L"\\EFI\\BOOT" FALLBACK, - EFI_FILE_READ_ONLY, 0); + EFI_FILE_MODE_READ, 0); if (EFI_ERROR(rc)) { Print(L"Could not open \"\\EFI\\BOOT%s\": %d\n", FALLBACK, rc); uefi_call_wrapper(vh->Close, 1, vh); -- cgit v1.2.3