aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efi
diff options
context:
space:
mode:
authorPeter Jones <pjones@redhat.com>2016-02-08 14:48:15 -0500
committerMatt Fleming <matt@codeblueprint.co.uk>2016-02-10 11:25:52 -0500
commited8b0de5a33d2a2557dce7f9429dca8cb5bc5879 (patch)
tree1dcd2e49cc432ae312677ec47453ec3abc18f1d2 /drivers/firmware/efi
parent8282f5d9c17fe15a9e658c06e3f343efae1a2a2f (diff)
efi: Make efivarfs entries immutable by default
"rm -rf" is bricking some peoples' laptops because of variables being used to store non-reinitializable firmware driver data that's required to POST the hardware. These are 100% bugs, and they need to be fixed, but in the mean time it shouldn't be easy to *accidentally* brick machines. We have to have delete working, and picking which variables do and don't work for deletion is quite intractable, so instead make everything immutable by default (except for a whitelist), and make tools that aren't quite so broad-spectrum unset the immutable flag. Signed-off-by: Peter Jones <pjones@redhat.com> Tested-by: Lee, Chun-Yi <jlee@suse.com> Acked-by: Matthew Garrett <mjg59@coreos.com> Signed-off-by: Matt Fleming <matt@codeblueprint.co.uk>
Diffstat (limited to 'drivers/firmware/efi')
-rw-r--r--drivers/firmware/efi/vars.c87
1 files changed, 64 insertions, 23 deletions
diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c
index 9a53da21e7b6..50f10bad2604 100644
--- a/drivers/firmware/efi/vars.c
+++ b/drivers/firmware/efi/vars.c
@@ -172,10 +172,12 @@ struct variable_validate {
172}; 172};
173 173
174/* 174/*
175 * This is the list of variables we need to validate. 175 * This is the list of variables we need to validate, as well as the
176 * whitelist for what we think is safe not to default to immutable.
176 * 177 *
177 * If it has a validate() method that's not NULL, it'll go into the 178 * If it has a validate() method that's not NULL, it'll go into the
178 * validation routine. If not, it is assumed valid. 179 * validation routine. If not, it is assumed valid, but still used for
180 * whitelisting.
179 * 181 *
180 * Note that it's sorted by {vendor,name}, but globbed names must come after 182 * Note that it's sorted by {vendor,name}, but globbed names must come after
181 * any other name with the same prefix. 183 * any other name with the same prefix.
@@ -193,11 +195,37 @@ static const struct variable_validate variable_validate[] = {
193 { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path }, 195 { EFI_GLOBAL_VARIABLE_GUID, "ErrOut", validate_device_path },
194 { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path }, 196 { EFI_GLOBAL_VARIABLE_GUID, "ErrOutDev", validate_device_path },
195 { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string }, 197 { EFI_GLOBAL_VARIABLE_GUID, "Lang", validate_ascii_string },
198 { EFI_GLOBAL_VARIABLE_GUID, "OsIndications", NULL },
196 { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string }, 199 { EFI_GLOBAL_VARIABLE_GUID, "PlatformLang", validate_ascii_string },
197 { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 }, 200 { EFI_GLOBAL_VARIABLE_GUID, "Timeout", validate_uint16 },
198 { NULL_GUID, "", NULL }, 201 { NULL_GUID, "", NULL },
199}; 202};
200 203
204static bool
205variable_matches(const char *var_name, size_t len, const char *match_name,
206 int *match)
207{
208 for (*match = 0; ; (*match)++) {
209 char c = match_name[*match];
210 char u = var_name[*match];
211
212 /* Wildcard in the matching name means we've matched */
213 if (c == '*')
214 return true;
215
216 /* Case sensitive match */
217 if (!c && *match == len)
218 return true;
219
220 if (c != u)
221 return false;
222
223 if (!c)
224 return true;
225 }
226 return true;
227}
228
201bool 229bool
202efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data, 230efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
203 unsigned long data_size) 231 unsigned long data_size)
@@ -221,35 +249,48 @@ efivar_validate(efi_guid_t vendor, efi_char16_t *var_name, u8 *data,
221 if (efi_guidcmp(vendor, variable_validate[i].vendor)) 249 if (efi_guidcmp(vendor, variable_validate[i].vendor))
222 continue; 250 continue;
223 251
224 for (match = 0; ; match++) { 252 if (variable_matches(utf8_name, utf8_size+1, name, &match)) {
225 char c = name[match]; 253 if (variable_validate[i].validate == NULL)
226 char u = utf8_name[match];
227
228 /* Wildcard in the matching name means we've matched */
229 if (c == '*') {
230 kfree(utf8_name);
231 return variable_validate[i].validate(var_name,
232 match, data, data_size);
233 }
234
235 /* Case sensitive match */
236 if (c != u)
237 break; 254 break;
238 255 kfree(utf8_name);
239 /* Reached the end of the string while matching */ 256 return variable_validate[i].validate(var_name, match,
240 if (!c) { 257 data, data_size);
241 kfree(utf8_name);
242 return variable_validate[i].validate(var_name,
243 match, data, data_size);
244 }
245 } 258 }
246 } 259 }
247
248 kfree(utf8_name); 260 kfree(utf8_name);
249 return true; 261 return true;
250} 262}
251EXPORT_SYMBOL_GPL(efivar_validate); 263EXPORT_SYMBOL_GPL(efivar_validate);
252 264
265bool
266efivar_variable_is_removable(efi_guid_t vendor, const char *var_name,
267 size_t len)
268{
269 int i;
270 bool found = false;
271 int match = 0;
272
273 /*
274 * Check if our variable is in the validated variables list
275 */
276 for (i = 0; variable_validate[i].name[0] != '\0'; i++) {
277 if (efi_guidcmp(variable_validate[i].vendor, vendor))
278 continue;
279
280 if (variable_matches(var_name, len,
281 variable_validate[i].name, &match)) {
282 found = true;
283 break;
284 }
285 }
286
287 /*
288 * If it's in our list, it is removable.
289 */
290 return found;
291}
292EXPORT_SYMBOL_GPL(efivar_variable_is_removable);
293
253static efi_status_t 294static efi_status_t
254check_var_size(u32 attributes, unsigned long size) 295check_var_size(u32 attributes, unsigned long size)
255{ 296{