diff options
| author | Matt Fleming <matt.fleming@intel.com> | 2013-01-31 14:02:03 -0500 |
|---|---|---|
| committer | Matt Fleming <matt.fleming@intel.com> | 2013-02-12 07:41:49 -0500 |
| commit | 47f531e8ba3bc3901a0c493f4252826c41dea1a1 (patch) | |
| tree | 5afbfc2451eb003cda695d4377077862ac932267 /drivers/firmware | |
| parent | 94a193fb7393a50625abd9ca21f8afea275a9f87 (diff) | |
efivarfs: Validate filenames much more aggressively
The only thing that efivarfs does to enforce a valid filename is
ensure that the name isn't too short. We need to strongly sanitise any
filenames, not least because variable creation is delayed until
efivarfs_file_write(), which means we can't rely on the firmware to
inform us of an invalid name, because if the file is never written to
we'll never know it's invalid.
Perform a couple of steps before agreeing to create a new file,
* hex_to_bin() returns a value indicating whether or not it was able
to convert its arguments to a binary representation - we should
check it.
* Ensure that the GUID portion of the filename is the correct length
and format.
* The variable name portion of the filename needs to be at least one
character in size.
Reported-by: Lingzhu Xiang <lxiang@redhat.com>
Cc: Matthew Garrett <mjg59@srcf.ucam.org>
Cc: Jeremy Kerr <jeremy.kerr@canonical.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Matt Fleming <matt.fleming@intel.com>
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/efivars.c | 49 |
1 files changed, 44 insertions, 5 deletions
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 371c44129525..868cea5cd4b8 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c | |||
| @@ -79,6 +79,7 @@ | |||
| 79 | #include <linux/device.h> | 79 | #include <linux/device.h> |
| 80 | #include <linux/slab.h> | 80 | #include <linux/slab.h> |
| 81 | #include <linux/pstore.h> | 81 | #include <linux/pstore.h> |
| 82 | #include <linux/ctype.h> | ||
| 82 | 83 | ||
| 83 | #include <linux/fs.h> | 84 | #include <linux/fs.h> |
| 84 | #include <linux/ramfs.h> | 85 | #include <linux/ramfs.h> |
| @@ -900,6 +901,48 @@ static struct inode *efivarfs_get_inode(struct super_block *sb, | |||
| 900 | return inode; | 901 | return inode; |
| 901 | } | 902 | } |
| 902 | 903 | ||
| 904 | /* | ||
| 905 | * Return true if 'str' is a valid efivarfs filename of the form, | ||
| 906 | * | ||
| 907 | * VariableName-12345678-1234-1234-1234-1234567891bc | ||
| 908 | */ | ||
| 909 | static bool efivarfs_valid_name(const char *str, int len) | ||
| 910 | { | ||
| 911 | static const char dashes[GUID_LEN] = { | ||
| 912 | [8] = 1, [13] = 1, [18] = 1, [23] = 1 | ||
| 913 | }; | ||
| 914 | const char *s = str + len - GUID_LEN; | ||
| 915 | int i; | ||
| 916 | |||
| 917 | /* | ||
| 918 | * We need a GUID, plus at least one letter for the variable name, | ||
| 919 | * plus the '-' separator | ||
| 920 | */ | ||
| 921 | if (len < GUID_LEN + 2) | ||
| 922 | return false; | ||
| 923 | |||
| 924 | /* GUID should be right after the first '-' */ | ||
| 925 | if (s - 1 != strchr(str, '-')) | ||
| 926 | return false; | ||
| 927 | |||
| 928 | /* | ||
| 929 | * Validate that 's' is of the correct format, e.g. | ||
| 930 | * | ||
| 931 | * 12345678-1234-1234-1234-123456789abc | ||
| 932 | */ | ||
| 933 | for (i = 0; i < GUID_LEN; i++) { | ||
| 934 | if (dashes[i]) { | ||
| 935 | if (*s++ != '-') | ||
| 936 | return false; | ||
| 937 | } else { | ||
| 938 | if (!isxdigit(*s++)) | ||
| 939 | return false; | ||
| 940 | } | ||
| 941 | } | ||
| 942 | |||
| 943 | return true; | ||
| 944 | } | ||
| 945 | |||
| 903 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) | 946 | static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid) |
| 904 | { | 947 | { |
| 905 | guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); | 948 | guid->b[0] = hex_to_bin(str[6]) << 4 | hex_to_bin(str[7]); |
| @@ -928,11 +971,7 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, | |||
| 928 | struct efivar_entry *var; | 971 | struct efivar_entry *var; |
| 929 | int namelen, i = 0, err = 0; | 972 | int namelen, i = 0, err = 0; |
| 930 | 973 | ||
| 931 | /* | 974 | if (!efivarfs_valid_name(dentry->d_name.name, dentry->d_name.len)) |
| 932 | * We need a GUID, plus at least one letter for the variable name, | ||
| 933 | * plus the '-' separator | ||
| 934 | */ | ||
| 935 | if (dentry->d_name.len < GUID_LEN + 2) | ||
| 936 | return -EINVAL; | 975 | return -EINVAL; |
| 937 | 976 | ||
| 938 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); | 977 | inode = efivarfs_get_inode(dir->i_sb, dir, mode, 0); |
