efi_loader: type of efi_secure_mode
[oweals/u-boot.git] / lib / efi_loader / efi_variable.c
index adb78470f2d64a1f1924e57d1316c65a1a740701..4d275b23ce74d72f8e6fc83e8ba8dc5b377ab855 100644 (file)
@@ -7,20 +7,43 @@
 
 #include <common.h>
 #include <efi_loader.h>
 
 #include <common.h>
 #include <efi_loader.h>
+#include <env.h>
 #include <env_internal.h>
 #include <hexdump.h>
 #include <malloc.h>
 #include <rtc.h>
 #include <search.h>
 #include <env_internal.h>
 #include <hexdump.h>
 #include <malloc.h>
 #include <rtc.h>
 #include <search.h>
+#include <uuid.h>
+#include <crypto/pkcs7_parser.h>
+#include <linux/bitops.h>
 #include <linux/compat.h>
 #include <u-boot/crc.h>
 #include <linux/compat.h>
 #include <u-boot/crc.h>
-#include "../lib/crypto/pkcs7_parser.h"
 
 
-const efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
+enum efi_secure_mode {
+       EFI_MODE_SETUP,
+       EFI_MODE_USER,
+       EFI_MODE_AUDIT,
+       EFI_MODE_DEPLOYED,
+};
+
 static bool efi_secure_boot;
 static bool efi_secure_boot;
+static enum efi_secure_mode efi_secure_mode;
+static u8 efi_vendor_keys;
 
 #define READ_ONLY BIT(31)
 
 
 #define READ_ONLY BIT(31)
 
