aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthew Garrett <mjg@redhat.com>2012-04-30 16:11:30 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-05-07 11:56:38 -0400
commit173c412ef8f37d781eb90f5cc8eeab118b987e68 (patch)
tree61a091ed208a07757dfde7673855720db9756083
parentca14f0481bc8653c39e7b2fca81bc5131ac9afa8 (diff)
efi: Validate UEFI boot variables
commit fec6c20b570bcf541e581fc97f2e0cbdb9725b98 upstream. 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> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 d5106c0f46c..a15c0d45f0a 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -166,6 +166,176 @@ utf16_strsize(efi_char16_t *data, unsigned long maxlength)
166 return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); 166 return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
167} 167}
168 168
169static bool
170validate_device_path(struct efi_variable *var, int match, u8 *buffer, int len)
171{
172 struct efi_generic_dev_path *node;
173 int offset = 0;
174
175 node = (struct efi_generic_dev_path *)buffer;
176
177 while (offset < len) {
178 offset += node->length;
179
180 if (offset > len)
181 return false;
182
183 if ((node->type == EFI_DEV_END_PATH ||
184 node->type == EFI_DEV_END_PATH2) &&
185 node->sub_type == EFI_DEV_END_ENTIRE)
186 return true;
187
188 node = (struct efi_generic_dev_path *)(buffer + offset);
189 }
190
191 /*
192 * If we're here then either node->length pointed past the end
193 * of the buffer or we reached the end of the buffer without
194 * finding a device path end node.
195 */
196 return false;
197}
198
199static bool
200validate_boot_order(struct efi_variable *var, int match, u8 *buffer, int len)
201{
202 /* An array of 16-bit integers */
203 if ((len % 2) != 0)
204 return false;
205
206 return true;
207}
208
209static bool
210validate_load_option(struct efi_variable *var, int match, u8 *buffer, int len)
211{
212 u16 filepathlength;
213 int i, desclength = 0;
214
215 /* Either "Boot" or "Driver" followed by four digits of hex */
216 for (i = match; i < match+4; i++) {
217 if (hex_to_bin(var->VariableName[i] & 0xff) < 0)
218 return true;
219 }
220
221 /* A valid entry must be at least 6 bytes */
222 if (len < 6)
223 return false;
224
225 filepathlength = buffer[4] | buffer[5] << 8;
226
227 /*
228 * There's no stored length for the description, so it has to be
229 * found by hand
230 */
231 desclength = utf16_strsize((efi_char16_t *)(buffer + 6), len) + 2;
232
233 /* Each boot entry must have a descriptor */
234 if (!desclength)
235 return false;
236
237 /*
238 * If the sum of the length of the description, the claimed filepath
239 * length and the original header are greater than the length of the
240 * variable, it's malformed
241 */
242 if ((desclength + filepathlength + 6) > len)
243 return false;
244
245 /*
246 * And, finally, check the filepath
247 */
248 return validate_device_path(var, match, buffer + desclength + 6,
249 filepathlength);
250}
251
252static bool
253validate_uint16(struct efi_variable *var, int match, u8 *buffer, int len)
254{
255 /* A single 16-bit integer */
256 if (len != 2)
257 return false;
258
259 return true;
260}
261
262static bool
263validate_ascii_string(struct efi_variable *var, int match, u8 *buffer, int len)
264{
265 int i;
266
267 for (i = 0; i < len; i++) {
268 if (buffer[i] > 127)
269 return false;
270
271 if (buffer[i] == 0)
272 return true;
273 }
274
275 return false;
276}
277
278struct variable_validate {
279 char *name;
280 bool (*validate)(struct efi_variable *var, int match, u8 *data,
281 int len);
282};
283
284static const struct variable_validate variable_validate[] = {
285 { "BootNext", validate_uint16 },
286 { "BootOrder", validate_boot_order },
287 { "DriverOrder", validate_boot_order },
288 { "Boot*", validate_load_option },
289 { "Driver*", validate_load_option },
290 { "ConIn", validate_device_path },
291 { "ConInDev", validate_device_path },
292 { "ConOut", validate_device_path },
293 { "ConOutDev", validate_device_path },
294 { "ErrOut", validate_device_path },
295 { "ErrOutDev", validate_device_path },
296 { "Timeout", validate_uint16 },
297 { "Lang", validate_ascii_string },
298 { "PlatformLang", validate_ascii_string },
299 { "", NULL },
300};
301
302static bool
303validate_var(struct efi_variable *var, u8 *data, int len)
304{
305 int i;
306 u16 *unicode_name = var->VariableName;
307
308 for (i = 0; variable_validate[i].validate != NULL; i++) {
309 const char *name = variable_validate[i].name;
310 int match;
311
312 for (match = 0; ; match++) {
313 char c = name[match];
314 u16 u = unicode_name[match];
315
316 /* All special variables are plain ascii */
317 if (u > 127)
318 return true;
319
320 /* Wildcard in the matching name means we've matched */
321 if (c == '*')
322 return variable_validate[i].validate(var,
323 match, data, len);
324
325 /* Case sensitive match */
326 if (c != u)
327 break;
328
329 /* Reached the end of the string while matching */
330 if (!c)
331 return variable_validate[i].validate(var,
332 match, data, len);
333 }
334 }
335
336 return true;
337}
338
169static efi_status_t 339static efi_status_t
170get_var_data(struct efivars *efivars, struct efi_variable *var) 340get_var_data(struct efivars *efivars, struct efi_variable *var)
171{ 341{
@@ -289,6 +459,12 @@ efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count)
289 return -EINVAL; 459 return -EINVAL;
290 } 460 }
291 461
462 if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
463 validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
464 printk(KERN_ERR "efivars: Malformed variable content\n");
465 return -EINVAL;
466 }
467
292 spin_lock(&efivars->lock); 468 spin_lock(&efivars->lock);
293 status = efivars->ops->set_variable(new_var->VariableName, 469 status = efivars->ops->set_variable(new_var->VariableName,
294 &new_var->VendorGuid, 470 &new_var->VendorGuid,
@@ -414,6 +590,12 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
414 if (!capable(CAP_SYS_ADMIN)) 590 if (!capable(CAP_SYS_ADMIN))
415 return -EACCES; 591 return -EACCES;
416 592
593 if ((new_var->Attributes & ~EFI_VARIABLE_MASK) != 0 ||
594 validate_var(new_var, new_var->Data, new_var->DataSize) == false) {
595 printk(KERN_ERR "efivars: Malformed variable content\n");
596 return -EINVAL;
597 }
598
417 spin_lock(&efivars->lock); 599 spin_lock(&efivars->lock);
418 600
419 /* 601 /*