On Tue, Feb 25, 2020 at 07:46:44AM +0100, Heinrich Schuchardt wrote: > On 1/28/20 9:25 AM, AKASHI Takahiro wrote: > > With this commit, EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS > > is supported for authenticated variables and the system secure state > > will transfer between setup mode and user mode as UEFI specification > > section 32.3 describes. > > > > Internally, authentication data is stored as part of authenticated > > variable's value. It is nothing but a pkcs7 message (but we need some > > wrapper, see efi_variable_parse_signature()) and will be validated by > > efi_variable_authenticate(), hence efi_signature_verify_with_db(). > > > > Associated time value will be encoded in "{...,time=...}" along with > > other UEFI variable's attributes. > > > > Signed-off-by: AKASHI Takahiro <takahiro.aka...@linaro.org> > > --- > > include/efi_loader.h | 3 + > > lib/efi_loader/efi_variable.c | 665 ++++++++++++++++++++++++++++------ > > 2 files changed, 564 insertions(+), 104 deletions(-) > > > > diff --git a/include/efi_loader.h b/include/efi_loader.h > > index 34f7b8eec8cd..f461c6195834 100644 > > --- a/include/efi_loader.h > > +++ b/include/efi_loader.h > > @@ -184,6 +184,7 @@ extern const efi_guid_t > > efi_guid_image_security_database; > > extern const efi_guid_t efi_guid_sha256; > > extern const efi_guid_t efi_guid_cert_x509; > > extern const efi_guid_t efi_guid_cert_x509_sha256; > > +extern const efi_guid_t efi_guid_cert_type_pkcs7; > > > > /* GUID of RNG protocol */ > > extern const efi_guid_t efi_guid_rng_protocol; > > @@ -738,6 +739,8 @@ efi_status_t efi_image_region_add(struct > > efi_image_regions *regs, > > > > void efi_sigstore_free(struct efi_signature_store *sigstore); > > struct efi_signature_store *efi_sigstore_parse_sigdb(u16 *name); > > + > > +bool efi_secure_boot_enabled(void); > > #endif /* CONFIG_EFI_SECURE_BOOT */ > > > > #else /* CONFIG_IS_ENABLED(EFI_LOADER) */ > > diff --git a/lib/efi_loader/efi_variable.c b/lib/efi_loader/efi_variable.c > > index c316bdfec0e4..2ae8222b1a94 100644 > > --- a/lib/efi_loader/efi_variable.c > > +++ b/lib/efi_loader/efi_variable.c > > @@ -10,8 +10,14 @@ > > #include <env_internal.h> > > #include <hexdump.h> > > #include <malloc.h> > > +#include <rtc.h> > > #include <search.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; > > +static bool efi_secure_boot; > > > > #define READ_ONLY BIT(31) > > > > @@ -108,7 +114,7 @@ static const char *prefix(const char *str, const char > > *prefix) > > * @attrp: pointer to UEFI attributes > > * Return: pointer to remainder of U-Boot variable value > > */ > > -static const char *parse_attr(const char *str, u32 *attrp) > > +static const char *parse_attr(const char *str, u32 *attrp, u64 *timep) > > ./lib/efi_loader/efi_variable.c:128: warning: Function parameter or > member 'timep' not described in 'parse_attr' > > All 'make htmldocs' warnings will be treated as errors after upcoming > Travis CI changes.
Okay, I fixed all of them. Thanks, -Takahiro Akashi > Best regards > > Heinrich > > > { > > u32 attr = 0; > > char sep = '{'; > > @@ -131,6 +137,12 @@ static const char *parse_attr(const char *str, u32 > > *attrp) > > attr |= EFI_VARIABLE_BOOTSERVICE_ACCESS; > > } else if ((s = prefix(str, "run"))) { > > attr |= EFI_VARIABLE_RUNTIME_ACCESS; > > + } else if ((s = prefix(str, "time="))) { > > + attr |= > > EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS; > > + hex2bin((u8 *)timep, s, sizeof(*timep)); > > + s += sizeof(*timep) * 2; > > + } else if (*str == '}') { > > + break; > > } else { > > printf("invalid attribute: %s\n", str); > > break; > > @@ -148,48 +160,291 @@ static const char *parse_attr(const char *str, u32 > > *attrp) > > } > > > > /** > > - * efi_get_variable() - retrieve value of a UEFI variable > > + * efi_secure_boot_enabled - return if secure boot is enabled or not > > * > > - * This function implements the GetVariable runtime service. > > + * Return: true if enabled, false if disabled > > + */ > > +bool efi_secure_boot_enabled(void) > > +{ > > + return efi_secure_boot; > > +} > > + > > +#ifdef CONFIG_EFI_SECURE_BOOT > > +static u8 pkcs7_hdr[] = { > > + /* SEQUENCE */ > > + 0x30, 0x82, 0x05, 0xc7, > > + /* OID: pkcs7-signedData */ > > + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x02, > > + /* Context Structured? */ > > + 0xa0, 0x82, 0x05, 0xb8, > > +}; > > + > > +/** > > + * efi_variable_parse_signature - parse a signature in variable > > + * @buf: Pointer to variable's value > > + * @buflen: Length of @buf > > * > > - * See the Unified Extensible Firmware Interface (UEFI) specification for > > - * details. > > + * Parse a signature embedded in variable's value and instantiate > > + * a pkcs7_message structure. Since pkcs7_parse_message() accepts only > > + * pkcs7's signedData, some header needed be prepended for correctly > > + * parsing authentication data, particularly for variable's. > > * > > - * @variable_name: name of the variable > > - * @vendor: vendor GUID > > - * @attributes: attributes of the variable > > - * @data_size: size of the buffer to which the variable value > > is copied > > - * @data: buffer to which the variable value is copied > > - * Return: status code > > + * Return: Pointer to pkcs7_message structure on success, NULL on error > > */ > > -efi_status_t EFIAPI efi_get_variable(u16 *variable_name, > > - const efi_guid_t *vendor, u32 *attributes, > > - efi_uintn_t *data_size, void *data) > > +static struct pkcs7_message *efi_variable_parse_signature(const void *buf, > > + size_t buflen) > > +{ > > + u8 *ebuf; > > + size_t ebuflen, len; > > + struct pkcs7_message *msg; > > + > > + /* > > + * This is the best assumption to check if the binary is > > + * already in a form of pkcs7's signedData. > > + */ > > + if (buflen > sizeof(pkcs7_hdr) && > > + !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { > > + msg = pkcs7_parse_message(buf, buflen); > > + goto out; > > + } > > + > > + /* > > + * Otherwise, we should add a dummy prefix sequence for pkcs7 > > + * message parser to be able to process. > > + * NOTE: EDK2 also uses similar hack in WrapPkcs7Data() > > + * in CryptoPkg/Library/BaseCryptLib/Pk/CryptPkcs7VerifyCommon.c > > + * TODO: > > + * The header should be composed in a more refined manner. > > + */ > > + debug("Makeshift prefix added to authentication data\n"); > > + ebuflen = sizeof(pkcs7_hdr) + buflen; > > + if (ebuflen <= 0x7f) { > > + debug("Data is too short\n"); > > + return NULL; > > + } > > + > > + ebuf = malloc(ebuflen); > > + if (!ebuf) { > > + debug("Out of memory\n"); > > + return NULL; > > + } > > + > > + memcpy(ebuf, pkcs7_hdr, sizeof(pkcs7_hdr)); > > + memcpy(ebuf + sizeof(pkcs7_hdr), buf, buflen); > > + len = ebuflen - 4; > > + ebuf[2] = (len >> 8) & 0xff; > > + ebuf[3] = len & 0xff; > > + len = ebuflen - 0x13; > > + ebuf[0x11] = (len >> 8) & 0xff; > > + ebuf[0x12] = len & 0xff; > > + > > + msg = pkcs7_parse_message(ebuf, ebuflen); > > + > > + free(ebuf); > > + > > +out: > > + if (IS_ERR(msg)) > > + return NULL; > > + > > + return msg; > > +} > > + > > +/** > > + * efi_variable_authenticate - authenticate a variable > > + * @variable: Variable name in u16 > > + * @vendor: Guid of variable > > + * @data_size: Size of @data > > + * @data: Pointer to variable's value > > + * @given_attr: Attributes to be given at SetVariable() > > + * @env_attr: Attributes that an existing variable holds > > + * @time: signed time that an existing variable holds > > + * > > + * Called by efi_set_variable() to verify that the input is correct. > > + * Will replace the given data pointer with another that points to > > + * the actual data to store in the internal memory. > > + * On success, @data and @data_size will be replaced with variable's > > + * actual data, excluding authentication data, and its size, and variable's > > + * attributes and signed time will also be returned in @env_attr and @time, > > + * respectively. > > + * > > + * Return: EFI_SUCCESS on success, status code (negative) on error > > + */ > > +static efi_status_t efi_variable_authenticate(u16 *variable, > > + const efi_guid_t *vendor, > > + efi_uintn_t *data_size, > > + const void **data, u32 given_attr, > > + u32 *env_attr, u64 *time) > > +{ > > + const struct efi_variable_authentication_2 *auth; > > + struct efi_signature_store *truststore, *truststore2; > > + struct pkcs7_message *var_sig; > > + struct efi_image_regions *regs; > > + struct efi_time timestamp; > > + struct rtc_time tm; > > + u64 new_time; > > + efi_status_t ret; > > + > > + var_sig = NULL; > > + truststore = NULL; > > + truststore2 = NULL; > > + regs = NULL; > > + ret = EFI_SECURITY_VIOLATION; > > + > > + if (*data_size < sizeof(struct efi_variable_authentication_2)) > > + goto err; > > + > > + /* authentication data */ > > + auth = *data; > > + if (*data_size < (sizeof(auth->time_stamp) > > + + auth->auth_info.hdr.dwLength)) > > + goto err; > > + > > + if (guidcmp(&auth->auth_info.cert_type, &efi_guid_cert_type_pkcs7)) > > + goto err; > > + > > + *data += sizeof(auth->time_stamp) + auth->auth_info.hdr.dwLength; > > + *data_size -= (sizeof(auth->time_stamp) > > + + auth->auth_info.hdr.dwLength); > > + > > + memcpy(×tamp, &auth->time_stamp, sizeof(timestamp)); > > + memset(&tm, 0, sizeof(tm)); > > + tm.tm_year = timestamp.year; > > + tm.tm_mon = timestamp.month; > > + tm.tm_mday = timestamp.day; > > + tm.tm_hour = timestamp.hour; > > + tm.tm_min = timestamp.minute; > > + tm.tm_sec = timestamp.second; > > + new_time = rtc_mktime(&tm); > > + > > + if (!efi_secure_boot_enabled()) { > > + /* finished checking */ > > + *time = new_time; > > + return EFI_SUCCESS; > > + } > > + > > + if (new_time <= *time) > > + goto err; > > + > > + /* data to be digested */ > > + regs = calloc(sizeof(*regs) + sizeof(struct image_region) * 5, 1); > > + if (!regs) > > + goto err; > > + regs->max = 5; > > + efi_image_region_add(regs, (uint8_t *)variable, > > + (uint8_t *)variable > > + + u16_strlen(variable) * sizeof(u16), 1); > > + efi_image_region_add(regs, (uint8_t *)vendor, > > + (uint8_t *)vendor + sizeof(*vendor), 1); > > + efi_image_region_add(regs, (uint8_t *)&given_attr, > > + (uint8_t *)&given_attr + sizeof(given_attr), 1); > > + efi_image_region_add(regs, (uint8_t *)×tamp, > > + (uint8_t *)×tamp + sizeof(timestamp), 1); > > + efi_image_region_add(regs, (uint8_t *)*data, > > + (uint8_t *)*data + *data_size, 1); > > + > > + /* variable's signature list */ > > + if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info)) > > + goto err; > > + 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)) { > > + debug("Parsing variable's signature failed\n"); > > + var_sig = NULL; > > + goto err; > > + } > > + > > + /* signature database used for authentication */ > > + if (u16_strcmp(variable, L"PK") == 0 || > > + u16_strcmp(variable, L"KEK") == 0) { > > + /* with PK */ > > + truststore = efi_sigstore_parse_sigdb(L"PK"); > > + if (!truststore) > > + goto err; > > + } else if (u16_strcmp(variable, L"db") == 0 || > > + u16_strcmp(variable, L"dbx") == 0) { > > + /* with PK and KEK */ > > + truststore = efi_sigstore_parse_sigdb(L"KEK"); > > + truststore2 = efi_sigstore_parse_sigdb(L"PK"); > > + > > + if (!truststore) { > > + if (!truststore2) > > + goto err; > > + > > + truststore = truststore2; > > + truststore2 = NULL; > > + } > > + } else { > > + /* TODO: support private authenticated variables */ > > + goto err; > > + } > > + > > + /* verify signature */ > > + if (efi_signature_verify_with_sigdb(regs, var_sig, truststore, NULL)) { > > + debug("Verified\n"); > > + } else { > > + if (truststore2 && > > + efi_signature_verify_with_sigdb(regs, var_sig, > > + truststore2, NULL)) { > > + debug("Verified\n"); > > + } else { > > + debug("Verifying variable's signature failed\n"); > > + goto err; > > + } > > + } > > + > > + /* finished checking */ > > + *time = rtc_mktime(&tm); > > + ret = EFI_SUCCESS; > > + > > +err: > > + efi_sigstore_free(truststore); > > + efi_sigstore_free(truststore2); > > + pkcs7_free_message(var_sig); > > + free(regs); > > + > > + return ret; > > +} > > +#else > > +static efi_status_t efi_variable_authenticate(u16 *variable, > > + const efi_guid_t *vendor, > > + efi_uintn_t *data_size, > > + const void **data, u32 given_attr, > > + u32 *env_attr, u64 *time) > > +{ > > + return EFI_SUCCESS; > > +} > > +#endif /* CONFIG_EFI_SECURE_BOOT */ > > + > > +static > > +efi_status_t EFIAPI 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) > > { > > char *native_name; > > efi_status_t ret; > > unsigned long in_size; > > - const char *val, *s; > > + const char *val = NULL, *s; > > + u64 time = 0; > > u32 attr; > > > > - EFI_ENTRY("\"%ls\" %pUl %p %p %p", variable_name, vendor, attributes, > > - data_size, data); > > - > > if (!variable_name || !vendor || !data_size) > > return EFI_EXIT(EFI_INVALID_PARAMETER); > > > > ret = efi_to_native(&native_name, variable_name, vendor); > > if (ret) > > - return EFI_EXIT(ret); > > + return ret; > > > > EFI_PRINT("get '%s'\n", native_name); > > > > val = env_get(native_name); > > free(native_name); > > if (!val) > > - return EFI_EXIT(EFI_NOT_FOUND); > > + return EFI_NOT_FOUND; > > > > - val = parse_attr(val, &attr); > > + val = parse_attr(val, &attr, &time); > > > > in_size = *data_size; > > > > @@ -198,7 +453,7 @@ efi_status_t EFIAPI efi_get_variable(u16 *variable_name, > > > > /* number of hexadecimal digits must be even */ > > if (len & 1) > > - return EFI_EXIT(EFI_DEVICE_ERROR); > > + return EFI_DEVICE_ERROR; > > > > /* two characters per byte: */ > > len /= 2; > > @@ -209,11 +464,13 @@ efi_status_t EFIAPI efi_get_variable(u16 > > *variable_name, > > goto out; > > } > > > > - if (!data) > > - return EFI_EXIT(EFI_INVALID_PARAMETER); > > + if (!data) { > > + debug("Variable with no data shouldn't exist.\n"); > > + return EFI_INVALID_PARAMETER; > > + } > > > > if (hex2bin(data, s, len)) > > - return EFI_EXIT(EFI_DEVICE_ERROR); > > + return EFI_DEVICE_ERROR; > > > > EFI_PRINT("got value: \"%s\"\n", s); > > } else if ((s = prefix(val, "(utf8)"))) { > > @@ -226,8 +483,10 @@ efi_status_t EFIAPI efi_get_variable(u16 > > *variable_name, > > goto out; > > } > > > > - if (!data) > > - return EFI_EXIT(EFI_INVALID_PARAMETER); > > + if (!data) { > > + debug("Variable with no data shouldn't exist.\n"); > > + return EFI_INVALID_PARAMETER; > > + } > > > > memcpy(data, s, len); > > ((char *)data)[len] = '\0'; > > @@ -235,13 +494,67 @@ efi_status_t EFIAPI efi_get_variable(u16 > > *variable_name, > > EFI_PRINT("got value: \"%s\"\n", (char *)data); > > } else { > > EFI_PRINT("invalid value: '%s'\n", val); > > - return EFI_EXIT(EFI_DEVICE_ERROR); > > + return EFI_DEVICE_ERROR; > > } > > > > out: > > if (attributes) > > *attributes = attr & EFI_VARIABLE_MASK; > > > > + 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 > > + * > > + * This function implements the GetVariable runtime service. > > + * > > + * See the Unified Extensible Firmware Interface (UEFI) specification for > > + * details. > > + * > > + * @variable_name: name of the variable > > + * @vendor: vendor GUID > > + * @attributes: attributes of the variable > > + * @data_size: size of the buffer to which the variable value > > is copied > > + * @data: buffer to which the variable value is copied > > + * Return: status code > > + */ > > +efi_status_t EFIAPI efi_get_variable(u16 *variable_name, > > + const efi_guid_t *vendor, u32 *attributes, > > + efi_uintn_t *data_size, void *data) > > +{ > > + efi_status_t ret; > > + > > + 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); > > + > > return EFI_EXIT(ret); > > } > > > > @@ -274,6 +587,7 @@ static efi_status_t parse_uboot_variable(char *variable, > > { > > char *guid, *name, *end, c; > > unsigned long name_len; > > + u64 time; > > u16 *p; > > > > guid = strchr(variable, '_'); > > @@ -308,7 +622,7 @@ static efi_status_t parse_uboot_variable(char *variable, > > *(name - 1) = c; > > > > /* attributes */ > > - parse_attr(end, attributes); > > + parse_attr(end, attributes, &time); > > > > return EFI_SUCCESS; > > } > > @@ -390,7 +704,7 @@ efi_status_t EFIAPI > > efi_get_next_variable_name(efi_uintn_t *variable_name_size, > > list_len = hexport_r(&env_htab, '\n', > > H_MATCH_REGEX | H_MATCH_KEY, > > &efi_variables_list, 0, 1, regexlist); > > - /* 1 indicates that no match was found */ > > + > > if (list_len <= 1) > > return EFI_EXIT(EFI_NOT_FOUND); > > > > @@ -403,143 +717,286 @@ efi_status_t EFIAPI > > efi_get_next_variable_name(efi_uintn_t *variable_name_size, > > return EFI_EXIT(ret); > > } > > > > -/** > > - * efi_set_variable() - set value of a UEFI variable > > - * > > - * This function implements the SetVariable runtime service. > > - * > > - * See the Unified Extensible Firmware Interface (UEFI) specification for > > - * details. > > - * > > - * @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 > > - */ > > -efi_status_t EFIAPI efi_set_variable(u16 *variable_name, > > - const efi_guid_t *vendor, u32 attributes, > > - efi_uintn_t data_size, const void *data) > > +static > > +efi_status_t EFIAPI 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) > > { > > - char *native_name = NULL, *val = NULL, *s; > > - const char *old_val; > > - size_t old_size; > > - efi_status_t ret = EFI_SUCCESS; > > + char *native_name = NULL, *old_data = NULL, *val = NULL, *s; > > + efi_uintn_t old_size; > > + bool append, delete; > > + u64 time = 0; > > u32 attr; > > + efi_status_t ret = EFI_SUCCESS; > > > > - EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, > > - data_size, data); > > + debug("%s: set '%s'\n", __func__, native_name); > > > > if (!variable_name || !*variable_name || !vendor || > > ((attributes & EFI_VARIABLE_RUNTIME_ACCESS) && > > !(attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS))) { > > ret = EFI_INVALID_PARAMETER; > > - goto out; > > + goto err; > > } > > > > ret = efi_to_native(&native_name, variable_name, vendor); > > if (ret) > > - goto out; > > + goto err; > > + > > + /* 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; > > + } > > + } > > > > - old_val = env_get(native_name); > > - if (old_val) { > > - old_val = parse_attr(old_val, &attr); > > + append = !!(attributes & EFI_VARIABLE_APPEND_WRITE); > > + attributes &= ~(u32)EFI_VARIABLE_APPEND_WRITE; > > + delete = !append && (!data_size || !attributes); > > > > - /* check read-only first */ > > - if (attr & READ_ONLY) { > > + /* check attributes */ > > + if (old_size) { > > + if (ro_check && (attr & READ_ONLY)) { > > ret = EFI_WRITE_PROTECTED; > > - goto out; > > - } > > - > > - if ((data_size == 0 && > > - !(attributes & EFI_VARIABLE_APPEND_WRITE)) || > > - !attributes) { > > - /* delete the variable: */ > > - env_set(native_name, NULL); > > - ret = EFI_SUCCESS; > > - goto out; > > + goto err; > > } > > > > /* attributes won't be changed */ > > - if (attr != (attributes & ~EFI_VARIABLE_APPEND_WRITE)) { > > + if (!delete && > > + ((ro_check && attr != attributes) || > > + (!ro_check && ((attr & ~(u32)READ_ONLY) > > + != (attributes & ~(u32)READ_ONLY))))) { > > ret = EFI_INVALID_PARAMETER; > > - goto out; > > - } > > - > > - if (attributes & EFI_VARIABLE_APPEND_WRITE) { > > - if (!prefix(old_val, "(blob)")) { > > - ret = EFI_DEVICE_ERROR; > > - goto out; > > - } > > - old_size = strlen(old_val); > > - } else { > > - old_size = 0; > > + goto err; > > } > > } else { > > - if (data_size == 0 || !attributes || > > - (attributes & EFI_VARIABLE_APPEND_WRITE)) { > > + if (delete || append) { > > /* > > * Trying to delete or to update a non-existent > > * variable. > > */ > > ret = EFI_NOT_FOUND; > > - goto out; > > + goto err; > > + } > > + } > > + > > + if (((!u16_strcmp(variable_name, L"PK") || > > + !u16_strcmp(variable_name, L"KEK")) && > > + !guidcmp(vendor, &efi_global_variable_guid)) || > > + ((!u16_strcmp(variable_name, L"db") || > > + !u16_strcmp(variable_name, L"dbx")) && > > + !guidcmp(vendor, &efi_guid_image_security_database))) { > > + /* authentication is mandatory */ > > + if (!(attributes & > > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { > > + debug("%ls: AUTHENTICATED_WRITE_ACCESS required\n", > > + variable_name); > > + ret = EFI_INVALID_PARAMETER; > > + goto err; > > } > > + } > > + > > + /* authenticate a variable */ > > + if (IS_ENABLED(CONFIG_EFI_SECURE_BOOT)) { > > + if (attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS) { > > + ret = EFI_INVALID_PARAMETER; > > + goto err; > > + } > > + if (attributes & > > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { > > + ret = efi_variable_authenticate(variable_name, vendor, > > + &data_size, &data, > > + attributes, &attr, > > + &time); > > + if (ret != EFI_SUCCESS) > > + goto err; > > + > > + /* last chance to check for delete */ > > + if (!data_size) > > + delete = true; > > + } > > + } else { > > + if (attributes & > > + (EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS | > > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)) { > > + debug("Secure boot is not configured\n"); > > + ret = EFI_INVALID_PARAMETER; > > + goto err; > > + } > > + } > > + > > + /* delete a variable */ > > + if (delete) { > > + /* !old_size case has been handled before */ > > + val = NULL; > > + ret = EFI_SUCCESS; > > + goto out; > > + } > > > > + if (append) { > > + old_data = malloc(old_size); > > + if (!old_data) { > > + return EFI_OUT_OF_RESOURCES; > > + goto err; > > + } > > + ret = EFI_CALL(efi_get_variable(variable_name, vendor, > > + &attr, &old_size, old_data)); > > + if (ret != EFI_SUCCESS) > > + goto err; > > + } else { > > old_size = 0; > > } > > > > - val = malloc(old_size + 2 * data_size > > - + strlen("{ro,run,boot,nv}(blob)") + 1); > > + val = malloc(2 * old_size + 2 * data_size > > + + strlen("{ro,run,boot,nv,time=0123456701234567}(blob)") > > + + 1); > > if (!val) { > > ret = EFI_OUT_OF_RESOURCES; > > - goto out; > > + goto err; > > } > > > > s = val; > > > > - /* store attributes */ > > - attributes &= (EFI_VARIABLE_NON_VOLATILE | > > + /* > > + * store attributes > > + */ > > + attributes &= (READ_ONLY | > > + EFI_VARIABLE_NON_VOLATILE | > > EFI_VARIABLE_BOOTSERVICE_ACCESS | > > - EFI_VARIABLE_RUNTIME_ACCESS); > > + EFI_VARIABLE_RUNTIME_ACCESS | > > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS); > > s += sprintf(s, "{"); > > while (attributes) { > > - u32 attr = 1 << (ffs(attributes) - 1); > > + attr = 1 << (ffs(attributes) - 1); > > > > - if (attr == EFI_VARIABLE_NON_VOLATILE) > > + if (attr == READ_ONLY) { > > + s += sprintf(s, "ro"); > > + } else if (attr == EFI_VARIABLE_NON_VOLATILE) { > > s += sprintf(s, "nv"); > > - else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) > > + } else if (attr == EFI_VARIABLE_BOOTSERVICE_ACCESS) { > > s += sprintf(s, "boot"); > > - else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) > > + } else if (attr == EFI_VARIABLE_RUNTIME_ACCESS) { > > s += sprintf(s, "run"); > > + } else if (attr == > > + EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) { > > + s += sprintf(s, "time="); > > + s = bin2hex(s, (u8 *)&time, sizeof(time)); > > + } > > > > attributes &= ~attr; > > if (attributes) > > s += sprintf(s, ","); > > } > > s += sprintf(s, "}"); > > - > > - if (old_size) > > - /* APPEND_WRITE */ > > - s += sprintf(s, old_val); > > - else > > - s += sprintf(s, "(blob)"); > > + s += sprintf(s, "(blob)"); > > > > /* store payload: */ > > + if (append) > > + s = bin2hex(s, old_data, old_size); > > s = bin2hex(s, data, data_size); > > *s = '\0'; > > > > EFI_PRINT("setting: %s=%s\n", native_name, val); > > > > +out: > > if (env_set(native_name, val)) > > ret = EFI_DEVICE_ERROR; > > + else > > + ret = EFI_SUCCESS; > > > > -out: > > +err: > > free(native_name); > > + free(old_data); > > free(val); > > > > - return EFI_EXIT(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 > > + * > > + * This function implements the SetVariable runtime service. > > + * > > + * See the Unified Extensible Firmware Interface (UEFI) specification for > > + * details. > > + * > > + * @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 > > + */ > > +efi_status_t EFIAPI efi_set_variable(u16 *variable_name, > > + const efi_guid_t *vendor, u32 attributes, > > + efi_uintn_t data_size, const void *data) > > +{ > > + EFI_ENTRY("\"%ls\" %pUl %x %zu %p", variable_name, vendor, attributes, > > + data_size, data); > > + > > + /* 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)); > > } > > > > /** > > >