+static efi_status_t efi_get_variable_common(u16 *variable_name,
+                                           const efi_guid_t *vendor,
+                                           u32 *attributes,
+                                           efi_uintn_t *data_size, void *data);
+
+static efi_status_t efi_set_variable_common(u16 *variable_name,
+                                           const efi_guid_t *vendor,
+                                           u32 attributes,
+                                           efi_uintn_t data_size,
+                                           const void *data,
+                                           bool ro_check);
+
 /*
  * Mapping between EFI variables and u-boot variables:
  *
 /*
  * Mapping between EFI variables and u-boot variables:
  *
@@ -160,6 +183,160 @@ static const char *parse_attr(const char *str, u32 *attrp, u64 *timep)
        return str;
 }
 
        return str;
 }
 
+/**
+ * efi_set_secure_state - modify secure boot state variables
+ * @sec_boot:          value of SecureBoot
+ * @setup_mode:                value of SetupMode
+ * @audit_mode:                value of AuditMode
+ * @deployed_mode:     value of DeployedMode
+ *
+ * Modify secure boot stat-related variables as indicated.
+ *
+ * Return:             status code
+ */
+static efi_status_t efi_set_secure_state(int sec_boot, int setup_mode,
+                                        int audit_mode, int deployed_mode)
+{
+       u32 attributes;
+       efi_status_t ret;
+
+       attributes = EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                    EFI_VARIABLE_RUNTIME_ACCESS |
+                    READ_ONLY;
+       ret = efi_set_variable_common(L"SecureBoot", &efi_global_variable_guid,
+                                     attributes, sizeof(sec_boot), &sec_boot,
+                                     false);
+       if (ret != EFI_SUCCESS)
+               goto err;
+
+       ret = efi_set_variable_common(L"SetupMode", &efi_global_variable_guid,
+                                     attributes, sizeof(setup_mode),
+                                     &setup_mode, false);
+       if (ret != EFI_SUCCESS)
+               goto err;
+
+       ret = efi_set_variable_common(L"AuditMode", &efi_global_variable_guid,
+                                     attributes, sizeof(audit_mode),
+                                     &audit_mode, false);
+       if (ret != EFI_SUCCESS)
+               goto err;
+
+       ret = efi_set_variable_common(L"DeployedMode",
+                                     &efi_global_variable_guid, attributes,
+                                     sizeof(deployed_mode), &deployed_mode,
+                                     false);
+err:
+       return ret;
+}
+
+/**
+ * efi_transfer_secure_state - handle a secure boot state transition
+ * @mode:      new state
+ *
+ * Depending on @mode, secure boot related variables are updated.
+ * Those variables are *read-only* for users, efi_set_variable_common()
+ * is called here.
+ *
+ * Return:     status code
+ */
+static efi_status_t efi_transfer_secure_state(enum efi_secure_mode mode)
+{
+       efi_status_t ret;
+
+       debug("Switching secure state from %d to %d\n", efi_secure_mode, mode);
+
+       if (mode == EFI_MODE_DEPLOYED) {
+               ret = efi_set_secure_state(1, 0, 0, 1);
+               if (ret != EFI_SUCCESS)
+                       goto err;
+
+               efi_secure_boot = true;
+       } else if (mode == EFI_MODE_AUDIT) {
+               ret = efi_set_variable_common(L"PK", &efi_global_variable_guid,
+                                             EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                                             EFI_VARIABLE_RUNTIME_ACCESS,
+                                             0, NULL, false);
+               if (ret != EFI_SUCCESS)
+                       goto err;
+
+               ret = efi_set_secure_state(0, 1, 1, 0);
+               if (ret != EFI_SUCCESS)
+                       goto err;
+
+               efi_secure_boot = true;
+       } else if (mode == EFI_MODE_USER) {
+               ret = efi_set_secure_state(1, 0, 0, 0);
+               if (ret != EFI_SUCCESS)
+                       goto err;
+
+               efi_secure_boot = true;
+       } else if (mode == EFI_MODE_SETUP) {
+               ret = efi_set_secure_state(0, 1, 0, 0);
+               if (ret != EFI_SUCCESS)
+                       goto err;
+       } else {
+               return EFI_INVALID_PARAMETER;
+       }
+
+       efi_secure_mode = mode;
+
+       return EFI_SUCCESS;
+
+err:
+       /* TODO: What action should be taken here? */
+       printf("ERROR: Secure state transition failed\n");
+       return ret;
+}
+
+/**
+ * efi_init_secure_state - initialize secure boot state
+ *
+ * Return:     status code
+ */
+static efi_status_t efi_init_secure_state(void)
+{
+       enum efi_secure_mode mode;
+       efi_uintn_t size;
+       efi_status_t ret;
+
+       /*
+        * TODO:
+        * Since there is currently no "platform-specific" installation
+        * method of Platform Key, we can't say if VendorKeys is 0 or 1
+        * precisely.
+        */
+
+       size = 0;
+       ret = efi_get_variable_common(L"PK", &efi_global_variable_guid,
+                                     NULL, &size, NULL);
+       if (ret == EFI_BUFFER_TOO_SMALL) {
+               if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT))
+                       mode = EFI_MODE_USER;
+               else
+                       mode = EFI_MODE_SETUP;
+
+               efi_vendor_keys = 0;
+       } else if (ret == EFI_NOT_FOUND) {
+               mode = EFI_MODE_SETUP;
+               efi_vendor_keys = 1;
+       } else {
+               goto err;
+       }
+
+       ret = efi_transfer_secure_state(mode);
+       if (ret == EFI_SUCCESS)
+               ret = efi_set_variable_common(L"VendorKeys",
+                                             &efi_global_variable_guid,
+                                             EFI_VARIABLE_BOOTSERVICE_ACCESS |
+                                             EFI_VARIABLE_RUNTIME_ACCESS |
+                                             READ_ONLY,
+                                             sizeof(efi_vendor_keys),
+                                             &efi_vendor_keys, false);
+
+err:
+       return ret;
+}
+
 /**
  * efi_secure_boot_enabled - return if secure boot is enabled or not
  *
 /**
  * efi_secure_boot_enabled - return if secure boot is enabled or not
  *
@@ -268,7 +445,7 @@ out:
  * attributes and signed time will also be returned in @env_attr and @time,
  * respectively.
  *
  * attributes and signed time will also be returned in @env_attr and @time,
  * respectively.
  *
- * Return:     EFI_SUCCESS on success, status code (negative) on error
+ * Return:     status code
  */
 static efi_status_t efi_variable_authenticate(u16 *variable,
                                              const efi_guid_t *vendor,
  */
 static efi_status_t efi_variable_authenticate(u16 *variable,
                                              const efi_guid_t *vendor,
@@ -349,9 +526,8 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
        var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
                                               auth->auth_info.hdr.dwLength
                                                   - sizeof(auth->auth_info));
        var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
                                               auth->auth_info.hdr.dwLength
                                                   - sizeof(auth->auth_info));
