diff options
Diffstat (limited to 'lib/efi_loader/efi_variable.c')
-rw-r--r-- | lib/efi_loader/efi_variable.c | 168 |
1 files changed, 142 insertions, 26 deletions
diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c index 40f7a0fb10d..0cbed53d1db 100644 --- a/lib/efi_loader/efi_variable.c +++ b/lib/efi_loader/efi_variable.c @@ -163,6 +163,7 @@ static efi_status_t efi_variable_authenticate(const u16 *variable, break; default: /* TODO: support private authenticated variables */ + ret = EFI_UNSUPPORTED; goto err; } @@ -208,35 +209,44 @@ efi_get_variable_int(const u16 *variable_name, const efi_guid_t *vendor, u32 *attributes, efi_uintn_t *data_size, void *data, u64 *timep) { - return efi_get_variable_mem(variable_name, vendor, attributes, data_size, data, timep); + return efi_get_variable_mem(variable_name, vendor, attributes, data_size, + data, timep, 0); } efi_status_t __efi_runtime efi_get_next_variable_name_int(efi_uintn_t *variable_name_size, u16 *variable_name, efi_guid_t *vendor) { - return efi_get_next_variable_name_mem(variable_name_size, variable_name, vendor); + return efi_get_next_variable_name_mem(variable_name_size, variable_name, + vendor, 0); } -efi_status_t efi_set_variable_int(const u16 *variable_name, - const efi_guid_t *vendor, - u32 attributes, efi_uintn_t data_size, - const void *data, bool ro_check) +/** + * setvariable_allowed() - checks defined by the UEFI spec for setvariable + * + * @variable_name: name of the variable + * @vendor: vendor GUID + * @attributes: attributes of the variable + * @data_size: size of the buffer with the variable value + * @data: buffer with the variable value + * Return: status code + */ +static efi_status_t __efi_runtime +setvariable_allowed(const u16 *variable_name, const efi_guid_t *vendor, + u32 attributes, efi_uintn_t data_size, const void *data) { - struct efi_var_entry *var; - efi_uintn_t ret; - bool append, delete; - u64 time = 0; - enum efi_auth_var_type var_type; - if (!variable_name || !*variable_name || !vendor) return EFI_INVALID_PARAMETER; if (data_size && !data) return EFI_INVALID_PARAMETER; - /* EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated */ - if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) + /* + * EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS is deprecated. + * We don't support EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS. + */ + if (attributes & (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | \ + EFI_VARIABLE_ENHANCED_AUTHENTICATED_ACCESS)) return EFI_UNSUPPORTED; /* Make sure if runtime bit is set, boot service bit is set also */ @@ -256,10 +266,29 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) return EFI_INVALID_PARAMETER; + return EFI_SUCCESS; +} + +efi_status_t efi_set_variable_int(const u16 *variable_name, + const efi_guid_t *vendor, + u32 attributes, efi_uintn_t data_size, + const void *data, bool ro_check) +{ + struct efi_var_entry *var; + efi_uintn_t ret; + bool append, delete; + u64 time = 0; + enum efi_auth_var_type var_type; + + ret = setvariable_allowed(variable_name, vendor, attributes, data_size, + data); + if (ret != EFI_SUCCESS) + return ret; + /* check if a variable exists */ var = efi_var_mem_find(vendor, variable_name, NULL); append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); - attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE; + attributes &= ~EFI_VARIABLE_APPEND_WRITE; delete = !append && (!data_size || !attributes); /* check attributes */ @@ -276,17 +305,27 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, /* attributes won't be changed */ if (!delete && ((ro_check && var->attr != attributes) || - (!ro_check && ((var->attr & ~(u32)EFI_VARIABLE_READ_ONLY) - != (attributes & ~(u32)EFI_VARIABLE_READ_ONLY))))) { + (!ro_check && ((var->attr & ~EFI_VARIABLE_READ_ONLY) + != (attributes & ~EFI_VARIABLE_READ_ONLY))))) { return EFI_INVALID_PARAMETER; } time = var->time; } else { - if (delete || append) - /* - * Trying to delete or to update a non-existent - * variable. - */ + /* + * UEFI specification does not clearly describe the expected + * behavior of append write with data size 0, we follow + * the EDK II reference implementation. + */ + if (append && !data_size) + return EFI_SUCCESS; + + /* + * EFI_VARIABLE_APPEND_WRITE to non-existent variable is accepted + * and new variable is created in EDK II reference implementation. + * We follow it and only check the deletion here. + */ + if (delete) + /* Trying to delete a non-existent variable. */ return EFI_NOT_FOUND; } @@ -329,7 +368,11 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, /* EFI_NOT_FOUND has been handled before */ attributes = var->attr; ret = EFI_SUCCESS; - } else if (append) { + } else if (append && var) { + /* + * data is appended if EFI_VARIABLE_APPEND_WRITE is set and + * variable exists. + */ u16 *old_data = var->name; for (; *old_data; ++old_data) @@ -342,11 +385,12 @@ efi_status_t efi_set_variable_int(const u16 *variable_name, ret = efi_var_mem_ins(variable_name, vendor, attributes, data_size, data, 0, NULL, time); } - efi_var_mem_del(var); if (ret != EFI_SUCCESS) return ret; + efi_var_mem_del(var); + if (var_type == EFI_AUTH_VAR_PK) ret = efi_init_secure_state(); else @@ -384,7 +428,7 @@ efi_status_t efi_query_variable_info_int(u32 attributes, EFI_VARIABLE_RUNTIME_ACCESS) return EFI_INVALID_PARAMETER; - if (attributes & ~(u32)EFI_VARIABLE_MASK) + if (attributes & ~EFI_VARIABLE_MASK) return EFI_INVALID_PARAMETER; *maximum_variable_storage_size = EFI_VAR_BUF_SIZE - @@ -434,7 +478,79 @@ efi_set_variable_runtime(u16 *variable_name, const efi_guid_t *vendor, u32 attributes, efi_uintn_t data_size, const void *data) { - return EFI_UNSUPPORTED; + struct efi_var_entry *var; + efi_uintn_t ret; + bool append, delete; + u64 time = 0; + + if (!IS_ENABLED(CONFIG_EFI_RT_VOLATILE_STORE)) + return EFI_UNSUPPORTED; + + /* + * Authenticated variables are not supported. The EFI spec + * in ยง32.3.6 requires keys to be stored in non-volatile storage which + * is tamper and delete resistant. + * The rest of the checks are in setvariable_allowed() + */ + if (attributes & EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) + return EFI_INVALID_PARAMETER; + + ret = setvariable_allowed(variable_name, vendor, attributes, data_size, + data); + if (ret != EFI_SUCCESS) + return ret; + + /* check if a variable exists */ + var = efi_var_mem_find(vendor, variable_name, NULL); + append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); + attributes &= ~EFI_VARIABLE_APPEND_WRITE; + delete = !append && (!data_size || !attributes); + + /* BS only variables are hidden deny writing them */ + if (!delete && !(attributes & EFI_VARIABLE_RUNTIME_ACCESS)) + return EFI_INVALID_PARAMETER; + + if (var) { + if (var->attr & EFI_VARIABLE_READ_ONLY || + !(var->attr & EFI_VARIABLE_NON_VOLATILE)) + return EFI_WRITE_PROTECTED; + + /* attributes won't be changed */ + if (!delete && (((var->attr & ~EFI_VARIABLE_READ_ONLY) != + (attributes & ~EFI_VARIABLE_READ_ONLY)))) + return EFI_INVALID_PARAMETER; + time = var->time; + } else { + if (!(attributes & EFI_VARIABLE_NON_VOLATILE)) + return EFI_INVALID_PARAMETER; + if (append && !data_size) + return EFI_SUCCESS; + if (delete) + return EFI_NOT_FOUND; + } + + if (delete) { + /* EFI_NOT_FOUND has been handled before */ + attributes = var->attr; + ret = EFI_SUCCESS; + } else if (append && var) { + u16 *old_data = (void *)((uintptr_t)var->name + + sizeof(u16) * (u16_strlen(var->name) + 1)); + + ret = efi_var_mem_ins(variable_name, vendor, attributes, + var->length, old_data, data_size, data, + time); + } else { + ret = efi_var_mem_ins(variable_name, vendor, attributes, + data_size, data, 0, NULL, time); + } + + if (ret != EFI_SUCCESS) + return ret; + /* We are always inserting new variables, get rid of the old copy */ + efi_var_mem_del(var); + + return EFI_SUCCESS; } /** |