X-Git-Url: https://git.librecmc.org/?p=oweals%2Fu-boot.git;a=blobdiff_plain;f=lib%2Fefi_loader%2Fefi_variable.c;h=4d275b23ce74d72f8e6fc83e8ba8dc5b377ab855;hp=adb78470f2d64a1f1924e57d1316c65a1a740701;hb=915f15ac5746739da0aa2ee2840c2d00dc65aaaa;hpb=767f6eeb01d331a7570a1a5e89010b09e5499bfb diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index adb78470f2..4d275b23ce 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -7,20 +7,43 @@ #include #include +#include #include #include #include #include #include +#include +#include +#include #include #include -#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 enum efi_secure_mode efi_secure_mode; +static u8 efi_vendor_keys; #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: * @@ -160,6 +183,160 @@ static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) 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 * @@ -268,7 +445,7 @@ out: * 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, @@ -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)); - if (IS_ERR(var_sig)) { + if (!var_sig) { debug("Parsing variable's signature failed\n"); - var_sig = NULL; goto err; } @@ -417,12 +593,10 @@ static efi_status_t efi_variable_authenticate(u16 *variable, } #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, - 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; @@ -505,27 +679,6 @@ out: 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 * @@ -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); - 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); } @@ -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 */ - 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 */ @@ -719,14 +871,12 @@ efi_status_t EFIAPI efi_get_next_variable_name(efi_uintn_t *variable_name_size, 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, - 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; @@ -735,8 +885,6 @@ efi_status_t EFIAPI efi_set_variable_common(u16 *variable_name, 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))) { @@ -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; - 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); @@ -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) { - return EFI_OUT_OF_RESOURCES; + ret = EFI_OUT_OF_RESOURCES; 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 { @@ -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: - if (env_set(native_name, val)) + if (env_set(native_name, val)) { 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); @@ -923,54 +1096,6 @@ err: 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 * @@ -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; - 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) { - return EFI_SUCCESS; + efi_status_t ret; + + ret = efi_init_secure_state(); + + return ret; }