diff options
| author | Gary Ching-Pang Lin <glin@suse.com> | 2012-09-11 16:38:29 +0800 |
|---|---|---|
| committer | Gary Ching-Pang Lin <glin@suse.com> | 2012-09-11 16:38:29 +0800 |
| commit | 481c1e1e76f8158d262589dc571f95623a74e846 (patch) | |
| tree | db3f281b599ddfce0456f7f2f13f9ceecac2041f | |
| parent | 1395a9916b1467367d17d2de51f199fbe11dbfb4 (diff) | |
| download | efi-boot-shim-481c1e1e76f8158d262589dc571f95623a74e846.tar.gz efi-boot-shim-481c1e1e76f8158d262589dc571f95623a74e846.zip | |
Add a separate efi application to manage MOKs
| -rw-r--r-- | Makefile | 9 | ||||
| -rw-r--r-- | MokManager.c | 685 |
2 files changed, 693 insertions, 1 deletions
@@ -28,9 +28,11 @@ LDFLAGS = -nostdlib -znocombreloc -T $(EFI_LDS) -shared -Bsymbolic -L$(EFI_PATH VERSION = 0.1 -TARGET = shim.efi +TARGET = shim.efi MokManager.efi OBJS = shim.o cert.o SOURCES = shim.c shim.h signature.h PeImage.h +MOK_OBJS = MokManager.o +MOK_SOURCES = MokManager.c shim.h all: $(TARGET) @@ -42,6 +44,11 @@ cert.o : cert.S shim.so: $(OBJS) Cryptlib/libcryptlib.a Cryptlib/OpenSSL/libopenssl.a cert.o $(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: $(MAKE) -C Cryptlib diff --git a/MokManager.c b/MokManager.c new file mode 100644 index 00000000..b42be707 --- /dev/null +++ b/MokManager.c @@ -0,0 +1,685 @@ +#include <efi.h> +#include <efilib.h> +#include <Library/BaseCryptLib.h> +#include <openssl/x509.h> +#include "shim.h" + +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; + char allocate = !(*size); + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, + attributes, size, buffer); + + if (efi_status != EFI_BUFFER_TOO_SMALL || !allocate) { + return efi_status; + } + + if (allocate) + *buffer = AllocatePool(*size); + + if (!*buffer) { + Print(L"Unable to allocate variable buffer\n"); + return EFI_OUT_OF_RESOURCES; + } + + efi_status = uefi_call_wrapper(RT->GetVariable, 5, name, &guid, + attributes, size, *buffer); + + return efi_status; +} + +static EFI_STATUS delete_variable (CHAR16 *name, EFI_GUID guid) +{ + EFI_STATUS efi_status; + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, name, &guid, + 0, 0, (UINT8 *)NULL); + + return efi_status; +} + +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_sha256sum (void *Data, int DataSize, 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 (!(Sha256Update(ctx, Data, DataSize))) { + 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 MokListNode *build_mok_list(UINT32 num, void *Data, UINTN DataSize) { + MokListNode *list; + int i, remain = DataSize; + void *ptr; + + list = AllocatePool(sizeof(MokListNode) * num); + + if (!list) { + Print(L"Unable to allocate MOK list\n"); + return NULL; + } + + ptr = Data; + for (i = 0; i < num; i++) { + if (remain < 0) { + Print(L"MOK list was corrupted\n"); + FreePool(list); + return NULL; + } + + CopyMem(&list[i].MokSize, ptr, sizeof(UINT32)); + ptr += sizeof(UINT32); + list[i].Mok = ptr; + ptr += list[i].MokSize; + + remain -= sizeof(UINT32) + list[i].MokSize; + } + + return list; +} + +/* XXX MOK functions */ +static void print_x509_name (X509_NAME *X509Name, char *name) +{ + char *str; + + str = X509_NAME_oneline(X509Name, NULL, 0); + if (str) { + APrint((CHAR8 *)"%a: %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, char *name) { + 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; + } + } + + APrint((CHAR8 *)"%a: %a %2d %02d:%02d:%02d%.*a %d%a", + name, mon[M-1],d,h,m,s,f_len,f,y,(gmt)?" GMT":""); +error: + return; +} + +static void print_x509_UTCTIME_time (ASN1_TIME *time, char *name) +{ + 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'); + + APrint((CHAR8 *)"%a: %a %2d %02d:%02d:%02d %d%a\n", + name, mon[M-1],d,h,m,s,y+1900,(gmt)?" GMT":""); +error: + return; +} + +static void print_x509_time (ASN1_TIME *time, char *name) +{ + if(time->type == V_ASN1_UTCTIME) + print_x509_UTCTIME_time(time, name); + + if(time->type == V_ASN1_GENERALIZEDTIME) + print_x509_GENERALIZEDTIME_time(time, name); +} + +static void show_x509_info (X509 *X509Cert) +{ + X509_NAME *X509Name; + ASN1_TIME *time; + + X509Name = X509_get_issuer_name(X509Cert); + if (X509Name) { + print_x509_name(X509Name, "Issuer"); + } + + X509Name = X509_get_subject_name(X509Cert); + if (X509Name) { + print_x509_name(X509Name, "Subject"); + } + + time = X509_get_notBefore(X509Cert); + if (time) { + print_x509_time(time, "Not Before"); + } + + time = X509_get_notAfter(X509Cert); + if (time) { + print_x509_time(time, "Not After"); + } +} + +static void show_mok_info (void *Mok, UINTN MokSize) +{ + EFI_STATUS efi_status; + UINT8 hash[SHA256_DIGEST_SIZE]; + unsigned int i; + X509 *X509Cert; + + if (!Mok || MokSize == 0) + return; + + efi_status = get_sha256sum(Mok, MokSize, hash); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to compute MOK fingerprint\n"); + return; + } + + if (X509ConstructCertificate(Mok, MokSize, (UINT8 **) &X509Cert) && + X509Cert != NULL) { + show_x509_info(X509Cert); + X509_free(X509Cert); + } + + Print(L"Fingerprint (SHA256):\n"); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) { + Print(L" %02x", hash[i]); + if (i % 16 == 15) + Print(L"\n"); + } +} + +static UINT8 delete_mok(MokListNode *list, UINT32 MokNum, UINT32 delete) +{ + if (!list || !MokNum || MokNum <= delete) + return 0; + + list[delete].Mok = NULL; + list[delete].MokSize = 0; + + return 1; +} + +static UINT8 mok_deletion_prompt(MokListNode *list, UINT32 MokNum) +{ + EFI_INPUT_KEY key; + CHAR16 line[10]; + unsigned int word_count = 0; + UINTN delete; + + Print(L"delete key: "); + do { + key = get_keystroke(); + if ((key.UnicodeChar < '0' || + key.UnicodeChar > '9' || + word_count >= 10) && + key.UnicodeChar != CHAR_BACKSPACE) + continue; + + if (word_count == 0 && key.UnicodeChar == CHAR_BACKSPACE) + continue; + + Print(L"%c", key.UnicodeChar); + + if (key.UnicodeChar == CHAR_BACKSPACE) { + word_count--; + line[word_count] = '\0'; + continue; + } + + line[word_count] = key.UnicodeChar; + word_count++; + } while (key.UnicodeChar != CHAR_CARRIAGE_RETURN); + Print(L"\n"); + + if (word_count == 0) + return 0; + + line[word_count] = '\0'; + delete = Atoi(line)-1; + + if (delete >= MokNum) { + Print(L"No such key\n"); + return 0; + } + + if (!list[delete].Mok) { + Print(L"Already deleted\n"); + return 0; + } + + Print(L"Delete this key?\n"); + show_mok_info(list[delete].Mok, list[delete].MokSize); + Print(L"(y/N) "); + key = get_keystroke(); + if (key.UnicodeChar != 'y' && key.UnicodeChar != 'Y') { + Print(L"N\nAbort\n"); + return 0; + } + Print(L"y\nDelete key %d\n", delete+1); + + return delete_mok(list, MokNum, delete); +} + +static void write_mok_list(void *MokListData, UINTN MokListDataSize, + MokListNode *list, UINT32 MokNum) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + UINT32 new_num = 0; + unsigned int i; + UINTN DataSize = 0; + void *Data, *ptr; + + if (!MokListData || !list) + return; + + for (i = 0; i < MokNum; i++) { + if (list[i].Mok && list[i].MokSize > 0) { + DataSize += list[i].MokSize + sizeof(UINT32); + if (new_num < i) { + list[new_num].Mok = list[i].Mok; + list[new_num].MokSize = list[i].MokSize; + } + new_num++; + } + } + + if (new_num == 0) { + Data = NULL; + goto done; + } + + DataSize += sizeof(UINT32); + + Data = AllocatePool(DataSize * sizeof(UINT8)); + ptr = Data; + + CopyMem(Data, &new_num, sizeof(new_num)); + ptr += sizeof(new_num); + + for (i = 0; i < new_num; i++) { + CopyMem(ptr, &list[i].MokSize, sizeof(list[i].MokSize)); + ptr += sizeof(list[i].MokSize); + CopyMem(ptr, list[i].Mok, list[i].MokSize); + ptr += list[i].MokSize; + } + +done: + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList", + &shim_lock_guid, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, Data); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to set variable %d\n", efi_status); + } + + if (Data) + FreePool(Data); +} + +static void mok_mgmt_shell (void) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + unsigned int i, changed = 0; + void *MokListData = NULL; + UINTN MokListDataSize = 0; + UINT32 MokNum; + UINT32 attributes; + MokListNode *list = NULL; + EFI_INPUT_KEY key; + + efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, + &MokListDataSize, &MokListData); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to get MokList\n"); + goto error; + } + + if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { + Print(L"MokList is compromised!\nErase all keys in MokList!\n"); + if (delete_variable(L"MokList", shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to erase MokList\n"); + } + goto error; + } + + CopyMem(&MokNum, MokListData, sizeof(UINT32)); + if (MokNum == 0) { + Print(L"No key enrolled\n"); + goto error; + } + list = build_mok_list(MokNum, + (void *)MokListData + sizeof(UINT32), + MokListDataSize - sizeof(UINT32)); + + if (!list) { + Print(L"Failed to construct MOK list\n"); + goto error; + } + + do { + Print(L"shim) "); + key = get_keystroke(); + Print(L"%c\n", key.UnicodeChar); + + switch (key.UnicodeChar) { + case 'l': + case 'L': + for (i = 0; i < MokNum; i++) { + if (list[i].Mok) { + Print(L"Key %d\n", i+1); + show_mok_info(list[i].Mok, list[i].MokSize); + Print(L"\n"); + } + } + break; + case 'd': + case 'D': + if (mok_deletion_prompt(list, MokNum) && changed == 0) + changed = 1; + break; + } + } while (key.UnicodeChar != 'c' && key.UnicodeChar != 'C'); + + if (changed) { + write_mok_list(MokListData, MokListDataSize, list, MokNum); + } + +error: + if (MokListData) + FreePool(MokListData); + if (list) + FreePool(list); +} + +static UINT8 mok_enrollment_prompt (void *Mok, UINTN MokSize) +{ + EFI_INPUT_KEY key; + + Print(L"New machine owner key:\n\n"); + show_mok_info(Mok, MokSize); + Print(L"\nEnroll the key? (y/N): "); + + key = get_keystroke(); + Print(L"%c\n", key.UnicodeChar); + + if (key.UnicodeChar == 'Y' || key.UnicodeChar == 'y') { + return 1; + } + + Print(L"Abort\n"); + + return 0; +} + +static EFI_STATUS enroll_mok (void *Mok, UINT32 MokSize, void *OldData, + UINT32 OldDataSize, UINT32 MokNum) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + void *Data, *ptr; + UINT32 DataSize = 0; + + if (OldData) + DataSize += OldDataSize; + else + DataSize += sizeof(UINT32); + DataSize += sizeof(UINT32); + DataSize += MokSize; + MokNum += 1; + + Data = AllocatePool(DataSize); + + if (!Data) { + Print(L"Failed to allocate buffer for MOK list\n"); + return EFI_OUT_OF_RESOURCES; + } + + ptr = Data; + + if (OldData) { + CopyMem(ptr, OldData, OldDataSize); + CopyMem(ptr, &MokNum, sizeof(MokNum)); + ptr += OldDataSize; + } else { + CopyMem(ptr, &MokNum, sizeof(MokNum)); + ptr += sizeof(MokNum); + } + + /* Write new MOK */ + CopyMem(ptr, &MokSize, sizeof(MokSize)); + ptr += sizeof(MokSize); + CopyMem(ptr, Mok, MokSize); + + efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"MokList", + &shim_lock_guid, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS, + DataSize, Data); + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to set variable %d\n", efi_status); + goto error; + } + +error: + if (Data) + FreePool(Data); + + return efi_status; +} + +static EFI_STATUS check_mok_request(EFI_HANDLE image_handle) +{ + EFI_GUID shim_lock_guid = SHIM_LOCK_GUID; + EFI_STATUS efi_status; + UINTN MokSize = 0, MokListDataSize = 0; + void *Mok = NULL, *MokListData = NULL; + UINT32 MokNum = 0; + UINT32 attributes; + MokListNode *list = NULL; + UINT8 confirmed; + + efi_status = get_variable(L"MokNew", shim_lock_guid, &attributes, + &MokSize, &Mok); + + if (efi_status != EFI_SUCCESS) { + goto error; + } + + efi_status = get_variable(L"MokList", shim_lock_guid, &attributes, + &MokListDataSize, &MokListData); + + if (efi_status == EFI_SUCCESS && MokListData) { + int i; + + if (attributes & EFI_VARIABLE_RUNTIME_ACCESS) { + Print(L"MokList is compromised!\nErase all keys in MokList!\n"); + if (delete_variable(L"MokList", shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to erase MokList\n"); + } + goto error; + } + + CopyMem(&MokNum, MokListData, sizeof(UINT32)); + list = build_mok_list(MokNum, + (void *)MokListData + sizeof(UINT32), + MokListDataSize - sizeof(UINT32)); + + if (!list) { + Print(L"Failed to construct MOK list\n"); + goto error; + } + + /* check if the key is already enrolled */ + for (i = 0; i < MokNum; i++) { + if (list[i].MokSize == MokSize && + CompareMem(list[i].Mok, Mok, MokSize) == 0) { + Print(L"MOK was already enrolled\n"); + goto error; + } + } + } + + confirmed = mok_enrollment_prompt(Mok, MokSize); + + if (!confirmed) + goto error; + + efi_status = enroll_mok(Mok, MokSize, MokListData, + MokListDataSize, MokNum); + + if (efi_status != EFI_SUCCESS) { + Print(L"Failed to enroll MOK\n"); + goto error; + } + + mok_mgmt_shell(); + +error: + if (Mok) { + if (delete_variable(L"MokNew", shim_lock_guid) != EFI_SUCCESS) { + Print(L"Failed to delete MokNew\n"); + } + FreePool (Mok); + } + + if (list) + FreePool (list); + + if (MokListData) + FreePool (MokListData); + + return EFI_SUCCESS; +} + +EFI_STATUS efi_main (EFI_HANDLE image_handle, EFI_SYSTEM_TABLE *systab) +{ + EFI_STATUS efi_status; + + InitializeLib(image_handle, systab); + + efi_status = check_mok_request(image_handle); + + return efi_status; +} |
