summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteve Langasek <steve.langasek@canonical.com>2013-07-02 15:24:04 +0000
committerSteve Langasek <steve.langasek@canonical.com>2013-07-02 15:24:04 +0000
commitbfab8d6791bccc38a8604cbc933048319c920780 (patch)
treed67bb5c284e745f6c3e38c6a8a7e7e83be16c6cf
parent7f61250830921321a77c879e33177fe0cb6336db (diff)
parentd141608bf820a1f1052b335073cd4c2dc9221d1d (diff)
downloadefi-boot-shim-bfab8d6791bccc38a8604cbc933048319c920780.tar.gz
efi-boot-shim-bfab8d6791bccc38a8604cbc933048319c920780.zip
Import upstream version 0.4
-rw-r--r--.gitignore24
-rw-r--r--Cryptlib/Makefile4
-rw-r--r--Cryptlib/OpenSSL/Makefile6
-rw-r--r--Makefile30
-rw-r--r--MokManager.c517
-rw-r--r--cert.S6
-rw-r--r--dbx.S6
-rw-r--r--elf_ia32_efi.lds69
-rw-r--r--elf_ia64_efi.lds75
-rw-r--r--elf_x86_64_efi.lds68
-rw-r--r--fallback.c673
-rw-r--r--netboot.c9
-rw-r--r--shim.c171
-rw-r--r--ucs2.h92
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
diff --git a/Makefile b/Makefile
index 412eba5e..4bdd6032 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
}
diff --git a/cert.S b/cert.S
index 7f89029e..2ed9b6d7 100644
--- a/cert.S
+++ b/cert.S
@@ -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
diff --git a/dbx.S b/dbx.S
index a26fc387..7b19c5c2 100644
--- a/dbx.S
+++ b/dbx.S
@@ -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;
+}
diff --git a/netboot.c b/netboot.c
index 90fb9cba..c44aeac5 100644
--- a/netboot.c
+++ b/netboot.c
@@ -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;
diff --git a/shim.c b/shim.c
index c3aae9e0..94b97104 100644
--- a/shim.c
+++ b/shim.c
@@ -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;
}
diff --git a/ucs2.h b/ucs2.h
new file mode 100644
index 00000000..d2ad6490
--- /dev/null
+++ b/ucs2.h
@@ -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 */