summaryrefslogtreecommitdiff
path: root/mok.c
diff options
context:
space:
mode:
authorSteve Langasek <steve.langasek@canonical.com>2019-02-09 21:28:06 -0800
committerSteve Langasek <steve.langasek@canonical.com>2019-02-09 21:32:44 -0800
commitab4c731c1dd379acd3e95971af57401fb0a650a1 (patch)
tree6a26fb8d0746cbbaa6c2d4b242c73442bcc1df06 /mok.c
parent0d63079c7da8e86104ce4bbdae2f6cb8d2ea40c6 (diff)
parent9c12130f9cd2ae11a9336813dd1f1669c0b64ad0 (diff)
downloadefi-boot-shim-debian/15+1533136590.3beb971-1.tar.gz
efi-boot-shim-debian/15+1533136590.3beb971-1.zip
* New upstream release.debian/15+1533136590.3beb971-1
- debian/patches/second-stage-path: dropped; the default loader path now includes an arch suffix. - debian/patches/sbsigntool-no-pesign: dropped; no longer needed. * Drop remaining patches that were not being applied. * Sync packaging from Ubuntu: - debian/copyright: Update upstream source location. - debian/control: add a Build-Depends on libelf-dev. - Enable arm64 build. - debian/patches/fixup_git.patch: don't run git in clean; we're not really in a git tree. - debian/rules, debian/shim.install: use the upstream install target as intended, and move files to the target directory using dh_install. - define RELEASE and COMMIT_ID for the snapshot. - Set ENABLE_HTTPBOOT to enable the HTTP Boot feature. - Update dh_auto_build/dh_auto_clean/dh_auto_install for new upstream options: set MAKELEVEL. - Define an EFI_ARCH variable, and use that for paths to shim. This makes it possible to build a shim for other architectures than amd64. - Set EFIDIR=$distro for dh_auto_install; that will let files be installed in the "right" final directories, and makes boot.csv for us. - Set ENABLE_SHIM_CERT, to keep using ephemeral self-signed certs built at compile-time for MokManager and fallback. - Set ENABLE_SBSIGN, to use sbsign instead of pesign for signing fallback and MokManager.
Diffstat (limited to 'mok.c')
-rw-r--r--mok.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/mok.c b/mok.c
new file mode 100644
index 00000000..38675211
--- /dev/null
+++ b/mok.c
@@ -0,0 +1,334 @@
+/*
+ * mok.c
+ * Copyright 2017 Peter Jones <pjones@redhat.com>
+ *
+ * Distributed under terms of the GPLv3 license.
+ */
+
+#include "shim.h"
+
+/*
+ * Check if a variable exists
+ */
+static BOOLEAN check_var(CHAR16 *varname)
+{
+ EFI_STATUS efi_status;
+ UINTN size = sizeof(UINT32);
+ UINT32 MokVar;
+ UINT32 attributes;
+
+ efi_status = gRT->GetVariable(varname, &SHIM_LOCK_GUID, &attributes,
+ &size, (void *)&MokVar);
+ if (!EFI_ERROR(efi_status) || 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
+ */
+static EFI_STATUS check_mok_request(EFI_HANDLE image_handle)
+{
+ EFI_STATUS efi_status;
+
+ if (check_var(L"MokNew") || check_var(L"MokSB") ||
+ check_var(L"MokPW") || check_var(L"MokAuth") ||
+ check_var(L"MokDel") || check_var(L"MokDB") ||
+ check_var(L"MokXNew") || check_var(L"MokXDel") ||
+ check_var(L"MokXAuth")) {
+ efi_status = start_image(image_handle, MOK_MANAGER);
+
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Failed to start MokManager: %r\n", efi_status);
+ return efi_status;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/*
+ * MoK variables that need to have their storage validated.
+ *
+ * The order here is important, since this is where we measure for the
+ * tpm as well.
+ */
+struct mok_state_variable {
+ CHAR16 *name;
+ char *name8;
+ CHAR16 *rtname;
+ EFI_GUID *guid;
+ UINT8 *data;
+ UINTN data_size;
+ /*
+ * These two are indirect pointers just to make initialization
+ * saner...
+ */
+ UINT8 **addend_source;
+ UINT32 *addend_size;
+ UINT32 yes_attr;
+ UINT32 no_attr;
+ UINT32 flags;
+ UINTN pcr;
+ UINT8 *state;
+};
+
+#define MOK_MIRROR_KEYDB 0x01
+#define MOK_MIRROR_DELETE_FIRST 0x02
+#define MOK_VARIABLE_MEASURE 0x04
+#define MOK_VARIABLE_LOG 0x08
+
+struct mok_state_variable mok_state_variables[] = {
+ {.name = L"MokList",
+ .name8 = "MokList",
+ .rtname = L"MokListRT",
+ .guid = &SHIM_LOCK_GUID,
+ .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
+ .addend_source = &vendor_cert,
+ .addend_size = &vendor_cert_size,
+ .flags = MOK_MIRROR_KEYDB |
+ MOK_VARIABLE_LOG,
+ .pcr = 14,
+ },
+ {.name = L"MokListX",
+ .name8 = "MokListX",
+ .rtname = L"MokListXRT",
+ .guid = &SHIM_LOCK_GUID,
+ .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
+ .flags = MOK_MIRROR_KEYDB |
+ MOK_VARIABLE_LOG,
+ .pcr = 14,
+ },
+ {.name = L"MokSBState",
+ .name8 = "MokSBState",
+ .rtname = L"MokSBStateRT",
+ .guid = &SHIM_LOCK_GUID,
+ .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
+ .flags = MOK_MIRROR_DELETE_FIRST |
+ MOK_VARIABLE_MEASURE |
+ MOK_VARIABLE_LOG,
+ .pcr = 14,
+ .state = &user_insecure_mode,
+ },
+ {.name = L"MokDBState",
+ .name8 = "MokDBState",
+ .rtname = L"MokIgnoreDB",
+ .guid = &SHIM_LOCK_GUID,
+ .yes_attr = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_NON_VOLATILE,
+ .no_attr = EFI_VARIABLE_RUNTIME_ACCESS,
+ .state = &ignore_db,
+ },
+ { NULL, }
+};
+
+static EFI_STATUS mirror_one_mok_variable(struct mok_state_variable *v)
+{
+ EFI_STATUS efi_status = EFI_SUCCESS;
+ void *FullData = NULL;
+ UINTN FullDataSize = 0;
+ uint8_t *p = NULL;
+
+ if ((v->flags & MOK_MIRROR_KEYDB) &&
+ v->addend_source && *v->addend_source &&
+ v->addend_size && *v->addend_size) {
+ EFI_SIGNATURE_LIST *CertList = NULL;
+ EFI_SIGNATURE_DATA *CertData = NULL;
+ FullDataSize = v->data_size
+ + sizeof (*CertList)
+ + sizeof (EFI_GUID)
+ + *v->addend_size;
+ FullData = AllocatePool(FullDataSize);
+ if (!FullData) {
+ perror(L"Failed to allocate space for MokListRT\n");
+ return EFI_OUT_OF_RESOURCES;
+ }
+ p = FullData;
+
+ if (!EFI_ERROR(efi_status) && v->data_size > 0) {
+ CopyMem(p, v->data, v->data_size);
+ p += v->data_size;
+ }
+ CertList = (EFI_SIGNATURE_LIST *)p;
+ p += sizeof (*CertList);
+ CertData = (EFI_SIGNATURE_DATA *)p;
+ p += sizeof (EFI_GUID);
+
+ CertList->SignatureType = EFI_CERT_TYPE_X509_GUID;
+ CertList->SignatureListSize = *v->addend_size
+ + sizeof (*CertList)
+ + sizeof (*CertData)
+ -1;
+ CertList->SignatureHeaderSize = 0;
+ CertList->SignatureSize = *v->addend_size + sizeof (EFI_GUID);
+
+ CertData->SignatureOwner = SHIM_LOCK_GUID;
+ CopyMem(p, *v->addend_source, *v->addend_size);
+
+ if (v->data && v->data_size)
+ FreePool(v->data);
+ v->data = FullData;
+ v->data_size = FullDataSize;
+ } else {
+ FullDataSize = v->data_size;
+ FullData = v->data;
+ }
+
+ if (FullDataSize) {
+ efi_status = gRT->SetVariable(v->rtname, v->guid,
+ EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS,
+ FullDataSize, FullData);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Failed to set %s: %r\n",
+ v->rtname, efi_status);
+ }
+ }
+
+ return efi_status;
+}
+
+/*
+ * Verify our non-volatile MoK state. This checks the variables above
+ * accessable and have valid attributes. If they don't, it removes
+ * them. If any of them can't be removed, our ability to do this is
+ * comprimized, so return EFI_SECURITY_VIOLATION.
+ *
+ * Any variable that isn't deleted and has ->measure == TRUE is then
+ * measured into the tpm.
+ *
+ * Any variable with a ->rtname element is then mirrored to a
+ * runtime-accessable version. The new ones won't be marked NV, so the OS
+ * can't modify them.
+ */
+EFI_STATUS import_mok_state(EFI_HANDLE image_handle)
+{
+ UINTN i;
+ EFI_STATUS ret = EFI_SUCCESS;
+ EFI_STATUS efi_status;
+
+ user_insecure_mode = 0;
+ ignore_db = 0;
+
+ for (i = 0; mok_state_variables[i].name != NULL; i++) {
+ struct mok_state_variable *v = &mok_state_variables[i];
+ UINT32 attrs = 0;
+ BOOLEAN delete = FALSE, present, addend;
+
+ efi_status = get_variable_attr(v->name,
+ &v->data, &v->data_size,
+ *v->guid, &attrs);
+ if (efi_status == EFI_NOT_FOUND)
+ continue;
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Could not verify %s: %r\n", v->name,
+ efi_status);
+ /*
+ * don't clobber EFI_SECURITY_VIOLATION from some
+ * other variable in the list.
+ */
+ if (ret != EFI_SECURITY_VIOLATION)
+ ret = efi_status;
+ continue;
+ }
+
+ if (!(attrs & v->yes_attr)) {
+ perror(L"Variable %s is missing attributes:\n",
+ v->name);
+ perror(L" 0x%08x should have 0x%08x set.\n",
+ attrs, v->yes_attr);
+ delete = TRUE;
+ }
+ if (attrs & v->no_attr) {
+ perror(L"Variable %s has incorrect attribute:\n",
+ v->name);
+ perror(L" 0x%08x should not have 0x%08x set.\n",
+ attrs, v->no_attr);
+ delete = TRUE;
+ }
+ if (delete == TRUE) {
+ perror(L"Deleting bad variable %s\n", v->name);
+ efi_status = LibDeleteVariable(v->name, v->guid);
+ if (EFI_ERROR(efi_status)) {
+ perror(L"Failed to erase %s\n", v->name);
+ ret = EFI_SECURITY_VIOLATION;
+ }
+ FreePool(v->data);
+ v->data = NULL;
+ v->data_size = 0;
+ continue;
+ }
+
+ if (v->data && v->data_size == sizeof(UINT8) && v->state) {
+ *v->state = v->data[0];
+ }
+
+ present = (v->data && v->data_size) ? TRUE : FALSE;
+ addend = (v->addend_source && v->addend_size &&
+ *v->addend_source && *v->addend_size)
+ ? TRUE : FALSE;
+
+ if (v->flags & MOK_VARIABLE_MEASURE && present) {
+ /*
+ * Measure this into PCR 7 in the Microsoft format
+ */
+ efi_status = tpm_measure_variable(v->name, *v->guid,
+ v->data_size,
+ v->data);
+ if (EFI_ERROR(efi_status)) {
+ if (ret != EFI_SECURITY_VIOLATION)
+ ret = efi_status;
+ }
+ }
+
+ if (v->flags & MOK_VARIABLE_LOG && present) {
+ /*
+ * Log this variable into whichever PCR the table
+ * says.
+ */
+ EFI_PHYSICAL_ADDRESS datap =
+ (EFI_PHYSICAL_ADDRESS)(UINTN)v->data,
+ efi_status = tpm_log_event(datap, v->data_size,
+ v->pcr, (CHAR8 *)v->name8);
+ if (EFI_ERROR(efi_status)) {
+ if (ret != EFI_SECURITY_VIOLATION)
+ ret = efi_status;
+ }
+ }
+
+ if (v->rtname && present && addend) {
+ if (v->flags & MOK_MIRROR_DELETE_FIRST)
+ LibDeleteVariable(v->rtname, v->guid);
+
+ efi_status = mirror_one_mok_variable(v);
+ if (EFI_ERROR(efi_status) &&
+ ret != EFI_SECURITY_VIOLATION)
+ ret = efi_status;
+ }
+ }
+
+ /*
+ * Enter MokManager if necessary. Any actual *changes* here will
+ * cause MokManager to demand a machine reboot, so this is safe to
+ * have after the entire loop.
+ */
+ efi_status = check_mok_request(image_handle);
+ if (EFI_ERROR(efi_status)) {
+ if (ret != EFI_SECURITY_VIOLATION)
+ ret = efi_status;
+ return ret;
+ }
+
+
+ return ret;
+}
+
+// vim:fenc=utf-8:tw=75