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); |