summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile48
-rw-r--r--MokManager.c1692
-rw-r--r--MokVars.txt51
-rw-r--r--dbx.S32
-rwxr-xr-xmake-certs554
-rw-r--r--netboot.c370
-rw-r--r--netboot.h9
-rw-r--r--shim.c723
-rw-r--r--shim.h22
-rw-r--r--signature.h5
10 files changed, 3354 insertions, 152 deletions
diff --git a/Makefile b/Makefile
index a886c763..412eba5e 100644
--- a/Makefile
+++ b/Makefile
@@ -26,20 +26,48 @@ endif
LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH) -L$(LIB_PATH) -LCryptlib -LCryptlib/OpenSSL $(EFI_CRT_OBJS)
-VERSION = 0.1
+VERSION = 0.2
-TARGET = shim.efi
-OBJS = shim.o cert.o
-SOURCES = shim.c shim.h signature.h PeImage.h
+TARGET = shim.efi MokManager.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
all: $(TARGET)
-shim.o: $(SOURCES)
+shim.crt:
+ ./make-certs shim shim@xn--u4h.net all codesign 1.3.6.1.4.1.311.10.3.1 </dev/null
+
+shim.cer: shim.crt
+ openssl x509 -outform der -in $< -out $@
+
+shim_cert.h: shim.cer
+ echo "static UINT8 shim_cert[] = {" > $@
+ hexdump -v -e '1/1 "0x%02x, "' $< >> $@
+ echo "};" >> $@
+
+certdb/secmod.db: shim.crt
+ -mkdir certdb
+ certutil -A -n 'my CA' -d certdb/ -t CT,CT,CT -i ca.crt
+ pk12util -d certdb/ -i shim.p12 -W "" -K ""
+ certutil -d certdb/ -A -i shim.crt -n shim -t u
+
+shim.o: $(SOURCES) shim_cert.h
cert.o : cert.S
$(CC) $(CFLAGS) -c -o $@ $<
-shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a cert.o
+dbx.o : dbx.S
+ $(CC) $(CFLAGS) -c -o $@ $<
+
+shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a
+ $(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
+
+MokManager.o: $(SOURCES)
+
+MokManager.so: $(MOK_OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a
$(LD) -o $@ $(LDFLAGS) $^ $(EFI_LIBS)
Cryptlib/libcryptlib.a:
@@ -58,12 +86,16 @@ Cryptlib/OpenSSL/libopenssl.a:
-j .rela -j .reloc -j .eh_frame \
-j .debug_info -j .debug_abbrev -j .debug_aranges \
-j .debug_line -j .debug_str -j .debug_ranges \
- --target=efi-app-$(ARCH) $^ shim.efi.debug
+ --target=efi-app-$(ARCH) $^ $@.debug
+
+%.efi.signed: %.efi certdb/secmod.db
+ pesign -n certdb -i $< -c "shim" -s -o $@ -f
clean:
$(MAKE) -C Cryptlib clean
$(MAKE) -C Cryptlib/OpenSSL clean
- rm -f $(TARGET) $(OBJS)
+ rm -rf $(TARGET) $(OBJS) $(MOK_OBJS) $(KEYS) certdb
+ rm -f *.debug *.so
GITTAG = $(VERSION)
diff --git a/MokManager.c b/MokManager.c
new file mode 100644
index 00000000..5802d274
--- /dev/null
+++ b/MokManager.c
@@ -0,0 +1,1692 @@
+#include <efi.h>
+#include <efilib.h>
+#include <Library/BaseCryptLib.h>
+#include <openssl/x509.h>
+#include "shim.h"
+#include "signature.h"
+#include "PeImage.h"
+
+#define PASSWORD_MAX 16
+#define PASSWORD_MIN 8
+#define SB_PASSWORD_LEN 8
+
+#ifndef SHIM_VENDOR
+#define SHIM_VENDOR L"Shim"
+#endif
+
+#define EFI_VARIABLE_APPEND_WRITE 0x00000040
+
+#define CERT_STRING L"Select an X509 certificate to enroll:\n\n"
+#define HASH_STRING L"Select a file to trust:\n\n"
+
+struct menu_item {
+ CHAR16 *text;
+ INTN (* callback)(void *data, void *data2, void *data3);
+ void *data;
+ void *data2;
+ void *data3;
+ UINTN colour;
+};
+
+typedef struct {
+ UINT32 MokSize;
+ UINT8 *Mok;
+} __attribute__ ((packed)) MokListNode;
+
+typedef struct {
+ UINT32 MokSBState;
+ UINT32 PWLen;
+ CHAR16 Password[PASSWORD_MAX];
+} __attribute__ ((packed)) MokSBvar;
+
+static EFI_INPUT_KEY get_keystroke (void)
+{
+ EFI_INPUT_KEY key;
+ UINTN EventIndex;
+
+ uefi_call_wrapper(BS->WaitForEvent, 3, 1, &ST->ConIn->WaitForKey,
+ &EventIndex);
+ uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn, &key);
+
+ return key;
+}
+
+static EFI_STATUS get_sha1sum (void *Data, int DataSize, UINT8 *hash)
+{
+ EFI_STATUS status;
+ unsigned int ctxsize;
+ void *ctx = NULL;
+
+ ctxsize = Sha1GetContextSize();
+ ctx = AllocatePool(ctxsize);
+
+ if (!ctx) {
+ Print(L"Unable to allocate memory for hash context\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!Sha1Init(ctx)) {
+ Print(L"Unable to initialise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ if (!(Sha1Update(ctx, Data, DataSize))) {
+ Print(L"Unable to generate hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ if (!(Sha1Final(ctx, hash))) {
+ Print(L"Unable to finalise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ status = EFI_SUCCESS;
+done:
+ return status;
+}
+
+static MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) {
+ MokListNode *list;
+ EFI_SIGNATURE_LIST *CertList = Data;
+ EFI_SIGNATURE_DATA *Cert;
+ EFI_GUID CertType = EfiCertX509Guid;
+ EFI_GUID HashType = EfiHashSha256Guid;
+ UINTN dbsize = DataSize;
+ UINTN count = 0;
+
+ list = AllocatePool(sizeof(MokListNode) * num);
+
+ if (!list) {
+ Print(L"Unable to allocate MOK list\n");
+ return NULL;
+ }
+
+ while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
+ if ((CompareGuid (&CertList->SignatureType, &CertType) != 0) &&
+ (CompareGuid (&CertList->SignatureType, &HashType) != 0)) {
+ dbsize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
+ CertList->SignatureListSize);
+ continue;
+ }
+
+ if ((CompareGuid (&CertList->SignatureType, &HashType) == 0) &&
+ (CertList->SignatureSize != 48)) {
+ dbsize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *)((UINT8 *) CertList +
+ CertList->SignatureListSize);
+ continue;
+ }
+
+ Cert = (EFI_SIGNATURE_DATA *) (((UINT8 *) CertList) +
+ sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
+
+ list[count].MokSize = CertList->SignatureSize;
+ list[count].Mok = (void *)Cert->SignatureData;
+
+ count++;
+ dbsize -= CertList->SignatureListSize;
+ CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList +
+ CertList->SignatureListSize);
+ }
+
+ return list;
+}
+
+static void print_x509_name (X509_NAME *X509Name, CHAR16 *name)
+{
+ char *str;
+
+ str = X509_NAME_oneline(X509Name, NULL, 0);
+ if (str) {
+ Print(L" %s:\n %a\n", name, str);
+ OPENSSL_free(str);
+ }
+}
+
+static const char *mon[12]= {
+"Jan","Feb","Mar","Apr","May","Jun",
+"Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+static void print_x509_GENERALIZEDTIME_time (ASN1_TIME *time, CHAR16 *time_string)
+{
+ char *v;
+ int gmt = 0;
+ int i;
+ int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
+ char *f = NULL;
+ int f_len = 0;
+
+ i=time->length;
+ v=(char *)time->data;
+
+ if (i < 12)
+ goto error;
+
+ if (v[i-1] == 'Z')
+ gmt=1;
+
+ for (i=0; i<12; i++) {
+ if ((v[i] > '9') || (v[i] < '0'))
+ goto error;
+ }
+
+ y = (v[0]-'0')*1000+(v[1]-'0')*100 + (v[2]-'0')*10+(v[3]-'0');
+ M = (v[4]-'0')*10+(v[5]-'0');
+
+ if ((M > 12) || (M < 1))
+ goto error;
+
+ d = (v[6]-'0')*10+(v[7]-'0');
+ h = (v[8]-'0')*10+(v[9]-'0');
+ m = (v[10]-'0')*10+(v[11]-'0');
+
+ if (time->length >= 14 &&
+ (v[12] >= '0') && (v[12] <= '9') &&
+ (v[13] >= '0') && (v[13] <= '9')) {
+ s = (v[12]-'0')*10+(v[13]-'0');
+ /* Check for fractions of seconds. */
+ if (time->length >= 15 && v[14] == '.') {
+ int l = time->length;
+ f = &v[14]; /* The decimal point. */
+ f_len = 1;
+ while (14 + f_len < l && f[f_len] >= '0' &&
+ f[f_len] <= '9')
+ ++f_len;
+ }
+ }
+
+ SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d%.*a %d%a",
+ mon[M-1], d, h, m, s, f_len, f, y, (gmt)?" GMT":"");
+error:
+ return;
+}
+
+static void print_x509_UTCTIME_time (ASN1_TIME *time, CHAR16 *time_string)
+{
+ char *v;
+ int gmt=0;
+ int i;
+ int y = 0,M = 0,d = 0,h = 0,m = 0,s = 0;
+
+ i=time->length;
+ v=(char *)time->data;
+
+ if (i < 10)
+ goto error;
+
+ if (v[i-1] == 'Z')
+ gmt=1;
+
+ for (i=0; i<10; i++)
+ if ((v[i] > '9') || (v[i] < '0'))
+ goto error;
+
+ y = (v[0]-'0')*10+(v[1]-'0');
+
+ if (y < 50)
+ y+=100;
+
+ M = (v[2]-'0')*10+(v[3]-'0');
+
+ if ((M > 12) || (M < 1))
+ goto error;
+
+ d = (v[4]-'0')*10+(v[5]-'0');
+ h = (v[6]-'0')*10+(v[7]-'0');
+ m = (v[8]-'0')*10+(v[9]-'0');
+
+ if (time->length >=12 &&
+ (v[10] >= '0') && (v[10] <= '9') &&
+ (v[11] >= '0') && (v[11] <= '9'))
+ s = (v[10]-'0')*10+(v[11]-'0');
+
+ SPrint(time_string, 0, L"%a %2d %02d:%02d:%02d %d%a",
+ mon[M-1], d, h, m, s, y+1900, (gmt)?" GMT":"");
+error:
+ return;
+}
+
+static void print_x509_time (ASN1_TIME *time, CHAR16 *name)
+{
+ CHAR16 time_string[30];
+
+ if (time->type == V_ASN1_UTCTIME) {
+ print_x509_UTCTIME_time(time, time_string);
+ } else if (time->type == V_ASN1_GENERALIZEDTIME) {
+ print_x509_GENERALIZEDTIME_time(time, time_string);
+ } else {
+ time_string[0] = '\0';
+ }
+
+ Print(L" %s:\n %s\n", name, time_string);
+}
+
+static void show_x509_info (X509 *X509Cert)
+{
+ ASN1_INTEGER *serial;
+ BIGNUM *bnser;
+ unsigned char hexbuf[30];
+ X509_NAME *X509Name;
+ ASN1_TIME *time;
+
+ serial = X509_get_serialNumber(X509Cert);
+ if (serial) {
+ int i, n;
+ bnser = ASN1_INTEGER_to_BN(serial, NULL);
+ n = BN_bn2bin(bnser, hexbuf);
+ Print(L" Serial Number:\n ");
+ for (i = 0; i < n-1; i++) {
+ Print(L"%02x:", hexbuf[i]);
+ }
+ Print(L"%02x\n", hexbuf[n-1]);
+ }
+
+ X509Name = X509_get_issuer_name(X509Cert);
+ if (X509Name) {
+ print_x509_name(X509Name, L"Issuer");
+ }
+
+ X509Name = X509_get_subject_name(X509Cert);
+ if (X509Name) {
+ print_x509_name(X509Name, L"Subject");
+ }
+
+ time = X509_get_notBefore(X509Cert);
+ if (time) {
+ print_x509_time(time, L"Validity from");
+ }
+
+ time = X509_get_notAfter(X509Cert);
+ if (time) {
+ print_x509_time(time, L"Validity till");
+ }
+}
+
+static void show_mok_info (void *Mok, UINTN MokSize)
+{
+ EFI_STATUS efi_status;
+ UINT8 hash[SHA1_DIGEST_SIZE];
+ unsigned int i;
+ X509 *X509Cert;
+
+ if (!Mok || MokSize == 0)
+ return;
+
+ if (MokSize != 48) {
+ if (X509ConstructCertificate(Mok, MokSize,
+ (UINT8 **) &X509Cert) && X509Cert != NULL) {
+ show_x509_info(X509Cert);
+ X509_free(X509Cert);
+ } else {
+ Print(L" Not a valid X509 certificate: %x\n\n",
+ ((UINT32 *)Mok)[0]);
+ return;
+ }
+
+ efi_status = get_sha1sum(Mok, MokSize, hash);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to compute MOK fingerprint\n");
+ return;
+ }
+
+ Print(L" Fingerprint (SHA1):\n ");
+ for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
+ Print(L" %02x", hash[i]);
+ if (i % 10 == 9)
+ Print(L"\n ");
+ }
+ } else {
+ Print(L"SHA256 hash:\n ");
+ for (i = 0; i < SHA256_DIGEST_SIZE; i++) {
+ Print(L" %02x", ((UINT8 *)Mok)[i]);
+ if (i % 10 == 9)
+ Print(L"\n ");
+ }
+ Print(L"\n");
+ }
+
+ Print(L"\n");
+}
+
+static INTN get_number ()
+{
+ EFI_INPUT_KEY input_key;
+ CHAR16 input[10];
+ int count = 0;
+
+ do {
+ input_key = get_keystroke();
+
+ if ((input_key.UnicodeChar < '0' ||
+ input_key.UnicodeChar > '9' ||
+ count >= 10) &&
+ input_key.UnicodeChar != CHAR_BACKSPACE) {
+ continue;
+ }
+
+ if (count == 0 && input_key.UnicodeChar == CHAR_BACKSPACE)
+ continue;
+
+ Print(L"%c", input_key.UnicodeChar);
+
+ if (input_key.UnicodeChar == CHAR_BACKSPACE) {
+ input[--count] = '\0';
+ continue;
+ }
+
+ input[count++] = input_key.UnicodeChar;
+ } while (input_key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+
+ if (count == 0)
+ return -1;
+
+ input[count] = '\0';
+
+ return (INTN)Atoi(input);
+}
+
+static UINT8 list_keys (void *MokNew, UINTN MokNewSize)
+{
+ 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))) {
+ 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);
+
+ if (!keys) {
+ Print(L"Failed to construct key list in MokNew\n");
+ return 0;
+ }
+
+ do {
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+ 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);
+
+ if (key_num > MokNum) {
+ Print(L"[Key %d]\n", key_num);
+ Print(L"No such key\n\n");
+ } else if (initial != 1 && key_num > 0){
+ Print(L"[Key %d]\n", key_num);
+ show_mok_info(keys[key_num-1].Mok, keys[key_num-1].MokSize);
+ }
+
+ Print(L"Key Number: ");
+
+ key_num = get_number();
+
+ Print(L"\n\n");
+
+ if (key_num == -1)
+ continue;
+
+ initial = 0;
+ } while (key_num != 0);
+
+ FreePool(keys);
+
+ return 1;
+}
+
+static UINT8 get_line (UINT32 *length, CHAR16 *line, UINT32 line_max, UINT8 show)
+{
+ EFI_INPUT_KEY key;
+ int count = 0;
+
+ do {
+ key = get_keystroke();
+
+ if ((count >= line_max &&
+ key.UnicodeChar != CHAR_BACKSPACE) ||
+ key.UnicodeChar == CHAR_NULL ||
+ key.UnicodeChar == CHAR_TAB ||
+ key.UnicodeChar == CHAR_LINEFEED ||
+ key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
+ continue;
+ }
+
+ if (count == 0 && key.UnicodeChar == CHAR_BACKSPACE) {
+ continue;
+ } else if (key.UnicodeChar == CHAR_BACKSPACE) {
+ if (show) {
+ Print(L"\b");
+ }
+ line[--count] = '\0';
+ continue;
+ }
+
+ if (show) {
+ Print(L"%c", key.UnicodeChar);
+ }
+
+ line[count++] = key.UnicodeChar;
+ } while (key.UnicodeChar != CHAR_CARRIAGE_RETURN);
+ Print(L"\n");
+
+ *length = count;
+
+ return 1;
+}
+
+static EFI_STATUS compute_pw_hash (void *MokNew, UINTN MokNewSize, CHAR16 *password,
+ UINT32 pw_length, UINT8 *hash)
+{
+ EFI_STATUS status;
+ unsigned int ctxsize;
+ void *ctx = NULL;
+
+ ctxsize = Sha256GetContextSize();
+ ctx = AllocatePool(ctxsize);
+
+ if (!ctx) {
+ Print(L"Unable to allocate memory for hash context\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (!Sha256Init(ctx)) {
+ Print(L"Unable to initialise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ if (MokNew && MokNewSize) {
+ if (!(Sha256Update(ctx, MokNew, MokNewSize))) {
+ Print(L"Unable to generate hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+ }
+
+ if (!(Sha256Update(ctx, password, pw_length * sizeof(CHAR16)))) {
+ Print(L"Unable to generate hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ if (!(Sha256Final(ctx, hash))) {
+ Print(L"Unable to finalise hash\n");
+ status = EFI_OUT_OF_RESOURCES;
+ goto done;
+ }
+
+ status = EFI_SUCCESS;
+done:
+ return status;
+}
+
+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;
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
+ &shim_lock_guid,
+ &attributes, &auth_size, auth);
+
+
+ if (efi_status != EFI_SUCCESS || auth_size != SHA256_DIGEST_SIZE) {
+ Print(L"Failed to get MokAuth %d\n", efi_status);
+ 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)
+ return EFI_ACCESS_DENIED;
+ }
+
+ if (!MokNewSize) {
+ /* Delete MOK */
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
+ &shim_lock_guid,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 0, NULL);
+ } else {
+ /* Write new MOK */
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList",
+ &shim_lock_guid,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_APPEND_WRITE,
+ MokNewSize, MokNew);
+ }
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set variable %d\n", efi_status);
+ return efi_status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+static UINTN mok_enrollment_prompt (void *MokNew, UINTN MokNewSize, int auth) {
+ CHAR16 line[1];
+ UINT32 length;
+ EFI_STATUS efi_status;
+
+ do {
+ if (!list_keys(MokNew, MokNewSize)) {
+ return 0;
+ }
+
+ Print(L"Enroll the key(s)? (y/n): ");
+
+ get_line (&length, line, 1, 1);
+
+ if (line[0] == 'Y' || line[0] == 'y') {
+ efi_status = store_keys(MokNew, MokNewSize, auth);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to enroll keys\n");
+ return -1;
+ }
+ return 0;
+ }
+ } while (line[0] != 'N' && line[0] != 'n');
+ return -1;
+}
+
+static INTN mok_enrollment_prompt_callback (void *MokNew, void *data2,
+ void *data3)
+{
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+ return mok_enrollment_prompt(MokNew, (UINTN)data2, TRUE);
+}
+
+static INTN mok_deletion_prompt (void *MokNew, void *data2, void *data3) {
+ CHAR16 line[1];
+ UINT32 length;
+ EFI_STATUS efi_status;
+
+ Print(L"Erase all stored keys? (y/N): ");
+
+ get_line (&length, line, 1, 1);
+
+ if (line[0] == 'Y' || line[0] == 'y') {
+ efi_status = store_keys(NULL, 0, TRUE);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to erase keys\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static INTN mok_sb_prompt (void *MokSB, void *data2, void *data3) {
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINTN MokSBSize = (UINTN)data2;
+ MokSBvar *var = MokSB;
+ CHAR16 pass1, pass2, pass3;
+ UINT8 fail_count = 0;
+ UINT32 length;
+ CHAR16 line[1];
+ UINT8 sbval = 1;
+ UINT8 pos1, pos2, pos3;
+
+ if (MokSBSize != sizeof(MokSBvar)) {
+ Print(L"Invalid MokSB variable contents\n");
+ return -1;
+ }
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ while (fail_count < 3) {
+ RandomBytes (&pos1, sizeof(pos1));
+ pos1 = (pos1 % var->PWLen);
+
+ do {
+ RandomBytes (&pos2, sizeof(pos2));
+ pos2 = (pos2 % var->PWLen);
+ } while (pos2 == pos1);
+
+ do {
+ RandomBytes (&pos3, sizeof(pos3));
+ pos3 = (pos3 % var->PWLen) ;
+ } while (pos3 == pos2 || pos3 == pos1);
+
+ Print(L"Enter password character %d: ", pos1 + 1);
+ get_line(&length, &pass1, 1, 0);
+
+ Print(L"Enter password character %d: ", pos2 + 1);
+ get_line(&length, &pass2, 1, 0);
+
+ Print(L"Enter password character %d: ", pos3 + 1);
+ get_line(&length, &pass3, 1, 0);
+
+ if (pass1 != var->Password[pos1] ||
+ pass2 != var->Password[pos2] ||
+ pass3 != var->Password[pos3]) {
+ Print(L"Invalid character\n");
+ fail_count++;
+ } else {
+ break;
+ }
+ }
+
+ if (fail_count >= 3) {
+ Print(L"Password limit reached\n");
+ return -1;
+ }
+
+ if (var->MokSBState == 0) {
+ Print(L"Disable Secure Boot? (y/n): ");
+ } else {
+ Print(L"Enable Secure Boot? (y/n): ");
+ }
+
+ do {
+ get_line (&length, line, 1, 1);
+
+ if (line[0] == 'Y' || line[0] == 'y') {
+ if (var->MokSBState == 0) {
+ efi_status = uefi_call_wrapper(RT->SetVariable,
+ 5, L"MokSBState",
+ &shim_lock_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ 1, &sbval);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set Secure Boot state\n");
+ return -1;
+ }
+ } else {
+ LibDeleteVariable(L"MokSBState",
+ &shim_lock_guid);
+ }
+
+ LibDeleteVariable(L"MokSB", &shim_lock_guid);
+
+ Print(L"Press 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_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];
+
+ if (MokPWSize != SHA256_DIGEST_SIZE) {
+ Print(L"Invalid MokPW variable contents\n");
+ return -1;
+ }
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ SetMem(hash, SHA256_DIGEST_SIZE, 0);
+
+ if (CompareMem(MokPW, hash, SHA256_DIGEST_SIZE) == 0) {
+ Print(L"Clear MOK password? (y/n): ");
+
+ do {
+ get_line (&length, line, 1, 1);
+
+ if (line[0] == 'Y' || line[0] == 'y') {
+ LibDeleteVariable(L"MokPWStore", &shim_lock_guid);
+ LibDeleteVariable(L"MokPW", &shim_lock_guid);
+ }
+ } while (line[0] != 'N' && line[0] != 'n');
+
+ 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) {
+ Print(L"Password limit reached\n");
+ return -1;
+ }
+
+ Print(L"Set MOK password? (y/n): ");
+
+ do {
+ get_line (&length, line, 1, 1);
+
+ if (line[0] == 'Y' || line[0] == 'y') {
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5,
+ L"MokPWStore",
+ &shim_lock_guid,
+ EFI_VARIABLE_NON_VOLATILE |
+ EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ MokPWSize, MokPW);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set MOK password\n");
+ return -1;
+ }
+
+ LibDeleteVariable(L"MokPW", &shim_lock_guid);
+
+ Print(L"Press 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 0;
+}
+
+static UINTN draw_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
+ UINTN count) {
+ UINTN i;
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_WHITE | EFI_BACKGROUND_BLACK);
+
+ Print(L"%s UEFI key management\n\n", SHIM_VENDOR);
+
+ if (header)
+ Print(L"%s", header);
+
+ for (i = 0; i < count; i++) {
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ items[i].colour | EFI_BACKGROUND_BLACK);
+ Print(L" %s\n", items[i].text);
+ }
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0, 0);
+ uefi_call_wrapper(ST->ConOut->EnableCursor, 2, ST->ConOut, TRUE);
+
+ return 2 + lines;
+}
+
+static void free_menu (struct menu_item *items, UINTN count) {
+ UINTN i;
+
+ for (i=0; i<count; i++) {
+ if (items[i].text)
+ FreePool(items[i].text);
+ }
+
+ FreePool(items);
+}
+
+static void update_time (UINTN position, UINTN timeout)
+{
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
+ position);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_BLACK | EFI_BACKGROUND_BLACK);
+
+ Print(L" ", timeout);
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut, 0,
+ position);
+
+ uefi_call_wrapper(ST->ConOut->SetAttribute, 2, ST->ConOut,
+ EFI_WHITE | EFI_BACKGROUND_BLACK);
+
+ if (timeout > 1)
+ Print(L"Booting in %d seconds\n", timeout);
+ else if (timeout)
+ Print(L"Booting in %d second\n", timeout);
+}
+
+static void run_menu (CHAR16 *header, UINTN lines, struct menu_item *items,
+ UINTN count, UINTN timeout) {
+ UINTN index, pos = 0, wait = 0, offset;
+ EFI_INPUT_KEY key;
+ EFI_STATUS status;
+ INTN ret;
+
+ if (timeout)
+ wait = 10000000;
+
+ offset = draw_menu (header, lines, items, count);
+
+ while (1) {
+ update_time(count + offset + 1, timeout);
+
+ uefi_call_wrapper(ST->ConOut->SetCursorPosition, 3, ST->ConOut,
+ 0, pos + offset);
+ status = WaitForSingleEvent(ST->ConIn->WaitForKey, wait);
+
+ if (status == EFI_TIMEOUT) {
+ timeout--;
+ if (!timeout) {
+ free_menu(items, count);
+ return;
+ }
+ continue;
+ }
+
+ wait = 0;
+ timeout = 0;
+
+ uefi_call_wrapper(BS->WaitForEvent, 3, 1,
+ &ST->ConIn->WaitForKey, &index);
+ uefi_call_wrapper(ST->ConIn->ReadKeyStroke, 2, ST->ConIn,
+ &key);
+
+ switch(key.ScanCode) {
+ case SCAN_UP:
+ if (pos == 0)
+ continue;
+ pos--;
+ continue;
+ break;
+ case SCAN_DOWN:
+ if (pos == (count - 1))
+ continue;
+ pos++;
+ continue;
+ break;
+ }
+
+ switch(key.UnicodeChar) {
+ case CHAR_LINEFEED:
+ case CHAR_CARRIAGE_RETURN:
+ if (items[pos].callback == NULL) {
+ free_menu(items, count);
+ return;
+ }
+
+ ret = items[pos].callback(items[pos].data,
+ items[pos].data2,
+ items[pos].data3);
+ if (ret < 0) {
+ Print(L"Press a key to continue\n");
+ Pause();
+ }
+ draw_menu (header, lines, items, count);
+ pos = 0;
+ break;
+ }
+ }
+}
+
+static UINTN verify_certificate(void *cert, UINTN size)
+{
+ X509 *X509Cert;
+ if (!cert || size == 0)
+ return FALSE;
+
+ if (!(X509ConstructCertificate(cert, size, (UINT8 **) &X509Cert)) ||
+ X509Cert == NULL) {
+ Print(L"Invalid X509 certificate\n");
+ Pause();
+ return FALSE;
+ }
+
+ X509_free(X509Cert);
+ return TRUE;
+}
+
+static INTN file_callback (void *data, void *data2, void *data3) {
+ EFI_FILE_INFO *buffer = NULL;
+ UINTN buffersize = 0, mokbuffersize;
+ EFI_STATUS status;
+ EFI_FILE *file;
+ CHAR16 *filename = data;
+ EFI_FILE *parent = data2;
+ BOOLEAN hash = !!data3;
+ EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_SIGNATURE_LIST *CertList;
+ EFI_SIGNATURE_DATA *CertData;
+ void *mokbuffer = NULL;
+
+ status = uefi_call_wrapper(parent->Open, 5, parent, &file, filename,
+ EFI_FILE_MODE_READ, 0);
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ status = uefi_call_wrapper(file->GetInfo, 4, file, &file_info_guid,
+ &buffersize, buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(file->GetInfo, 4, file,
+ &file_info_guid, &buffersize,
+ buffer);
+ }
+
+ if (!buffer)
+ return 0;
+
+ buffersize = buffer->FileSize;
+
+ if (hash) {
+ void *binary;
+ UINT8 sha256[SHA256_DIGEST_SIZE];
+ UINT8 sha1[SHA1_DIGEST_SIZE];
+ SHIM_LOCK *shim_lock;
+ EFI_GUID shim_guid = SHIM_LOCK_GUID;
+ PE_COFF_LOADER_IMAGE_CONTEXT context;
+
+ status = LibLocateProtocol(&shim_guid, (VOID **)&shim_lock);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ mokbuffersize = sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID) +
+ SHA256_DIGEST_SIZE;
+
+ mokbuffer = AllocatePool(mokbuffersize);
+
+ if (!mokbuffer)
+ goto out;
+
+ binary = AllocatePool(buffersize);
+
+ status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
+ binary);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ status = shim_lock->Context(binary, buffersize, &context);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ status = shim_lock->Hash(binary, buffersize, &context, sha256,
+ sha1);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ CertList = mokbuffer;
+ CertList->SignatureType = EfiHashSha256Guid;
+ CertList->SignatureSize = 16 + SHA256_DIGEST_SIZE;
+ CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
+ sizeof(EFI_SIGNATURE_LIST));
+ CopyMem(CertData->SignatureData, sha256, SHA256_DIGEST_SIZE);
+ } else {
+ mokbuffersize = buffersize + sizeof(EFI_SIGNATURE_LIST) +
+ sizeof(EFI_GUID);
+ mokbuffer = AllocatePool(mokbuffersize);
+
+ if (!mokbuffer)
+ goto out;
+
+ CertList = mokbuffer;
+ CertList->SignatureType = EfiCertX509Guid;
+ CertList->SignatureSize = 16 + buffersize;
+ status = uefi_call_wrapper(file->Read, 3, file, &buffersize,
+ mokbuffer + sizeof(EFI_SIGNATURE_LIST) + 16);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+ CertData = (EFI_SIGNATURE_DATA *)(((UINT8 *)mokbuffer) +
+ sizeof(EFI_SIGNATURE_LIST));
+ }
+
+ CertList->SignatureListSize = mokbuffersize;
+ CertList->SignatureHeaderSize = 0;
+ CertData->SignatureOwner = shim_lock_guid;
+
+ if (!hash) {
+ if (!verify_certificate(CertData->SignatureData, buffersize))
+ goto out;
+ }
+
+ mok_enrollment_prompt(mokbuffer, mokbuffersize, FALSE);
+out:
+ if (buffer)
+ FreePool(buffer);
+
+ if (mokbuffer)
+ FreePool(mokbuffer);
+
+ return 0;
+}
+
+static INTN directory_callback (void *data, void *data2, void *data3) {
+ EFI_FILE_INFO *buffer = NULL;
+ UINTN buffersize = 0;
+ EFI_STATUS status;
+ UINTN dircount = 0, i = 0;
+ struct menu_item *dircontent;
+ EFI_FILE *dir;
+ CHAR16 *filename = data;
+ EFI_FILE *root = data2;
+ BOOLEAN hash = !!data3;
+
+ status = uefi_call_wrapper(root->Open, 5, root, &dir, filename,
+ EFI_FILE_MODE_READ, 0);
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ while (1) {
+ status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(dir->Read, 3, dir,
+ &buffersize, buffer);
+ }
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ if (!buffersize)
+ break;
+
+ if ((StrCmp(buffer->FileName, L".") == 0) ||
+ (StrCmp(buffer->FileName, L"..") == 0))
+ continue;
+
+ dircount++;
+
+ FreePool(buffer);
+ buffersize = 0;
+ }
+
+ dircount++;
+
+ dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
+
+ dircontent[0].text = StrDuplicate(L"..");
+ dircontent[0].callback = NULL;
+ dircontent[0].colour = EFI_YELLOW;
+ i++;
+
+ uefi_call_wrapper(dir->SetPosition, 2, dir, 0);
+
+ while (1) {
+ status = uefi_call_wrapper(dir->Read, 3, dir, &buffersize,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(dir->Read, 3, dir,
+ &buffersize, buffer);
+ }
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ if (!buffersize)
+ break;
+
+ if ((StrCmp(buffer->FileName, L".") == 0) ||
+ (StrCmp(buffer->FileName, L"..") == 0))
+ continue;
+
+ if (buffer->Attribute & EFI_FILE_DIRECTORY) {
+ dircontent[i].text = StrDuplicate(buffer->FileName);
+ dircontent[i].callback = directory_callback;
+ dircontent[i].data = dircontent[i].text;
+ dircontent[i].data2 = dir;
+ dircontent[i].data3 = data3;
+ dircontent[i].colour = EFI_YELLOW;
+ } else {
+ dircontent[i].text = StrDuplicate(buffer->FileName);
+ dircontent[i].callback = file_callback;
+ dircontent[i].data = dircontent[i].text;
+ dircontent[i].data2 = dir;
+ dircontent[i].data3 = data3;
+ dircontent[i].colour = EFI_WHITE;
+ }
+
+ i++;
+ FreePool(buffer);
+ buffersize = 0;
+ buffer = NULL;
+ }
+
+ if (hash)
+ run_menu(HASH_STRING, 2, dircontent, dircount, 0);
+ else
+ run_menu(CERT_STRING, 2, dircontent, dircount, 0);
+
+ return 0;
+}
+
+static INTN filesystem_callback (void *data, void *data2, void *data3) {
+ EFI_FILE_INFO *buffer = NULL;
+ UINTN buffersize = 0;
+ EFI_STATUS status;
+ UINTN dircount = 0, i = 0;
+ struct menu_item *dircontent;
+ EFI_FILE *root = data;
+ BOOLEAN hash = !!data3;
+
+ uefi_call_wrapper(root->SetPosition, 2, root, 0);
+
+ while (1) {
+ status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(root->Read, 3, root,
+ &buffersize, buffer);
+ }
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ if (!buffersize)
+ break;
+
+ if ((StrCmp(buffer->FileName, L".") == 0) ||
+ (StrCmp(buffer->FileName, L"..") == 0))
+ continue;
+
+ dircount++;
+
+ FreePool(buffer);
+ buffersize = 0;
+ }
+
+ dircount++;
+
+ dircontent = AllocatePool(sizeof(struct menu_item) * dircount);
+
+ dircontent[0].text = StrDuplicate(L"Return to filesystem list");
+ dircontent[0].callback = NULL;
+ dircontent[0].colour = EFI_YELLOW;
+ i++;
+
+ uefi_call_wrapper(root->SetPosition, 2, root, 0);
+
+ while (1) {
+ status = uefi_call_wrapper(root->Read, 3, root, &buffersize,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(root->Read, 3, root,
+ &buffersize, buffer);
+ }
+
+ if (status != EFI_SUCCESS)
+ return 1;
+
+ if (!buffersize)
+ break;
+
+ if ((StrCmp(buffer->FileName, L".") == 0) ||
+ (StrCmp(buffer->FileName, L"..") == 0))
+ continue;
+
+ if (buffer->Attribute & EFI_FILE_DIRECTORY) {
+ dircontent[i].text = StrDuplicate(buffer->FileName);
+ dircontent[i].callback = directory_callback;
+ dircontent[i].data = dircontent[i].text;
+ dircontent[i].data2 = root;
+ dircontent[i].data3 = data3;
+ dircontent[i].colour = EFI_YELLOW;
+ } else {
+ dircontent[i].text = StrDuplicate(buffer->FileName);
+ dircontent[i].callback = file_callback;
+ dircontent[i].data = dircontent[i].text;
+ dircontent[i].data2 = root;
+ dircontent[i].data3 = data3;
+ dircontent[i].colour = EFI_WHITE;
+ }
+
+ i++;
+ FreePool(buffer);
+ buffer = NULL;
+ buffersize = 0;
+ }
+
+ if (hash)
+ run_menu(HASH_STRING, 2, dircontent, dircount, 0);
+ else
+ run_menu(CERT_STRING, 2, dircontent, dircount, 0);
+
+ return 0;
+}
+
+static INTN find_fs (void *data, void *data2, void *data3) {
+ EFI_GUID fs_guid = SIMPLE_FILE_SYSTEM_PROTOCOL;
+ UINTN count, i;
+ UINTN OldSize, NewSize;
+ EFI_HANDLE *filesystem_handles = NULL;
+ struct menu_item *filesystems;
+ BOOLEAN hash = !!data3;
+
+ uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol, &fs_guid,
+ NULL, &count, &filesystem_handles);
+
+ if (!count || !filesystem_handles) {
+ Print(L"No filesystems?\n");
+ return 1;
+ }
+
+ count++;
+
+ filesystems = AllocatePool(sizeof(struct menu_item) * count);
+
+ filesystems[0].text = StrDuplicate(L"Exit");
+ filesystems[0].callback = NULL;
+ filesystems[0].colour = EFI_YELLOW;
+
+ for (i=1; i<count; i++) {
+ EFI_HANDLE fs = filesystem_handles[i-1];
+ EFI_FILE_IO_INTERFACE *fs_interface;
+ EFI_DEVICE_PATH *path;
+ EFI_FILE *root;
+ EFI_STATUS status;
+ CHAR16 *VolumeLabel = NULL;
+ EFI_FILE_SYSTEM_INFO *buffer = NULL;
+ UINTN buffersize = 0;
+ EFI_GUID file_info_guid = EFI_FILE_INFO_ID;
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3, fs, &fs_guid,
+ (void **)&fs_interface);
+
+ if (status != EFI_SUCCESS || !fs_interface)
+ continue;
+
+ path = DevicePathFromHandle(fs);
+
+ status = uefi_call_wrapper(fs_interface->OpenVolume, 2,
+ fs_interface, &root);
+
+ if (status != EFI_SUCCESS || !root)
+ continue;
+
+ status = uefi_call_wrapper(root->GetInfo, 4, root,
+ &file_info_guid, &buffersize,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ buffer = AllocatePool(buffersize);
+ status = uefi_call_wrapper(root->GetInfo, 4, root,
+ &file_info_guid,
+ &buffersize, buffer);
+ }
+
+ if (status == EFI_SUCCESS)
+ VolumeLabel = buffer->VolumeLabel;
+
+ if (path)
+ filesystems[i].text = DevicePathToStr(path);
+ else
+ filesystems[i].text = StrDuplicate(L"Unknown device\n");
+ if (VolumeLabel) {
+ OldSize = (StrLen(filesystems[i].text) + 1) * sizeof(CHAR16);
+ NewSize = OldSize + StrLen(VolumeLabel) * sizeof(CHAR16);
+ filesystems[i].text = ReallocatePool(filesystems[i].text,
+ OldSize, NewSize);
+ StrCat(filesystems[i].text, VolumeLabel);
+ }
+
+ if (buffersize)
+ FreePool(buffer);
+
+ filesystems[i].data = root;
+ filesystems[i].data2 = NULL;
+ filesystems[i].data3 = data3;
+ filesystems[i].callback = filesystem_callback;
+ filesystems[i].colour = EFI_YELLOW;
+ }
+
+ uefi_call_wrapper(BS->FreePool, 1, filesystem_handles);
+
+ if (hash)
+ run_menu(HASH_STRING, 2, filesystems, count, 0);
+ else
+ run_menu(CERT_STRING, 2, filesystems, count, 0);
+
+ return 0;
+}
+
+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",
+ &shim_lock_guid, &attributes, &size,
+ pwhash);
+
+ /*
+ * If anything can attack the password it could just set it to a
+ * known value, so there's no safety advantage in failing to validate
+ * purely because of a failure to read the variable
+ */
+ if (efi_status != EFI_SUCCESS)
+ return TRUE;
+
+ if (attributes & EFI_VARIABLE_RUNTIME_ACCESS)
+ return TRUE;
+
+ 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;
+ }
+
+ Print(L"Password limit reached\n");
+ return FALSE;
+}
+
+static EFI_STATUS enter_mok_menu(EFI_HANDLE image_handle, void *MokNew,
+ UINTN MokNewSize, void *MokSB,
+ UINTN MokSBSize, void *MokPW, UINTN MokPWSize)
+{
+ struct menu_item *menu_item;
+ UINT32 MokAuth = 0;
+ UINTN menucount = 3, i = 0;
+ EFI_STATUS efi_status;
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ UINT8 auth[SHA256_DIGEST_SIZE];
+ UINTN auth_size = SHA256_DIGEST_SIZE;
+ UINT32 attributes;
+
+ if (verify_pw() == FALSE)
+ return EFI_ACCESS_DENIED;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"MokAuth",
+ &shim_lock_guid,
+ &attributes, &auth_size, auth);
+
+ if ((efi_status == EFI_SUCCESS) && (auth_size == SHA256_DIGEST_SIZE))
+ MokAuth = 1;
+
+ if (MokNew || MokAuth)
+ menucount++;
+
+ if (MokSB)
+ menucount++;
+
+ if (MokPW)
+ menucount++;
+
+ menu_item = AllocateZeroPool(sizeof(struct menu_item) * menucount);
+
+ if (!menu_item)
+ return EFI_OUT_OF_RESOURCES;
+
+ menu_item[i].text = StrDuplicate(L"Continue boot");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = NULL;
+
+ i++;
+
+ if (MokNew || MokAuth) {
+ if (!MokNew) {
+ menu_item[i].text = StrDuplicate(L"Delete MOK");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = mok_deletion_prompt;
+ } else {
+ menu_item[i].text = StrDuplicate(L"Enroll MOK");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].data = MokNew;
+ menu_item[i].data2 = (void *)MokNewSize;
+ menu_item[i].callback = mok_enrollment_prompt_callback;
+ }
+ i++;
+ }
+
+ if (MokSB) {
+ menu_item[i].text = StrDuplicate(L"Change Secure Boot state");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = mok_sb_prompt;
+ menu_item[i].data = MokSB;
+ menu_item[i].data2 = (void *)MokSBSize;
+ i++;
+ }
+
+ if (MokPW) {
+ menu_item[i].text = StrDuplicate(L"Set MOK password");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = mok_pw_prompt;
+ menu_item[i].data = MokPW;
+ menu_item[i].data2 = (void *)MokPWSize;
+ i++;
+ }
+
+ menu_item[i].text = StrDuplicate(L"Enroll key from disk");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = find_fs;
+ menu_item[i].data3 = (void *)FALSE;
+
+ i++;
+
+ menu_item[i].text = StrDuplicate(L"Enroll hash from disk");
+ menu_item[i].colour = EFI_WHITE;
+ menu_item[i].callback = find_fs;
+ menu_item[i].data3 = (void *)TRUE;
+
+ i++;
+
+ run_menu(NULL, 0, menu_item, menucount, 10);
+
+ uefi_call_wrapper(ST->ConOut->ClearScreen, 1, ST->ConOut);
+
+ return 0;
+}
+
+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;
+ void *MokNew = NULL;
+ void *MokSB = NULL;
+ void *MokPW = NULL;
+
+ MokNew = LibGetVariableAndSize(L"MokNew", &shim_lock_guid, &MokNewSize);
+
+ 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);
+
+ if (MokNew) {
+ if (LibDeleteVariable(L"MokNew", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to delete MokNew\n");
+ }
+ FreePool (MokNew);
+ }
+
+ if (MokSB) {
+ if (LibDeleteVariable(L"MokSB", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to delete MokSB\n");
+ }
+ FreePool (MokNew);
+ }
+
+ if (MokPW) {
+ if (LibDeleteVariable(L"MokPW", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to delete MokPW\n");
+ }
+ FreePool (MokNew);
+ }
+
+ LibDeleteVariable(L"MokAuth", &shim_lock_guid);
+
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS setup_rand (void)
+{
+ EFI_TIME time;
+ EFI_STATUS efi_status;
+ UINT64 seed;
+ BOOLEAN status;
+
+ efi_status = uefi_call_wrapper(RT->GetTime, 2, &time, NULL);
+
+ if (efi_status != EFI_SUCCESS)
+ return efi_status;
+
+ seed = ((UINT64)time.Year << 48) | ((UINT64)time.Month << 40) |
+ ((UINT64)time.Day << 32) | ((UINT64)time.Hour << 24) |
+ ((UINT64)time.Minute << 16) | ((UINT64)time.Second << 8) |
+ ((UINT64)time.Daylight);
+
+ status = RandomSeed((UINT8 *)&seed, sizeof(seed));
+
+ if (!status)
+ return EFI_ABORTED;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab)
+{
+ EFI_STATUS efi_status;
+
+ InitializeLib(image_handle, systab);
+
+ setup_rand();
+
+ efi_status = check_mok_request(image_handle);
+
+ return efi_status;
+}
diff --git a/MokVars.txt b/MokVars.txt
new file mode 100644
index 00000000..74f09083
--- /dev/null
+++ b/MokVars.txt
@@ -0,0 +1,51 @@
+Variables used by Shim and Mokmanager
+
+Request variables:
+
+MokPW: Set by MokUtil when setting a password. A SHA-256 hash of the
+UCS-2 representation of the password. The user will be asked to
+re-enter the password to confirm. If the hash of the entered password
+matches the contents of MokPW, the user will be prompted to copy MokPW
+into MokPWState. BS,RT,NV
+
+MokSB: Set by MokUtil when requesting a change in state of signature
+validation. A packed structure as follows:
+
+typedef struct {
+ UINT32 MokSBState;
+ UINT32 PWLen;
+ CHAR16 Password[PASSWORD_MAX];
+} __attribute__ ((packed)) MokSBvar;
+
+If MokSBState is 0, the user will be prompted to disable signature
+validation. Otherwise, the user will be prompted to enable it. PWLen
+is the length of the password, in characters. Password is a UCS-2
+representation of the password. The user will be prompted to enter
+three randomly chosen characters from the password. If successful,
+they will then be prompted to change the signature validation
+according to MokSBState. BS,RT,NV
+
+MokNew: Set by MokUtil when requesting the addition or removal of keys
+from MokList. Is an EFI_SIGNATURE_LIST as described in the UEFI
+specification. BS,RT,NV
+
+MokAuth: A hash dependent upon the contents of MokNew and the sealing
+password. The user's password in UCS-2 form should be appended to the
+contents of MokNew and a SHA-256 hash generated and stored in MokAuth.
+The hash will be regenerated by MokManager after the user is requested
+to enter their password to confirm enrolment of the keys. If the hash
+matches MokAuth, the user will be prompted to enrol the keys. BS,RT,NV
+
+State variables:
+
+MokList: A list of whitelisted keys and hashes. An EFI_SIGNATURE_LIST
+as described in the UEFI specification. BS,NV
+
+MokListRT: A copy of MokList made available to the kernel at runtime. RT
+
+MokSBState: An 8-bit unsigned integer. If 1, shim will switch to
+insecure mode. BS,NV
+
+MokPWStore: A SHA-256 representation of the password set by the user
+via MokPW. The user will be prompted to enter this password in order
+to interact with MokManager.
diff --git a/dbx.S b/dbx.S
new file mode 100644
index 00000000..a26fc387
--- /dev/null
+++ b/dbx.S
@@ -0,0 +1,32 @@
+#if defined(VENDOR_DBX_FILE)
+ .globl vendor_dbx_size
+ .data
+ .align 1
+ .type vendor_dbx_size, @object
+ .size vendor_dbx_size, 4
+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
+vendor_dbx:
+.incbin VENDOR_DBX_FILE
+.L0:
+#else
+ .globl vendor_dbx
+ .bss
+ .type vendor_dbx, @object
+ .size vendor_dbx, 1
+vendor_dbx:
+ .zero 1
+
+ .globl vendor_dbx_size
+ .data
+ .align 4
+ .type vendor_dbx_size, @object
+ .size vendor_dbx_size, 4
+vendor_dbx_size:
+ .long 0
+#endif
diff --git a/make-certs b/make-certs
new file mode 100755
index 00000000..3e9293b2
--- /dev/null
+++ b/make-certs
@@ -0,0 +1,554 @@
+#!/bin/bash -e
+#
+# Generate a root CA cert for signing, and then a subject cert.
+# Usage: make-certs.sh hostname [user[@domain]] [more ...]
+# For testing only, probably still has some bugs in it.
+#
+
+DOMAIN=xn--u4h.net
+DAYS=365
+KEYTYPE=RSA
+KEYSIZE=2048
+DIGEST=SHA256
+CRLHOURS=24
+CRLDAYS=
+
+# Cleanup temporary files at exit.
+touch openssl.cnf
+newcertdir=`mktemp -d`
+cleanup() {
+ test -f openssl.cnf && rm -f openssl.cnf
+ test -f ca.txt && rm -f ca.txt
+ test -f ocsp.txt && rm -f ocsp.txt
+ test -n "$newcertdir" && rm -fr "$newcertdir"
+}
+trap cleanup EXIT
+
+# The first argument is either a common name value or a flag indicating that
+# we're doing something other than issuing a cert.
+commonname="$1"
+refresh_crl=false
+revoke_cert=false
+ocsp_serve=false
+if test "x$commonname" = "x-refresh-crl" ; then
+ refresh_crl=true
+ commonname="$1"
+fi
+if test "x$commonname" = "x-refresh_crl" ; then
+ refresh_crl=true
+ commonname="$1"
+fi
+if test "x$commonname" = "x-revoke" ; then
+ revoke_cert=true
+ shift
+ commonname="$1"
+fi
+if test "x$commonname" = "x-ocsp" ; then
+ ocsp_serve=true
+ commonname="$1"
+fi
+if test "x$commonname" = x ; then
+ echo Usage: `basename $0` 'commonname' user'[@domain]' '[more [...]]'
+ echo Usage: `basename $0` -revoke 'commonname'
+ echo Usage: `basename $0` -ocsp
+ echo Usage: `basename $0` -refresh-crl
+ echo More:
+ echo -e \\tKey usage: "[sign|signing|encrypt|encryption|all]"
+ echo -e \\tAuthority Access Info OCSP responder: "ocsp:URI"
+ echo -e \\tCRL distribution point: "crl:URI"
+ echo -e \\tSubject Alternative Name:
+ echo -e \\t\\tHostname: "*"
+ echo -e \\t\\tIP address: w.x.y.z
+ echo -e \\t\\tEmail address: "*@*.com/edu/net/org/local"
+ echo -e \\t\\tKerberos principal name: "*@*.COM/EDU/NET/ORG/LOCAL"
+ echo -e \\tExtended key usage:
+ echo -e \\t\\t1....
+ echo -e \\t\\t2....
+ echo -e \\t\\tid-kp-server-auth \| tls-server
+ echo -e \\t\\tid-kp-client-auth \| tls-client
+ echo -e \\t\\tid-kp-email-protection \| email
+ echo -e \\t\\tid-ms-kp-sc-logon \| id-ms-sc-logon
+ echo -e \\t\\tid-pkinit-kp-client-auth \| id-pkinit-client
+ echo -e \\t\\tid-pkinit-kp-kdc \| id-pkinit-kdc
+ echo -e \\t\\tca \| CA
+ exit 1
+fi
+
+# Choose a user name part for email attributes.
+GIVENUSER=$2
+test x"$GIVENUSER" = x && GIVENUSER=$USER
+echo "$GIVENUSER" | grep -q @ || GIVENUSER="$GIVENUSER"@$DOMAIN
+DOMAIN=`echo "$GIVENUSER" | cut -f2- -d@`
+
+shift || true
+shift || true
+
+# Done already?
+done=:
+
+keygen() {
+ case "$KEYTYPE" in
+ DSA)
+ openssl dsaparam -out "$1".param $KEYSIZE
+ openssl gendsa "$1".param
+ ;;
+ RSA|*)
+ #openssl genrsa $KEYSIZE -passout pass:qweqwe
+ openssl genrsa $KEYSIZE
+ #openssl genrsa $KEYSIZE -nodes
+ ;;
+ esac
+}
+
+# Set some defaults.
+CA=FALSE
+if test -s ca.crldp.uri.txt ; then
+ crlval="`cat ca.crldp.uri.txt`"
+ crl="URI:$crlval"
+fi
+if test -s ca.ocsp.uri.txt ; then
+ aiaval="`cat ca.ocsp.uri.txt`"
+ aia="OCSP;URI:$aiaval"
+fi
+if test -s ca.domain.txt ; then
+ domval="`cat ca.domain.txt`"
+ if test -n "$domval" ; then
+ DOMAIN="$domval"
+ fi
+fi
+
+# Parse the arguments which indicate what sort of information we want.
+while test $# -gt 0 ; do
+ type=
+ value="$1"
+ case "$value" in
+ RSA|rsa)
+ KEYTYPE=RSA
+ ;;
+ DSA|dsa)
+ KEYTYPE=DSA
+ ;;
+ OCSP:*|ocsp:*)
+ aiaval=`echo "$value" | cut -f2- -d:`
+ aia="OCSP;URI:$aiaval"
+ ;;
+ CRL:*|crl:*)
+ crlval=`echo "$value" | cut -f2- -d:`
+ crl="URI:$crlval"
+ ;;
+ signing|sign)
+ keyusage="${keyusage:+${keyusage},}nonRepudiation,digitalSignature"
+ ;;
+ encryption|encrypt)
+ keyusage="${keyusage:+${keyusage},}keyEncipherment,dataEncipherment"
+ ;;
+ all)
+ keyusage="digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign,encipherOnly,decipherOnly"
+ ;;
+ ca|CA)
+ CA=TRUE
+ keyusage="${keyusage:+${keyusage},}nonRepudiation,digitalSignature,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign"
+ ;;
+ 1.*|2.*|id-*|tls-*|email|mail|codesign)
+ ekuval=`echo "$value" | tr '[A-Z]' '[a-z]' | sed 's,\-,,g'`
+ case "$ekuval" in
+ idkpserverauth|tlsserver) ekuval=1.3.6.1.5.5.7.3.1;;
+ idkpclientauth|tlsclient) ekuval=1.3.6.1.5.5.7.3.2;;
+ idkpemailprotection|email|mail) ekuval=1.3.6.1.5.5.7.3.4;;
+ idkpcodesign|codesign) ekuval=1.3.6.1.5.5.7.3.3;;
+ idmskpsclogon|idmssclogon) ekuval=1.3.6.1.4.1.311.20.2.2;;
+ idpkinitkpclientauth|idpkinitclient) ekuval=1.3.6.1.5.2.3.4;;
+ idpkinitkpkdc|idpkinitkdc) ekuval=1.3.6.1.5.2.3.5;;
+ esac
+ if test -z "$eku" ; then
+ eku="$ekuval"
+ else
+ eku="$eku,$ekuval"
+ fi
+ ;;
+ *@*.COM|*@*.EDU|*@*.NET|*@*.ORG|*@*.LOCAL)
+ luser=`echo "$value" | tr '[A-Z]' '[a-z]'`
+ if test "$luser" = "$value" ; then
+ luser=
+ fi
+ type="otherName:1.3.6.1.5.2.2;SEQUENCE:$value,${luser:+otherName:1.3.6.1.4.1.311.20.2.3;UTF8:${luser},}otherName:1.3.6.1.4.1.311.20.2.3;UTF8"
+ unset luser
+ principals="$principals $value"
+ ;;
+ *@*.com|*@*.edu|*@*.net|*@*.org|*@*.local) type=email;;
+ [0-9]*.[0-9]*.[0-9]*.[0-9]*) type=IP;;
+ *) type=DNS;;
+ esac
+ if test -n "$type" ; then
+ newvalue="${type}:$value"
+ if test -z "$altnames" ; then
+ altnames="${newvalue}"
+ else
+ altnames="${altnames},${newvalue}"
+ fi
+ fi
+ shift
+done
+
+# Build the configuration file, including bits on how to construct the CA
+# certificate, an OCSP responder certificate, and the issued certificate.
+cat > openssl.cnf <<- EOF
+[ca]
+default_ca = issuer
+
+[issuer]
+private_key = `pwd`/ca.key
+certificate = `pwd`/ca.crt
+database = `pwd`/ca.db
+serial = `pwd`/ca.srl
+default_md = $DIGEST
+new_certs_dir = $newcertdir
+policy = no_policy
+
+[no_policy]
+
+[req_oids]
+domainComponent = 0.9.2342.19200300.100.1.25
+
+[req_ca]
+prompt = no
+oid_section = req_oids
+distinguished_name = req_ca_name
+default_md = $DIGEST
+subjectKeyIdentifier=hash
+
+[req_ca_name]
+C=US
+#stateOrProvinceName=SomeState
+localityName=SomeCity
+O=SomeOrg
+EOF
+#echo $DOMAIN | awk 'BEGIN {FS="."}{for(i=NF;i>0;i--){print NF-i ".domainComponent="$i;}}' >> openssl.cnf
+cat >> openssl.cnf <<- EOF
+#commonName = Test Certifying CA
+
+[v3_ca]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always
+#authorityKeyIdentifier=keyid:always,issuer:always
+keyUsage=nonRepudiation,digitalSignature,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
+basicConstraints=critical,CA:TRUE
+nsComment="Testing CA Certificate"
+EOF
+if test -n "$aia" ; then
+ echo "authorityInfoAccess = ${aia}" >> openssl.cnf
+ echo -n "$aiaval" > ca.ocsp.uri.txt
+fi
+if test -n "$crl" ; then
+ echo "crlDistributionPoints = ${crl}" >> openssl.cnf
+ echo -n "$crlval" > ca.crldp.uri.txt
+fi
+echo "$DOMAIN" > ca.domain.txt
+cat >> openssl.cnf <<- EOF
+
+[req_ocsp]
+prompt = no
+oid_section = req_oids
+distinguished_name = req_ocsp_name
+default_md = $DIGEST
+
+[req_ocsp_name]
+C=US
+#stateOrProvinceName=SomeState
+localityName=SomeOrg
+O=SomeOrg
+EOF
+#echo $DOMAIN | awk 'BEGIN {FS="."}{for(i=NF;i>0;i--){print NF-i ".domainComponent="$i;}}' >> openssl.cnf
+cat >> openssl.cnf <<- EOF
+#commonName = OCSP Signer for Test Certifying CA
+
+[v3_ocsp]
+subjectKeyIdentifier=hash
+#authorityKeyIdentifier=keyid:always,issuer:always
+authorityKeyIdentifier=keyid:always
+keyUsage=digitalSignature,nonRepudiation,keyEncipherment,dataEncipherment,keyAgreement,keyCertSign,cRLSign
+extendedKeyUsage=1.3.6.1.5.5.7.3.9
+#basicConstraints=CA:FALSE
+basicConstraints=CA:TRUE
+nsComment="Testing OCSP Certificate"
+1.3.6.1.5.5.7.48.1.5=ASN1:NULL
+EOF
+if test -n "$aia" ; then
+ echo "authorityInfoAccess = ${aia}" >> openssl.cnf
+fi
+if test -n "$crl" ; then
+ echo "crlDistributionPoints = ${crl}" >> openssl.cnf
+fi
+cat >> openssl.cnf <<- EOF
+
+[req_issued]
+prompt = no
+oid_section = req_oids
+distinguished_name = req_issued_name
+default_md = $DIGEST
+
+[req_issued_name]
+C=US
+#stateOrProvinceName=SomeState
+localityName=SomeCity
+O=SomeOrg
+EOF
+#echo $DOMAIN | awk 'BEGIN {FS="."}{for(i=NF;i>0;i--){print NF-i ".domainComponent="$i;}}' >> openssl.cnf
+#mail = $GIVENUSER
+cat >> openssl.cnf <<- EOF
+commonName = $commonname
+
+[v3_issued]
+#certificatePolicies=2.5.29.32.0${eku:+,${eku}}
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always
+#authorityKeyIdentifier=keyid:always,issuer:always
+EOF
+if test -n "$aia" ; then
+ echo "authorityInfoAccess = ${aia}" >> openssl.cnf
+fi
+if test -n "$crl" ; then
+ echo "crlDistributionPoints = ${crl}" >> openssl.cnf
+fi
+if test -n "$keyusage" ; then
+ echo "keyUsage = critical,${keyusage}" >> openssl.cnf
+fi
+if test -n "$altnames" ; then
+ echo "subjectAltName = ${altnames}" >> openssl.cnf
+fi
+if test -n "$eku" ; then
+ echo "extendedKeyUsage = ${eku}" >> openssl.cnf
+ :
+fi
+if test "x$CA" = xTRUE ; then
+ echo "basicConstraints=critical,CA:TRUE" >> openssl.cnf
+ echo 'nsComment="Testing CA Certificate for '"$commonname"'"' >> openssl.cnf
+else
+ echo "basicConstraints=CA:FALSE" >> openssl.cnf
+ echo 'nsComment="Testing Certificate for '"$commonname"'"' >> openssl.cnf
+fi
+for value in $principals; do
+ user=`echo "$value" | cut -f1 -d@`
+ realm=`echo "$value" | cut -f2- -d@`
+ echo "" >> openssl.cnf
+ echo "[$value]" >> openssl.cnf
+ echo "realm=EXPLICIT:0,GeneralString:$realm" >> openssl.cnf
+ echo "kerberosname=EXPLICIT:1,SEQUENCE:krb5$user" >> openssl.cnf
+
+ echo "" >> openssl.cnf
+ echo "[krb5$user]" >> openssl.cnf
+ echo "nametype=EXPLICIT:0,INTEGER:1" >> openssl.cnf
+ echo "namelist=EXPLICIT:1,SEQUENCE:krb5basic$user" >> openssl.cnf
+
+ echo "[krb5basic$user]" >> openssl.cnf
+ count=0
+ for part in `echo "$user" | sed 's,/, ,g'` ; do
+ echo "$count.part=GeneralString:$part" >> openssl.cnf
+ count=`expr "$count" + 1`
+ done
+done
+
+# Create the data files for a new CA.
+if ! test -s ca.srl ; then
+ (dd if=/dev/urandom bs=8 count=1 2> /dev/null) | od -t x1c | head -n 1 | awk '{$1="00";OFS="";print}' > ca.srl
+else
+ echo "You already have a ca.srl file; not replacing."
+fi
+if ! test -s ca.db ; then
+ touch ca.db
+else
+ echo "You already have a ca.db file; not replacing."
+fi
+if ! test -s ca.db.attr ; then
+ touch ca.db.attr
+else
+ echo "You already have a ca.db.attr file; not replacing."
+fi
+
+# If we need a CA key, generate one.
+if ! test -s ca.key ; then
+ umask=`umask -p`
+ umask 077
+ keygen ca > ca.key 2> /dev/null
+ $umask
+else
+ echo "You already have a ca.key file; not replacing."
+ done=echo
+fi
+
+# If we need a CA certificate, generate one.
+if ! test -s ca.crt ; then
+ sed -i -e 's,^\[req_ca\]$,\[req\],g' `pwd`/openssl.cnf
+ openssl req -config `pwd`/openssl.cnf -new -key ca.key > ca.csr 2> /dev/null -passin pass:shim
+ sed -i -e 's,^\[req\]$,\[req_ca\],g' `pwd`/openssl.cnf
+ openssl x509 -extfile `pwd`/openssl.cnf -CAserial ca.srl -signkey ca.key -extensions v3_ca -req -in ca.csr -days $DAYS -out ca.crt ; : 2> /dev/null
+ openssl x509 -noout -text -in ca.crt > ca.txt
+ cat ca.crt >> ca.txt
+ cat ca.txt > ca.crt
+ rm ca.txt
+ cat ca.crt > ca.chain.crt
+else
+ echo "You already have a ca.crt file; not replacing."
+ done=echo
+fi
+
+# If we need an OCSP key, generate one.
+if ! test -s ocsp.key ; then
+ umask=`umask -p`
+ umask 077
+ keygen ocsp > ocsp.key 2> /dev/null
+ $umask
+else
+ echo "You already have an ocsp.key file; not replacing."
+ done=echo
+fi
+
+# Generate the OCSP signing cert. Set the X.509v3 basic constraints and EKU.
+if ! test -s ocsp.crt ; then
+ sed -i -e 's,^\[req_ocsp\]$,\[req\],g' `pwd`/openssl.cnf
+ openssl req -config `pwd`/openssl.cnf -new -key ocsp.key > ocsp.csr 2> /dev/null
+ sed -i -e 's,^\[req\]$,\[req_ocsp\],g' `pwd`/openssl.cnf
+ openssl ca -batch -config `pwd`/openssl.cnf -extensions v3_ocsp -preserveDN -in ocsp.csr -days $DAYS -out ocsp.crt 2> /dev/null
+ openssl x509 -noout -text -in ocsp.crt > ocsp.txt
+ cat ocsp.crt >> ocsp.txt
+ cat ocsp.txt > ocsp.crt
+ rm ocsp.txt
+else
+ echo "You already have an ocsp.crt file; not replacing."
+ done=echo
+fi
+
+# If we were told to revoke the certificate with the specified common name,
+# do so.
+if $revoke_cert ; then
+ openssl ca -config `pwd`/openssl.cnf -revoke "$commonname".crt
+fi
+
+# Always refresh the CRL.
+openssl ca -config `pwd`/openssl.cnf -gencrl ${CRLHOURS:+-crlhours ${CRLHOURS}} ${CRLDAYS:+-crldays ${CRLDAYS}} -out ca.crl.pem
+openssl crl -in ca.crl.pem -outform der -out ca.crl
+openssl crl -in ca.crl -inform der -noout -text > ca.crl.pem
+openssl crl -in ca.crl -inform der >> ca.crl.pem
+
+# If we were told to start up the mini OCSP server, do so.
+if $ocsp_serve ; then
+ openssl ocsp -text -index `pwd`/ca.db -CA `pwd`/ca.crt -rsigner `pwd`/ocsp.crt -rkey `pwd`/ocsp.key -rother `pwd`/ocsp.crt -port "`cut -f3 -d/ ca.ocsp.uri.txt | sed -r 's,(^[^:]*),0.0.0.0,g'`"
+ exit 0
+fi
+
+# If we're just here to do a revocation or refresh the CRL, we're done.
+if $revoke_cert || $refresh_crl ; then
+ exit 0
+fi
+
+# Create a new serial number and whatnot if this is a new sub-CA.
+if test "x$CA" = xTRUE ; then
+ if ! test -d "$commonname" ; then
+ mkdir "$commonname"
+ fi
+ if ! test -s "$commonname/ca.srl" ; then
+ (dd if=/dev/urandom bs=8 count=1 2> /dev/null) | od -t x1c | head -n 1 | awk '{$1="00";OFS="";print}' > "$commonname/ca.srl"
+ else
+ echo "You already have a $commonname/ca.srl file; not replacing."
+ fi
+ if test -n "$aia" ; then
+ echo -n "$aiaval" > "$commonname/ca.ocsp.uri.txt"
+ fi
+ if test -n "$crl" ; then
+ echo -n "$crlval" > "$commonname/ca.crldp.uri.txt"
+ fi
+ echo "$DOMAIN" > "$commonname/ca.domain.txt"
+ touch "$commonname/ca.db" "$commonname/ca.db.attr"
+ cert="$commonname/ca.crt"
+ csr="$commonname/ca.csr"
+ key="$commonname/ca.key"
+ pem="$commonname/ca.pem"
+ pfx="$commonname/ca.p12"
+ ln -s ../`basename $0` "$commonname"/
+else
+ cert="$commonname.crt"
+ csr="$commonname.csr"
+ key="$commonname.key"
+ pem="$commonname.pem"
+ pfx="$commonname.p12"
+fi
+
+# Generate the subject's certificate. Set the X.509v3 basic constraints.
+if ! test -s "$cert" ; then
+ # Generate another key, unless we have a key or CSR.
+ if ! test -s "$key" && ! test -s "$csr" ; then
+ umask=`umask -p`
+ umask 077
+ keygen "$commonname" > "$key" 2> /dev/null
+ $umask
+ else
+ echo "You already have a $key or $csr file; not replacing."
+ done=echo
+ fi
+
+ if ! test -s "$csr" ; then
+ sed -i -e 's,^\[req_issued\]$,\[req\],g' `pwd`/openssl.cnf
+ openssl req -config `pwd`/openssl.cnf -new -key "$key" > "$csr" 2> /dev/null
+ sed -i -e 's,^\[req\]$,\[req_issued\],g' `pwd`/openssl.cnf
+ fi
+ openssl ca -batch -config `pwd`/openssl.cnf -extensions v3_issued -preserveDN -in "$csr" -days $DAYS -out "$cert" 2> /dev/null
+ openssl x509 -noout -text -in "$cert" > "$cert.txt"
+ cat "$cert" >> "$cert.txt"
+ cat "$cert.txt" > "$cert"
+ rm -f "$cert.txt"
+else
+ echo "You already have a $cert file; not replacing."
+ done=echo
+fi
+
+if test -s ca.chain.crt ; then
+ chain=ca.chain.crt
+else
+ chain=ca.crt
+fi
+if test "x$CA" = xTRUE ; then
+ cat "$chain" "$cert" > "$commonname/ca.chain.crt"
+fi
+
+# Create ca.pem and the subject's name.pem for the benefit of applications
+# which expect both the private key and the certificate in one file.
+umask=`umask -p`
+umask 077
+if ! test -s ca.pem ; then
+ cat ca.key ca.crt > ca.pem
+else
+ echo "You already have a ca.pem file; not replacing."
+ done=echo
+fi
+if ! test -s "$pem" ; then
+ cat "$key" "$cert" > "$pem"
+else
+ echo "You already have a $pem file; not replacing."
+ done=echo
+fi
+if ! test -s "$pfx" ; then
+ #openssl pkcs12 -export -inkey "$key" -in "$cert" -name "$commonname" -out "$pfx" -nodes -passout pass:qweqwe
+ openssl pkcs12 -export -inkey "$key" -in "$cert" -name "$commonname" -out "$pfx" -nodes -passout pass:
+else
+ echo "You already have a $pfx file; not replacing."
+ done=echo
+fi
+$umask
+$done
+
+echo CA certificate:
+openssl x509 -noout -issuer -in ca.crt | sed s,=\ ,\ ,g
+openssl x509 -noout -subject -in ca.crt | sed s,=\ ,\ ,g
+echo
+echo End entity certificate:
+openssl x509 -noout -issuer -in "$cert" | sed s,=\ ,\ ,g
+openssl x509 -noout -subject -in "$cert" | sed s,=\ ,\ ,g
+openssl x509 -noout -serial -in "$cert" | sed s,=,\ ,g
+echo
+echo PKCS12 bag:
+openssl pkcs12 -in "$pfx" -nodes -nokeys -nocerts -info -passin pass:
+#openssl pkcs12 -in "$pfx" -nodes -nokeys -nocerts -info -passin pass:qweqwe
+echo
+echo Verifying:
+echo + openssl verify -CAfile "$chain" "$cert"
+openssl verify -CAfile "$chain" "$cert"
diff --git a/netboot.c b/netboot.c
new file mode 100644
index 00000000..90fb9cba
--- /dev/null
+++ b/netboot.c
@@ -0,0 +1,370 @@
+/*
+ * netboot - trivial UEFI first-stage bootloader netboot support
+ *
+ * Copyright 2012 Red Hat, Inc <mjg@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.
+ */
+
+#include <efi.h>
+#include <efilib.h>
+#include <string.h>
+#include "shim.h"
+#include "netboot.h"
+
+
+static inline unsigned short int __swap16(unsigned short int x)
+{
+ __asm__("xchgb %b0,%h0"
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+#define ntohs(x) __swap16(x)
+#define htons(x) ntohs(x)
+
+static EFI_PXE_BASE_CODE *pxe;
+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;
+ UINT8 Data[1];
+} EFI_DHCP6_PACKET_OPTION;
+
+/*
+ * usingNetboot
+ * Returns TRUE if we identify a protocol that is enabled and Providing us with
+ * the needed information to fetch a grubx64.efi image
+ */
+BOOLEAN findNetboot(EFI_HANDLE image_handle)
+{
+ UINTN bs = sizeof(EFI_HANDLE);
+ EFI_GUID pxe_base_code_protocol = EFI_PXE_BASE_CODE_PROTOCOL;
+ EFI_HANDLE *hbuf;
+ BOOLEAN rc = FALSE;
+ void *buffer = AllocatePool(bs);
+ UINTN errcnt = 0;
+ UINTN i;
+ EFI_STATUS status;
+
+ if (!buffer)
+ return FALSE;
+
+try_again:
+ status = uefi_call_wrapper(BS->LocateHandle,5, ByProtocol,
+ &pxe_base_code_protocol, NULL, &bs,
+ buffer);
+
+ if (status == EFI_BUFFER_TOO_SMALL) {
+ errcnt++;
+ FreePool(buffer);
+ if (errcnt > 1)
+ return FALSE;
+ buffer = AllocatePool(bs);
+ if (!buffer)
+ return FALSE;
+ goto try_again;
+ }
+
+ if (status == EFI_NOT_FOUND) {
+ FreePool(buffer);
+ return FALSE;
+ }
+
+ /*
+ * We have a list of pxe supporting protocols, lets see if any are
+ * active
+ */
+ hbuf = buffer;
+ pxe = NULL;
+ for (i=0; i < (bs / sizeof(EFI_HANDLE)); i++) {
+ status = uefi_call_wrapper(BS->OpenProtocol, 6, hbuf[i],
+ &pxe_base_code_protocol,
+ &pxe, image_handle, NULL,
+ EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+
+ if (status != EFI_SUCCESS) {
+ pxe = NULL;
+ continue;
+ }
+
+ if (!pxe || !pxe->Mode) {
+ pxe = NULL;
+ continue;
+ }
+
+ if (pxe->Mode->Started && pxe->Mode->DhcpAckReceived) {
+ /*
+ * We've located a pxe protocol handle thats been
+ * started and has received an ACK, meaning its
+ * something we'll be able to get tftp server info
+ * out of
+ */
+ rc = TRUE;
+ break;
+ }
+
+ }
+
+ FreePool(buffer);
+ return rc;
+}
+
+static char *get_v6_bootfile_url(EFI_PXE_BASE_CODE_DHCPV6_PACKET *pkt)
+{
+ void *optr;
+ EFI_DHCP6_PACKET_OPTION *option;
+ char *url;
+ UINT32 urllen;
+
+ optr = pkt->DhcpOptions;
+
+ for(;;) {
+ option = (EFI_DHCP6_PACKET_OPTION *)optr;
+
+ if (ntohs(option->OpCode) == 0)
+ return NULL;
+
+ if (ntohs(option->OpCode) == 59) {
+ /* This is the bootfile url option */
+ urllen = ntohs(option->Length);
+ url = AllocatePool(urllen+2);
+ if (!url)
+ return NULL;
+ memset(url, 0, urllen+2);
+ memcpy(url, option->Data, urllen);
+ return url;
+ }
+ optr += 4 + ntohs(option->Length);
+ }
+
+ return NULL;
+}
+
+static UINT16 str2ns(UINT8 *str)
+{
+ UINT16 ret = 0;
+ UINT8 v;
+ for(;*str;str++) {
+ if ('0' <= *str && *str <= '9')
+ v = *str - '0';
+ else if ('A' <= *str && *str <= 'F')
+ v = *str - 'A' + 10;
+ else if ('a' <= *str && *str <= 'f')
+ v = *str - 'a' + 10;
+ else
+ v = 0;
+ ret = (ret << 4) + v;
+ }
+ return htons(ret);
+}
+
+static UINT8 *str2ip6(char *str)
+{
+ UINT8 i, j, p;
+ size_t len;
+ UINT8 *a, *b, t;
+ static UINT16 ip[8];
+
+ for(i=0; i < 8; i++) {
+ ip[i] = 0;
+ }
+ len = strlen((UINT8 *)str);
+ a = b = (UINT8 *)str;
+ for(i=p=0; i < len; i++, b++) {
+ if (*b != ':')
+ continue;
+ *b = '\0';
+ ip[p++] = str2ns(a);
+ *b = ':';
+ a = b + 1;
+ if ( *(b+1) == ':' )
+ break;
+ }
+ a = b = (UINT8 *)(str + len);
+ for(j=len, p=7; j > i; j--, a--) {
+ if (*a != ':')
+ continue;
+ t = *b;
+ *b = '\0';
+ ip[p--] = str2ns(a+1);
+ *b = t;
+ b = a;
+ }
+ return (UINT8 *)ip;
+}
+
+static BOOLEAN extract_tftp_info(char *url)
+{
+ char *start, *end;
+ char ip6str[128];
+ char *template = "/grubx64.efi";
+
+ if (strncmp((UINT8 *)url, (UINT8 *)"tftp://", 7)) {
+ Print(L"URLS MUST START WITH tftp://\n");
+ return FALSE;
+ }
+ start = url + 7;
+ if (*start != '[') {
+ Print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
+ return FALSE;
+ }
+
+ start++;
+ end = start;
+ while ((*end != '\0') && (*end != ']')) {
+ end++;
+ }
+ if (end == '\0') {
+ Print(L"TFTP SERVER MUST BE ENCLOSED IN [..]\n");
+ return FALSE;
+ }
+ *end = '\0';
+ memset(ip6str, 0, 128);
+ memcpy(ip6str, start, strlen((UINT8 *)start));
+ *end = ']';
+ end++;
+ memcpy(&tftp_addr.v6, str2ip6(ip6str), 16);
+ full_path = AllocatePool(strlen((UINT8 *)end)+strlen((UINT8 *)template)+1);
+ if (!full_path)
+ return FALSE;
+ memset(full_path, 0, strlen((UINT8 *)end)+strlen((UINT8 *)template));
+ memcpy(full_path, end, strlen((UINT8 *)end));
+ end = strrchr(full_path, '/');
+ if (!end)
+ end = full_path;
+ memcpy(end, template, strlen((UINT8 *)template));
+
+ return TRUE;
+}
+
+static EFI_STATUS parseDhcp6()
+{
+ EFI_PXE_BASE_CODE_DHCPV6_PACKET *packet = (EFI_PXE_BASE_CODE_DHCPV6_PACKET *)&pxe->Mode->DhcpAck.Raw;
+ char *bootfile_url;
+
+
+ bootfile_url = get_v6_bootfile_url(packet);
+ if (extract_tftp_info(bootfile_url) == FALSE)
+ return EFI_NOT_FOUND;
+ if (!bootfile_url)
+ return EFI_NOT_FOUND;
+ return EFI_SUCCESS;
+}
+
+static EFI_STATUS parseDhcp4()
+{
+ char *template = "/grubx64.efi";
+ char *tmp = AllocatePool(16);
+
+
+ if (!tmp)
+ return EFI_OUT_OF_RESOURCES;
+
+
+ memcpy(&tftp_addr.v4, pxe->Mode->DhcpAck.Dhcpv4.BootpSiAddr, 4);
+
+ memcpy(tmp, template, 12);
+ tmp[13] = '\0';
+ full_path = tmp;
+
+ /* Note we don't capture the filename option here because we know its shim.efi
+ * We instead assume the filename at the end of the path is going to be grubx64.efi
+ */
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle)
+{
+
+ EFI_STATUS rc;
+
+ if (!pxe)
+ return EFI_NOT_READY;
+
+ memset((UINT8 *)&tftp_addr, 0, sizeof(tftp_addr));
+
+ /*
+ * If we've discovered an active pxe protocol figure out
+ * if its ipv4 or ipv6
+ */
+ if (pxe->Mode->UsingIpv6){
+ rc = parseDhcp6();
+ } else
+ rc = parseDhcp4();
+ return rc;
+}
+
+EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINTN *bufsiz)
+{
+ EFI_STATUS rc;
+ EFI_PXE_BASE_CODE_TFTP_OPCODE read = EFI_PXE_BASE_CODE_TFTP_READ_FILE;
+ BOOLEAN overwrite = FALSE;
+ BOOLEAN nobuffer = FALSE;
+ UINTN blksz = 512;
+
+ Print(L"Fetching Netboot Image\n");
+ if (*buffer == NULL) {
+ *buffer = AllocatePool(4096 * 1024);
+ if (!*buffer)
+ return EFI_OUT_OF_RESOURCES;
+ *bufsiz = 4096 * 1024;
+ }
+
+try_again:
+ rc = uefi_call_wrapper(pxe->Mtftp, 10, pxe, read, *buffer, overwrite,
+ &bufsiz, &blksz, &tftp_addr, full_path, NULL, nobuffer);
+
+ if (rc == EFI_BUFFER_TOO_SMALL) {
+ /* try again, doubling buf size */
+ *bufsiz *= 2;
+ FreePool(*buffer);
+ *buffer = AllocatePool(*bufsiz);
+ if (!*buffer)
+ return EFI_OUT_OF_RESOURCES;
+ goto try_again;
+ }
+
+ return rc;
+
+}
diff --git a/netboot.h b/netboot.h
new file mode 100644
index 00000000..2cdb4218
--- /dev/null
+++ b/netboot.h
@@ -0,0 +1,9 @@
+#ifndef _NETBOOT_H_
+#define _NETBOOT_H_
+
+extern BOOLEAN findNetboot(EFI_HANDLE image_handle);
+
+extern EFI_STATUS parseNetbootinfo(EFI_HANDLE image_handle);
+
+extern EFI_STATUS FetchNetbootimage(EFI_HANDLE image_handle, VOID **buffer, UINTN *bufsiz);
+#endif
diff --git a/shim.c b/shim.c
index 59825099..c3aae9e0 100644
--- a/shim.c
+++ b/shim.c
@@ -39,8 +39,11 @@
#include "PeImage.h"
#include "shim.h"
#include "signature.h"
+#include "netboot.h"
+#include "shim_cert.h"
#define SECOND_STAGE L"\\grub.efi"
+#define MOK_MANAGER L"\\MokManager.efi"
static EFI_SYSTEM_TABLE *systab;
static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *system_table);
@@ -50,31 +53,38 @@ static EFI_STATUS (EFIAPI *entry_point) (EFI_HANDLE image_handle, EFI_SYSTEM_TAB
*/
extern UINT8 vendor_cert[];
extern UINT32 vendor_cert_size;
+extern EFI_SIGNATURE_LIST *vendor_dbx;
+extern UINT32 vendor_dbx_size;
#define EFI_IMAGE_SECURITY_DATABASE_GUID { 0xd719b2cb, 0x3d3a, 0x4596, { 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f }}
+static UINT8 insecure_mode;
+
typedef enum {
DATA_FOUND,
DATA_NOT_FOUND,
VAR_NOT_FOUND
} CHECK_STATUS;
-static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid,
+typedef struct {
+ UINT32 MokSize;
+ UINT8 *Mok;
+} MokListNode;
+
+static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid, UINT32 *attributes,
UINTN *size, void **buffer)
{
EFI_STATUS efi_status;
- UINT32 attributes;
char allocate = !(*size);
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
- &attributes, size, buffer);
+ attributes, size, buffer);
if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) {
return efi_status;
}
- if (allocate)
- *buffer = AllocatePool(*size);
+ *buffer = AllocatePool(*size);
if (!*buffer) {
Print(L"Unable to allocate variable buffer\n");
@@ -82,7 +92,7 @@ static EFI_STATUS get_variable (CHAR16 *name, EFI_GUID guid,
}
efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid,
- &attributes, size, *buffer);
+ attributes, size, *buffer);
return efi_status;
}
@@ -202,25 +212,16 @@ static EFI_STATUS relocate_coff (PE_COFF_LOADER_IMAGE_CONTEXT *context,
return EFI_SUCCESS;
}
-static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
+static CHECK_STATUS check_db_cert_in_ram(EFI_SIGNATURE_LIST *CertList,
+ UINTN dbsize,
+ WIN_CERTIFICATE_EFI_PKCS *data,
+ UINT8 *hash)
{
- EFI_STATUS efi_status;
- EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
- EFI_SIGNATURE_LIST *CertList;
EFI_SIGNATURE_DATA *Cert;
- UINTN dbsize = 0;
UINTN CertCount, Index;
BOOLEAN IsFound = FALSE;
- void *db;
EFI_GUID CertType = EfiCertX509Guid;
- efi_status = get_variable(dbname, secure_var, &dbsize, &db);
-
- if (efi_status != EFI_SUCCESS)
- return VAR_NOT_FOUND;
-
- CertList = db;
-
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
if (CompareGuid (&CertList->SignatureType, &CertType) == 0) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
@@ -233,44 +234,60 @@ static CHECK_STATUS check_db_cert(CHAR16 *dbname, WIN_CERTIFICATE_EFI_PKCS *data
hash, SHA256_DIGEST_SIZE);
if (IsFound)
break;
+
+ Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
- Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) Cert + CertList->SignatureSize);
}
+ if (IsFound)
+ break;
+
dbsize -= CertList->SignatureListSize;
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
- FreePool(db);
-
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
-static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data)
+static CHECK_STATUS check_db_cert(CHAR16 *dbname, EFI_GUID guid,
+ WIN_CERTIFICATE_EFI_PKCS *data, UINT8 *hash)
{
+ CHECK_STATUS rc;
EFI_STATUS efi_status;
- EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
EFI_SIGNATURE_LIST *CertList;
- EFI_SIGNATURE_DATA *Cert;
UINTN dbsize = 0;
- UINTN CertCount, Index;
- BOOLEAN IsFound = FALSE;
+ UINT32 attributes;
void *db;
- unsigned int SignatureSize = SHA256_DIGEST_SIZE;
- EFI_GUID CertType = EfiHashSha256Guid;
- efi_status = get_variable(dbname, secure_var, &dbsize, &db);
+ efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
- if (efi_status != EFI_SUCCESS) {
+ if (efi_status != EFI_SUCCESS)
return VAR_NOT_FOUND;
- }
CertList = db;
+ rc = check_db_cert_in_ram(CertList, dbsize, data, hash);
+
+ FreePool(db);
+
+ return rc;
+}
+
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a buffer
+ */
+static CHECK_STATUS check_db_hash_in_ram(EFI_SIGNATURE_LIST *CertList,
+ UINTN dbsize, UINT8 *data,
+ int SignatureSize, EFI_GUID CertType)
+{
+ EFI_SIGNATURE_DATA *Cert;
+ UINTN CertCount, Index;
+ BOOLEAN IsFound = FALSE;
+
while ((dbsize > 0) && (dbsize >= CertList->SignatureListSize)) {
CertCount = (CertList->SignatureListSize - CertList->SignatureHeaderSize) / CertList->SignatureSize;
Cert = (EFI_SIGNATURE_DATA *) ((UINT8 *) CertList + sizeof (EFI_SIGNATURE_LIST) + CertList->SignatureHeaderSize);
@@ -295,29 +312,93 @@ static CHECK_STATUS check_db_hash(CHAR16 *dbname, UINT8 *data)
CertList = (EFI_SIGNATURE_LIST *) ((UINT8 *) CertList + CertList->SignatureListSize);
}
- FreePool(db);
-
if (IsFound)
return DATA_FOUND;
return DATA_NOT_FOUND;
}
-static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
+/*
+ * Check a hash against an EFI_SIGNATURE_LIST in a UEFI variable
+ */
+static CHECK_STATUS check_db_hash(CHAR16 *dbname, EFI_GUID guid, UINT8 *data,
+ int SignatureSize, EFI_GUID CertType)
+{
+ EFI_STATUS efi_status;
+ EFI_SIGNATURE_LIST *CertList;
+ UINT32 attributes;
+ UINTN dbsize = 0;
+ void *db;
+
+ efi_status = get_variable(dbname, guid, &attributes, &dbsize, &db);
+
+ if (efi_status != EFI_SUCCESS) {
+ return VAR_NOT_FOUND;
+ }
+
+ CertList = db;
+
+ CHECK_STATUS rc = check_db_hash_in_ram(CertList, dbsize, data,
+ SignatureSize, CertType);
+ FreePool(db);
+ return rc;
+
+}
+
+/*
+ * Check whether the binary signature or hash are present in dbx or the
+ * built-in blacklist
+ */
+static EFI_STATUS check_blacklist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- if (check_db_hash(L"dbx", hash) == DATA_FOUND)
+ EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+
+ if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha256hash,
+ SHA256_DIGEST_SIZE, EfiHashSha256Guid) ==
+ DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_hash_in_ram(vendor_dbx, vendor_dbx_size, sha1hash,
+ SHA1_DIGEST_SIZE, EfiHashSha1Guid) ==
+ DATA_FOUND)
return EFI_ACCESS_DENIED;
- if (check_db_cert(L"dbx", cert, hash) == DATA_FOUND)
+ if (check_db_cert_in_ram(vendor_dbx, vendor_dbx_size, cert,
+ sha256hash) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+
+ if (check_db_hash(L"dbx", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_hash(L"dbx", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+ EfiHashSha1Guid) == DATA_FOUND)
+ return EFI_ACCESS_DENIED;
+ if (check_db_cert(L"dbx", secure_var, cert, sha256hash) == DATA_FOUND)
return EFI_ACCESS_DENIED;
return EFI_SUCCESS;
}
-static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert, UINT8 *hash)
+/*
+ * Check whether the binary signature or hash are present in db or MokList
+ */
+static EFI_STATUS check_whitelist (WIN_CERTIFICATE_EFI_PKCS *cert,
+ UINT8 *sha256hash, UINT8 *sha1hash)
{
- if (check_db_hash(L"db", hash) == DATA_FOUND)
+ EFI_GUID secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
+ EFI_GUID shim_var = SHIM_LOCK_GUID;
+
+ if (check_db_hash(L"db", secure_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_hash(L"db", secure_var, sha1hash, SHA1_DIGEST_SIZE,
+ EfiHashSha1Guid) == DATA_FOUND)
return EFI_SUCCESS;
- if (check_db_cert(L"db", cert, hash) == DATA_FOUND)
+ if (check_db_hash(L"MokList", shim_var, sha256hash, SHA256_DIGEST_SIZE,
+ EfiHashSha256Guid) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_cert(L"db", secure_var, cert, sha256hash) == DATA_FOUND)
+ return EFI_SUCCESS;
+ if (check_db_cert(L"MokList", shim_var, cert, sha256hash) == DATA_FOUND)
return EFI_SUCCESS;
return EFI_ACCESS_DENIED;
@@ -333,8 +414,13 @@ static BOOLEAN secure_mode (void)
EFI_GUID global_var = EFI_GLOBAL_VARIABLE;
UINTN charsize = sizeof(char);
UINT8 sb, setupmode;
+ UINT32 attributes;
+
+ if (insecure_mode)
+ return FALSE;
- status = get_variable(L"SecureBoot", global_var, &charsize, (void *)&sb);
+ status = get_variable(L"SecureBoot", global_var, &attributes, &charsize,
+ (void *)&sb);
/* FIXME - more paranoia here? */
if (status != EFI_SUCCESS || sb != 1) {
@@ -342,7 +428,8 @@ static BOOLEAN secure_mode (void)
return FALSE;
}
- status = get_variable(L"SetupMode", global_var, &charsize, (void *)&setupmode);
+ status = get_variable(L"SetupMode", global_var, &attributes, &charsize,
+ (void *)&setupmode);
if (status == EFI_SUCCESS && setupmode == 1) {
Print(L"Platform is in setup mode\n");
@@ -353,49 +440,38 @@ static BOOLEAN secure_mode (void)
}
/*
- * Check that the signature is valid and matches the binary
+ * Calculate the SHA1 and SHA256 hashes of a binary
*/
-static EFI_STATUS verify_buffer (char *data, int datasize,
- PE_COFF_LOADER_IMAGE_CONTEXT *context, int whitelist)
+
+static EFI_STATUS generate_hash (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash, UINT8 *sha1hash)
+
{
+ unsigned int sha256ctxsize, sha1ctxsize;
unsigned int size = datasize;
- unsigned int ctxsize;
- void *ctx = NULL;
- UINT8 hash[SHA256_DIGEST_SIZE];
- EFI_STATUS status = EFI_ACCESS_DENIED;
+ void *sha256ctx = NULL, *sha1ctx = NULL;
char *hashbase;
unsigned int hashsize;
- WIN_CERTIFICATE_EFI_PKCS *cert;
unsigned int SumOfBytesHashed, SumOfSectionBytes;
unsigned int index, pos;
EFI_IMAGE_SECTION_HEADER *Section;
EFI_IMAGE_SECTION_HEADER *SectionHeader = NULL;
EFI_IMAGE_SECTION_HEADER *SectionCache;
+ EFI_STATUS status = EFI_SUCCESS;
- cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
-
- if (!cert) {
- Print(L"Certificate located outside the image\n");
- return EFI_INVALID_PARAMETER;
- }
-
- if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
- Print(L"Unsupported certificate type %x\n",
- cert->Hdr.wCertificateType);
- return EFI_UNSUPPORTED;
- }
-
- /* FIXME: Check which kind of hash */
+ sha256ctxsize = Sha256GetContextSize();
+ sha256ctx = AllocatePool(sha256ctxsize);
- ctxsize = Sha256GetContextSize();
- ctx = AllocatePool(ctxsize);
+ sha1ctxsize = Sha1GetContextSize();
+ sha1ctx = AllocatePool(sha1ctxsize);
- if (!ctx) {
+ if (!sha256ctx || !sha1ctx) {
Print(L"Unable to allocate memory for hash context\n");
return EFI_OUT_OF_RESOURCES;
}
- if (!Sha256Init(ctx)) {
+ if (!Sha256Init(sha256ctx) || !Sha1Init(sha1ctx)) {
Print(L"Unable to initialise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -406,7 +482,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
hashsize = (char *)&context->PEHdr->Pe32.OptionalHeader.CheckSum -
hashbase;
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -417,7 +494,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
sizeof (int);
hashsize = (char *)context->SecDir - hashbase;
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -428,7 +506,8 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
hashsize = context->PEHdr->Pe32Plus.OptionalHeader.SizeOfHeaders -
(int) ((char *) (&context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY + 1]) - data);
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -484,10 +563,12 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
if (!hashbase) {
Print(L"Malformed section header\n");
- return EFI_INVALID_PARAMETER;
+ status = EFI_INVALID_PARAMETER;
+ goto done;
}
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
@@ -503,50 +584,145 @@ static EFI_STATUS verify_buffer (char *data, int datasize,
context->PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY].Size -
SumOfBytesHashed);
- if (!(Sha256Update(ctx, hashbase, hashsize))) {
+ if (!(Sha256Update(sha256ctx, hashbase, hashsize)) ||
+ !(Sha1Update(sha1ctx, hashbase, hashsize))) {
Print(L"Unable to generate hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
}
- if (!(Sha256Final(ctx, hash))) {
+ if (!(Sha256Final(sha256ctx, sha256hash)) ||
+ !(Sha1Final(sha1ctx, sha1hash))) {
Print(L"Unable to finalise hash\n");
status = EFI_OUT_OF_RESOURCES;
goto done;
}
- status = check_blacklist(cert, hash);
+done:
+ if (SectionHeader)
+ FreePool(SectionHeader);
+ if (sha1ctx)
+ FreePool(sha1ctx);
+ if (sha256ctx)
+ FreePool(sha256ctx);
+
+ return status;
+}
+
+/*
+ * Ensure that the MOK database hasn't been set or modified from an OS
+ */
+static EFI_STATUS verify_mok (void) {
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS status = EFI_SUCCESS;
+ void *MokListData = NULL;
+ UINTN MokListDataSize = 0;
+ UINT32 attributes;
+
+ 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");
+ }
+ status = EFI_ACCESS_DENIED;
+ return status;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Check that the signature is valid and matches the binary
+ */
+static EFI_STATUS verify_buffer (char *data, int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context)
+{
+ UINT8 sha256hash[SHA256_DIGEST_SIZE];
+ UINT8 sha1hash[SHA1_DIGEST_SIZE];
+ EFI_STATUS status = EFI_ACCESS_DENIED;
+ WIN_CERTIFICATE_EFI_PKCS *cert;
+ unsigned int size = datasize;
+
+ if (context->SecDir->Size == 0) {
+ Print(L"Empty security header\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ cert = ImageAddress (data, size, context->SecDir->VirtualAddress);
+
+ if (!cert) {
+ Print(L"Certificate located outside the image\n");
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (cert->Hdr.wCertificateType != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
+ Print(L"Unsupported certificate type %x\n",
+ cert->Hdr.wCertificateType);
+ return EFI_UNSUPPORTED;
+ }
+
+ status = generate_hash(data, datasize, context, sha256hash, sha1hash);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ /*
+ * Check that the MOK database hasn't been modified
+ */
+ verify_mok();
+
+ /*
+ * Ensure that the binary isn't blacklisted
+ */
+ status = check_blacklist(cert, sha256hash, sha1hash);
if (status != EFI_SUCCESS) {
Print(L"Binary is blacklisted\n");
- goto done;
+ return status;
}
- if (whitelist) {
- status = check_whitelist(cert, hash);
+ /*
+ * Check whether the binary is whitelisted in any of the firmware
+ * databases
+ */
+ status = check_whitelist(cert, sha256hash, sha1hash);
- if (status == EFI_SUCCESS) {
- Print(L"Binary is whitelisted\n");
- goto done;
- }
+ if (status == EFI_SUCCESS) {
+ Print(L"Binary is whitelisted\n");
+ return status;
}
- if (!AuthenticodeVerify(cert->CertData,
- context->SecDir->Size - sizeof(cert->Hdr),
- vendor_cert, vendor_cert_size, hash,
- SHA256_DIGEST_SIZE)) {
- Print(L"Invalid signature\n");
- status = EFI_ACCESS_DENIED;
- } else {
+ /*
+ * Check against the shim build key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ shim_cert, sizeof(shim_cert), sha256hash,
+ SHA256_DIGEST_SIZE)) {
status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
}
-done:
- if (SectionHeader)
- FreePool(SectionHeader);
- if (ctx)
- FreePool(ctx);
+
+ /*
+ * And finally, check against shim's built-in key
+ */
+ if (AuthenticodeVerify(cert->CertData,
+ context->SecDir->Size - sizeof(cert->Hdr),
+ vendor_cert, vendor_cert_size, sha256hash,
+ SHA256_DIGEST_SIZE)) {
+ status = EFI_SUCCESS;
+ Print(L"Binary is verified by the vendor certificate\n");
+ return status;
+ }
+
+ Print(L"Invalid signature\n");
+ status = EFI_ACCESS_DENIED;
return status;
}
@@ -560,9 +736,19 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
EFI_IMAGE_DOS_HEADER *DosHdr = data;
EFI_IMAGE_OPTIONAL_HEADER_UNION *PEHdr = data;
+ if (datasize < sizeof(EFI_IMAGE_DOS_HEADER)) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
if (DosHdr->e_magic == EFI_IMAGE_DOS_SIGNATURE)
PEHdr = (EFI_IMAGE_OPTIONAL_HEADER_UNION *)((char *)data + DosHdr->e_lfanew);
+ if ((((UINT8 *)PEHdr - (UINT8 *)data) + sizeof(EFI_IMAGE_OPTIONAL_HEADER_UNION)) > datasize) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
+ }
+
if (PEHdr->Te.Signature != EFI_IMAGE_NT_SIGNATURE) {
Print(L"Unsupported image type\n");
return EFI_UNSUPPORTED;
@@ -589,24 +775,28 @@ static EFI_STATUS read_header(void *data, unsigned int datasize,
context->FirstSection = (EFI_IMAGE_SECTION_HEADER *)((char *)PEHdr + PEHdr->Pe32.FileHeader.SizeOfOptionalHeader + sizeof(UINT32) + sizeof(EFI_IMAGE_FILE_HEADER));
context->SecDir = (EFI_IMAGE_DATA_DIRECTORY *) &PEHdr->Pe32Plus.OptionalHeader.DataDirectory[EFI_IMAGE_DIRECTORY_ENTRY_SECURITY];
- if (context->SecDir->VirtualAddress >= datasize) {
- Print(L"Malformed security header\n");
- return EFI_INVALID_PARAMETER;
+ if (context->ImageSize < context->SizeOfHeaders) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
}
- if (context->SecDir->Size == 0) {
- Print(L"Empty security header\n");
- return EFI_INVALID_PARAMETER;
+ if (((UINT8 *)context->SecDir - (UINT8 *)data) > (datasize - sizeof(EFI_IMAGE_DATA_DIRECTORY))) {
+ Print(L"Invalid image\n");
+ return EFI_UNSUPPORTED;
}
+ if (context->SecDir->VirtualAddress >= datasize) {
+ Print(L"Malformed security header\n");
+ return EFI_INVALID_PARAMETER;
+ }
return EFI_SUCCESS;
}
/*
* Once the image has been loaded it needs to be validated and relocated
*/
-static EFI_STATUS handle_grub (void *data, unsigned int datasize,
- EFI_LOADED_IMAGE *li)
+static EFI_STATUS handle_image (void *data, unsigned int datasize,
+ EFI_LOADED_IMAGE *li)
{
EFI_STATUS efi_status;
char *buffer;
@@ -615,14 +805,20 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
char *base, *end;
PE_COFF_LOADER_IMAGE_CONTEXT context;
+ /*
+ * The binary header contains relevant context and section pointers
+ */
efi_status = read_header(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to read header\n");
return efi_status;
}
+ /*
+ * We only need to verify the binary if we're in secure mode
+ */
if (secure_mode ()) {
- efi_status = verify_buffer(data, datasize, &context, 0);
+ efi_status = verify_buffer(data, datasize, &context);
if (efi_status != EFI_SUCCESS) {
Print(L"Verification failed\n");
@@ -639,6 +835,9 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
CopyMem(buffer, data, context.SizeOfHeaders);
+ /*
+ * Copy the executable's sections to their desired offsets
+ */
Section = context.FirstSection;
for (i = 0; i < context.NumberOfSections; i++) {
size = Section->Misc.VirtualSize;
@@ -663,6 +862,9 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
Section += 1;
}
+ /*
+ * Run the relocation fixups
+ */
efi_status = relocate_coff(&context, buffer);
if (efi_status != EFI_SUCCESS) {
@@ -672,6 +874,10 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
}
entry_point = ImageAddress(buffer, context.ImageSize, context.EntryPoint);
+ /*
+ * grub needs to know its location and size in memory, so fix up
+ * the loaded image protocol values
+ */
li->ImageBase = buffer;
li->ImageSize = context.ImageSize;
@@ -684,7 +890,12 @@ static EFI_STATUS handle_grub (void *data, unsigned int datasize,
return EFI_SUCCESS;
}
-static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
+/*
+ * Generate the path of an executable given shim's path and the name
+ * of the executable
+ */
+static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, CHAR16 *ImagePath,
+ EFI_DEVICE_PATH **grubpath, CHAR16 **PathName)
{
EFI_DEVICE_PATH *devpath;
EFI_HANDLE device;
@@ -707,10 +918,10 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath
bootpath[i+1] = '\0';
- if (bootpath[i-i] == '\\')
+ if (i == 0 || bootpath[i-i] == '\\')
bootpath[i] = '\0';
- *PathName = AllocatePool(StrSize(bootpath) + StrSize(SECOND_STAGE));
+ *PathName = AllocatePool(StrSize(bootpath) + StrSize(ImagePath));
if (!*PathName) {
Print(L"Failed to allocate path buffer\n");
@@ -720,7 +931,7 @@ static EFI_STATUS generate_path(EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **grubpath
*PathName[0] = '\0';
StrCat(*PathName, bootpath);
- StrCat(*PathName, SECOND_STAGE);
+ StrCat(*PathName, ImagePath);
*grubpath = FileDevicePath(device, *PathName);
@@ -729,10 +940,10 @@ error:
}
/*
- * Locate the second stage bootloader and read it into a buffer
+ * Open the second stage bootloader and read it into a buffer
*/
-static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
- int *datasize, CHAR16 *PathName)
+static EFI_STATUS load_image (EFI_LOADED_IMAGE *li, void **data,
+ int *datasize, CHAR16 *PathName)
{
EFI_GUID simple_file_system_protocol = SIMPLE_FILE_SYSTEM_PROTOCOL;
EFI_GUID file_info_id = EFI_FILE_INFO_ID;
@@ -745,8 +956,12 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
device = li->DeviceHandle;
+ /*
+ * Open the device
+ */
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
- &simple_file_system_protocol, &drive);
+ &simple_file_system_protocol,
+ (void **)&drive);
if (efi_status != EFI_SUCCESS) {
Print(L"Failed to find fs\n");
@@ -760,6 +975,9 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
goto error;
}
+ /*
+ * And then open the file
+ */
efi_status = uefi_call_wrapper(root->Open, 5, root, &grub, PathName,
EFI_FILE_MODE_READ, 0);
@@ -776,10 +994,15 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
goto error;
}
+ /*
+ * Find out how big the file is in order to allocate the storage
+ * buffer
+ */
efi_status = uefi_call_wrapper(grub->GetInfo, 4, grub, &file_info_id,
&buffersize, fileinfo);
if (efi_status == EFI_BUFFER_TOO_SMALL) {
+ FreePool(fileinfo);
fileinfo = AllocatePool(buffersize);
if (!fileinfo) {
Print(L"Unable to allocate file info buffer\n");
@@ -805,6 +1028,10 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
efi_status = EFI_OUT_OF_RESOURCES;
goto error;
}
+
+ /*
+ * Perform the actual read
+ */
efi_status = uefi_call_wrapper(grub->Read, 3, grub, &buffersize,
*data);
@@ -822,19 +1049,24 @@ static EFI_STATUS load_grub (EFI_LOADED_IMAGE *li, void **data,
*datasize = buffersize;
+ FreePool(fileinfo);
+
return EFI_SUCCESS;
error:
if (*data) {
FreePool(*data);
*data = NULL;
}
- if (PathName)
- FreePool(PathName);
+
if (fileinfo)
FreePool(fileinfo);
return efi_status;
}
+/*
+ * Protocol entry point. If secure boot is enabled, verify that the provided
+ * buffer is signed with a trusted key.
+ */
EFI_STATUS shim_verify (void *buffer, UINT32 size)
{
EFI_STATUS status;
@@ -848,75 +1080,238 @@ EFI_STATUS shim_verify (void *buffer, UINT32 size)
if (status != EFI_SUCCESS)
return status;
- status = verify_buffer(buffer, size, &context, 1);
+ status = verify_buffer(buffer, size, &context);
return status;
}
-EFI_STATUS init_grub(EFI_HANDLE image_handle)
+/*
+ * Load and run an EFI executable
+ */
+EFI_STATUS start_image(EFI_HANDLE image_handle, CHAR16 *ImagePath)
{
+ EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
EFI_STATUS efi_status;
- EFI_HANDLE grub_handle = NULL;
EFI_LOADED_IMAGE *li, li_bak;
- EFI_DEVICE_PATH *grubpath;
- CHAR16 *PathName;
- EFI_GUID loaded_image_protocol = LOADED_IMAGE_PROTOCOL;
+ EFI_DEVICE_PATH *path;
+ CHAR16 *PathName = NULL;
+ void *sourcebuffer = NULL;
+ UINTN sourcesize = 0;
void *data = NULL;
int datasize;
+ /*
+ * We need to refer to the loaded image protocol on the running
+ * binary in order to find our path
+ */
efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image_handle,
- &loaded_image_protocol, &li);
+ &loaded_image_protocol, (void **)&li);
if (efi_status != EFI_SUCCESS) {
Print(L"Unable to init protocol\n");
return efi_status;
}
- efi_status = generate_path(li, &grubpath, &PathName);
+ /*
+ * Build a new path from the existing one plus the executable name
+ */
+ efi_status = generate_path(li, ImagePath, &path, &PathName);
if (efi_status != EFI_SUCCESS) {
- Print(L"Unable to generate grub path\n");
+ Print(L"Unable to generate path: %s\n", ImagePath);
goto done;
}
- efi_status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image_handle,
- grubpath, NULL, 0, &grub_handle);
-
+ if (findNetboot(image_handle)) {
+ efi_status = parseNetbootinfo(image_handle);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Netboot parsing failed: %d\n", efi_status);
+ return EFI_PROTOCOL_ERROR;
+ }
+ efi_status = FetchNetbootimage(image_handle, &sourcebuffer,
+ &sourcesize);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to fetch TFTP image\n");
+ return efi_status;
+ }
+ data = sourcebuffer;
+ datasize = sourcesize;
+ } else {
+ /*
+ * Read the new executable off disk
+ */
+ efi_status = load_image(li, &data, &datasize, PathName);
- if (efi_status == EFI_SUCCESS) {
- /* Image validates - start it */
- Print(L"Starting file via StartImage\n");
- efi_status = uefi_call_wrapper(BS->StartImage, 3, grub_handle, NULL,
- NULL);
- uefi_call_wrapper(BS->UnloadImage, 1, grub_handle);
- goto done;
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to load image\n");
+ goto done;
+ }
}
- efi_status = load_grub(li, &data, &datasize, PathName);
+ /*
+ * We need to modify the loaded image protocol entry before running
+ * the new binary, so back it up
+ */
+ CopyMem(&li_bak, li, sizeof(li_bak));
+
+ /*
+ * Verify and, if appropriate, relocate and execute the executable
+ */
+ efi_status = handle_image(data, datasize, li);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load grub\n");
+ Print(L"Failed to load image\n");
+ CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
- CopyMem(&li_bak, li, sizeof(li_bak));
+ /*
+ * The binary is trusted and relocated. Run it
+ */
+ efi_status = uefi_call_wrapper(entry_point, 2, image_handle, systab);
- efi_status = handle_grub(data, datasize, li);
+ /*
+ * Restore our original loaded image values
+ */
+ CopyMem(li, &li_bak, sizeof(li_bak));
+done:
+ if (PathName)
+ FreePool(PathName);
+
+ if (data)
+ FreePool(data);
+
+ return efi_status;
+}
+
+/*
+ * Load and run grub. If that fails because grub isn't trusted, load and
+ * run MokManager.
+ */
+EFI_STATUS init_grub(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ 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;
+}
+
+/*
+ * Copy the boot-services only MokList variable to the runtime-accessible
+ * MokListRT variable. It's not marked NV, so the OS can't modify it.
+ */
+EFI_STATUS mirror_mok_list()
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS efi_status;
+ UINT32 attributes;
+ void *Data = NULL;
+ UINTN DataSize = 0;
+
+ efi_status = get_variable(L"MokList", shim_lock_guid, &attributes,
+ &DataSize, &Data);
if (efi_status != EFI_SUCCESS) {
- Print(L"Failed to load grub\n");
- CopyMem(li, &li_bak, sizeof(li_bak));
goto done;
}
- efi_status = uefi_call_wrapper(entry_point, 3, image_handle, systab);
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokListRT",
+ &shim_lock_guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_RUNTIME_ACCESS,
+ DataSize, Data);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to set MokListRT %d\n", efi_status);
+ }
- CopyMem(li, &li_bak, sizeof(li_bak));
done:
-
return efi_status;
}
+/*
+ * Check if a variable exists
+ */
+static BOOLEAN check_var(CHAR16 *varname)
+{
+ EFI_STATUS efi_status;
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ UINTN size = sizeof(UINT32);
+ UINT32 MokVar;
+ UINT32 attributes;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, varname,
+ &shim_lock_guid, &attributes,
+ &size, (void *)&MokVar);
+
+ if (efi_status == EFI_SUCCESS || efi_status == EFI_BUFFER_TOO_SMALL)
+ return TRUE;
+
+ return FALSE;
+}
+
+/*
+ * If the OS has set any of these variables we need to drop into MOK and
+ * handle them appropriately
+ */
+EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ if (check_var(L"MokNew") || check_var(L"MokSB") ||
+ check_var(L"MokPW") || check_var(L"MokAuth")) {
+ efi_status = start_image(image_handle, MOK_MANAGER);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to start MokManager\n");
+ return efi_status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * Verify that MokSBState is valid, and if appropriate set insecure mode
+ */
+
+static EFI_STATUS check_mok_sb (void)
+{
+ EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
+ EFI_STATUS status = EFI_SUCCESS;
+ void *MokSBState = NULL;
+ UINTN MokSBStateSize = 0;
+ UINT32 attributes;
+
+ status = get_variable(L"MokSBState", shim_lock_guid, &attributes,
+ &MokSBStateSize, &MokSBState);
+
+ if (status != EFI_SUCCESS)
+ return EFI_ACCESS_DENIED;
+
+ /*
+ * Delete and ignore the variable if it's been set from or could be
+ * modified by the OS
+ */
+ if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) {
+ Print(L"MokSBState is compromised! Clearing it\n");
+ if (LibDeleteVariable(L"MokSBState", &shim_lock_guid) != EFI_SUCCESS) {
+ Print(L"Failed to erase MokSBState\n");
+ }
+ status = EFI_ACCESS_DENIED;
+ } else {
+ if (*(UINT8 *)MokSBState == 1) {
+ insecure_mode = 1;
+ }
+ }
+
+ return status;
+}
+
EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
{
EFI_GUID shim_lock_guid = SHIM_LOCK_GUID;
@@ -924,18 +1319,62 @@ EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *passed_systab)
EFI_HANDLE handle = NULL;
EFI_STATUS efi_status;
+ /*
+ * Set up the shim lock protocol so that grub and MokManager can
+ * call back in and use shim functions
+ */
shim_lock_interface.Verify = shim_verify;
+ shim_lock_interface.Hash = generate_hash;
+ shim_lock_interface.Context = read_header;
systab = passed_systab;
+ /*
+ * Ensure that gnu-efi functions are available
+ */
InitializeLib(image_handle, systab);
+ /*
+ * Check whether the user has configured the system to run in
+ * insecure mode
+ */
+ check_mok_sb();
+
+ /*
+ * Tell the user that we're in insecure mode if necessary
+ */
+ if (insecure_mode) {
+ Print(L"Booting in insecure mode\n");
+ uefi_call_wrapper(BS->Stall, 1, 2000000);
+ }
+
+ /*
+ * Install the protocol
+ */
uefi_call_wrapper(BS->InstallProtocolInterface, 4, &handle,
&shim_lock_guid, EFI_NATIVE_INTERFACE,
&shim_lock_interface);
+ /*
+ * Enter MokManager if necessary
+ */
+ efi_status = check_mok_request(image_handle);
+
+ /*
+ * Copy the MOK list to a runtime variable so the kernel can make
+ * use of it
+ */
+ efi_status = mirror_mok_list();
+
+ /*
+ * Hand over control to the second stage bootloader
+ */
+
efi_status = init_grub(image_handle);
+ /*
+ * If we're back here then clean everything up before exiting
+ */
uefi_call_wrapper(BS->UninstallProtocolInterface, 3, handle,
&shim_lock_guid, &shim_lock_interface);
diff --git a/shim.h b/shim.h
index b917ea24..08192591 100644
--- a/shim.h
+++ b/shim.h
@@ -1,3 +1,5 @@
+#include "PeImage.h"
+
#define SHIM_LOCK_GUID \
{ 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} }
@@ -10,6 +12,26 @@ EFI_STATUS
IN UINT32 size
);
+typedef
+EFI_STATUS
+(*EFI_SHIM_LOCK_HASH) (
+ IN char *data,
+ IN int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context,
+ UINT8 *sha256hash,
+ UINT8 *sha1hash
+ );
+
+typedef
+EFI_STATUS
+(*EFI_SHIM_LOCK_CONTEXT) (
+ IN VOID *data,
+ IN unsigned int datasize,
+ PE_COFF_LOADER_IMAGE_CONTEXT *context
+ );
+
typedef struct _SHIM_LOCK {
EFI_SHIM_LOCK_VERIFY Verify;
+ EFI_SHIM_LOCK_HASH Hash;
+ EFI_SHIM_LOCK_CONTEXT Context;
} SHIM_LOCK;
diff --git a/signature.h b/signature.h
index d2a8843e..722dbe64 100644
--- a/signature.h
+++ b/signature.h
@@ -1,5 +1,6 @@
#define SHA256_DIGEST_SIZE 32
+EFI_GUID EfiHashSha1Guid = { 0x826ca512, 0xcf10, 0x4ac9, {0xb1, 0x87, 0xbe, 0x1, 0x49, 0x66, 0x31, 0xbd }};
EFI_GUID EfiHashSha256Guid = { 0xc1c41626, 0x504c, 0x4092, {0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 }};
EFI_GUID EfiCertX509Guid = { 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 }};
@@ -12,7 +13,7 @@ typedef struct {
/// The format of the signature is defined by the SignatureType.
///
UINT8 SignatureData[1];
-} EFI_SIGNATURE_DATA;
+} __attribute__ ((packed)) EFI_SIGNATURE_DATA;
typedef struct {
///
@@ -39,4 +40,4 @@ typedef struct {
/// An array of signatures. Each signature is SignatureSize bytes in length.
/// EFI_SIGNATURE_DATA Signatures[][SignatureSize];
///
-} EFI_SIGNATURE_LIST;
+} __attribute__ ((packed)) EFI_SIGNATURE_LIST;