-       if (IS_ERR(var_sig)) {
+       if (!var_sig) {
                debug("Parsing variable's signature failed\n");
                debug("Parsing variable's signature failed\n");
-               var_sig = NULL;
                goto err;
        }
 
                goto err;
        }
 
@@ -417,12 +593,10 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
 }
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
 }
 #endif /* CONFIG_EFI_SECURE_BOOT */
 
-static
-efi_status_t EFIAPI efi_get_variable_common(u16 *variable_name,
+static efi_status_t efi_get_variable_common(u16 *variable_name,
                                            const efi_guid_t *vendor,
                                            u32 *attributes,
                                            const efi_guid_t *vendor,
                                            u32 *attributes,
-                                           efi_uintn_t *data_size, void *data,
-                                           bool is_non_volatile)
+                                           efi_uintn_t *data_size, void *data)
 {
        char *native_name;
        efi_status_t ret;
 {
        char *native_name;
        efi_status_t ret;
@@ -505,27 +679,6 @@ out:
        return ret;
 }
 
        return ret;
 }
 
-static
-efi_status_t EFIAPI efi_get_volatile_variable(u16 *variable_name,
-                                             const efi_guid_t *vendor,
-                                             u32 *attributes,
-                                             efi_uintn_t *data_size,
-                                             void *data)
-{
-       return efi_get_variable_common(variable_name, vendor, attributes,
-                                      data_size, data, false);
-}
-
-efi_status_t EFIAPI efi_get_nonvolatile_variable(u16 *variable_name,
-                                                const efi_guid_t *vendor,
-                                                u32 *attributes,
-                                                efi_uintn_t *data_size,
-                                                void *data)
-{
-       return efi_get_variable_common(variable_name, vendor, attributes,
-                                      data_size, data, true);
-}
-
 /**
  * efi_efi_get_variable() - retrieve value of a UEFI variable
  *
 /**
  * efi_efi_get_variable() - retrieve value of a UEFI variable
  *
@@ -550,12 +703,8 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name,
        EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
                  data_size, data);
 
        EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes,
                  data_size, data);
 
-       ret = efi_get_volatile_variable(variable_name, vendor, attributes,
-                                       data_size, data);
-       if (ret == EFI_NOT_FOUND)
-               ret = efi_get_nonvolatile_variable(variable_name, vendor,
-                                                  attributes, data_size, data);
-
+       ret = efi_get_variable_common(variable_name, vendor, attributes,
+                                     data_size, data);
        return EFI_EXIT(ret);
 }
 
        return EFI_EXIT(ret);
 }
 
@@ -620,7 +769,10 @@ static efi_status_t parse_uboot_variable(char *variable,
        /* guid */
        c = *(name - 1);
        *(name - 1) = '\0'; /* guid need be null-terminated here */
        /* guid */
        c = *(name - 1);
        *(name - 1) = '\0'; /* guid need be null-terminated here */
-       uuid_str_to_bin(guid, (unsigned char *)vendor, UUID_STR_FORMAT_GUID);
+       if (uuid_str_to_bin(guid, (unsigned char *)vendor,
+                           UUID_STR_FORMAT_GUID))
+               /* The only error would be EINVAL. */
+               return EFI_INVALID_PARAMETER;
        *(name - 1) = c;
 
        /* attributes */
        *(name - 1) = c;
 
        /* attributes */
