diff options
| author | Steve Langasek <steve.langasek@canonical.com> | 2013-07-02 15:24:04 +0000 |
|---|---|---|
| committer | Steve Langasek <steve.langasek@canonical.com> | 2013-07-02 15:24:04 +0000 |
| commit | bfab8d6791bccc38a8604cbc933048319c920780 (patch) | |
| tree | d67bb5c284e745f6c3e38c6a8a7e7e83be16c6cf | |
| parent | 7f61250830921321a77c879e33177fe0cb6336db (diff) | |
| parent | d141608bf820a1f1052b335073cd4c2dc9221d1d (diff) | |
| download | efi-boot-shim-bfab8d6791bccc38a8604cbc933048319c920780.tar.gz efi-boot-shim-bfab8d6791bccc38a8604cbc933048319c920780.zip | |
Import upstream version 0.4
| -rw-r--r-- | .gitignore | 24 | ||||
| -rw-r--r-- | Cryptlib/Makefile | 4 | ||||
| -rw-r--r-- | Cryptlib/OpenSSL/Makefile | 6 | ||||
| -rw-r--r-- | Makefile | 30 | ||||
| -rw-r--r-- | MokManager.c | 517 | ||||
| -rw-r--r-- | cert.S | 6 | ||||
| -rw-r--r-- | dbx.S | 6 | ||||
| -rw-r--r-- | elf_ia32_efi.lds | 69 | ||||
| -rw-r--r-- | elf_ia64_efi.lds | 75 | ||||
| -rw-r--r-- | elf_x86_64_efi.lds | 68 | ||||
| -rw-r--r-- | fallback.c | 673 | ||||
| -rw-r--r-- | netboot.c | 9 | ||||
| -rw-r--r-- | shim.c | 171 | ||||
| -rw-r--r-- | ucs2.h | 92 |
14 files changed, 1582 insertions, 168 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..e46ec8a1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.*.sw? +certdb +shim_cert.h +*.a +*.cer +*.crl +*.crt +*.csr +*.db +*.db.attr +*.db.attr.old +*.db.old +*.domain.txt +*.efi +*.efi.debug +*.efi.signed +*.key +*.key +*.o +*.pem +*.p12 +*.so +*.srl +*.srl.old diff --git a/Cryptlib/Makefile b/Cryptlib/Makefile index 925db8db..08908389 100644 --- a/Cryptlib/Makefile +++ b/Cryptlib/Makefile @@ -10,9 +10,9 @@ LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi $(LIB_GCC) CFLAGS = -ggdb -O0 -I. -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar \ - -Wall $(EFI_INCLUDES) -mno-red-zone + -Wall $(EFI_INCLUDES) -mno-red-zone -maccumulate-outgoing-args -mno-sse -mno-mmx ifeq ($(ARCH),x86_64) - CFLAGS += -DEFI_FUNCTION_WRAPPER + CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI endif LDFLAGS = -nostdlib -znocombreloc diff --git a/Cryptlib/OpenSSL/Makefile b/Cryptlib/OpenSSL/Makefile index 7fde3829..1960b6b3 100644 --- a/Cryptlib/OpenSSL/Makefile +++ b/Cryptlib/OpenSSL/Makefile @@ -9,10 +9,10 @@ EFI_PATH = /usr/lib64/gnuefi LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi $(LIB_GCC) -CFLAGS = -ggdb -O0 -I. -I.. -I../Include/ -Icrypto -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar -nostdinc \ - -Wall $(EFI_INCLUDES) -DOPENSSL_SYSNAME_UWIN -DOPENSSL_SYS_UEFI -DL_ENDIAN -DSIXTY_FOUR_BIT_LONG -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_SOCK -DOPENSSL_NO_CMS -DOPENSSL_NO_JPAKE -DOPENSSL_NO_CAPIENG -DOPENSSL_NO_ERR -DOPENSSL_NO_KRB5 -DOPENSSL_NO_DYNAMIC_ENGINE -DGETPID_IS_MEANINGLESS -DOPENSSL_NO_STDIO -DOPENSSL_NO_FP_API -DOPENSSL_NO_DGRAM -DOPENSSL_NO_SHA0 -DOPENSSL_NO_SHA512 -DOPENSSL_NO_LHASH -DOPENSSL_NO_HW -DOPENSSL_NO_OCSP -DOPENSSL_NO_LOCKING -DOPENSSL_NO_DEPRECATED -DOPENSSL_SMALL_FOOTPRINT -DPEDANTIC -mno-red-zone +CFLAGS = -ggdb -O0 -I. -I.. -I../Include/ -Icrypto -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar -nostdinc -mno-mmx -mno-sse -mno-red-zone -maccumulate-outgoing-args \ + -Wall $(EFI_INCLUDES) -DOPENSSL_SYSNAME_UWIN -DOPENSSL_SYS_UEFI -DL_ENDIAN -DSIXTY_FOUR_BIT_LONG -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE -DOPENSSL_NO_CAMELLIA -DOPENSSL_NO_SEED -DOPENSSL_NO_RC5 -DOPENSSL_NO_MDC2 -DOPENSSL_NO_SOCK -DOPENSSL_NO_CMS -DOPENSSL_NO_JPAKE -DOPENSSL_NO_CAPIENG -DOPENSSL_NO_ERR -DOPENSSL_NO_KRB5 -DOPENSSL_NO_DYNAMIC_ENGINE -DGETPID_IS_MEANINGLESS -DOPENSSL_NO_STDIO -DOPENSSL_NO_FP_API -DOPENSSL_NO_DGRAM -DOPENSSL_NO_SHA0 -DOPENSSL_NO_SHA512 -DOPENSSL_NO_LHASH -DOPENSSL_NO_HW -DOPENSSL_NO_OCSP -DOPENSSL_NO_LOCKING -DOPENSSL_NO_DEPRECATED -DOPENSSL_SMALL_FOOTPRINT -DPEDANTIC ifeq ($(ARCH),x86_64) - CFLAGS += -DEFI_FUNCTION_WRAPPER + CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI endif LDFLAGS = -nostdlib -znocombreloc @@ -5,35 +5,41 @@ SUBDIRS = Cryptlib LIB_PATH = /usr/lib64 EFI_INCLUDE = /usr/include/efi -EFI_INCLUDES = -nostdinc -ICryptlib -ICryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol +EFI_INCLUDES = -nostdinc -ICryptlib -ICryptlib/Include -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol EFI_PATH = /usr/lib64/gnuefi LIB_GCC = $(shell $(CC) -print-libgcc-file-name) EFI_LIBS = -lefi -lgnuefi --start-group Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a --end-group $(LIB_GCC) EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o -EFI_LDS = $(EFI_PATH)/elf_$(ARCH)_efi.lds +EFI_LDS = elf_$(ARCH)_efi.lds -CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic -fshort-wchar \ - -Wall -mno-red-zone \ +CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \ + -fshort-wchar -Wall -mno-red-zone -maccumulate-outgoing-args \ + -mno-mmx -mno-sse \ $(EFI_INCLUDES) ifeq ($(ARCH),x86_64) - CFLAGS += -DEFI_FUNCTION_WRAPPER + CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI endif ifneq ($(origin VENDOR_CERT_FILE), undefined) CFLAGS += -DVENDOR_CERT_FILE=\"$(VENDOR_CERT_FILE)\" endif +ifneq ($(origin VENDOR_DBX_FILE), undefined) + CFLAGS += -DVENDOR_DBX_FILE=\"$(VENDOR_DBX_FILE)\" +endif LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS) -VERSION = 0.2 +VERSION = 0.4 -TARGET = shim.efi MokManager.efi.signed +TARGET = shim.efi MokManager.efi.signed fallback.efi.signed OBJS = shim.o netboot.o cert.o dbx.o KEYS = shim_cert.h ocsp.* ca.* shim.crt shim.csr shim.p12 shim.pem shim.key SOURCES = shim.c shim.h netboot.c signature.h PeImage.h MOK_OBJS = MokManager.o MOK_SOURCES = MokManager.c shim.h +FALLBACK_OBJS = fallback.o +FALLBACK_SRCS = fallback.c all: $(TARGET) @@ -65,6 +71,11 @@ dbx.o : dbx.S shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) +fallback.o: $(FALLBACK_SRCS) + +fallback.so: $(FALLBACK_OBJS) + $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS) + MokManager.o: $(SOURCES) MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a @@ -80,6 +91,7 @@ Cryptlib/OpenSSL/libopenssl.a: objcopy -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel \ -j .rela -j .reloc -j .eh_frame \ + -j .vendor_cert \ --target=efi-app-$(ARCH) $^ $@ objcopy -j .text -j .sdata -j .data \ -j .dynamic -j .dynsym -j .rel \ @@ -94,8 +106,8 @@ Cryptlib/OpenSSL/libopenssl.a: clean: $(MAKE) -C Cryptlib clean $(MAKE) -C Cryptlib/OpenSSL clean - rm -rf $(TARGET) $(OBJS) $(MOK_OBJS) $(KEYS) certdb - rm -f *.debug *.so + rm -rf $(TARGET) $(OBJS) $(MOK_OBJS) $(FALLBACK_OBJS) $(KEYS) certdb + rm -f *.debug *.so *.efi GITTAG = $(VERSION) diff --git a/MokManager.c b/MokManager.c index 5802d274..97588cb6 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; @@ -124,8 +187,9 @@ static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize); - list[count].MokSize = CertList->SignatureSize; + list[count].MokSize = CertList->SignatureSize - sizeof(EFI_GUID); list[count].Mok = (void *)Cert->SignatureData; + list[count].Type = CertList->SignatureType; count++; dbsize -= CertList->SignatureListSize; @@ -317,7 +381,7 @@ static void show_mok_info (void *Mok, UINTN MokSize) if (!Mok || MokSize == 0) return; - if (MokSize != 48) { + if (MokSize != SHA256_DIGEST_SIZE) { if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) && X509Cert != NULL) { show_x509_info(X509Cert); @@ -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); @@ -558,17 +596,61 @@ done: return status; } +static EFI_STATUS match_password (void *Data, UINTN DataSize, + UINT8 auth[SHA256_DIGEST_SIZE], + CHAR16 *prompt) +{ + EFI_STATUS efi_status; + UINT8 hash[SHA256_DIGEST_SIZE]; + CHAR16 password[PASSWORD_MAX]; + UINT32 pw_length; + UINT8 fail_count = 0; + + while (fail_count < 3) { + if (prompt) { + Print(L"%s", prompt); + } else { + Print(L"Password: "); + } + get_line(&pw_length, password, PASSWORD_MAX, 0); + + if (pw_length < PASSWORD_MIN || pw_length > PASSWORD_MAX) { + Print(L"Invalid password length\n"); + fail_count++; + continue; + } + + efi_status = compute_pw_hash(Data, DataSize, password, + pw_length, hash); + + if (efi_status != EFI_SUCCESS) { + Print(L"Unable to generate password hash\n"); + fail_count++; + continue; + } + + if (CompareMem(auth, hash, SHA256_DIGEST_SIZE) != 0) { + Print(L"Password doesn't match\n"); + fail_count++; + continue; + } + + break; + } + + if (fail_count >= 3) + return EFI_ACCESS_DENIED; + + return EFI_SUCCESS; +} + static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - UINT8 hash[SHA256_DIGEST_SIZE]; UINT8 auth[SHA256_DIGEST_SIZE]; UINTN auth_size; UINT32 attributes; - CHAR16 password[PASSWORD_MAX]; - UINT32 pw_length; - UINT8 fail_count = 0; if (authenticate) { auth_size = SHA256_DIGEST_SIZE; @@ -582,32 +664,8 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) return efi_status; } - while (fail_count < 3) { - Print(L"Password(%d-%d characters): ", - PASSWORD_MIN, PASSWORD_MAX); - get_line(&pw_length, password, PASSWORD_MAX, 0); - - if (pw_length < 8) { - Print(L"At least %d characters for the password\n", - PASSWORD_MIN); - } - - efi_status = compute_pw_hash(MokNew, MokNewSize, password, - pw_length, hash); - - if (efi_status != EFI_SUCCESS) { - return efi_status; - } - - if (CompareMem(auth, hash, SHA256_DIGEST_SIZE) != 0) { - Print(L"Password doesn't match\n"); - fail_count++; - } else { - break; - } - } - - if (fail_count >= 3) + efi_status = match_password(MokNew, MokNewSize, auth, NULL); + if (efi_status != EFI_SUCCESS) return EFI_ACCESS_DENIED; } @@ -637,12 +695,13 @@ static EFI_STATUS store_keys (void *MokNew, UINTN MokNewSize, int authenticate) } static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) { + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; CHAR16 line[1]; UINT32 length; EFI_STATUS efi_status; do { - if (!list_keys(MokNew, MokNewSize)) { + if (!list_keys(MokNew, MokNewSize, L"[Enroll MOK]")) { return 0; } @@ -657,6 +716,19 @@ static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) { Print(L"Failed to enroll keys\n"); return -1; } + + if (auth) { + LibDeleteVariable(L"MokNew", &shim_lock_guid); + LibDeleteVariable(L"MokAuth", &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; + } + return 0; } } while (line[0] != 'N' && line[0] != 'n'); @@ -670,11 +742,14 @@ 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; EFI_STATUS efi_status; + uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); Print(L"Erase all stored keys? (y/N): "); get_line (&length, line, 1, 1); @@ -686,11 +761,195 @@ static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) { Print(L"Failed to erase keys\n"); return -1; } + + LibDeleteVariable(L"MokNew", &shim_lock_guid); + LibDeleteVariable(L"MokAuth", &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; } 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; @@ -793,9 +1052,7 @@ static INTN mok_pw_prompt (void *MokPW, void *data2, void *data3) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; UINTN MokPWSize = (UINTN)data2; - UINT8 fail_count = 0; UINT8 hash[SHA256_DIGEST_SIZE]; - CHAR16 password[PASSWORD_MAX]; UINT32 length; CHAR16 line[1]; @@ -823,34 +1080,8 @@ static INTN mok_pw_prompt (void *MokPW, void *data2, void *data3) { return 0; } - while (fail_count < 3) { - Print(L"Confirm MOK passphrase: "); - get_line(&length, password, PASSWORD_MAX, 0); - - if ((length < PASSWORD_MIN) || (length > PASSWORD_MAX)) { - Print(L"Invalid password length\n"); - fail_count++; - continue; - } - - efi_status = compute_pw_hash(NULL, 0, password, length, hash); - - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to generate password hash\n"); - fail_count++; - continue; - } - - if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) != 0) { - Print(L"Password doesn't match\n"); - fail_count++; - continue; - } - - break; - } - - if (fail_count >= 3) { + efi_status = match_password(NULL, 0, MokPW, L"Confirm MOK passphrase: "); + if (efi_status != EFI_SUCCESS) { Print(L"Password limit reached\n"); return -1; } @@ -1010,6 +1241,9 @@ static void run_menu (CHAR16 *header, UINTN lines, struct menu_item *items, if (ret < 0) { Print(L"Press a key to continue\n"); Pause(); + /* Clear the key in the queue */ + uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, + ST->ConIn, &key); } draw_menu (header, lines, items, count); pos = 0; @@ -1457,12 +1691,8 @@ static BOOLEAN verify_pw(void) { EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; EFI_STATUS efi_status; - CHAR16 password[PASSWORD_MAX]; - UINT8 fail_count = 0; - UINT8 hash[SHA256_DIGEST_SIZE]; UINT8 pwhash[SHA256_DIGEST_SIZE]; UINTN size = SHA256_DIGEST_SIZE; - UINT32 length; UINT32 attributes; efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokPWStore", @@ -1482,43 +1712,24 @@ static BOOLEAN verify_pw(void) uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut); - while (fail_count < 3) { - Print(L"Enter MOK password: "); - get_line(&length, password, PASSWORD_MAX, 0); - - if (length < PASSWORD_MIN || length > PASSWORD_MAX) { - Print(L"Invalid password length\n"); - fail_count++; - continue; - } - - efi_status = compute_pw_hash(NULL, 0, password, length, hash); - - if (efi_status != EFI_SUCCESS) { - Print(L"Unable to generate password hash\n"); - fail_count++; - continue; - } - - if (CompareMem(pwhash, hash, SHA256_DIGEST_SIZE) != 0) { - Print(L"Password doesn't match\n"); - fail_count++; - continue; - } - - return TRUE; + efi_status = match_password(NULL, 0, pwhash, L"Enter MOK password: "); + if (efi_status != EFI_SUCCESS) { + Print(L"Password limit reached\n"); + return FALSE; } - Print(L"Password limit reached\n"); - return FALSE; + 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; @@ -1536,9 +1747,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++; @@ -1558,9 +1779,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; @@ -1571,6 +1792,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; @@ -1613,19 +1843,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) { @@ -1634,6 +1867,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"); @@ -1649,6 +1889,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; } @@ -4,13 +4,15 @@ .align 1 .type vendor_cert_size, @object .size vendor_cert_size, 4 + .section .vendor_cert, "a", @progbits vendor_cert_size: .long .L0 - vendor_cert .globl vendor_cert .data .align 1 .type vendor_cert, @object - .size vendor_cert_size, vendor_cert_size-vendor_cert + .size vendor_cert, .L0-vendor_cert + .section .vendor_cert, "a", @progbits vendor_cert: .incbin VENDOR_CERT_FILE .L0: @@ -19,6 +21,7 @@ vendor_cert: .bss .type vendor_cert, @object .size vendor_cert, 1 + .section .vendor_cert, "a", @progbits vendor_cert: .zero 1 @@ -27,6 +30,7 @@ vendor_cert: .align 4 .type vendor_cert_size, @object .size vendor_cert_size, 4 + .section .vendor_cert, "a", @progbits vendor_cert_size: .long 1 #endif @@ -4,13 +4,15 @@ .align 1 .type vendor_dbx_size, @object .size vendor_dbx_size, 4 + .section .vendor_cert, "a", @progbits vendor_dbx_size: .long .L0 - vendor_dbx .globl vendor_dbx .data .align 1 .type vendor_dbx, @object - .size vendor_dbx_size, vendor_dbx_size-vendor_dbx + .size vendor_dbx, .L0-vendor_dbx + .section .vendor_cert, "a", @progbits vendor_dbx: .incbin VENDOR_DBX_FILE .L0: @@ -19,6 +21,7 @@ vendor_dbx: .bss .type vendor_dbx, @object .size vendor_dbx, 1 + .section .vendor_cert, "a", @progbits vendor_dbx: .zero 1 @@ -27,6 +30,7 @@ vendor_dbx: .align 4 .type vendor_dbx_size, @object .size vendor_dbx_size, 4 + .section .vendor_cert, "a", @progbits vendor_dbx_size: .long 0 #endif diff --git a/elf_ia32_efi.lds b/elf_ia32_efi.lds new file mode 100644 index 00000000..12d4085b --- /dev/null +++ b/elf_ia32_efi.lds @@ -0,0 +1,69 @@ +OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") +OUTPUT_ARCH(i386) +ENTRY(_start) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .text : + { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } + .reloc : + { + *(.reloc) + } + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.data) + *(.data1) + *(.data.*) + *(.sdata) + *(.got.plt) + *(.got) + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(4096); + .vendor_cert : + { + *(.vendor_cert) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rel : + { + *(.rel.data) + *(.rel.data.*) + *(.rel.got) + *(.rel.stab) + *(.data.rel.ro.local) + *(.data.rel.local) + *(.data.rel.ro) + *(.data.rel*) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + /DISCARD/ : + { + *(.rel.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} diff --git a/elf_ia64_efi.lds b/elf_ia64_efi.lds new file mode 100644 index 00000000..d8dea4b7 --- /dev/null +++ b/elf_ia64_efi.lds @@ -0,0 +1,75 @@ +OUTPUT_FORMAT("elf64-ia64-little") +OUTPUT_ARCH(ia64) +ENTRY(_start_plabel) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .text : + { + *(.text) + *(.text.*) + *(.gnu.linkonce.t.*) + } + . = ALIGN(4096); + __gp = ALIGN (8) + 0x200000; + .sdata : + { + *(.got.plt) + *(.got) + *(.srodata) + *(.sdata) + *(.sbss) + *(.scommon) + } + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.ctors) + *(.data*) + *(.gnu.linkonce.d*) + *(.plabel) /* data whose relocs we want to ignore */ + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.dynbss) + *(.bss) + *(COMMON) + } + . = ALIGN(4096); + .vendor_cert : + { + *(.vendor_cert) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.text) + *(.rela.data*) + *(.rela.sdata) + *(.rela.got) + *(.rela.gnu.linkonce.d*) + *(.rela.stab) + *(.rela.ctors) + } + . = ALIGN(4096); + .reloc : /* This is the PECOFF .reloc section! */ + { + *(.reloc) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + /DISCARD/ : + { + *(.rela.plabel) + *(.rela.reloc) + *(.IA_64.unwind*) + *(.IA64.unwind*) + } +} diff --git a/elf_x86_64_efi.lds b/elf_x86_64_efi.lds new file mode 100644 index 00000000..f9811028 --- /dev/null +++ b/elf_x86_64_efi.lds @@ -0,0 +1,68 @@ +/* Same as elf_x86_64_fbsd_efi.lds, except for OUTPUT_FORMAT below - KEEP IN SYNC */ +OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64", "elf64-x86-64") +OUTPUT_ARCH(i386:x86-64) +ENTRY(_start) +SECTIONS +{ + . = 0; + ImageBase = .; + .hash : { *(.hash) } /* this MUST come first! */ + . = ALIGN(4096); + .eh_frame : + { + *(.eh_frame) + } + . = ALIGN(4096); + .text : + { + *(.text) + } + . = ALIGN(4096); + .reloc : + { + *(.reloc) + } + . = ALIGN(4096); + .data : + { + *(.rodata*) + *(.got.plt) + *(.got) + *(.data*) + *(.sdata) + /* the EFI loader doesn't seem to like a .bss section, so we stick + it all into .data: */ + *(.sbss) + *(.scommon) + *(.dynbss) + *(.bss) + *(COMMON) + *(.rel.local) + } + . = ALIGN(4096); + .vendor_cert : + { + *(.vendor_cert) + } + . = ALIGN(4096); + .dynamic : { *(.dynamic) } + . = ALIGN(4096); + .rela : + { + *(.rela.data*) + *(.rela.got) + *(.rela.stab) + } + . = ALIGN(4096); + .dynsym : { *(.dynsym) } + . = ALIGN(4096); + .dynstr : { *(.dynstr) } + . = ALIGN(4096); + .ignored.reloc : + { + *(.rela.reloc) + *(.eh_frame) + *(.note.GNU-stack) + } + .comment 0 : { *(.comment) } +} diff --git a/fallback.c b/fallback.c new file mode 100644 index 00000000..82ddbf2f --- /dev/null +++ b/fallback.c @@ -0,0 +1,673 @@ +/* + * Copyright 2012-2013 Red Hat, Inc. + * All rights reserved. + * + * See "COPYING" for license terms. + * + * Author(s): Peter Jones <pjones@redhat.com> + */ + +#include <efi.h> +#include <efilib.h> + +#include "ucs2.h" + +EFI_LOADED_IMAGE *this_image = NULL; + +static EFI_STATUS +get_file_size(EFI_FILE_HANDLE fh, UINT64 *retsize) +{ + EFI_STATUS rc; + void *buffer = NULL; + UINTN bs = 0; + EFI_GUID finfo = EFI_FILE_INFO_ID; + + /* The API here is "Call it once with bs=0, it fills in bs, + * then allocate a buffer and ask again to get it filled. */ + rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL); + if (rc == EFI_BUFFER_TOO_SMALL) { + buffer = AllocateZeroPool(bs); + if (!buffer) { + Print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, + &bs, buffer); + } + /* This checks *either* the error from the first GetInfo, if it isn't + * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call + * in *any* case. */ + if (EFI_ERROR(rc)) { + Print(L"Could not get file info: %d\n", rc); + if (buffer) + FreePool(buffer); + return rc; + } + EFI_FILE_INFO *fi = buffer; + *retsize = fi->FileSize; + FreePool(buffer); + return EFI_SUCCESS; +} + +EFI_STATUS +read_file(EFI_FILE_HANDLE fh, CHAR16 *fullpath, CHAR16 **buffer, UINT64 *bs) +{ + EFI_FILE_HANDLE fh2; + EFI_STATUS rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, fullpath, + EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(rc)) { + Print(L"Couldn't open \"%s\": %d\n", fullpath, rc); + return rc; + } + + UINT64 len = 0; + CHAR16 *b = NULL; + rc = get_file_size(fh2, &len); + if (EFI_ERROR(rc)) { + uefi_call_wrapper(fh2->Close, 1, fh2); + return rc; + } + + b = AllocateZeroPool(len + 2); + if (!buffer) { + Print(L"Could not allocate memory\n"); + uefi_call_wrapper(fh2->Close, 1, fh2); + return EFI_OUT_OF_RESOURCES; + } + + rc = uefi_call_wrapper(fh->Read, 3, fh, &len, b); + if (EFI_ERROR(rc)) { + FreePool(buffer); + uefi_call_wrapper(fh2->Close, 1, fh2); + Print(L"Could not read file: %d\n", rc); + return rc; + } + *buffer = b; + *bs = len; + uefi_call_wrapper(fh2->Close, 1, fh2); + return EFI_SUCCESS; +} + +EFI_STATUS +make_full_path(CHAR16 *dirname, CHAR16 *filename, CHAR16 **out, UINT64 *outlen) +{ + UINT64 len; + + len = StrLen(dirname) + StrLen(filename) + StrLen(L"\\EFI\\\\") + 2; + + CHAR16 *fullpath = AllocateZeroPool(len*sizeof(CHAR16)); + if (!fullpath) { + Print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + + StrCat(fullpath, L"\\EFI\\"); + StrCat(fullpath, dirname); + StrCat(fullpath, L"\\"); + StrCat(fullpath, filename); + + *out = fullpath; + *outlen = len; + return EFI_SUCCESS; +} + +CHAR16 *bootorder = NULL; +int nbootorder = 0; + +EFI_DEVICE_PATH *first_new_option = NULL; +VOID *first_new_option_args = NULL; +UINTN first_new_option_size = 0; + +EFI_STATUS +add_boot_option(EFI_DEVICE_PATH *dp, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) +{ + static int i = 0; + CHAR16 varname[] = L"Boot0000"; + CHAR16 hexmap[] = L"0123456789ABCDEF"; + EFI_GUID global = EFI_GLOBAL_VARIABLE; + EFI_STATUS rc; + + for(; i <= 0xffff; i++) { + varname[4] = hexmap[(i & 0xf000) >> 12]; + varname[5] = hexmap[(i & 0x0f00) >> 8]; + varname[6] = hexmap[(i & 0x00f0) >> 4]; + varname[7] = hexmap[(i & 0x000f) >> 0]; + + void *var = LibGetVariable(varname, &global); + if (!var) { + int size = sizeof(UINT32) + sizeof (UINT16) + + StrLen(label)*2 + 2 + DevicePathSize(dp) + + StrLen(arguments) * 2 + 2; + + CHAR8 *data = AllocateZeroPool(size); + CHAR8 *cursor = data; + *(UINT32 *)cursor = LOAD_OPTION_ACTIVE; + cursor += sizeof (UINT32); + *(UINT16 *)cursor = DevicePathSize(dp); + cursor += sizeof (UINT16); + StrCpy((CHAR16 *)cursor, label); + cursor += StrLen(label)*2 + 2; + CopyMem(cursor, dp, DevicePathSize(dp)); + cursor += DevicePathSize(dp); + StrCpy((CHAR16 *)cursor, arguments); + + Print(L"Creating boot entry \"%s\" with label \"%s\" " + L"for file \"%s\"\n", + varname, label, filename); + rc = uefi_call_wrapper(RT->SetVariable, 5, varname, + &global, EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, data); + + FreePool(data); + + if (EFI_ERROR(rc)) { + Print(L"Could not create variable: %d\n", rc); + return rc; + } + + CHAR16 *newbootorder = AllocateZeroPool(sizeof (CHAR16) + * (nbootorder + 1)); + if (!newbootorder) + return EFI_OUT_OF_RESOURCES; + + int j = 0; + if (nbootorder) { + for (j = 0; j < nbootorder; j++) + newbootorder[j] = bootorder[j]; + FreePool(bootorder); + } + newbootorder[j] = i & 0xffff; + bootorder = newbootorder; + nbootorder += 1; +#ifdef DEBUG_FALLBACK + Print(L"nbootorder: %d\nBootOrder: ", nbootorder); + for (j = 0 ; j < nbootorder ; j++) + Print(L"%04x ", bootorder[j]); + Print(L"\n"); +#endif + + return EFI_SUCCESS; + } + } + return EFI_OUT_OF_RESOURCES; +} + +EFI_STATUS +update_boot_order(void) +{ + CHAR16 *oldbootorder; + UINTN size; + EFI_GUID global = EFI_GLOBAL_VARIABLE; + CHAR16 *newbootorder = NULL; + + oldbootorder = LibGetVariableAndSize(L"BootOrder", &global, &size); + if (oldbootorder) { + int n = size / sizeof (CHAR16) + nbootorder; + + newbootorder = AllocateZeroPool(n * sizeof (CHAR16)); + if (!newbootorder) + return EFI_OUT_OF_RESOURCES; + CopyMem(newbootorder, bootorder, nbootorder * sizeof (CHAR16)); + CopyMem(newbootorder + nbootorder, oldbootorder, size); + size = n * sizeof (CHAR16); + } else { + size = nbootorder * sizeof(CHAR16); + newbootorder = AllocateZeroPool(size); + if (!newbootorder) + return EFI_OUT_OF_RESOURCES; + CopyMem(newbootorder, bootorder, size); + } + +#ifdef DEBUG_FALLBACK + Print(L"nbootorder: %d\nBootOrder: ", size / sizeof (CHAR16)); + int j; + for (j = 0 ; j < size / sizeof (CHAR16); j++) + Print(L"%04x ", newbootorder[j]); + Print(L"\n"); +#endif + + if (oldbootorder) { + LibDeleteVariable(L"BootOrder", &global); + FreePool(oldbootorder); + } + + EFI_STATUS rc; + rc = uefi_call_wrapper(RT->SetVariable, 5, L"BootOrder", &global, + EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | + EFI_VARIABLE_RUNTIME_ACCESS, + size, newbootorder); + FreePool(newbootorder); + return rc; +} + +EFI_STATUS +add_to_boot_list(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *label, CHAR16 *arguments) +{ + CHAR16 *fullpath = NULL; + UINT64 pathlen = 0; + EFI_STATUS rc = EFI_SUCCESS; + + rc = make_full_path(dirname, filename, &fullpath, &pathlen); + if (EFI_ERROR(rc)) + return rc; + + EFI_DEVICE_PATH *dph = NULL, *dpf = NULL, *dp = NULL; + + dph = DevicePathFromHandle(this_image->DeviceHandle); + if (!dph) { + rc = EFI_OUT_OF_RESOURCES; + goto err; + } + + dpf = FileDevicePath(fh, fullpath); + if (!dpf) { + rc = EFI_OUT_OF_RESOURCES; + goto err; + } + + dp = AppendDevicePath(dph, dpf); + if (!dp) { + rc = EFI_OUT_OF_RESOURCES; + goto err; + } + +#ifdef DEBUG_FALLBACK + UINTN s = DevicePathSize(dp); + int i; + UINT8 *dpv = (void *)dp; + for (i = 0; i < s; i++) { + if (i > 0 && i % 16 == 0) + Print(L"\n"); + Print(L"%02x ", dpv[i]); + } + Print(L"\n"); + + CHAR16 *dps = DevicePathToStr(dp); + Print(L"device path: \"%s\"\n", dps); +#endif + if (!first_new_option) { + CHAR16 *dps = DevicePathToStr(dp); + Print(L"device path: \"%s\"\n", dps); + first_new_option = DuplicateDevicePath(dp); + first_new_option_args = arguments; + first_new_option_size = StrLen(arguments) * sizeof (CHAR16); + } + + add_boot_option(dp, fullpath, label, arguments); + +err: + if (dpf) + FreePool(dpf); + if (dp) + FreePool(dp); + if (fullpath) + FreePool(fullpath); + return rc; +} + +EFI_STATUS +populate_stanza(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename, CHAR16 *csv) +{ +#ifdef DEBUG_FALLBACK + Print(L"CSV data: \"%s\"\n", csv); +#endif + CHAR16 *file = csv; + + UINTN comma0 = StrCSpn(csv, L","); + if (comma0 == 0) + return EFI_INVALID_PARAMETER; + file[comma0] = L'\0'; +#ifdef DEBUG_FALLBACK + Print(L"filename: \"%s\"\n", file); +#endif + + CHAR16 *label = csv + comma0 + 1; + UINTN comma1 = StrCSpn(label, L","); + if (comma1 == 0) + return EFI_INVALID_PARAMETER; + label[comma1] = L'\0'; +#ifdef DEBUG_FALLBACK + Print(L"label: \"%s\"\n", label); +#endif + + CHAR16 *arguments = csv + comma0 +1 + comma1 +1; + UINTN comma2 = StrCSpn(arguments, L","); + arguments[comma2] = L'\0'; + /* This one is optional, so don't check if comma2 is 0 */ +#ifdef DEBUG_FALLBACK + Print(L"arguments: \"%s\"\n", arguments); +#endif + + add_to_boot_list(fh, dirname, file, label, arguments); + + return EFI_SUCCESS; +} + +EFI_STATUS +try_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname, CHAR16 *filename) +{ + CHAR16 *fullpath = NULL; + UINT64 pathlen = 0; + EFI_STATUS rc; + + rc = make_full_path(dirname, filename, &fullpath, &pathlen); + if (EFI_ERROR(rc)) + return rc; + +#ifdef DEBUG_FALLBACK + Print(L"Found file \"%s\"\n", fullpath); +#endif + + CHAR16 *buffer; + UINT64 bs; + rc = read_file(fh, fullpath, &buffer, &bs); + if (EFI_ERROR(rc)) { + Print(L"Could not read file \"%s\": %d\n", fullpath, rc); + FreePool(fullpath); + return rc; + } + FreePool(fullpath); + +#ifdef DEBUG_FALLBACK + Print(L"File looks like:\n%s\n", buffer); +#endif + + CHAR16 *start = buffer; + /* The file may or may not start with the Unicode byte order marker. + * Sadness ensues. Since UEFI is defined as LE, I'm going to decree + * that these files must also be LE. + * + * IT IS THUS SO. + * + * But if we find the LE byte order marker, just skip it. + */ + if (*start == 0xfeff) + start++; + while (*start) { + while (*start == L'\r' || *start == L'\n') + start++; + UINTN l = StrCSpn(start, L"\r\n"); + if (l == 0) { + if (start[l] == L'\0') + break; + start++; + continue; + } + CHAR16 c = start[l]; + start[l] = L'\0'; + + populate_stanza(fh, dirname, filename, start); + + start[l] = c; + start += l; + } + + FreePool(buffer); + return EFI_SUCCESS; +} + +EFI_STATUS +find_boot_csv(EFI_FILE_HANDLE fh, CHAR16 *dirname) +{ + EFI_STATUS rc; + void *buffer = NULL; + UINTN bs = 0; + EFI_GUID finfo = EFI_FILE_INFO_ID; + + /* The API here is "Call it once with bs=0, it fills in bs, + * then allocate a buffer and ask again to get it filled. */ + rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, &bs, NULL); + if (rc == EFI_BUFFER_TOO_SMALL) { + buffer = AllocateZeroPool(bs); + if (!buffer) { + Print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + rc = uefi_call_wrapper(fh->GetInfo, 4, fh, &finfo, + &bs, buffer); + } + /* This checks *either* the error from the first GetInfo, if it isn't + * the EFI_BUFFER_TOO_SMALL we're expecting, or the second GetInfo call + * in *any* case. */ + if (EFI_ERROR(rc)) { + Print(L"Could not get info for \"%s\": %d\n", dirname, rc); + if (buffer) + FreePool(buffer); + return rc; + } + + EFI_FILE_INFO *fi = buffer; + if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { + FreePool(buffer); + return EFI_SUCCESS; + } + FreePool(buffer); + + bs = 0; + do { + bs = 0; + rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, NULL); + if (rc == EFI_BUFFER_TOO_SMALL) { + buffer = AllocateZeroPool(bs); + if (!buffer) { + Print(L"Could not allocate memory\n"); + return EFI_OUT_OF_RESOURCES; + } + + rc = uefi_call_wrapper(fh->Read, 3, fh, &bs, buffer); + } + if (EFI_ERROR(rc)) { + Print(L"Could not read \\EFI\\%s\\: %d\n", dirname, rc); + FreePool(buffer); + return rc; + } + if (bs == 0) + break; + + fi = buffer; + + if (!StrCaseCmp(fi->FileName, L"boot.csv")) { + EFI_FILE_HANDLE fh2; + rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, + fi->FileName, + EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(rc) || fh2 == NULL) { + Print(L"Couldn't open \\EFI\\%s\\%s: %d\n", + dirname, fi->FileName, rc); + FreePool(buffer); + buffer = NULL; + continue; + } + rc = try_boot_csv(fh2, dirname, fi->FileName); + uefi_call_wrapper(fh2->Close, 1, fh2); + } + + FreePool(buffer); + buffer = NULL; + } while (bs != 0); + + rc = EFI_SUCCESS; + + return rc; +} + +EFI_STATUS +find_boot_options(EFI_HANDLE device) +{ + EFI_STATUS rc = EFI_SUCCESS; + + EFI_FILE_IO_INTERFACE *fio = NULL; + rc = uefi_call_wrapper(BS->HandleProtocol, 3, device, + &FileSystemProtocol, (void **)&fio); + if (EFI_ERROR(rc)) { + Print(L"Couldn't find file system: %d\n", rc); + return rc; + } + + /* EFI_FILE_HANDLE is a pointer to an EFI_FILE, and I have + * *no idea* what frees the memory allocated here. Hopefully + * Close() does. */ + EFI_FILE_HANDLE fh = NULL; + rc = uefi_call_wrapper(fio->OpenVolume, 2, fio, &fh); + if (EFI_ERROR(rc) || fh == NULL) { + Print(L"Couldn't open file system: %d\n", rc); + return rc; + } + + EFI_FILE_HANDLE fh2 = NULL; + rc = uefi_call_wrapper(fh->Open, 5, fh, &fh2, L"EFI", + EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(rc) || fh2 == NULL) { + Print(L"Couldn't open EFI: %d\n", rc); + uefi_call_wrapper(fh->Close, 1, fh); + return rc; + } + rc = uefi_call_wrapper(fh2->SetPosition, 2, fh2, 0); + if (EFI_ERROR(rc)) { + Print(L"Couldn't set file position: %d\n", rc); + uefi_call_wrapper(fh2->Close, 1, fh2); + uefi_call_wrapper(fh->Close, 1, fh); + return rc; + } + + void *buffer; + UINTN bs; + do { + bs = 0; + rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, NULL); + if (rc == EFI_BUFFER_TOO_SMALL || + (rc == EFI_SUCCESS && bs != 0)) { + buffer = AllocateZeroPool(bs); + if (!buffer) { + Print(L"Could not allocate memory\n"); + /* sure, this might work, why not? */ + uefi_call_wrapper(fh2->Close, 1, fh2); + uefi_call_wrapper(fh->Close, 1, fh); + return EFI_OUT_OF_RESOURCES; + } + + rc = uefi_call_wrapper(fh2->Read, 3, fh2, &bs, buffer); + } + if (bs == 0) + break; + + if (EFI_ERROR(rc)) { + Print(L"Could not read \\EFI\\: %d\n", rc); + if (buffer) { + FreePool(buffer); + buffer = NULL; + } + uefi_call_wrapper(fh2->Close, 1, fh2); + uefi_call_wrapper(fh->Close, 1, fh); + return rc; + } + EFI_FILE_INFO *fi = buffer; + + if (!(fi->Attribute & EFI_FILE_DIRECTORY)) { + FreePool(buffer); + buffer = NULL; + continue; + } + if (!StrCmp(fi->FileName, L".") || + !StrCmp(fi->FileName, L"..") || + !StrCaseCmp(fi->FileName, L"BOOT")) { + FreePool(buffer); + buffer = NULL; + continue; + } +#ifdef DEBUG_FALLBACK + Print(L"Found directory named \"%s\"\n", fi->FileName); +#endif + + EFI_FILE_HANDLE fh3; + rc = uefi_call_wrapper(fh->Open, 5, fh2, &fh3, fi->FileName, + EFI_FILE_READ_ONLY, 0); + if (EFI_ERROR(rc)) { + Print(L"%d Couldn't open %s: %d\n", __LINE__, fi->FileName, rc); + FreePool(buffer); + buffer = NULL; + continue; + } + + rc = find_boot_csv(fh3, fi->FileName); + FreePool(buffer); + buffer = NULL; + if (rc == EFI_OUT_OF_RESOURCES) + break; + + } while (1); + + if (rc == EFI_SUCCESS && nbootorder > 0) + rc = update_boot_order(); + + uefi_call_wrapper(fh2->Close, 1, fh2); + uefi_call_wrapper(fh->Close, 1, fh); + return rc; +} + +static EFI_STATUS +try_start_first_option(EFI_HANDLE parent_image_handle) +{ + EFI_STATUS rc; + EFI_HANDLE image_handle; + + if (!first_new_option) { + return EFI_SUCCESS; + } + + rc = uefi_call_wrapper(BS->LoadImage, 6, 0, parent_image_handle, + first_new_option, NULL, 0, + &image_handle); + if (EFI_ERROR(rc)) { + Print(L"LoadImage failed: %d\n", rc); + uefi_call_wrapper(BS->Stall, 1, 2000000); + return rc; + } + + EFI_LOADED_IMAGE *image; + rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, &LoadedImageProtocol, (void *)&image); + if (!EFI_ERROR(rc)) { + image->LoadOptions = first_new_option_args; + image->LoadOptionsSize = first_new_option_size; + } + + rc = uefi_call_wrapper(BS->StartImage, 3, image_handle, NULL, NULL); + if (EFI_ERROR(rc)) { + Print(L"StartImage failed: %d\n", rc); + uefi_call_wrapper(BS->Stall, 1, 2000000); + } + return rc; +} + +EFI_STATUS +efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *systab) +{ + EFI_STATUS rc; + + InitializeLib(image, systab); + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (void *)&this_image); + if (EFI_ERROR(rc)) { + Print(L"Error: could not find loaded image: %d\n", rc); + return rc; + } + + Print(L"System BootOrder not found. Initializing defaults.\n"); + + rc = find_boot_options(this_image->DeviceHandle); + if (EFI_ERROR(rc)) { + Print(L"Error: could not find boot options: %d\n", rc); + return rc; + } + + try_start_first_option(image); + + Print(L"Reset System\n"); + uefi_call_wrapper(RT->ResetSystem, 4, EfiResetCold, + EFI_SUCCESS, 0, NULL); + + return EFI_SUCCESS; +} @@ -56,15 +56,6 @@ static EFI_IP_ADDRESS tftp_addr; static char *full_path; -/* - * Not in the EFI header set yet, so I have to declare it here - */ -typedef struct { - UINT32 MessageType:8; - UINT32 TransactionId:24; - UINT8 DhcpOptions[1024]; -} EFI_PXE_BASE_CODE_DHCPV6_PACKET; - typedef struct { UINT16 OpCode; UINT16 Length; @@ -41,13 +41,19 @@ #include "signature.h" #include "netboot.h" #include "shim_cert.h" +#include "ucs2.h" -#define SECOND_STAGE L"\\grub.efi" +#define DEFAULT_LOADER L"\\grub.efi" +#define FALLBACK L"\\fallback.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 +887,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); @@ -890,6 +900,66 @@ 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; + unsigned int pathlen = 0; + CHAR16 *bootpath; + EFI_FILE_IO_INTERFACE *fio = NULL; + EFI_FILE *vh; + EFI_FILE *fh; + EFI_STATUS rc; + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle, + &loaded_image_protocol, (void **)&li); + if (EFI_ERROR(rc)) { + Print(L"Could not get image for bootx64.efi: %d\n", rc); + return 0; + } + + bootpath = DevicePathToStr(li->FilePath); + + /* 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; + + rc = uefi_call_wrapper(BS->HandleProtocol, 3, li->DeviceHandle, + &FileSystemProtocol, (void **)&fio); + 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)) { + 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_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); + 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 @@ -930,7 +1000,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); @@ -1192,11 +1263,13 @@ 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); -done: return efi_status; } @@ -1263,7 +1336,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) { @@ -1312,6 +1386,84 @@ 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; + CHAR16 *loader_str = NULL; + int loader_len = 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; + } + + /* + * 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' ') { + *c = L'\0'; + start = c + 1; + remaining_size = li->LoadOptionsSize - i - 2; + break; + } + } + + if (!start || remaining_size <= 0) + return EFI_SUCCESS; + + 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; + } + + 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 +1486,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 @@ -1378,5 +1533,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; } @@ -0,0 +1,92 @@ +/* + * shim - trivial UEFI first-stage bootloader + * + * Copyright 2013 Red Hat, Inc <pjones@redhat.com> + * + * 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; +} + +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; +} + +static inline UINTN +__attribute__((unused)) +StrCSpn(const CHAR16 *s, const CHAR16 *reject) +{ + UINTN ret; + + for (ret = 0; s[ret] != L'\0'; ret++) { + int i; + for (i = 0; reject[i] != L'\0'; i++) { + if (reject[i] == s[ret]) + return ret; + } + } + return ret; +} + +#endif /* SHIM_UCS2_H */ |
