summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMatthew Garrett <matthew.garrett@nebula.com>2013-05-19 18:13:01 +0100
committerPeter Jones <pjones@redhat.com>2013-09-26 11:57:59 -0400
commitd359712e1b1b0ed7ca611dfd11d9f78754a7a013 (patch)
treebb526a90c21e07c1177da069c50788e7a705bc3f /lib
parentc62b9d16de8bcd743ba59b34aff9d53875e5d793 (diff)
downloadefi-boot-shim-d359712e1b1b0ed7ca611dfd11d9f78754a7a013.tar.gz
efi-boot-shim-d359712e1b1b0ed7ca611dfd11d9f78754a7a013.zip
Port MokManager to Linux Foundation loader UI code
This is the first stage of porting the MokManager UI to the UI code used by the Linux Foundation UEFI loader.
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile28
-rw-r--r--lib/configtable.c144
-rw-r--r--lib/console.c402
-rw-r--r--lib/execute.c127
-rw-r--r--lib/guid.c47
-rw-r--r--lib/security_policy.c391
-rw-r--r--lib/shell.c57
-rw-r--r--lib/simple_file.c501
-rw-r--r--lib/variables.c340
9 files changed, 2037 insertions, 0 deletions
diff --git a/lib/Makefile b/lib/Makefile
new file mode 100644
index 00000000..be5f3542
--- /dev/null
+++ b/lib/Makefile
@@ -0,0 +1,28 @@
+TARGET = lib.a
+
+LIBFILES = simple_file.o guid.o console.o execute.o configtable.o shell.o
+
+ARCH = $(shell uname -m | sed s,i[3456789]86,ia32,)
+
+EFI_INCLUDE = /usr/include/efi
+EFI_INCLUDES = -I$(EFI_INCLUDE) -I$(EFI_INCLUDE)/$(ARCH) -I$(EFI_INCLUDE)/protocol -I../include
+EFI_PATH = /usr/lib64/gnuefi
+
+EFI_CRT_OBJS = $(EFI_PATH)/crt0-efi-$(ARCH).o
+EFI_LDS = $(EFI_PATH)/elf_$(ARCH)_efi.lds
+
+CFLAGS = -ggdb -O0 -fno-stack-protector -fno-strict-aliasing -fpic \
+ -fshort-wchar -Wall -mno-red-zone -DBUILD_EFI $(EFI_INCLUDES)
+ifeq ($(ARCH),x86_64)
+ CFLAGS += -DEFI_FUNCTION_WRAPPER -DGNU_EFI_USE_MS_ABI
+endif
+
+lib.a: $(LIBFILES)
+ ar rcs lib.a $(LIBFILES)
+
+all: $(TARGET)
+
+clean:
+ rm -f lib.a
+ rm -f $(LIBFILES)
+
diff --git a/lib/configtable.c b/lib/configtable.c
new file mode 100644
index 00000000..735ce8f8
--- /dev/null
+++ b/lib/configtable.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ *
+ * read some platform configuration tables
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include <guid.h>
+#include <configtable.h>
+
+void *
+configtable_get_table(EFI_GUID *guid)
+{
+ int i;
+
+ for (i = 0; i < ST->NumberOfTableEntries; i++) {
+ EFI_CONFIGURATION_TABLE *CT = &ST->ConfigurationTable[i];
+
+ if (CompareGuid(guid, &CT->VendorGuid) == 0) {
+ return CT->VendorTable;
+ }
+ }
+ return NULL;
+}
+
+EFI_IMAGE_EXECUTION_INFO_TABLE *
+configtable_get_image_table(void)
+{
+ return configtable_get_table(&SIG_DB);
+}
+
+EFI_IMAGE_EXECUTION_INFO *
+configtable_find_image(const EFI_DEVICE_PATH *DevicePath)
+{
+ EFI_IMAGE_EXECUTION_INFO_TABLE *t = configtable_get_image_table();
+
+ if (!t)
+ return NULL;
+
+ int entries = t->NumberOfImages;
+ EFI_IMAGE_EXECUTION_INFO *e = t->InformationInfo;
+
+ int i;
+ for (i = 0; i < entries; i++) {
+#ifdef DEBUG_CONFIG
+ Print(L"InfoSize = %d Action = %d\n", e->InfoSize, e->Action);
+
+ /* print what we have for debugging */
+ UINT8 *d = (UINT8 *)e; // + sizeof(UINT32)*2;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ d += 16;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ d += 16;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ d += 16;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ d += 16;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+ d += 16;
+ Print(L"Data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
+#endif
+ CHAR16 *name = (CHAR16 *)(e->Data);
+ int skip = 0;
+
+ /* There's a bug in a lot of EFI platforms and they forget to
+ * put the name here. The only real way of detecting it is to
+ * look for either a UC16 NULL or ASCII as UC16 */
+ if (name[0] == '\0' || (e->Data[1] == 0 && e->Data[3] == 0)) {
+ skip = StrSize(name);
+#ifdef DEBUG_CONFIG
+ Print(L"FOUND NAME %s (%d)\n", name, skip);
+#endif
+ }
+ EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *)(e->Data + skip), *dpn = dp;
+ if (dp->Type == 0 || dp->Type > 6 || dp->SubType == 0
+ || (((dp->Length[1] << 8) + dp->Length[0]) > e->InfoSize)) {
+ /* Parse error, table corrupt, bail */
+ Print(L"Image Execution Information table corrupt\n");
+ break;
+ }
+
+ UINTN Size;
+ DevicePathInstance(&dpn, &Size);
+#ifdef DEBUG_CONFIG
+ Print(L"Path: %s\n", DevicePathToStr(dp));
+ Print(L"Device Path Size %d\n", Size);
+#endif
+ if (Size > e->InfoSize) {
+ /* parse error; the platform obviously has a
+ * corrupted image table; bail */
+ Print(L"Image Execution Information table corrupt\n");
+ break;
+ }
+
+ if (CompareMem(dp, DevicePath, Size) == 0) {
+#ifdef DEBUG_CONFIG
+ Print(L"***FOUND\n");
+ console_get_keystroke();
+#endif
+ return e;
+ }
+ e = (EFI_IMAGE_EXECUTION_INFO *)((UINT8 *)e + e->InfoSize);
+ }
+
+#ifdef DEBUG_CONFIG
+ Print(L"***NOT FOUND\n");
+ console_get_keystroke();
+#endif
+
+ return NULL;
+}
+
+int
+configtable_image_is_forbidden(const EFI_DEVICE_PATH *DevicePath)
+{
+ EFI_IMAGE_EXECUTION_INFO *e = configtable_find_image(DevicePath);
+
+ /* Image may not be in DB if it gets executed successfully If it is,
+ * and EFI_IMAGE_EXECUTION_INITIALIZED is not set, then the image
+ * isn't authenticated. If there's no signature, usually
+ * EFI_IMAGE_EXECUTION_AUTH_UNTESTED is set, if the hash is in dbx,
+ * EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND is returned, and if the key is
+ * in dbx, EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED is returned*/
+
+ if (e && (e->Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FOUND
+ || e->Action == EFI_IMAGE_EXECUTION_AUTH_SIG_FAILED)) {
+ /* this means the images signing key is in dbx */
+#ifdef DEBUG_CONFIG
+ Print(L"SIGNATURE IS IN DBX, FORBIDDING EXECUTION\n");
+#endif
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/lib/console.c b/lib/console.c
new file mode 100644
index 00000000..af01f035
--- /dev/null
+++ b/lib/console.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ */
+#include <efi/efi.h>
+#include <efi/efilib.h>
+
+#include <console.h>
+#include <errors.h>
+
+static int min(int a, int b)
+{
+ if (a < b)
+ return a;
+ return b;
+}
+
+static int
+count_lines(CHAR16 *str_arr[])
+{
+ int i = 0;
+
+ while (str_arr[i])
+ i++;
+ return i;
+}
+
+static void
+SetMem16(CHAR16 *dst, UINT32 n, CHAR16 c)
+{
+ int i;
+
+ for (i = 0; i < n/2; i++) {
+ dst[i] = c;
+ }
+}
+
+EFI_INPUT_KEY
+console_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;
+}
+
+void
+console_print_box_at(CHAR16 *str_arr[], int highlight, int start_col, int start_row, int size_cols, int size_rows, int offset, int lines)
+{
+ int i;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
+ UINTN rows, cols;
+ CHAR16 *Line;
+
+ if (lines == 0)
+ return;
+
+ uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows);
+
+ /* last row on screen is unusable without scrolling, so ignore it */
+ rows--;
+
+ if (size_rows < 0)
+ size_rows = rows + size_rows + 1;
+ if (size_cols < 0)
+ size_cols = cols + size_cols + 1;
+
+ if (start_col < 0)
+ start_col = (cols + start_col + 2)/2;
+ if (start_row < 0)
+ start_row = (rows + start_row + 2)/2;
+ if (start_col < 0)
+ start_col = 0;
+ if (start_row < 0)
+ start_row = 0;
+
+ if (start_col > cols || start_row > rows) {
+ Print(L"Starting Position (%d,%d) is off screen\n",
+ start_col, start_row);
+ return;
+ }
+ if (size_cols + start_col > cols)
+ size_cols = cols - start_col;
+ if (size_rows + start_row > rows)
+ size_rows = rows - start_row;
+
+ if (lines > size_rows - 2)
+ lines = size_rows - 2;
+
+ Line = AllocatePool((size_cols+1)*sizeof(CHAR16));
+ if (!Line) {
+ Print(L"Failed Allocation\n");
+ return;
+ }
+
+ SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL);
+
+ Line[0] = BOXDRAW_DOWN_RIGHT;
+ Line[size_cols - 1] = BOXDRAW_DOWN_LEFT;
+ Line[size_cols] = L'\0';
+ uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, start_row);
+ uefi_call_wrapper(co->OutputString, 2, co, Line);
+
+ int start;
+ if (offset == 0)
+ /* middle */
+ start = (size_rows - lines)/2 + start_row + offset;
+ else if (offset < 0)
+ /* from bottom */
+ start = start_row + size_rows - lines + offset - 1;
+ else
+ /* from top */
+ start = start_row + offset;
+
+
+ for (i = start_row + 1; i < size_rows + start_row - 1; i++) {
+ int line = i - start;
+
+ SetMem16 (Line, size_cols*2, L' ');
+ Line[0] = BOXDRAW_VERTICAL;
+ Line[size_cols - 1] = BOXDRAW_VERTICAL;
+ Line[size_cols] = L'\0';
+ if (line >= 0 && line < lines) {
+ CHAR16 *s = str_arr[line];
+ int len = StrLen(s);
+ int col = (size_cols - 2 - len)/2;
+
+ if (col < 0)
+ col = 0;
+
+ CopyMem(Line + col + 1, s, min(len, size_cols - 2)*2);
+ }
+ if (line >= 0 && line == highlight)
+ uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLACK);
+ uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i);
+ uefi_call_wrapper(co->OutputString, 2, co, Line);
+ if (line >= 0 && line == highlight)
+ uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
+
+ }
+ SetMem16 (Line, size_cols * 2, BOXDRAW_HORIZONTAL);
+ Line[0] = BOXDRAW_UP_RIGHT;
+ Line[size_cols - 1] = BOXDRAW_UP_LEFT;
+ Line[size_cols] = L'\0';
+ uefi_call_wrapper(co->SetCursorPosition, 3, co, start_col, i);
+ uefi_call_wrapper(co->OutputString, 2, co, Line);
+
+ FreePool (Line);
+
+}
+
+void
+console_print_box(CHAR16 *str_arr[], int highlight)
+{
+ SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
+ CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode));
+ uefi_call_wrapper(co->EnableCursor, 2, co, FALSE);
+ uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
+
+ console_print_box_at(str_arr, highlight, 0, 0, -1, -1, 0,
+ count_lines(str_arr));
+
+ console_get_keystroke();
+
+ uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
+
+ uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
+ uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);
+ uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute);
+}
+
+int
+console_select(CHAR16 *title[], CHAR16* selectors[], int start)
+{
+ SIMPLE_TEXT_OUTPUT_MODE SavedConsoleMode;
+ SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
+ EFI_INPUT_KEY k;
+ int selector;
+ int selector_lines = count_lines(selectors);
+ int selector_max_cols = 0;
+ int i, offs_col, offs_row, size_cols, size_rows, lines;
+ int selector_offset;
+ UINTN cols, rows;
+
+ uefi_call_wrapper(co->QueryMode, 4, co, co->Mode->Mode, &cols, &rows);
+
+ for (i = 0; i < selector_lines; i++) {
+ int len = StrLen(selectors[i]);
+
+ if (len > selector_max_cols)
+ selector_max_cols = len;
+ }
+
+ if (start < 0)
+ start = 0;
+ if (start >= selector_lines)
+ start = selector_lines - 1;
+
+ offs_col = - selector_max_cols - 4;
+ size_cols = selector_max_cols + 4;
+
+ if (selector_lines > rows - 10) {
+ int title_lines = count_lines(title);
+ offs_row = title_lines + 1;
+ size_rows = rows - 3 - title_lines;
+ lines = size_rows - 2;
+ } else {
+ offs_row = - selector_lines - 4;
+ size_rows = selector_lines + 2;
+ lines = selector_lines;
+ }
+
+ if (start > lines) {
+ selector = lines;
+ selector_offset = start - lines;
+ } else {
+ selector = start;
+ selector_offset = 0;
+ }
+
+ CopyMem(&SavedConsoleMode, co->Mode, sizeof(SavedConsoleMode));
+ uefi_call_wrapper(co->EnableCursor, 2, co, FALSE);
+ uefi_call_wrapper(co->SetAttribute, 2, co, EFI_LIGHTGRAY | EFI_BACKGROUND_BLUE);
+
+ console_print_box_at(title, -1, 0, 0, -1, -1, 1, count_lines(title));
+
+ console_print_box_at(selectors, selector, offs_col, offs_row,
+ size_cols, size_rows, 0, lines);
+
+ do {
+ k = console_get_keystroke();
+
+ if (k.ScanCode == SCAN_ESC) {
+ selector = -1;
+ break;
+ }
+
+ if (k.ScanCode == SCAN_UP) {
+ if (selector > 0)
+ selector--;
+ else if (selector_offset > 0)
+ selector_offset--;
+ } else if (k.ScanCode == SCAN_DOWN) {
+ if (selector < lines - 1)
+ selector++;
+ else if (selector_offset < (selector_lines - lines))
+ selector_offset++;
+ }
+
+ console_print_box_at(&selectors[selector_offset], selector,
+ offs_col, offs_row,
+ size_cols, size_rows, 0, lines);
+ } while (!(k.ScanCode == SCAN_NULL
+ && k.UnicodeChar == CHAR_CARRIAGE_RETURN));
+
+ uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
+
+ uefi_call_wrapper(co->EnableCursor, 2, co, SavedConsoleMode.CursorVisible);
+ uefi_call_wrapper(co->SetCursorPosition, 3, co, SavedConsoleMode.CursorColumn, SavedConsoleMode.CursorRow);
+ uefi_call_wrapper(co->SetAttribute, 2, co, SavedConsoleMode.Attribute);
+
+ if (selector < 0)
+ /* ESC pressed */
+ return selector;
+ return selector + selector_offset;
+}
+
+
+int
+console_yes_no(CHAR16 *str_arr[])
+{
+ return console_select(str_arr, (CHAR16 *[]){ L"No", L"Yes", NULL }, 0);
+}
+
+void
+console_alertbox(CHAR16 **title)
+{
+ console_select(title, (CHAR16 *[]){ L"OK", 0 }, 0);
+}
+
+void
+console_errorbox(CHAR16 *err)
+{
+ CHAR16 **err_arr = (CHAR16 *[]){
+ L"ERROR",
+ L"",
+ 0,
+ 0,
+ };
+
+ err_arr[2] = err;
+
+ console_alertbox(err_arr);
+}
+
+void
+console_notify(CHAR16 *string)
+{
+ CHAR16 **str_arr = (CHAR16 *[]){
+ 0,
+ 0,
+ };
+
+ str_arr[0] = string;
+
+ console_alertbox(str_arr);
+}
+
+#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
+
+/* Copy of gnu-efi-3.0 with the added secure boot strings */
+static struct {
+ EFI_STATUS Code;
+ WCHAR *Desc;
+} error_table[] = {
+ { EFI_SUCCESS, L"Success"},
+ { EFI_LOAD_ERROR, L"Load Error"},
+ { EFI_INVALID_PARAMETER, L"Invalid Parameter"},
+ { EFI_UNSUPPORTED, L"Unsupported"},
+ { EFI_BAD_BUFFER_SIZE, L"Bad Buffer Size"},
+ { EFI_BUFFER_TOO_SMALL, L"Buffer Too Small"},
+ { EFI_NOT_READY, L"Not Ready"},
+ { EFI_DEVICE_ERROR, L"Device Error"},
+ { EFI_WRITE_PROTECTED, L"Write Protected"},
+ { EFI_OUT_OF_RESOURCES, L"Out of Resources"},
+ { EFI_VOLUME_CORRUPTED, L"Volume Corrupt"},
+ { EFI_VOLUME_FULL, L"Volume Full"},
+ { EFI_NO_MEDIA, L"No Media"},
+ { EFI_MEDIA_CHANGED, L"Media changed"},
+ { EFI_NOT_FOUND, L"Not Found"},
+ { EFI_ACCESS_DENIED, L"Access Denied"},
+ { EFI_NO_RESPONSE, L"No Response"},
+ { EFI_NO_MAPPING, L"No mapping"},
+ { EFI_TIMEOUT, L"Time out"},
+ { EFI_NOT_STARTED, L"Not started"},
+ { EFI_ALREADY_STARTED, L"Already started"},
+ { EFI_ABORTED, L"Aborted"},
+ { EFI_ICMP_ERROR, L"ICMP Error"},
+ { EFI_TFTP_ERROR, L"TFTP Error"},
+ { EFI_PROTOCOL_ERROR, L"Protocol Error"},
+ { EFI_INCOMPATIBLE_VERSION, L"Incompatible Version"},
+ { EFI_SECURITY_VIOLATION, L"Security Violation"},
+
+ // warnings
+ { EFI_WARN_UNKOWN_GLYPH, L"Warning Unknown Glyph"},
+ { EFI_WARN_DELETE_FAILURE, L"Warning Delete Failure"},
+ { EFI_WARN_WRITE_FAILURE, L"Warning Write Failure"},
+ { EFI_WARN_BUFFER_TOO_SMALL, L"Warning Buffer Too Small"},
+ { 0, NULL}
+} ;
+
+
+static CHAR16 *
+err_string (
+ IN EFI_STATUS Status
+ )
+{
+ UINTN Index;
+
+ for (Index = 0; error_table[Index].Desc; Index +=1) {
+ if (error_table[Index].Code == Status) {
+ return error_table[Index].Desc;
+ }
+ }
+
+ return L"";
+}
+
+
+void
+console_error(CHAR16 *err, EFI_STATUS status)
+{
+ CHAR16 **err_arr = (CHAR16 *[]){
+ L"ERROR",
+ L"",
+ 0,
+ 0,
+ };
+ CHAR16 str[512];
+
+ SPrint(str, sizeof(str), L"%s: (%d) %s", err, status, err_string(status));
+
+ err_arr[2] = str;
+
+ console_alertbox(err_arr);
+}
+
+void
+console_reset(void)
+{
+ SIMPLE_TEXT_OUTPUT_INTERFACE *co = ST->ConOut;
+
+ uefi_call_wrapper(co->Reset, 2, co, TRUE);
+ /* set mode 0 - required to be 80x25 */
+ uefi_call_wrapper(co->SetMode, 2, co, 0);
+ uefi_call_wrapper(co->ClearScreen, 1, co);
+}
diff --git a/lib/execute.c b/lib/execute.c
new file mode 100644
index 00000000..8d726eb8
--- /dev/null
+++ b/lib/execute.c
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ *
+ * --
+ *
+ * generate_path is a cut and paste from
+ *
+ * git://github.com/mjg59/shim.git
+ *
+ * Code 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.
+ *
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <guid.h>
+#include <execute.h>
+
+EFI_STATUS
+generate_path(CHAR16* name, EFI_LOADED_IMAGE *li, EFI_DEVICE_PATH **path, CHAR16 **PathName)
+{
+ unsigned int pathlen;
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ CHAR16 *devpathstr = DevicePathToStr(li->FilePath),
+ *found = NULL;
+ int i;
+
+ for (i = 0; i < StrLen(devpathstr); i++) {
+ if (devpathstr[i] == '/')
+ devpathstr[i] = '\\';
+ if (devpathstr[i] == '\\')
+ found = &devpathstr[i];
+ }
+ if (!found) {
+ pathlen = 0;
+ } else {
+ while (*(found - 1) == '\\')
+ --found;
+ *found = '\0';
+ pathlen = StrLen(devpathstr);
+ }
+
+ if (name[0] != '\\')
+ pathlen++;
+
+ *PathName = AllocatePool((pathlen + 1 + StrLen(name))*sizeof(CHAR16));
+
+ if (!*PathName) {
+ Print(L"Failed to allocate path buffer\n");
+ efi_status = EFI_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ StrCpy(*PathName, devpathstr);
+
+ if (name[0] != '\\')
+ StrCat(*PathName, L"\\");
+ StrCat(*PathName, name);
+
+ *path = FileDevicePath(li->DeviceHandle, *PathName);
+
+error:
+ FreePool(devpathstr);
+
+ return efi_status;
+}
+
+EFI_STATUS
+execute(EFI_HANDLE image, CHAR16 *name)
+{
+ EFI_STATUS status;
+ EFI_HANDLE h;
+ EFI_LOADED_IMAGE *li;
+ EFI_DEVICE_PATH *devpath;
+ CHAR16 *PathName;
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3, image,
+ &IMAGE_PROTOCOL, &li);
+ if (status != EFI_SUCCESS)
+ return status;
+
+
+ status = generate_path(name, li, &devpath, &PathName);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ status = uefi_call_wrapper(BS->LoadImage, 6, FALSE, image,
+ devpath, NULL, 0, &h);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ status = uefi_call_wrapper(BS->StartImage, 3, h, NULL, NULL);
+ uefi_call_wrapper(BS->UnloadImage, 1, h);
+
+ out:
+ FreePool(PathName);
+ FreePool(devpath);
+ return status;
+}
diff --git a/lib/guid.c b/lib/guid.c
new file mode 100644
index 00000000..25db91a7
--- /dev/null
+++ b/lib/guid.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ */
+
+#include <guid.h>
+#include <stdio.h>
+
+#ifndef BUILD_EFI
+/* EFI has %g for this, so it's only needed in platform c */
+const char *guid_to_str(EFI_GUID *guid)
+{
+ static char str[256];
+
+ sprintf(str, "%08x-%04hx-%04hx-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx",
+ guid->Data1, guid->Data2, guid->Data3,
+ guid->Data4[0], guid->Data4[1], guid->Data4[2],
+ guid->Data4[3], guid->Data4[4], guid->Data4[5],
+ guid->Data4[6], guid->Data4[7]);
+
+ return str;
+}
+
+void str_to_guid(const char *str, EFI_GUID *guid)
+{
+ sscanf(str, "%8x-%4hx-%4hx-%2hhx%2hhx-%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
+ &guid->Data1, &guid->Data2, &guid->Data3,
+ guid->Data4, guid->Data4 + 1, guid->Data4 + 2,
+ guid->Data4 + 3, guid->Data4 + 4, guid->Data4 + 5,
+ guid->Data4 + 6, guid->Data4 + 7);
+}
+#endif
+
+/* all the necessary guids */
+EFI_GUID GV_GUID = EFI_GLOBAL_VARIABLE;
+EFI_GUID SIG_DB = { 0xd719b2cb, 0x3d3a, 0x4596, {0xa3, 0xbc, 0xda, 0xd0, 0xe, 0x67, 0x65, 0x6f }};
+
+EFI_GUID X509_GUID = { 0xa5c059a1, 0x94e4, 0x4aa7, {0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72} };
+EFI_GUID RSA2048_GUID = { 0x3c5766e8, 0x269c, 0x4e34, {0xaa, 0x14, 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6} };
+EFI_GUID PKCS7_GUID = { 0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} };
+EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL;
+EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL;
+EFI_GUID EFI_CERT_SHA256_GUID = { 0xc1c41626, 0x504c, 0x4092, { 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 } };
+EFI_GUID MOK_OWNER = { 0x605dab50, 0xe046, 0x4300, {0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23} };
+EFI_GUID SECURITY_PROTOCOL_GUID = { 0xA46423E3, 0x4617, 0x49f1, {0xB9, 0xFF, 0xD1, 0xBF, 0xA9, 0x11, 0x58, 0x39 } };
+EFI_GUID SECURITY2_PROTOCOL_GUID = { 0x94ab2f58, 0x1438, 0x4ef1, {0x91, 0x52, 0x18, 0x94, 0x1a, 0x3a, 0x0e, 0x68 } };
diff --git a/lib/security_policy.c b/lib/security_policy.c
new file mode 100644
index 00000000..e7becbf4
--- /dev/null
+++ b/lib/security_policy.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ *
+ * Install and remove a platform security2 override policy
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <guid.h>
+#include <sha256.h>
+#include <variables.h>
+#include <simple_file.h>
+#include <errors.h>
+
+#include <security_policy.h>
+
+/*
+ * See the UEFI Platform Initialization manual (Vol2: DXE) for this
+ */
+struct _EFI_SECURITY2_PROTOCOL;
+struct _EFI_SECURITY_PROTOCOL;
+typedef struct _EFI_SECURITY2_PROTOCOL EFI_SECURITY2_PROTOCOL;
+typedef struct _EFI_SECURITY_PROTOCOL EFI_SECURITY_PROTOCOL;
+typedef EFI_DEVICE_PATH EFI_DEVICE_PATH_PROTOCOL;
+
+typedef EFI_STATUS (EFIAPI *EFI_SECURITY_FILE_AUTHENTICATION_STATE) (
+ const EFI_SECURITY_PROTOCOL *This,
+ UINT32 AuthenticationStatus,
+ const EFI_DEVICE_PATH_PROTOCOL *File
+ );
+typedef EFI_STATUS (EFIAPI *EFI_SECURITY2_FILE_AUTHENTICATION) (
+ const EFI_SECURITY2_PROTOCOL *This,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *FileBuffer,
+ UINTN FileSize,
+ BOOLEAN BootPolicy
+ );
+
+struct _EFI_SECURITY2_PROTOCOL {
+ EFI_SECURITY2_FILE_AUTHENTICATION FileAuthentication;
+};
+
+struct _EFI_SECURITY_PROTOCOL {
+ EFI_SECURITY_FILE_AUTHENTICATION_STATE FileAuthenticationState;
+};
+
+
+static UINT8 *security_policy_esl = NULL;
+static UINTN security_policy_esl_len;
+
+static EFI_STATUS
+security_policy_check_mok(void *data, UINTN len)
+{
+ EFI_STATUS status;
+ UINT8 hash[SHA256_DIGEST_SIZE];
+ UINT32 attr;
+ UINT8 *VarData;
+ UINTN VarLen;
+
+ /* first check is MokSBState. If we're in insecure mode, boot
+ * anyway regardless of dbx contents */
+ status = get_variable_attr(L"MokSBState", &VarData, &VarLen,
+ MOK_OWNER, &attr);
+ if (status == EFI_SUCCESS) {
+ UINT8 MokSBState = VarData[0];
+
+ FreePool(VarData);
+ if ((attr & EFI_VARIABLE_RUNTIME_ACCESS) == 0
+ && MokSBState)
+ return EFI_SUCCESS;
+ }
+
+ status = sha256_get_pecoff_digest_mem(data, len, hash);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ if (find_in_variable_esl(L"dbx", SIG_DB, hash, SHA256_DIGEST_SIZE)
+ == EFI_SUCCESS)
+ /* MOK list cannot override dbx */
+ return EFI_SECURITY_VIOLATION;
+
+ status = get_variable_attr(L"MokList", &VarData, &VarLen, MOK_OWNER,
+ &attr);
+ if (status != EFI_SUCCESS)
+ goto check_tmplist;
+
+ FreePool(VarData);
+
+ if (attr & EFI_VARIABLE_RUNTIME_ACCESS)
+ goto check_tmplist;
+
+ if (find_in_variable_esl(L"MokList", MOK_OWNER, hash, SHA256_DIGEST_SIZE) == EFI_SUCCESS)
+ return EFI_SUCCESS;
+
+ check_tmplist:
+ if (security_policy_esl
+ && find_in_esl(security_policy_esl, security_policy_esl_len, hash,
+ SHA256_DIGEST_SIZE) == EFI_SUCCESS)
+ return EFI_SUCCESS;
+
+ return EFI_SECURITY_VIOLATION;
+}
+
+static EFI_SECURITY_FILE_AUTHENTICATION_STATE esfas = NULL;
+static EFI_SECURITY2_FILE_AUTHENTICATION es2fa = NULL;
+
+static EFI_STATUS thunk_security_policy_authentication(
+ const EFI_SECURITY_PROTOCOL *This,
+ UINT32 AuthenticationStatus,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePath
+ )
+__attribute__((unused));
+
+static EFI_STATUS thunk_security2_policy_authentication(
+ const EFI_SECURITY2_PROTOCOL *This,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *FileBuffer,
+ UINTN FileSize,
+ BOOLEAN BootPolicy
+ )
+__attribute__((unused));
+
+static __attribute__((used)) EFI_STATUS
+security2_policy_authentication (
+ const EFI_SECURITY2_PROTOCOL *This,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePath,
+ VOID *FileBuffer,
+ UINTN FileSize,
+ BOOLEAN BootPolicy
+ )
+{
+ EFI_STATUS status, auth;
+
+ /* Chain original security policy */
+
+ status = uefi_call_wrapper(es2fa, 5, This, DevicePath, FileBuffer,
+ FileSize, BootPolicy);
+
+ /* if OK, don't bother with MOK check */
+ if (status == EFI_SUCCESS)
+ return status;
+
+ auth = security_policy_check_mok(FileBuffer, FileSize);
+
+ if (auth == EFI_SECURITY_VIOLATION || auth == EFI_ACCESS_DENIED)
+ /* return previous status, which is the correct one
+ * for the platform: may be either EFI_ACCESS_DENIED
+ * or EFI_SECURITY_VIOLATION */
+ return status;
+
+ return auth;
+}
+
+static __attribute__((used)) EFI_STATUS
+security_policy_authentication (
+ const EFI_SECURITY_PROTOCOL *This,
+ UINT32 AuthenticationStatus,
+ const EFI_DEVICE_PATH_PROTOCOL *DevicePathConst
+ )
+{
+ EFI_STATUS status, fail_status;
+ EFI_DEVICE_PATH *DevPath
+ = DuplicateDevicePath((EFI_DEVICE_PATH *)DevicePathConst),
+ *OrigDevPath = DevPath;
+ EFI_HANDLE h;
+ EFI_FILE *f;
+ VOID *FileBuffer;
+ UINTN FileSize;
+ CHAR16* DevPathStr;
+
+ /* Chain original security policy */
+ status = uefi_call_wrapper(esfas, 3, This, AuthenticationStatus,
+ DevicePathConst);
+
+ /* if OK avoid checking MOK: It's a bit expensive to
+ * read the whole file in again (esfas already did this) */
+ if (status == EFI_SUCCESS)
+ goto out;
+
+ /* capture failure status: may be either EFI_ACCESS_DENIED or
+ * EFI_SECURITY_VIOLATION */
+ fail_status = status;
+
+ status = uefi_call_wrapper(BS->LocateDevicePath, 3,
+ &SIMPLE_FS_PROTOCOL, &DevPath, &h);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ DevPathStr = DevicePathToStr(DevPath);
+
+ status = simple_file_open_by_handle(h, DevPathStr, &f,
+ EFI_FILE_MODE_READ);
+ FreePool(DevPathStr);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ status = simple_file_read_all(f, &FileSize, &FileBuffer);
+ simple_file_close(f);
+ if (status != EFI_SUCCESS)
+ goto out;
+
+ status = security_policy_check_mok(FileBuffer, FileSize);
+ FreePool(FileBuffer);
+
+ if (status == EFI_ACCESS_DENIED || status == EFI_SECURITY_VIOLATION)
+ /* return what the platform originally said */
+ status = fail_status;
+ out:
+ FreePool(OrigDevPath);
+ return status;
+}
+
+
+/* Nasty: ELF and EFI have different calling conventions. Here is the map for
+ * calling ELF -> EFI
+ *
+ * 1) rdi -> rcx (32 saved)
+ * 2) rsi -> rdx (32 saved)
+ * 3) rdx -> r8 ( 32 saved)
+ * 4) rcx -> r9 (32 saved)
+ * 5) r8 -> 32(%rsp) (48 saved)
+ * 6) r9 -> 40(%rsp) (48 saved)
+ * 7) pad+0(%rsp) -> 48(%rsp) (64 saved)
+ * 8) pad+8(%rsp) -> 56(%rsp) (64 saved)
+ * 9) pad+16(%rsp) -> 64(%rsp) (80 saved)
+ * 10) pad+24(%rsp) -> 72(%rsp) (80 saved)
+ * 11) pad+32(%rsp) -> 80(%rsp) (96 saved)
+
+ *
+ * So for a five argument callback, the map is ignore the first two arguments
+ * and then map (EFI -> ELF) assuming pad = 0.
+ *
+ * ARG4 -> ARG1
+ * ARG3 -> ARG2
+ * ARG5 -> ARG3
+ * ARG6 -> ARG4
+ * ARG11 -> ARG5
+ *
+ * Calling conventions also differ over volatile and preserved registers in
+ * MS: RBX, RBP, RDI, RSI, R12, R13, R14, and R15 are considered nonvolatile .
+ * In ELF: Registers %rbp, %rbx and %r12 through %r15 “belong” to the calling
+ * function and the called function is required to preserve their values.
+ *
+ * This means when accepting a function callback from MS -> ELF, we have to do
+ * separate preservation on %rdi, %rsi before swizzling the arguments and
+ * handing off to the ELF function.
+ */
+
+asm (
+".type security2_policy_authentication,@function\n"
+"thunk_security2_policy_authentication:\n\t"
+ "mov 0x28(%rsp), %r10 # ARG5\n\t"
+ "push %rdi\n\t"
+ "push %rsi\n\t"
+ "mov %r10, %rdi\n\t"
+ "subq $8, %rsp # space for storing stack pad\n\t"
+ "mov $0x08, %rax\n\t"
+ "mov $0x10, %r10\n\t"
+ "and %rsp, %rax\n\t"
+ "cmovnz %rax, %r11\n\t"
+ "cmovz %r10, %r11\n\t"
+ "subq %r11, %rsp\n\t"
+ "addq $8, %r11\n\t"
+ "mov %r11, (%rsp)\n\t"
+"# five argument swizzle\n\t"
+ "mov %rdi, %r10\n\t"
+ "mov %rcx, %rdi\n\t"
+ "mov %rdx, %rsi\n\t"
+ "mov %r8, %rdx\n\t"
+ "mov %r9, %rcx\n\t"
+ "mov %r10, %r8\n\t"
+ "callq security2_policy_authentication@PLT\n\t"
+ "mov (%rsp), %r11\n\t"
+ "addq %r11, %rsp\n\t"
+ "pop %rsi\n\t"
+ "pop %rdi\n\t"
+ "ret\n"
+);
+
+asm (
+".type security_policy_authentication,@function\n"
+"thunk_security_policy_authentication:\n\t"
+ "push %rdi\n\t"
+ "push %rsi\n\t"
+ "subq $8, %rsp # space for storing stack pad\n\t"
+ "mov $0x08, %rax\n\t"
+ "mov $0x10, %r10\n\t"
+ "and %rsp, %rax\n\t"
+ "cmovnz %rax, %r11\n\t"
+ "cmovz %r10, %r11\n\t"
+ "subq %r11, %rsp\n\t"
+ "addq $8, %r11\n\t"
+ "mov %r11, (%rsp)\n\t"
+"# three argument swizzle\n\t"
+ "mov %rcx, %rdi\n\t"
+ "mov %rdx, %rsi\n\t"
+ "mov %r8, %rdx\n\t"
+ "callq security_policy_authentication@PLT\n\t"
+ "mov (%rsp), %r11\n\t"
+ "addq %r11, %rsp\n\t"
+ "pop %rsi\n\t"
+ "pop %rdi\n\t"
+ "ret\n"
+);
+
+EFI_STATUS
+security_policy_install(void)
+{
+ EFI_SECURITY_PROTOCOL *security_protocol;
+ EFI_SECURITY2_PROTOCOL *security2_protocol = NULL;
+ EFI_STATUS status;
+
+ if (esfas)
+ /* Already Installed */
+ return EFI_ALREADY_STARTED;
+
+ /* Don't bother with status here. The call is allowed
+ * to fail, since SECURITY2 was introduced in PI 1.2.1
+ * If it fails, use security2_protocol == NULL as indicator */
+ uefi_call_wrapper(BS->LocateProtocol, 3,
+ &SECURITY2_PROTOCOL_GUID, NULL,
+ &security2_protocol);
+
+ status = uefi_call_wrapper(BS->LocateProtocol, 3,
+ &SECURITY_PROTOCOL_GUID, NULL,
+ &security_protocol);
+ if (status != EFI_SUCCESS)
+ /* This one is mandatory, so there's a serious problem */
+ return status;
+
+ if (security2_protocol) {
+ es2fa = security2_protocol->FileAuthentication;
+ security2_protocol->FileAuthentication =
+ thunk_security2_policy_authentication;
+ }
+
+ esfas = security_protocol->FileAuthenticationState;
+ security_protocol->FileAuthenticationState =
+ thunk_security_policy_authentication;
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+security_policy_uninstall(void)
+{
+ EFI_STATUS status;
+
+ if (esfas) {
+ EFI_SECURITY_PROTOCOL *security_protocol;
+
+ status = uefi_call_wrapper(BS->LocateProtocol, 3,
+ &SECURITY_PROTOCOL_GUID, NULL,
+ &security_protocol);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ security_protocol->FileAuthenticationState = esfas;
+ esfas = NULL;
+ } else {
+ /* nothing installed */
+ return EFI_NOT_STARTED;
+ }
+
+ if (es2fa) {
+ EFI_SECURITY2_PROTOCOL *security2_protocol;
+
+ status = uefi_call_wrapper(BS->LocateProtocol, 3,
+ &SECURITY2_PROTOCOL_GUID, NULL,
+ &security2_protocol);
+
+ if (status != EFI_SUCCESS)
+ return status;
+
+ security2_protocol->FileAuthentication = es2fa;
+ es2fa = NULL;
+ }
+
+ return EFI_SUCCESS;
+}
+
+void
+security_protocol_set_hashes(unsigned char *esl, int len)
+{
+ security_policy_esl = esl;
+ security_policy_esl_len = len;
+}
diff --git a/lib/shell.c b/lib/shell.c
new file mode 100644
index 00000000..51de4e0d
--- /dev/null
+++ b/lib/shell.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ *
+ * misc shell helper functions
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include <shell.h>
+
+EFI_STATUS
+argsplit(EFI_HANDLE image, int *argc, CHAR16*** ARGV)
+{
+ int i, count = 1;
+ EFI_STATUS status;
+ EFI_LOADED_IMAGE *info;
+ CHAR16 *start;
+
+ *argc = 0;
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3, image, &LoadedImageProtocol, (VOID **) &info);
+ if (EFI_ERROR(status)) {
+ Print(L"Failed to get arguments\n");
+ return status;
+ }
+
+ for (i = 0; i < info->LoadOptionsSize; i += 2) {
+ CHAR16 *c = (CHAR16 *)(info->LoadOptions + i);
+ if (*c == L' ' && *(c+1) != '\0') {
+ (*argc)++;
+ }
+ }
+
+ (*argc)++; /* we counted spaces, so add one for initial */
+
+ *ARGV = AllocatePool(*argc * sizeof(*ARGV));
+ if (!*ARGV) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ (*ARGV)[0] = (CHAR16 *)info->LoadOptions;
+ for (i = 0; i < info->LoadOptionsSize; i += 2) {
+ CHAR16 *c = (CHAR16 *)(info->LoadOptions + i);
+ if (*c == L' ') {
+ *c = L'\0';
+ if (*(c + 1) == '\0')
+ /* strip trailing space */
+ break;
+ start = c + 1;
+ (*ARGV)[count++] = start;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
diff --git a/lib/simple_file.c b/lib/simple_file.c
new file mode 100644
index 00000000..0e5ecd25
--- /dev/null
+++ b/lib/simple_file.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ */
+
+#include <efi.h>
+#include <efilib.h>
+
+#include <console.h>
+#include <simple_file.h>
+#include <efiauthenticated.h>
+#include <execute.h> /* for generate_path() */
+
+static EFI_GUID IMAGE_PROTOCOL = LOADED_IMAGE_PROTOCOL;
+static EFI_GUID SIMPLE_FS_PROTOCOL = SIMPLE_FILE_SYSTEM_PROTOCOL;
+static EFI_GUID FILE_INFO = EFI_FILE_INFO_ID;
+static EFI_GUID FS_INFO = EFI_FILE_SYSTEM_INFO_ID;
+
+EFI_STATUS
+simple_file_open_by_handle(EFI_HANDLE device, CHAR16 *name, EFI_FILE **file, UINT64 mode)
+{
+ EFI_STATUS efi_status;
+ EFI_FILE_IO_INTERFACE *drive;
+ EFI_FILE *root;
+
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, device,
+ &SIMPLE_FS_PROTOCOL, &drive);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to find simple file protocol (%d)\n", efi_status);
+ goto error;
+ }
+
+ efi_status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to open drive volume (%d)\n", efi_status);
+ goto error;
+ }
+
+ efi_status = uefi_call_wrapper(root->Open, 5, root, file, name,
+ mode, 0);
+
+ error:
+ return efi_status;
+}
+
+EFI_STATUS
+simple_file_open(EFI_HANDLE image, CHAR16 *name, EFI_FILE **file, UINT64 mode)
+{
+ EFI_STATUS efi_status;
+ EFI_HANDLE device;
+ EFI_LOADED_IMAGE *li;
+ EFI_DEVICE_PATH *loadpath = NULL;
+ CHAR16 *PathName = NULL;
+
+ efi_status = uefi_call_wrapper(BS->HandleProtocol, 3, image,
+ &IMAGE_PROTOCOL, &li);
+
+ if (efi_status != EFI_SUCCESS)
+ return simple_file_open_by_handle(image, name, file, mode);
+
+ efi_status = generate_path(name, li, &loadpath, &PathName);
+
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Unable to generate load path for %s\n", name);
+ return efi_status;
+ }
+
+ device = li->DeviceHandle;
+
+ efi_status = simple_file_open_by_handle(device, PathName, file, mode);
+
+ FreePool(PathName);
+ FreePool(loadpath);
+
+ return efi_status;
+}
+
+EFI_STATUS
+simple_dir_read_all_by_handle(EFI_HANDLE image, EFI_FILE *file, CHAR16* name, EFI_FILE_INFO **entries,
+ int *count)
+{
+ EFI_STATUS status;
+ char buf[4096];
+ UINTN size = sizeof(buf);
+ EFI_FILE_INFO *fi = (void *)buf;
+
+ status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO,
+ &size, fi);
+ if (status != EFI_SUCCESS) {
+ Print(L"Failed to get file info\n");
+ goto out;
+ }
+ if ((fi->Attribute & EFI_FILE_DIRECTORY) == 0) {
+ Print(L"Not a directory %s\n", name);
+ status = EFI_INVALID_PARAMETER;
+ goto out;
+ }
+ size = 0;
+ *count = 0;
+ for (;;) {
+ UINTN len = sizeof(buf);
+ status = uefi_call_wrapper(file->Read, 3, file, &len, buf);
+ if (status != EFI_SUCCESS || len == 0)
+ break;
+ (*count)++;
+ size += len;
+ }
+ uefi_call_wrapper(file->SetPosition, 2, file, 0);
+
+ char *ptr = AllocatePool(size);
+ *entries = (EFI_FILE_INFO *)ptr;
+ if (!*entries)
+ return EFI_OUT_OF_RESOURCES;
+ int i;
+ for (i = 0; i < *count; i++) {
+ int len = size;
+ uefi_call_wrapper(file->Read, 3, file, &len, ptr);
+ ptr += len;
+ size -= len;
+ }
+ status = EFI_SUCCESS;
+ out:
+ simple_file_close(file);
+ if (status != EFI_SUCCESS && *entries) {
+ FreePool(*entries);
+ *entries = NULL;
+ }
+ return status;
+}
+
+EFI_STATUS
+simple_dir_read_all(EFI_HANDLE image, CHAR16 *name, EFI_FILE_INFO **entries,
+ int *count)
+{
+ EFI_FILE *file;
+ EFI_STATUS status;
+
+ status = simple_file_open(image, name, &file, EFI_FILE_MODE_READ);
+ if (status != EFI_SUCCESS) {
+ Print(L"failed to open file %s: %d\n", name, status);
+ return status;
+ }
+
+ return simple_dir_read_all_by_handle(image, file, name, entries, count);
+}
+
+EFI_STATUS
+simple_file_read_all(EFI_FILE *file, UINTN *size, void **buffer)
+{
+ EFI_STATUS efi_status;
+ EFI_FILE_INFO *fi;
+ char buf[1024];
+
+ *size = sizeof(buf);
+ fi = (void *)buf;
+
+
+ efi_status = uefi_call_wrapper(file->GetInfo, 4, file, &FILE_INFO,
+ size, fi);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to get file info\n");
+ return efi_status;
+ }
+
+ *size = fi->FileSize;
+
+ *buffer = AllocatePool(*size);
+ if (!*buffer) {
+ Print(L"Failed to allocate buffer of size %d\n", *size);
+ return EFI_OUT_OF_RESOURCES;
+ }
+ efi_status = uefi_call_wrapper(file->Read, 3, file, size, *buffer);
+
+ return efi_status;
+}
+
+
+EFI_STATUS
+simple_file_write_all(EFI_FILE *file, UINTN size, void *buffer)
+{
+ EFI_STATUS efi_status;
+
+ efi_status = uefi_call_wrapper(file->Write, 3, file, &size, buffer);
+
+ return efi_status;
+}
+
+void
+simple_file_close(EFI_FILE *file)
+{
+ uefi_call_wrapper(file->Close, 1, file);
+}
+
+EFI_STATUS
+simple_volume_selector(CHAR16 **title, CHAR16 **selected, EFI_HANDLE *h)
+{
+ UINTN count, i;
+ EFI_HANDLE *vol_handles = NULL;
+ EFI_STATUS status;
+ CHAR16 **entries;
+ int val;
+
+ uefi_call_wrapper(BS->LocateHandleBuffer, 5, ByProtocol,
+ &SIMPLE_FS_PROTOCOL, NULL, &count, &vol_handles);
+
+ if (!count || !vol_handles)
+ return EFI_NOT_FOUND;
+
+ entries = AllocatePool(sizeof(CHAR16 *) * (count+1));
+ if (!entries)
+ return EFI_OUT_OF_RESOURCES;
+
+ for (i = 0; i < count; i++) {
+ char buf[4096];
+ UINTN size = sizeof(buf);
+ EFI_FILE_SYSTEM_INFO *fi = (void *)buf;
+ EFI_FILE *root;
+ CHAR16 *name;
+ EFI_FILE_IO_INTERFACE *drive;
+
+ status = uefi_call_wrapper(BS->HandleProtocol, 3,
+ vol_handles[i],
+ &SIMPLE_FS_PROTOCOL, &drive);
+ if (status != EFI_SUCCESS || !drive)
+ continue;
+
+ status = uefi_call_wrapper(drive->OpenVolume, 2, drive, &root);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ status = uefi_call_wrapper(root->GetInfo, 4, root, &FS_INFO,
+ &size, fi);
+ if (status != EFI_SUCCESS)
+ continue;
+
+ name = fi->VolumeLabel;
+
+ if (!name || StrLen(name) == 0 || StrCmp(name, L" ") == 0)
+ name = DevicePathToStr(DevicePathFromHandle(vol_handles[i]));
+
+ entries[i] = AllocatePool((StrLen(name) + 2) * sizeof(CHAR16));
+ if (!entries[i])
+ break;
+ StrCpy(entries[i], name);
+ }
+ entries[i] = NULL;
+
+ val = console_select(title, entries, 0);
+
+ if (val >= 0) {
+ *selected = AllocatePool((StrLen(entries[val]) + 1) * sizeof(CHAR16));
+ if (*selected) {
+ StrCpy(*selected , entries[val]);
+ }
+ *h = vol_handles[val];
+ } else {
+ *selected = NULL;
+ *h = 0;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (entries[i])
+ FreePool(entries[i]);
+ }
+ FreePool(entries);
+ FreePool(vol_handles);
+
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+simple_dir_filter(EFI_HANDLE image, CHAR16 *name, CHAR16 *filter,
+ CHAR16 ***result, int *count, EFI_FILE_INFO **entries)
+{
+ EFI_STATUS status;
+ int tot, offs = StrLen(filter), i, c, filtercount = 1;
+ EFI_FILE_INFO *next;
+ void *ptr;
+ CHAR16 *newfilter = AllocatePool((StrLen(filter) + 1) * sizeof(CHAR16)),
+ **filterarr;
+
+ if (!newfilter)
+ return EFI_OUT_OF_RESOURCES;
+
+ /* just in case efi ever stops writeable strings */
+ StrCpy(newfilter, filter);
+
+ for (i = 0; i < offs; i++) {
+ if (filter[i] == '|')
+ filtercount++;
+ }
+ filterarr = AllocatePool(filtercount * sizeof(void *));
+ if (!filterarr)
+ return EFI_OUT_OF_RESOURCES;
+ c = 0;
+ filterarr[c++] = newfilter;
+ for (i = 0; i < offs; i++) {
+ if (filter[i] == '|') {
+ newfilter[i] = '\0';
+ filterarr[c++] = &newfilter[i+1];
+ }
+ }
+
+ *count = 0;
+
+ status = simple_dir_read_all(image, name, entries, &tot);
+
+ if (status != EFI_SUCCESS)
+ goto out;
+ ptr = next = *entries;
+
+ for (i = 0; i < tot; i++) {
+ int len = StrLen(next->FileName);
+
+ for (c = 0; c < filtercount; c++) {
+ offs = StrLen(filterarr[c]);
+
+ if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0
+ || (next->Attribute & EFI_FILE_DIRECTORY)) {
+ (*count)++;
+ break;
+ }
+ }
+ ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16);
+ next = ptr;
+ }
+ if (*count)
+ *result = AllocatePool(((*count) + 1) * sizeof(void *));
+ else
+ *result = AllocatePool(2 * sizeof(void *));
+
+ *count = 0;
+ ptr = next = *entries;
+
+ for (i = 0; i < tot; i++) {
+ int len = StrLen(next->FileName);
+
+ if (StrCmp(next->FileName, L".") == 0)
+ /* ignore . directory */
+ goto next;
+
+ if (next->Attribute & EFI_FILE_DIRECTORY) {
+ (*result)[(*count)] = next->FileName;
+ (*result)[(*count)][len] = '/';
+ (*result)[(*count)++][len + 1] = '\0';
+ goto next;
+ }
+
+ for (c = 0; c < filtercount; c++) {
+ offs = StrLen(filterarr[c]);
+
+ if (StrCmp(&next->FileName[len - offs], filterarr[c]) == 0) {
+ (*result)[(*count)++] = next->FileName;
+ } else {
+ continue;
+ }
+ break;
+ }
+
+ next:
+ if (StrCmp(next->FileName, L"../") == 0) {
+ /* place .. directory first */
+ CHAR16 *tmp = (*result)[(*count) - 1];
+
+ (*result)[(*count) - 1] = (*result)[0];
+ (*result)[0] = tmp;
+ }
+
+ ptr += OFFSET_OF(EFI_FILE_INFO, FileName) + (len + 1)*sizeof(CHAR16);
+ next = ptr;
+ }
+ if (*count == 0) {
+ /* no entries at all ... can happen because top level dir has no . or .. */
+ (*result)[(*count)++] = L"./";
+ }
+ (*result)[*count] = NULL;
+ status = EFI_SUCCESS;
+
+ out:
+ if (status != EFI_SUCCESS) {
+ if (*entries)
+ FreePool(*entries);
+ *entries = NULL;
+ if (*result)
+ FreePool(*result);
+ *result = NULL;
+ }
+ return status;
+}
+
+void
+simple_file_selector(EFI_HANDLE *im, CHAR16 **title, CHAR16 *name,
+ CHAR16 *filter, CHAR16 **result)
+{
+ EFI_STATUS status;
+ CHAR16 **entries;
+ EFI_FILE_INFO *dmp;
+ int count, select, len;
+ CHAR16 *newname, *selected;
+
+ *result = NULL;
+ if (!name)
+ name = L"\\";
+ if (!filter)
+ filter = L"";
+ if (!*im) {
+ EFI_HANDLE h;
+ CHAR16 *volname;
+
+ simple_volume_selector(title, &volname, &h);
+ if (!volname)
+ return;
+ FreePool(volname);
+ *im = h;
+ }
+
+ newname = AllocatePool((StrLen(name) + 1)*sizeof(CHAR16));
+ if (!newname)
+ return;
+
+ StrCpy(newname, name);
+ name = newname;
+
+ redo:
+ status = simple_dir_filter(*im, name, filter, &entries, &count, &dmp);
+
+ if (status != EFI_SUCCESS)
+ goto out_free_name;
+
+ select = console_select(title, entries, 0);
+ if (select < 0)
+ /* ESC key */
+ goto out_free;
+ selected = entries[select];
+ FreePool(entries);
+ entries = NULL;
+ /* note that memory used by selected is valid until dmp is freed */
+ len = StrLen(selected);
+ if (selected[len - 1] == '/') {
+ CHAR16 *newname;
+
+ /* stay where we are */
+ if (StrCmp(selected, L"./") == 0) {
+ FreePool(dmp);
+ goto redo;
+ } else if (StrCmp(selected, L"../") == 0) {
+ int i;
+
+ i = StrLen(name) - 1;
+
+
+ for (i = StrLen(name); i > 0; --i) {
+ if (name[i] == '\\')
+ break;
+ }
+ if (i == 0)
+ i = 1;
+
+ if (StrCmp(name, L"\\") != 0
+ && StrCmp(&name[i], L"..") != 0) {
+ name[i] = '\0';
+ FreePool(dmp);
+ goto redo;
+ }
+ }
+ newname = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16));
+ if (!newname)
+ goto out_free;
+ StrCpy(newname, name);
+
+ if (name[StrLen(name) - 1] != '\\')
+ StrCat(newname, L"\\");
+ StrCat(newname, selected);
+ /* remove trailing / */
+ newname[StrLen(newname) - 1] = '\0';
+
+ FreePool(dmp);
+ FreePool(name);
+ name = newname;
+
+ goto redo;
+ }
+ *result = AllocatePool((StrLen(name) + len + 2)*sizeof(CHAR16));
+ if (*result) {
+ StrCpy(*result, name);
+ if (name[StrLen(name) - 1] != '\\')
+ StrCat(*result, L"\\");
+ StrCat(*result, selected);
+ }
+
+ out_free:
+ FreePool(dmp);
+ if (entries)
+ FreePool(entries);
+ out_free_name:
+ FreePool(name);
+}
diff --git a/lib/variables.c b/lib/variables.c
new file mode 100644
index 00000000..9db64809
--- /dev/null
+++ b/lib/variables.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2012 <James.Bottomley@HansenPartnership.com>
+ *
+ * see COPYING file
+ *
+ * Portions of this file are a direct cut and paste from Tianocore
+ * (http://tianocore.sf.net)
+ *
+ * SecurityPkg/VariableAuthenticated/SecureBootConfigDxe/SecureBootConfigImpl.c
+ *
+ * Copyright (c) 2011 - 2012, Intel Corporation. All rights reserved.<BR>
+ * This program and the accompanying materials
+ * are licensed and made available under the terms and conditions of the BSD License
+ * which accompanies this distribution. The full text of the license may be found
+ * at
+ * http://opensource.org/licenses/bsd-license.php
+ *
+ * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+ *
+ */
+#include <efi.h>
+#include <efilib.h>
+
+#include <efiauthenticated.h>
+
+#include <variables.h>
+#include <guid.h>
+#include <console.h>
+#include <sha256.h>
+#include <errors.h>
+
+EFI_STATUS
+variable_create_esl(void *cert, int cert_len, EFI_GUID *type, EFI_GUID *owner,
+ void **out, int *outlen)
+{
+ *outlen = cert_len + sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_GUID);
+
+ *out = AllocateZeroPool(*outlen);
+ if (!*out)
+ return EFI_OUT_OF_RESOURCES;
+
+ EFI_SIGNATURE_LIST *sl = *out;
+
+ sl->SignatureHeaderSize = 0;
+ sl->SignatureType = *type;
+ sl->SignatureSize = cert_len + sizeof(EFI_GUID);
+ sl->SignatureListSize = *outlen;
+
+ EFI_SIGNATURE_DATA *sd = *out + sizeof(EFI_SIGNATURE_LIST);
+
+ if (owner)
+ sd->SignatureOwner = *owner;
+
+ CopyMem(sd->SignatureData, cert, cert_len);
+
+ return EFI_SUCCESS;
+}
+
+
+EFI_STATUS
+CreateTimeBasedPayload (
+ IN OUT UINTN *DataSize,
+ IN OUT UINT8 **Data
+ )
+{
+ EFI_STATUS Status;
+ UINT8 *NewData;
+ UINT8 *Payload;
+ UINTN PayloadSize;
+ EFI_VARIABLE_AUTHENTICATION_2 *DescriptorData;
+ UINTN DescriptorSize;
+ EFI_TIME Time;
+ EFI_GUID efi_cert_type = EFI_CERT_TYPE_PKCS7_GUID;
+
+ if (Data == NULL || DataSize == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ //
+ // In Setup mode or Custom mode, the variable does not need to be signed but the
+ // parameters to the SetVariable() call still need to be prepared as authenticated
+ // variable. So we create EFI_VARIABLE_AUTHENTICATED_2 descriptor without certificate
+ // data in it.
+ //
+ Payload = *Data;
+ PayloadSize = *DataSize;
+
+ DescriptorSize = OFFSET_OF(EFI_VARIABLE_AUTHENTICATION_2, AuthInfo) + OFFSET_OF(WIN_CERTIFICATE_UEFI_GUID, CertData);
+ NewData = (UINT8*) AllocateZeroPool (DescriptorSize + PayloadSize);
+ if (NewData == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if ((Payload != NULL) && (PayloadSize != 0)) {
+ CopyMem (NewData + DescriptorSize, Payload, PayloadSize);
+ }
+
+ DescriptorData = (EFI_VARIABLE_AUTHENTICATION_2 *) (NewData);
+
+ ZeroMem (&Time, sizeof (EFI_TIME));
+ Status = uefi_call_wrapper(RT->GetTime,2, &Time, NULL);
+ if (EFI_ERROR (Status)) {
+ FreePool(NewData);
+ return Status;
+ }
+ Time.Pad1 = 0;
+ Time.Nanosecond = 0;
+ Time.TimeZone = 0;
+ Time.Daylight = 0;
+ Time.Pad2 = 0;
+ CopyMem (&DescriptorData->TimeStamp, &Time, sizeof (EFI_TIME));
+
+ DescriptorData->AuthInfo.Hdr.dwLength = OFFSET_OF (WIN_CERTIFICATE_UEFI_GUID, CertData);
+ DescriptorData->AuthInfo.Hdr.wRevision = 0x0200;
+ DescriptorData->AuthInfo.Hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID;
+ DescriptorData->AuthInfo.CertType = efi_cert_type;
+
+ /* we're expecting an EFI signature list, so don't free the input since
+ * it might not be in a pool */
+#if 0
+ if (Payload != NULL) {
+ FreePool(Payload);
+ }
+#endif
+
+ *DataSize = DescriptorSize + PayloadSize;
+ *Data = NewData;
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+SetSecureVariable(CHAR16 *var, UINT8 *Data, UINTN len, EFI_GUID owner,
+ UINT32 options, int createtimebased)
+{
+ EFI_SIGNATURE_LIST *Cert;
+ UINTN DataSize;
+ EFI_STATUS efi_status;
+
+ /* Microsoft request: Bugs in some UEFI platforms mean that PK or any
+ * other secure variable can be updated or deleted programmatically,
+ * so prevent */
+ if (!variable_is_setupmode())
+ return EFI_SECURITY_VIOLATION;
+
+ if (createtimebased) {
+ int ds;
+ efi_status = variable_create_esl(Data, len, &X509_GUID, NULL,
+ (void **)&Cert, &ds);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to create %s certificate %d\n", var, efi_status);
+ return efi_status;
+ }
+
+ DataSize = ds;
+ } else {
+ /* we expect an efi signature list rather than creating it */
+ Cert = (EFI_SIGNATURE_LIST *)Data;
+ DataSize = len;
+ }
+ efi_status = CreateTimeBasedPayload(&DataSize, (UINT8 **)&Cert);
+ if (efi_status != EFI_SUCCESS) {
+ Print(L"Failed to create time based payload %d\n", efi_status);
+ return efi_status;
+ }
+
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_RUNTIME_ACCESS
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS
+ | options,
+ DataSize, Cert);
+
+ return efi_status;
+}
+
+UINT64
+GetOSIndications(void)
+{
+ UINT64 indications;
+ UINTN DataSize = sizeof(indications);
+ EFI_STATUS efi_status;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, L"OsIndicationsSupported", &GV_GUID, NULL, &DataSize, &indications);
+ if (efi_status != EFI_SUCCESS)
+ return 0;
+
+ return indications;
+}
+
+EFI_STATUS
+SETOSIndicationsAndReboot(UINT64 indications)
+{
+ UINTN DataSize = sizeof(indications);
+ EFI_STATUS efi_status;
+
+ efi_status = uefi_call_wrapper(RT->SetVariable, 5, L"OsIndications",
+ &GV_GUID,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_RUNTIME_ACCESS
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS,
+ DataSize, &indications);
+
+ if (efi_status != EFI_SUCCESS)
+ return efi_status;
+
+ uefi_call_wrapper(RT->ResetSystem, 4, EfiResetWarm, EFI_SUCCESS, 0, NULL);
+ /* does not return */
+
+ return EFI_SUCCESS;
+}
+
+EFI_STATUS
+get_variable_attr(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner,
+ UINT32 *attributes)
+{
+ EFI_STATUS efi_status;
+
+ *len = 0;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner,
+ NULL, len, NULL);
+ if (efi_status != EFI_BUFFER_TOO_SMALL)
+ return efi_status;
+
+ *data = AllocateZeroPool(*len);
+ if (!data)
+ return EFI_OUT_OF_RESOURCES;
+
+ efi_status = uefi_call_wrapper(RT->GetVariable, 5, var, &owner,
+ attributes, len, *data);
+
+ if (efi_status != EFI_SUCCESS) {
+ FreePool(*data);
+ *data = NULL;
+ }
+ return efi_status;
+}
+
+EFI_STATUS
+get_variable(CHAR16 *var, UINT8 **data, UINTN *len, EFI_GUID owner)
+{
+ return get_variable_attr(var, data, len, owner, NULL);
+}
+
+EFI_STATUS
+find_in_esl(UINT8 *Data, UINTN DataSize, UINT8 *key, UINTN keylen)
+{
+ EFI_SIGNATURE_LIST *CertList;
+
+ certlist_for_each_certentry(CertList, Data, DataSize, DataSize) {
+ if (CertList->SignatureSize != keylen + sizeof(EFI_GUID))
+ continue;
+ EFI_SIGNATURE_DATA *Cert;
+
+ certentry_for_each_cert(Cert, CertList)
+ if (CompareMem (Cert->SignatureData, key, keylen) == 0)
+ return EFI_SUCCESS;
+ }
+ return EFI_NOT_FOUND;
+}
+
+EFI_STATUS
+find_in_variable_esl(CHAR16* var, EFI_GUID owner, UINT8 *key, UINTN keylen)
+{
+ UINTN DataSize;
+ UINT8 *Data;
+ EFI_STATUS status;
+
+ status = get_variable(var, &Data, &DataSize, owner);
+ if (status != EFI_SUCCESS)
+ return status;
+
+ status = find_in_esl(Data, DataSize, key, keylen);
+
+ FreePool(Data);
+
+ return status;
+}
+
+int
+variable_is_setupmode(void)
+{
+ /* set to 1 because we return true if SetupMode doesn't exist */
+ UINT8 SetupMode = 1;
+ UINTN DataSize = sizeof(SetupMode);
+
+ uefi_call_wrapper(RT->GetVariable, 5, L"SetupMode", &GV_GUID, NULL,
+ &DataSize, &SetupMode);
+
+ return SetupMode;
+}
+
+int
+variable_is_secureboot(void)
+{
+ /* return false if variable doesn't exist */
+ UINT8 SecureBoot = 0;
+ UINTN DataSize;
+
+ DataSize = sizeof(SecureBoot);
+ uefi_call_wrapper(RT->GetVariable, 5, L"SecureBoot", &GV_GUID, NULL,
+ &DataSize, &SecureBoot);
+
+ return SecureBoot;
+}
+
+EFI_STATUS
+variable_enroll_hash(CHAR16 *var, EFI_GUID owner,
+ UINT8 hash[SHA256_DIGEST_SIZE])
+{
+ EFI_STATUS status;
+
+ if (find_in_variable_esl(var, owner, hash, SHA256_DIGEST_SIZE)
+ == EFI_SUCCESS)
+ /* hash already present */
+ return EFI_ALREADY_STARTED;
+
+ UINT8 sig[sizeof(EFI_SIGNATURE_LIST) + sizeof(EFI_SIGNATURE_DATA) - 1 + SHA256_DIGEST_SIZE];
+ EFI_SIGNATURE_LIST *l = (void *)sig;
+ EFI_SIGNATURE_DATA *d = (void *)sig + sizeof(EFI_SIGNATURE_LIST);
+ SetMem(sig, 0, sizeof(sig));
+ l->SignatureType = EFI_CERT_SHA256_GUID;
+ l->SignatureListSize = sizeof(sig);
+ l->SignatureSize = 16 +32; /* UEFI defined */
+ CopyMem(&d->SignatureData, hash, SHA256_DIGEST_SIZE);
+ d->SignatureOwner = MOK_OWNER;
+
+ if (CompareGuid(&owner, &SIG_DB) == 0)
+ status = SetSecureVariable(var, sig, sizeof(sig), owner,
+ EFI_VARIABLE_APPEND_WRITE, 0);
+ else
+ status = uefi_call_wrapper(RT->SetVariable, 5, var, &owner,
+ EFI_VARIABLE_NON_VOLATILE
+ | EFI_VARIABLE_BOOTSERVICE_ACCESS
+ | EFI_VARIABLE_APPEND_WRITE,
+ sizeof(sig), sig);
+ return status;
+}