[U-Boot] [PATCH 07/16] efi_loader: variable: support variable authentication

AKASHI Takahiro
2019-11-18T07:08:21Z

On Sat, Nov 16, 2019 at 09:02:45PM +0100, Heinrich Schuchardt wrote:
> On 11/13/19 1:52 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.
>
> Please, provide a unit test testing this single feature.

My test_efi_secboot/test_authvar.py provides various test cases.
Pytest is much better solution than "ut" command in terms of
flexible test scenario (and due to complexed setup).

-Takahiro Akashi

> Best regards
>
> Heinrich
>
> >
> >Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
> >---
> > include/efi_loader.h | 3 +
> > lib/efi_loader/efi_variable.c | 664 ++++++++++++++++++++++++++++------
> > 2 files changed, 563 insertions(+), 104 deletions(-)
> >
> >diff --git a/include/efi_loader.h b/include/efi_loader.h
> >index 92b683b7653a..2922ee5089a8 100644
> >--- a/include/efi_loader.h
> >+++ b/include/efi_loader.h
> >@@ -175,6 +175,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;
> >
> > extern unsigned int __efi_runtime_start, __efi_runtime_stop;
> > extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
> >@@ -697,6 +698,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 46f35bc60ba0..7e7db8e4a10d 100644
> >--- a/lib/efi_loader/efi_variable.c
> >+++ b/lib/efi_loader/efi_variable.c
> >@@ -10,7 +10,13 @@
> > #include <env_internal.h>
> > #include <hexdump.h>
> > #include <malloc.h>
> >+#include <rtc.h>
> > #include <search.h>
> >+#include <linux/compat.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)
> >
> >@@ -107,7 +113,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)
> > {
> > u32 attr = 0;
> > char sep = '{';
> >@@ -130,6 +136,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;
> >@@ -147,48 +159,290 @@ 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;
> >+ const 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);
> >+
> >+ timestamp = &auth->time_stamp;
> >+ 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(1, sizeof(*regs));
> >+ if (!regs)
> >+ goto err;
> >+ 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 *)timestamp,
> >+ (uint8_t *)timestamp + 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;
> >
> >@@ -197,7 +451,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;
> >@@ -208,11 +462,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)"))) {
> >@@ -225,8 +481,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';
> >@@ -234,13 +492,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);
> > }
> >
> >@@ -273,6 +585,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, '_');
> >@@ -307,7 +620,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;
> > }
> >@@ -389,7 +702,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);
> >
> >@@ -402,143 +715,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));
> > }
> >
> > /**
> >
>