aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/efivars.c
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-04-30 16:11:30 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-04-30 18:30:18 -0400
commitfec6c20b570bcf541e581fc97f2e0cbdb9725b98 (patch)
tree2c84d6748a2aeff0f98ef5abbcc0144273bb8978 /drivers/firmware/efivars.c
parent41b3254c93acc56adc3c4477fef7c9512d47659e (diff)
efi: Validate UEFI boot variables
A common flaw in UEFI systems is a refusal to POST triggered by a malformed boot variable. Once in this state, machines may only be restored by reflashing their firmware with an external hardware device. While this is obviously a firmware bug, the serious nature of the outcome suggests that operating systems should filter their variable writes in order to prevent a malicious user from rendering the machine unusable. Signed-off-by: Matthew Garrett <mjg@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/firmware/efivars.c')
-rw-r--r--drivers/firmware/efivars.c182
1 files changed, 182 insertions, 0 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index d25599f2a3f8..891e4674d29b 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -191,6 +191,176 @@ utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
191 } 191 }
192} 192}
193 193
194static bool
195validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len)
196{
197 struct efi_generic_dev_path *node;
198 int offset = 0;
199
200 node = (struct efi_generic_dev_path *)buffer;
201
202 while (offset < len) {
203 offset += node->length;
204
205 if (offset > len)
206 return false;
207
208 if ((node->type == EFI_DEV_END_PATH ||
209 node->type == EFI_DEV_END_PATH2) &&
210 node->sub_type == EFI_DEV_END_ENTIRE)
211 return true;
212
213 node = (struct efi_generic_dev_path *)(buffer + offset);
214 }
215
216 /*
217 * If we're here then either node->length pointed past the end
218 * of the buffer or we reached the end of the buffer without
219 * finding a device path end node.
220 */
221 return false;
222}
223
224static bool
225validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len)
226{
227 /* An array of 16-bit integers */
228 if ((len % 2) != 0)
229 return false;
230
231 return true;
232}
233
234static bool
235validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len)
236{
237 u16 filepathlength;
238 int i, desclength = 0;
239
240 /* Either "Boot" or "Driver" followed by four digits of hex */
241 for (i = match; i < match+4; i++) {
242 if (hex_to_bin(var->VariableName[i] & 0xff) < 0)
243 return true;
244 }
245
246 /* A valid entry must be at least 6 bytes */
247 if (len < 6)
248 return false;
249
250 filepathlength = buffer[4] | buffer[5] << 8;
251
252 /*
253 * There's no stored length for the description, so it has to be
254 * found by hand
255 */
256 desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2;
257
258 /* Each boot entry must have a descriptor */
259 if (!desclength)
260 return false;
261
262 /*
263 * If the sum of the length of the description, the claimed filepath
264 * length and the original header are greater than the length of the
265 * variable, it's malformed
266 */
267 if ((desclength + filepathlength + 6) > len)
268 return false;
269
270 /*
271 * And, finally, check the filepath
272 */
273 return validate_device_path(var, match, buffer + desclength + 6,
274 filepathlength);
275}
276
277static bool
278validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len)
279{
280 /* A single 16-bit integer */
281 if (len != 2)
282 return false;
283
284 return true;
285}
286
287static bool
288validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len)
289{
290 int i;
291
292 for (i = 0; i < len; i++) {
293 if (buffer[i] > 127)
294 return false;
295
296 if (buffer[i] == 0)
297 return true;
298 }
299
300 return false;
301}
302
303struct variable_validate {
304 char *name;
305 bool (*validate)(struct efi_variable *var, int match, u8 *data,
306 int len);
307};
308
309static const struct variable_validate variable_validate[] = {
310 { "BootNext", validate_uint16 },
311 { "BootOrder", validate_boot_order },
312 { "DriverOrder", validate_boot_order },
313 { "Boot*", validate_load_option },
314 { "Driver*", validate_load_option },
315 { "ConIn", validate_device_path },
316 { "ConInDev", validate_device_path },
317 { "ConOut", validate_device_path },
318 { "ConOutDev", validate_device_path },
319 { "ErrOut", validate_device_path },
320 { "ErrOutDev", validate_device_path },
321 { "Timeout", validate_uint16 },
322 { "Lang", validate_ascii_string },
323 { "PlatformLang", validate_ascii_string },
324 { "", NULL },
325};
326
327static bool
328validate_var(struct efi_variable *var, u8 *data, int len)
329{
330 int i;
331 u16 *unicode_name = var->VariableName;
332
333 for (i = 0; variable_validate[i].validate != NULL; i++) {
334 const char *name = variable_validate[i].name;
335 int match;
336
337 for (match = 0; ; match++) {
338 char c = name[match];
339 u16 u = unicode_name[match];
340
341 /* All special variables are plain ascii */
342 if (u > 127)
343 return true;
344
345 /* Wildcard in the matching name means we've matched */
346 if (c == '*')
347 return variable_validate[i].validate(var,
348 match, data, len);
349
350 /* Case sensitive match */
351 if (c != u)
352 break;
353
354 /* Reached the end of the string while matching */
355 if (!c)
356 return variable_validate[i].validate(var,
357 match, data, len);
358 }
359 }
360
361 return true;
362}
363
194static efi_status_t 364static efi_status_t
195get_var_data_locked(struct efivars *efivars, struct efi_variable *var) 365get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
196{ 366{
@@ -324,6 +494,12 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
324 return -EINVAL; 494 return -EINVAL;
325 } 495 }
326 496
497 if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
498 validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
499 printk(KERN_ERR "efivars: Malformed variable content\n");
500 return -EINVAL;
501 }
502
327 spin_lock(&efivars->lock); 503 spin_lock(&efivars->lock);
328 status = efivars->ops->set_variable(new_var->VariableName, 504 status = efivars->ops->set_variable(new_var->VariableName,
329 &new_var->VendorGuid, 505 &new_var->VendorGuid,
@@ -626,6 +802,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
626 if (!capable(CAP_SYS_ADMIN)) 802 if (!capable(CAP_SYS_ADMIN))
627 return -EACCES; 803 return -EACCES;
628 804
805 if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
806 validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
807 printk(KERN_ERR "efivars: Malformed variable content\n");
808 return -EINVAL;
809 }
810
629 spin_lock(&efivars->lock); 811 spin_lock(&efivars->lock);
630 812
631 /* 813 /*