|
@@ -165,67 +165,132 @@ validate_ascii_string(efi_char16_t *var_name, int match, u8 *buffer,
|
|
|
}
|
|
|
|
|
|
struct variable_validate {
|
|
|
+ efi_guid_t vendor;
|
|
|
char *name;
|
|
|
bool (*validate)(efi_char16_t *var_name, int match, u8 *data,
|
|
|
unsigned long len);
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * This is the list of variables we need to validate, as well as the
|
|
|
+ * whitelist for what we think is safe not to default to immutable.
|
|
|
+ *
|
|
|
+ * If it has a validate() method that's not NULL, it'll go into the
|
|
|
+ * validation routine. If not, it is assumed valid, but still used for
|
|
|
+ * whitelisting.
|
|
|
+ *
|
|
|
+ * Note that it's sorted by {vendor,name}, but globbed names must come after
|
|
|
+ * any other name with the same prefix.
|
|
|
+ */
|
|
|
static const struct variable_validate variable_validate[] = {
|
|
|
- { "BootNext", validate_uint16 },
|
|
|
- { "BootOrder", validate_boot_order },
|
|
|
- { "DriverOrder", validate_boot_order },
|
|
|
- { "Boot*", validate_load_option },
|
|
|
- { "Driver*", validate_load_option },
|
|
|
- { "ConIn", validate_device_path },
|
|
|
- { "ConInDev", validate_device_path },
|
|
|
- { "ConOut", validate_device_path },
|
|
|
- { "ConOutDev", validate_device_path },
|
|
|
- { "ErrOut", validate_device_path },
|
|
|
- { "ErrOutDev", validate_device_path },
|
|
|
- { "Timeout", validate_uint16 },
|
|
|
- { "Lang", validate_ascii_string },
|
|
|
- { "PlatformLang", validate_ascii_string },
|
|
|
- { "", NULL },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "BootNext", validate_uint16 },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "BootOrder", validate_boot_order },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "Boot*", validate_load_option },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "DriverOrder", validate_boot_order },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "Driver*", validate_load_option },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ConIn", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ConInDev", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ConOut", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ConOutDev", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
|
|
|
+ { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
|
|
|
+ { NULL_GUID, "", NULL },
|
|
|
};
|
|
|
|
|
|
+static bool
|
|
|
+variable_matches(const char *var_name, size_t len, const char *match_name,
|
|
|
+ int *match)
|
|
|
+{
|
|
|
+ for (*match = 0; ; (*match)++) {
|
|
|
+ char c = match_name[*match];
|
|
|
+ char u = var_name[*match];
|
|
|
+
|
|
|
+ /* Wildcard in the matching name means we've matched */
|
|
|
+ if (c == '*')
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* Case sensitive match */
|
|
|
+ if (!c && *match == len)
|
|
|
+ return true;
|
|
|
+
|
|
|
+ if (c != u)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (!c)
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
bool
|
|
|
-efivar_validate(efi_char16_t *var_name, u8 *data, unsigned long len)
|
|
|
+efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
|
|
|
+ unsigned long data_size)
|
|
|
{
|
|
|
int i;
|
|
|
- u16 *unicode_name = var_name;
|
|
|
+ unsigned long utf8_size;
|
|
|
+ u8 *utf8_name;
|
|
|
|
|
|
- for (i = 0; variable_validate[i].validate != NULL; i++) {
|
|
|
- const char *name = variable_validate[i].name;
|
|
|
- int match;
|
|
|
+ utf8_size = ucs2_utf8size(var_name);
|
|
|
+ utf8_name = kmalloc(utf8_size + 1, GFP_KERNEL);
|
|
|
+ if (!utf8_name)
|
|
|
+ return false;
|
|
|
|
|
|
- for (match = 0; ; match++) {
|
|
|
- char c = name[match];
|
|
|
- u16 u = unicode_name[match];
|
|
|
+ ucs2_as_utf8(utf8_name, var_name, utf8_size);
|
|
|
+ utf8_name[utf8_size] = '\0';
|
|
|
|
|
|
- /* All special variables are plain ascii */
|
|
|
- if (u > 127)
|
|
|
- return true;
|
|
|
+ for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
|
|
|
+ const char *name = variable_validate[i].name;
|
|
|
+ int match = 0;
|
|
|
|
|
|
- /* Wildcard in the matching name means we've matched */
|
|
|
- if (c == '*')
|
|
|
- return variable_validate[i].validate(var_name,
|
|
|
- match, data, len);
|
|
|
+ if (efi_guidcmp(vendor, variable_validate[i].vendor))
|
|
|
+ continue;
|
|
|
|
|
|
- /* Case sensitive match */
|
|
|
- if (c != u)
|
|
|
+ if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
|
|
|
+ if (variable_validate[i].validate == NULL)
|
|
|
break;
|
|
|
-
|
|
|
- /* Reached the end of the string while matching */
|
|
|
- if (!c)
|
|
|
- return variable_validate[i].validate(var_name,
|
|
|
- match, data, len);
|
|
|
+ kfree(utf8_name);
|
|
|
+ return variable_validate[i].validate(var_name, match,
|
|
|
+ data, data_size);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+ kfree(utf8_name);
|
|
|
return true;
|
|
|
}
|
|
|
EXPORT_SYMBOL_GPL(efivar_validate);
|
|
|
|
|
|
+bool
|
|
|
+efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
|
|
|
+ size_t len)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ bool found = false;
|
|
|
+ int match = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if our variable is in the validated variables list
|
|
|
+ */
|
|
|
+ for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
|
|
|
+ if (efi_guidcmp(variable_validate[i].vendor, vendor))
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (variable_matches(var_name, len,
|
|
|
+ variable_validate[i].name, &match)) {
|
|
|
+ found = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If it's in our list, it is removable.
|
|
|
+ */
|
|
|
+ return found;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL_GPL(efivar_variable_is_removable);
|
|
|
+
|
|
|
static efi_status_t
|
|
|
check_var_size(u32 attributes, unsigned long size)
|
|
|
{
|
|
@@ -852,7 +917,7 @@ int efivar_entry_set_get_size(struct efivar_entry *entry, u32 attributes,
|
|
|
|
|
|
*set = false;
|
|
|
|
|
|
- if (efivar_validate(name, data, *size) == false)
|
|
|
+ if (efivar_validate(*vendor, name, data, *size) == false)
|
|
|
return -EINVAL;
|
|
|
|
|
|
/*
|