@@ -719,14 +871,12 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size,
        return EFI_EXIT(ret);
 }
 
        return EFI_EXIT(ret);
 }
 
-static
-efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
+static efi_status_t efi_set_variable_common(u16 *variable_name,
                                            const efi_guid_t *vendor,
                                            u32 attributes,
                                            efi_uintn_t data_size,
                                            const void *data,
                                            const efi_guid_t *vendor,
                                            u32 attributes,
                                            efi_uintn_t data_size,
                                            const void *data,
-                                           bool ro_check,
-                                           bool is_non_volatile)
+                                           bool ro_check)
 {
        char *native_name = NULL, *old_data = NULL, *val = NULL, *s;
        efi_uintn_t old_size;
 {
        char *native_name = NULL, *old_data = NULL, *val = NULL, *s;
        efi_uintn_t old_size;
@@ -735,8 +885,6 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
        u32 attr;
        efi_status_t ret = EFI_SUCCESS;
 
        u32 attr;
        efi_status_t ret = EFI_SUCCESS;
 
-       debug("%s: set '%s'\n", __func__, native_name);
-
        if (!variable_name || !*variable_name || !vendor ||
            ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
             !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
        if (!variable_name || !*variable_name || !vendor ||
            ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) &&
             !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) {
@@ -751,16 +899,8 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
        /* check if a variable exists */
        old_size = 0;
        attr = 0;
        /* check if a variable exists */
        old_size = 0;
        attr = 0;
-       ret = EFI_CALL(efi_get_variable(variable_name, vendor, &attr,
-                                       &old_size, NULL));
-       if (ret == EFI_BUFFER_TOO_SMALL) {
-               if ((is_non_volatile && !(attr & EFI_VARIABLE_NON_VOLATILE)) ||
-                   (!is_non_volatile && (attr & EFI_VARIABLE_NON_VOLATILE))) {
-                       ret = EFI_INVALID_PARAMETER;
-                       goto err;
-               }
-       }
-
+       ret = efi_get_variable_common(variable_name, vendor, &attr,
+                                     &old_size, NULL);
        append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
        attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
        delete = !append && (!data_size || !attributes);
        append = !!(attributes & EFI_VARIABLE_APPEND_WRITE);
        attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE;
        delete = !append && (!data_size || !attributes);
@@ -847,11 +987,11 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
        if (append) {
                old_data = malloc(old_size);
                if (!old_data) {
        if (append) {
                old_data = malloc(old_size);
                if (!old_data) {
-                       return EFI_OUT_OF_RESOURCES;
+                       ret = EFI_OUT_OF_RESOURCES;
                        goto err;
                }
                        goto err;
                }
-               ret = EFI_CALL(efi_get_variable(variable_name, vendor,
-                                               &attr, &old_size, old_data));
+               ret = efi_get_variable_common(variable_name, vendor,
+                                             &attr, &old_size, old_data);
                if (ret != EFI_SUCCESS)
                        goto err;
        } else {
                if (ret != EFI_SUCCESS)
                        goto err;
        } else {
@@ -910,10 +1050,43 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name,
        EFI_PRINT("setting: %s=%s\n", native_name, val);
 
 out:
        EFI_PRINT("setting: %s=%s\n", native_name, val);
 
 out:
-       if (env_set(native_name, val))
+       if (env_set(native_name, val)) {
                ret = EFI_DEVICE_ERROR;
                ret = EFI_DEVICE_ERROR;
-       else
-               ret = EFI_SUCCESS;
+       } else {
+               bool vendor_keys_modified = false;
+
+               if ((u16_strcmp(variable_name, L"PK") == 0 &&
+                    guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+                       ret = efi_transfer_secure_state(
+                                       (delete ? EFI_MODE_SETUP :
+                                                 EFI_MODE_USER));
+                       if (ret != EFI_SUCCESS)
+                               goto err;
+
+                       if (efi_secure_mode != EFI_MODE_SETUP)
+                               vendor_keys_modified = true;
+               } else if ((u16_strcmp(variable_name, L"KEK") == 0 &&
+                    guidcmp(vendor, &efi_global_variable_guid) == 0)) {
+                       if (efi_secure_mode != EFI_MODE_SETUP)
+                               vendor_keys_modified = true;
+               }
+
+               /* update VendorKeys */
+               if (vendor_keys_modified & efi_vendor_keys) {
+                       efi_vendor_keys = 0;
+                       ret = efi_set_variable_common(
+                                               L"VendorKeys",
+                                               &efi_global_variable_guid,
+                                               EFI_VARIABLE_BOOTSERVICE_ACCESS
+                                                | EFI_VARIABLE_RUNTIME_ACCESS
+                                                | READ_ONLY,
+                                               sizeof(efi_vendor_keys),
+                                               &efi_vendor_keys,
+                                               false);
+               } else {
+                       ret = EFI_SUCCESS;
+               }
+       }
 
 err:
        free(native_name);
 
 err:
        free(native_name);
@@ -923,54 +1096,6 @@ err:
        return ret;
 }
 
        return ret;
 }
 
-static
-efi_status_t EFIAPI efi_set_volatile_variable(u16 *variable_name,
-                                             const efi_guid_t *vendor,
-                                             u32 attributes,
-                                             efi_uintn_t data_size,
-                                             const void *data,
-                                             bool ro_check)
-{
-       return efi_set_variable_common(variable_name, vendor, attributes,
-                                      data_size, data, ro_check, false);
-}
-
-efi_status_t EFIAPI efi_set_nonvolatile_variable(u16 *variable_name,
-                                                const efi_guid_t *vendor,
-                                                u32 attributes,
-                                                efi_uintn_t data_size,
-                                                const void *data,
-                                                bool ro_check)
-{
-       efi_status_t ret;
-
-       ret = efi_set_variable_common(variable_name, vendor, attributes,
-                                     data_size, data, ro_check, true);
-
-       return ret;
-}
-
-static efi_status_t efi_set_variable_internal(u16 *variable_name,
-                                             const efi_guid_t *vendor,
-                                             u32 attributes,
-                                             efi_uintn_t data_size,
-                                             const void *data,
-                                             bool ro_check)
-{
-       efi_status_t ret;
-
-       if (attributes & EFI_VARIABLE_NON_VOLATILE)
-               ret = efi_set_nonvolatile_variable(variable_name, vendor,
-                                                  attributes,
-                                                  data_size, data, ro_check);
-       else
-               ret = efi_set_volatile_variable(variable_name, vendor,
-                                               attributes, data_size, data,
-                                               ro_check);
-
-       return ret;
-}
-
 /**
  * efi_set_variable() - set value of a UEFI variable
  *
 /**
  * efi_set_variable() - set value of a UEFI variable
  *
@@ -996,9 +1121,9 @@ efi_status_t EFIAPI efi_set_variable(u16 *variable_name,
        /* READ_ONLY bit is not part of API */
        attributes &= ~(u32)READ_ONLY;
 
        /* READ_ONLY bit is not part of API */
        attributes &= ~(u32)READ_ONLY;
 
-       return EFI_EXIT(efi_set_variable_internal(variable_name, vendor,
-                                                 attributes, data_size, data,
-                                                 true));
+       return EFI_EXIT(efi_set_variable_common(variable_name, vendor,
+                                               attributes, data_size, data,
+                                               true));
 }
 
 /**
 }
 
 /**
@@ -1098,5 +1223,9 @@ void efi_variables_boot_exit_notify(void)
  */
 efi_status_t efi_init_variables(void)
 {
  */
 efi_status_t efi_init_variables(void)
 {
-       return EFI_SUCCESS;
+       efi_status_t ret;
+
+       ret = efi_init_secure_state();
+
+       return ret;
 }
 }