From ed04630b34c117347423a49af35125f5affff6b9 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Wed, 29 Apr 2015 16:30:43 -0700 Subject: firmware: simplify dev_*() print messages for generic helpers Simplify a few of the *generic* shared dev_warn() and dev_dbg() print messages for three reasons: 0) Historically firmware_class code was added to help get device driver firmware binaries but these days request_firmware*() helpers are being repurposed for general *system data* needed by the kernel. 1) This will also help generalize shared code as much as possible later in the future in consideration for a new extensible firmware API which will enable to separate usermode helper code out as much as possible. 2) Kees Cook pointed out the the prints already have the device associated as dev_*() helpers are used, that should help identify the user and case in which the helpers are used. That should provide enough context and simplifies the messages further. v4: generalize debug/warn messages even further as suggested by Kees Cook. Cc: Rusty Russell Cc: Andrew Morton Cc: Greg Kroah-Hartman Cc: David Howells Cc: Kees Cook Cc: Casey Schaufler Cc: Ming Lei Cc: Takashi Iwai Cc: Vojtěch Pavlík Cc: Kyle McMartin Cc: Matthew Garrett Cc: linux-kernel@vger.kernel.org Signed-off-by: Luis R. Rodriguez Signed-off-by: Mimi Zohar Acked-by: Kees Cook Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b9250e564ebf..ce88355eb128 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -353,15 +353,15 @@ static int fw_get_filesystem_firmware(struct device *device, rc = fw_read_file_contents(file, buf); fput(file); if (rc) - dev_warn(device, "firmware, attempted to load %s, but failed with error %d\n", - path, rc); + dev_warn(device, "loading %s failed with error %d\n", + path, rc); else break; } __putname(path); if (!rc) { - dev_dbg(device, "firmware: direct-loading firmware %s\n", + dev_dbg(device, "direct-loading %s\n", buf->fw_id); mutex_lock(&fw_lock); set_bit(FW_STATUS_DONE, &buf->status); @@ -1051,7 +1051,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, } if (fw_get_builtin_firmware(firmware, name)) { - dev_dbg(device, "firmware: using built-in firmware %s\n", name); + dev_dbg(device, "using built-in %s\n", name); return 0; /* assigned */ } -- cgit v1.2.3-55-g7522 From 5275d194e0e56db2bdc43e58f5e54b8e36d6fb03 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Thu, 30 Jul 2015 15:48:57 -0700 Subject: firmware: move completing fw into a helper This will be re-used later through a new extensible interface. Reviewed-by: Josh Boyer Signed-off-by: Luis R. Rodriguez Signed-off-by: Mimi Zohar Acked-by: Kees Cook --- drivers/base/firmware_class.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index ce88355eb128..7bc4ad0f36d5 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -322,6 +322,15 @@ fail: return rc; } +static void fw_finish_direct_load(struct device *device, + struct firmware_buf *buf) +{ + mutex_lock(&fw_lock); + set_bit(FW_STATUS_DONE, &buf->status); + complete_all(&buf->completion); + mutex_unlock(&fw_lock); +} + static int fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) { @@ -363,10 +372,7 @@ static int fw_get_filesystem_firmware(struct device *device, if (!rc) { dev_dbg(device, "direct-loading %s\n", buf->fw_id); - mutex_lock(&fw_lock); - set_bit(FW_STATUS_DONE, &buf->status); - complete_all(&buf->completion); - mutex_unlock(&fw_lock); + fw_finish_direct_load(device, buf); } return rc; -- cgit v1.2.3-55-g7522 From 4b2530d819e179ae3352c38a1ceff929a922d070 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 4 Feb 2016 13:15:02 -0800 Subject: firmware: clean up filesystem load exit path This makes the error and success paths more readable while trying to load firmware from the filesystem. Signed-off-by: Kees Cook Cc: Josh Boyer Cc: David Howells Acked-by: Luis R. Rodriguez Signed-off-by: Mimi Zohar --- drivers/base/firmware_class.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 7bc4ad0f36d5..c743a2f18c33 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -361,19 +361,17 @@ static int fw_get_filesystem_firmware(struct device *device, continue; rc = fw_read_file_contents(file, buf); fput(file); - if (rc) + if (rc) { dev_warn(device, "loading %s failed with error %d\n", path, rc); - else - break; - } - __putname(path); - - if (!rc) { + continue; + } dev_dbg(device, "direct-loading %s\n", buf->fw_id); fw_finish_direct_load(device, buf); + break; } + __putname(path); return rc; } -- cgit v1.2.3-55-g7522 From e40ba6d56b41754b37b995dbc8035b2b3a6afd8a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 19 Nov 2015 12:39:22 -0500 Subject: firmware: replace call to fw_read_file_contents() with kernel version Replace the fw_read_file_contents with kernel_file_read_from_path(). Although none of the upstreamed LSMs define a kernel_fw_from_file hook, IMA is called by the security function to prevent unsigned firmware from being loaded and to measure/appraise signed firmware, based on policy. Instead of reading the firmware twice, once for measuring/appraising the firmware and again for reading the firmware contents into memory, the kernel_post_read_file() security hook calculates the file hash based on the in memory file buffer. The firmware is read once. This patch removes the LSM kernel_fw_from_file() hook and security call. Changelog v4+: - revert dropped buf->size assignment - reported by Sergey Senozhatsky v3: - remove kernel_fw_from_file hook - use kernel_file_read_from_path() - requested by Luis v2: - reordered and squashed firmware patches - fix MAX firmware size (Kees Cook) Signed-off-by: Mimi Zohar Acked-by: Kees Cook Acked-by: Luis R. Rodriguez --- drivers/base/firmware_class.c | 52 ++++++++------------------------------- include/linux/fs.h | 1 + include/linux/ima.h | 6 ----- include/linux/lsm_hooks.h | 11 --------- include/linux/security.h | 7 ------ security/integrity/ima/ima_main.c | 21 ++++++++-------- security/security.c | 13 ---------- 7 files changed, 21 insertions(+), 90 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c743a2f18c33..a414008ea64c 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -291,37 +292,6 @@ static const char * const fw_path[] = { module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); -static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) -{ - int size; - char *buf; - int rc; - - if (!S_ISREG(file_inode(file)->i_mode)) - return -EINVAL; - size = i_size_read(file_inode(file)); - if (size <= 0) - return -EINVAL; - buf = vmalloc(size); - if (!buf) - return -ENOMEM; - rc = kernel_read(file, 0, buf, size); - if (rc != size) { - if (rc > 0) - rc = -EIO; - goto fail; - } - rc = security_kernel_fw_from_file(file, buf, size); - if (rc) - goto fail; - fw_buf->data = buf; - fw_buf->size = size; - return 0; -fail: - vfree(buf); - return rc; -} - static void fw_finish_direct_load(struct device *device, struct firmware_buf *buf) { @@ -334,6 +304,7 @@ static void fw_finish_direct_load(struct device *device, static int fw_get_filesystem_firmware(struct device *device, struct firmware_buf *buf) { + loff_t size; int i, len; int rc = -ENOENT; char *path; @@ -343,8 +314,6 @@ static int fw_get_filesystem_firmware(struct device *device, return -ENOMEM; for (i = 0; i < ARRAY_SIZE(fw_path); i++) { - struct file *file; - /* skip the unset customized path */ if (!fw_path[i][0]) continue; @@ -356,18 +325,16 @@ static int fw_get_filesystem_firmware(struct device *device, break; } - file = filp_open(path, O_RDONLY, 0); - if (IS_ERR(file)) - continue; - rc = fw_read_file_contents(file, buf); - fput(file); + buf->size = 0; + rc = kernel_read_file_from_path(path, &buf->data, &size, + INT_MAX, READING_FIRMWARE); if (rc) { dev_warn(device, "loading %s failed with error %d\n", path, rc); continue; } - dev_dbg(device, "direct-loading %s\n", - buf->fw_id); + dev_dbg(device, "direct-loading %s\n", buf->fw_id); + buf->size = size; fw_finish_direct_load(device, buf); break; } @@ -689,8 +656,9 @@ static ssize_t firmware_loading_store(struct device *dev, dev_err(dev, "%s: map pages failed\n", __func__); else - rc = security_kernel_fw_from_file(NULL, - fw_buf->data, fw_buf->size); + rc = security_kernel_post_read_file(NULL, + fw_buf->data, fw_buf->size, + READING_FIRMWARE); /* * Same logic as fw_load_abort, only the DONE bit diff --git a/include/linux/fs.h b/include/linux/fs.h index 00fa5c45fd63..c8bc4d8c843f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2577,6 +2577,7 @@ static inline void i_readcount_inc(struct inode *inode) extern int do_pipe_flags(int *, int); enum kernel_read_file_id { + READING_FIRMWARE = 1, READING_MAX_ID }; diff --git a/include/linux/ima.h b/include/linux/ima.h index d29a6a23fc19..7aea4863c244 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -19,7 +19,6 @@ extern int ima_file_check(struct file *file, int mask, int opened); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_module_check(struct file *file); -extern int ima_fw_from_file(struct file *file, char *buf, size_t size); extern int ima_post_read_file(struct file *file, void *buf, loff_t size, enum kernel_read_file_id id); @@ -49,11 +48,6 @@ static inline int ima_module_check(struct file *file) return 0; } -static inline int ima_fw_from_file(struct file *file, char *buf, size_t size) -{ - return 0; -} - static inline int ima_post_read_file(struct file *file, void *buf, loff_t size, enum kernel_read_file_id id) { diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 2337f33913c1..7d04a1220223 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -541,15 +541,6 @@ * @inode points to the inode to use as a reference. * The current task must be the one that nominated @inode. * Return 0 if successful. - * @kernel_fw_from_file: - * Load firmware from userspace (not called for built-in firmware). - * @file contains the file structure pointing to the file containing - * the firmware to load. This argument will be NULL if the firmware - * was loaded via the uevent-triggered blob-based interface exposed - * by CONFIG_FW_LOADER_USER_HELPER. - * @buf pointer to buffer containing firmware contents. - * @size length of the firmware contents. - * Return 0 if permission is granted. * @kernel_module_request: * Ability to trigger the kernel to automatically upcall to userspace for * userspace to load a kernel module with the given name. @@ -1462,7 +1453,6 @@ union security_list_options { void (*cred_transfer)(struct cred *new, const struct cred *old); int (*kernel_act_as)(struct cred *new, u32 secid); int (*kernel_create_files_as)(struct cred *new, struct inode *inode); - int (*kernel_fw_from_file)(struct file *file, char *buf, size_t size); int (*kernel_module_request)(char *kmod_name); int (*kernel_module_from_file)(struct file *file); int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size, @@ -1725,7 +1715,6 @@ struct security_hook_heads { struct list_head cred_transfer; struct list_head kernel_act_as; struct list_head kernel_create_files_as; - struct list_head kernel_fw_from_file; struct list_head kernel_post_read_file; struct list_head kernel_module_request; struct list_head kernel_module_from_file; diff --git a/include/linux/security.h b/include/linux/security.h index d920718dc845..cee1349e1155 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -300,7 +300,6 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); void security_transfer_creds(struct cred *new, const struct cred *old); int security_kernel_act_as(struct cred *new, u32 secid); int security_kernel_create_files_as(struct cred *new, struct inode *inode); -int security_kernel_fw_from_file(struct file *file, char *buf, size_t size); int security_kernel_module_request(char *kmod_name); int security_kernel_module_from_file(struct file *file); int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, @@ -854,12 +853,6 @@ static inline int security_kernel_create_files_as(struct cred *cred, return 0; } -static inline int security_kernel_fw_from_file(struct file *file, - char *buf, size_t size) -{ - return 0; -} - static inline int security_kernel_module_request(char *kmod_name) { return 0; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 757765354158..e9651be17b72 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -337,17 +337,6 @@ int ima_module_check(struct file *file) return process_measurement(file, NULL, 0, MAY_EXEC, MODULE_CHECK, 0); } -int ima_fw_from_file(struct file *file, char *buf, size_t size) -{ - if (!file) { - if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && - (ima_appraise & IMA_APPRAISE_ENFORCE)) - return -EACCES; /* INTEGRITY_UNKNOWN */ - return 0; - } - return process_measurement(file, NULL, 0, MAY_EXEC, FIRMWARE_CHECK, 0); -} - /** * ima_post_read_file - in memory collect/appraise/audit measurement * @file: pointer to the file to be measured/appraised/audit @@ -366,12 +355,22 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, { enum ima_hooks func = FILE_CHECK; + if (!file && read_id == READING_FIRMWARE) { + if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; /* INTEGRITY_UNKNOWN */ + return 0; + } + if (!file || !buf || size == 0) { /* should never happen */ if (ima_appraise & IMA_APPRAISE_ENFORCE) return -EACCES; return 0; } + if (read_id == READING_FIRMWARE) + func = FIRMWARE_CHECK; + return process_measurement(file, buf, size, MAY_READ, func, 0); } diff --git a/security/security.c b/security/security.c index ef4c65a9fd17..cd85be61c416 100644 --- a/security/security.c +++ b/security/security.c @@ -884,17 +884,6 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return call_int_hook(kernel_create_files_as, 0, new, inode); } -int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) -{ - int ret; - - ret = call_int_hook(kernel_fw_from_file, 0, file, buf, size); - if (ret) - return ret; - return ima_fw_from_file(file, buf, size); -} -EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); - int security_kernel_module_request(char *kmod_name) { return call_int_hook(kernel_module_request, 0, kmod_name); @@ -1703,8 +1692,6 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.kernel_act_as), .kernel_create_files_as = LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as), - .kernel_fw_from_file = - LIST_HEAD_INIT(security_hook_heads.kernel_fw_from_file), .kernel_module_request = LIST_HEAD_INIT(security_hook_heads.kernel_module_request), .kernel_module_from_file = -- cgit v1.2.3-55-g7522 From 8e516aa52cea98b23e14c5d67fe74147e73d25f7 Mon Sep 17 00:00:00 2001 From: Luis R. Rodriguez Date: Sun, 28 Feb 2016 21:57:55 +0100 Subject: firmware: change kernel read fail to dev_dbg() When we now use the new kernel_read_file_from_path() we are reporting a failure when we iterate over all the paths possible for firmware. Before using kernel_read_file_from_path() we only reported a failure once we confirmed a file existed with filp_open() but failed with fw_read_file_contents(). With kernel_read_file_from_path() both are done for us and we obviously are now reporting too much information given that some optional paths will always fail and clutter the logs. fw_get_filesystem_firmware() already has a check for failure and uses an internal flag, FW_OPT_NO_WARN, but this does not let us capture other unxpected errors. This enables that as changed by Neil via commit: "firmware: Be a bit more verbose about direct firmware loading failure" Reported-by: Heiner Kallweit Cc: Neil Horman Cc: Heiner Kallweit Cc: Mimi Zohar Cc: Kees Cook Signed-off-by: Luis R. Rodriguez Acked-by: Kees Cook Acked-by: Ming Lei Signed-off-by: James Morris --- drivers/base/firmware_class.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index a414008ea64c..f3f7215ad378 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -329,8 +329,12 @@ static int fw_get_filesystem_firmware(struct device *device, rc = kernel_read_file_from_path(path, &buf->data, &size, INT_MAX, READING_FIRMWARE); if (rc) { - dev_warn(device, "loading %s failed with error %d\n", - path, rc); + if (rc == -ENOENT) + dev_dbg(device, "loading %s failed with error %d\n", + path, rc); + else + dev_warn(device, "loading %s failed with error %d\n", + path, rc); continue; } dev_dbg(device, "direct-loading %s\n", buf->fw_id); -- cgit v1.2.3-55-g7522