diff options
author | James Morris <james.l.morris@oracle.com> | 2016-02-26 05:06:41 +0100 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2016-02-26 05:06:41 +0100 |
commit | 481873d06f2bf2ad732450a3a5fa5b8c2a07ef88 (patch) | |
tree | e2d16457d7902795073e4e4ad479798242a0ef80 | |
parent | Merge branch 'smack-for-4.6' of https://github.com/cschaufler/smack-next into... (diff) | |
parent | ima: require signed IMA policy (diff) | |
download | linux-481873d06f2bf2ad732450a3a5fa5b8c2a07ef88.tar.xz linux-481873d06f2bf2ad732450a3a5fa5b8c2a07ef88.zip |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity into next
-rw-r--r-- | Documentation/ABI/testing/ima_policy | 1 | ||||
-rw-r--r-- | drivers/base/firmware_class.c | 74 | ||||
-rw-r--r-- | fs/exec.c | 92 | ||||
-rw-r--r-- | include/linux/fs.h | 15 | ||||
-rw-r--r-- | include/linux/ima.h | 10 | ||||
-rw-r--r-- | include/linux/lsm_hooks.h | 35 | ||||
-rw-r--r-- | include/linux/security.h | 16 | ||||
-rw-r--r-- | kernel/kexec_file.c | 73 | ||||
-rw-r--r-- | kernel/module.c | 68 | ||||
-rw-r--r-- | security/integrity/iint.c | 4 | ||||
-rw-r--r-- | security/integrity/ima/ima.h | 49 | ||||
-rw-r--r-- | security/integrity/ima/ima_api.c | 25 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 69 | ||||
-rw-r--r-- | security/integrity/ima/ima_crypto.c | 120 | ||||
-rw-r--r-- | security/integrity/ima/ima_fs.c | 53 | ||||
-rw-r--r-- | security/integrity/ima/ima_init.c | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 92 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 115 | ||||
-rw-r--r-- | security/integrity/ima/ima_template.c | 2 | ||||
-rw-r--r-- | security/integrity/ima/ima_template_lib.c | 1 | ||||
-rw-r--r-- | security/integrity/integrity.h | 15 | ||||
-rw-r--r-- | security/security.c | 34 |
22 files changed, 601 insertions, 364 deletions
diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 0a378a88217a..bb0f9a135e21 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -27,6 +27,7 @@ Description: base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] [FIRMWARE_CHECK] + [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK] mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND] [[^]MAY_EXEC] fsmagic:= hex value diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index b9250e564ebf..a414008ea64c 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -23,6 +23,7 @@ #include <linux/sched.h> #include <linux/file.h> #include <linux/list.h> +#include <linux/fs.h> #include <linux/async.h> #include <linux/pm.h> #include <linux/suspend.h> @@ -291,40 +292,19 @@ 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) +static void fw_finish_direct_load(struct device *device, + struct firmware_buf *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; + 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) { + loff_t size; int i, len; int rc = -ENOENT; char *path; @@ -334,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; @@ -347,28 +325,21 @@ static int fw_get_filesystem_firmware(struct device *device, break; } - file = filp_open(path, O_RDONLY, 0); - if (IS_ERR(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; - 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); - else - break; + } + dev_dbg(device, "direct-loading %s\n", buf->fw_id); + buf->size = size; + fw_finish_direct_load(device, buf); + break; } __putname(path); - if (!rc) { - dev_dbg(device, "firmware: direct-loading firmware %s\n", - buf->fw_id); - mutex_lock(&fw_lock); - set_bit(FW_STATUS_DONE, &buf->status); - complete_all(&buf->completion); - mutex_unlock(&fw_lock); - } - return rc; } @@ -685,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 @@ -1051,7 +1023,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 */ } diff --git a/fs/exec.c b/fs/exec.c index dcd4ac7d3f1e..9bdf0edf570d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -56,6 +56,7 @@ #include <linux/pipe_fs_i.h> #include <linux/oom.h> #include <linux/compat.h> +#include <linux/vmalloc.h> #include <asm/uaccess.h> #include <asm/mmu_context.h> @@ -831,6 +832,97 @@ int kernel_read(struct file *file, loff_t offset, EXPORT_SYMBOL(kernel_read); +int kernel_read_file(struct file *file, void **buf, loff_t *size, + loff_t max_size, enum kernel_read_file_id id) +{ + loff_t i_size, pos; + ssize_t bytes = 0; + int ret; + + if (!S_ISREG(file_inode(file)->i_mode) || max_size < 0) + return -EINVAL; + + ret = security_kernel_read_file(file, id); + if (ret) + return ret; + + i_size = i_size_read(file_inode(file)); + if (max_size > 0 && i_size > max_size) + return -EFBIG; + if (i_size <= 0) + return -EINVAL; + + *buf = vmalloc(i_size); + if (!*buf) + return -ENOMEM; + + pos = 0; + while (pos < i_size) { + bytes = kernel_read(file, pos, (char *)(*buf) + pos, + i_size - pos); + if (bytes < 0) { + ret = bytes; + goto out; + } + + if (bytes == 0) + break; + pos += bytes; + } + + if (pos != i_size) { + ret = -EIO; + goto out; + } + + ret = security_kernel_post_read_file(file, *buf, i_size, id); + if (!ret) + *size = pos; + +out: + if (ret < 0) { + vfree(*buf); + *buf = NULL; + } + return ret; +} +EXPORT_SYMBOL_GPL(kernel_read_file); + +int kernel_read_file_from_path(char *path, void **buf, loff_t *size, + loff_t max_size, enum kernel_read_file_id id) +{ + struct file *file; + int ret; + + if (!path || !*path) + return -EINVAL; + + file = filp_open(path, O_RDONLY, 0); + if (IS_ERR(file)) + return PTR_ERR(file); + + ret = kernel_read_file(file, buf, size, max_size, id); + fput(file); + return ret; +} +EXPORT_SYMBOL_GPL(kernel_read_file_from_path); + +int kernel_read_file_from_fd(int fd, void **buf, loff_t *size, loff_t max_size, + enum kernel_read_file_id id) +{ + struct fd f = fdget(fd); + int ret = -EBADF; + + if (!f.file) + goto out; + + ret = kernel_read_file(f.file, buf, size, max_size, id); +out: + fdput(f); + return ret; +} +EXPORT_SYMBOL_GPL(kernel_read_file_from_fd); + ssize_t read_code(struct file *file, unsigned long addr, loff_t pos, size_t len) { ssize_t res = vfs_read(file, (void __user *)addr, len, &pos); diff --git a/include/linux/fs.h b/include/linux/fs.h index ae681002100a..e514f76db04f 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2576,7 +2576,22 @@ static inline void i_readcount_inc(struct inode *inode) #endif extern int do_pipe_flags(int *, int); +enum kernel_read_file_id { + READING_FIRMWARE = 1, + READING_MODULE, + READING_KEXEC_IMAGE, + READING_KEXEC_INITRAMFS, + READING_POLICY, + READING_MAX_ID +}; + extern int kernel_read(struct file *, loff_t, char *, unsigned long); +extern int kernel_read_file(struct file *, void **, loff_t *, loff_t, + enum kernel_read_file_id); +extern int kernel_read_file_from_path(char *, void **, loff_t *, loff_t, + enum kernel_read_file_id); +extern int kernel_read_file_from_fd(int, void **, loff_t *, loff_t, + enum kernel_read_file_id); extern ssize_t kernel_write(struct file *, const char *, size_t, loff_t); extern ssize_t __kernel_write(struct file *, const char *, size_t, loff_t *); extern struct file * open_exec(const char *); diff --git a/include/linux/ima.h b/include/linux/ima.h index 120ccc53fcb7..e6516cbbe9bf 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -18,8 +18,9 @@ extern int ima_bprm_check(struct linux_binprm *bprm); 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_read_file(struct file *file, enum kernel_read_file_id id); +extern int ima_post_read_file(struct file *file, void *buf, loff_t size, + enum kernel_read_file_id id); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -42,12 +43,13 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) return 0; } -static inline int ima_module_check(struct file *file) +static inline int ima_read_file(struct file *file, enum kernel_read_file_id id) { return 0; } -static inline int ima_fw_from_file(struct file *file, char *buf, size_t size) +static inline int ima_post_read_file(struct file *file, void *buf, loff_t size, + enum kernel_read_file_id id) { return 0; } diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 71969de4058c..cdee11cbcdf1 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -541,25 +541,24 @@ * @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. * @kmod_name name of the module requested by the kernel * Return 0 if successful. - * @kernel_module_from_file: - * Load a kernel module from userspace. - * @file contains the file structure pointing to the file containing - * the kernel module to load. If the module is being loaded from a blob, - * this argument will be NULL. + * @kernel_read_file: + * Read a file specified by userspace. + * @file contains the file structure pointing to the file being read + * by the kernel. + * @id kernel read file identifier + * Return 0 if permission is granted. + * @kernel_post_read_file: + * Read a file specified by userspace. + * @file contains the file structure pointing to the file being read + * by the kernel. + * @buf pointer to buffer containing the file contents. + * @size length of the file contents. + * @id kernel read file identifier * Return 0 if permission is granted. * @task_fix_setuid: * Update the module's state after setting one or more of the user @@ -1454,9 +1453,11 @@ 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_read_file)(struct file *file, enum kernel_read_file_id id); + int (*kernel_post_read_file)(struct file *file, char *buf, loff_t size, + enum kernel_read_file_id id); int (*task_fix_setuid)(struct cred *new, const struct cred *old, int flags); int (*task_setpgid)(struct task_struct *p, pid_t pgid); @@ -1715,9 +1716,9 @@ 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_read_file; + struct list_head kernel_post_read_file; struct list_head kernel_module_request; - struct list_head kernel_module_from_file; struct list_head task_fix_setuid; struct list_head task_setpgid; struct list_head task_getpgid; diff --git a/include/linux/security.h b/include/linux/security.h index 4824a4ccaf1c..157f0cb1e4d2 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -24,10 +24,12 @@ #include <linux/key.h> #include <linux/capability.h> +#include <linux/fs.h> #include <linux/slab.h> #include <linux/err.h> #include <linux/string.h> #include <linux/mm.h> +#include <linux/fs.h> struct linux_binprm; struct cred; @@ -298,9 +300,11 @@ 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_read_file(struct file *file, enum kernel_read_file_id id); +int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, + enum kernel_read_file_id id); int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags); int security_task_setpgid(struct task_struct *p, pid_t pgid); @@ -850,18 +854,20 @@ 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) +static inline int security_kernel_module_request(char *kmod_name) { return 0; } -static inline int security_kernel_module_request(char *kmod_name) +static inline int security_kernel_read_file(struct file *file, + enum kernel_read_file_id id) { return 0; } -static inline int security_kernel_module_from_file(struct file *file) +static inline int security_kernel_post_read_file(struct file *file, + char *buf, loff_t size, + enum kernel_read_file_id id) { return 0; } diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 007b791f676d..b696c3f3708f 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -18,6 +18,7 @@ #include <linux/kexec.h> #include <linux/mutex.h> #include <linux/list.h> +#include <linux/fs.h> #include <crypto/hash.h> #include <crypto/sha.h> #include <linux/syscalls.h> @@ -33,65 +34,6 @@ size_t __weak kexec_purgatory_size = 0; static int kexec_calculate_store_digests(struct kimage *image); -static int copy_file_from_fd(int fd, void **buf, unsigned long *buf_len) -{ - struct fd f = fdget(fd); - int ret; - struct kstat stat; - loff_t pos; - ssize_t bytes = 0; - - if (!f.file) - return -EBADF; - - ret = vfs_getattr(&f.file->f_path, &stat); - if (ret) - goto out; - - if (stat.size > INT_MAX) { - ret = -EFBIG; - goto out; - } - - /* Don't hand 0 to vmalloc, it whines. */ - if (stat.size == 0) { - ret = -EINVAL; - goto out; - } - - *buf = vmalloc(stat.size); - if (!*buf) { - ret = -ENOMEM; - goto out; - } - - pos = 0; - while (pos < stat.size) { - bytes = kernel_read(f.file, pos, (char *)(*buf) + pos, - stat.size - pos); - if (bytes < 0) { - vfree(*buf); - ret = bytes; - goto out; - } - - if (bytes == 0) - break; - pos += bytes; - } - - if (pos != stat.size) { - ret = -EBADF; - vfree(*buf); - goto out; - } - - *buf_len = pos; -out: - fdput(f); - return ret; -} - /* Architectures can provide this probe function */ int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf, unsigned long buf_len) @@ -182,16 +124,17 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, { int ret = 0; void *ldata; + loff_t size; - ret = copy_file_from_fd(kernel_fd, &image->kernel_buf, - &image->kernel_buf_len); + ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf, + &size, INT_MAX, READING_KEXEC_IMAGE); if (ret) return ret; + image->kernel_buf_len = size; /* Call arch image probe handlers */ ret = arch_kexec_kernel_image_probe(image, image->kernel_buf, image->kernel_buf_len); - if (ret) goto out; @@ -206,10 +149,12 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, #endif /* It is possible that there no initramfs is being loaded */ if (!(flags & KEXEC_FILE_NO_INITRAMFS)) { - ret = copy_file_from_fd(initrd_fd, &image->initrd_buf, - &image->initrd_buf_len); + ret = kernel_read_file_from_fd(initrd_fd, &image->initrd_buf, + &size, INT_MAX, + READING_KEXEC_INITRAMFS); if (ret) goto out; + image->initrd_buf_len = size; } if (cmdline_len) { diff --git a/kernel/module.c b/kernel/module.c index 8358f4697c0c..955410928696 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2654,7 +2654,7 @@ static int copy_module_from_user(const void __user *umod, unsigned long len, if (info->len < sizeof(*(info->hdr))) return -ENOEXEC; - err = security_kernel_module_from_file(NULL); + err = security_kernel_read_file(NULL, READING_MODULE); if (err) return err; @@ -2672,63 +2672,6 @@ static int copy_module_from_user(const void __user *umod, unsigned long len, return 0; } -/* Sets info->hdr and info->len. */ -static int copy_module_from_fd(int fd, struct load_info *info) -{ - struct fd f = fdget(fd); - int err; - struct kstat stat; - loff_t pos; - ssize_t bytes = 0; - - if (!f.file) - return -ENOEXEC; - - err = security_kernel_module_from_file(f.file); - if (err) - goto out; - - err = vfs_getattr(&f.file->f_path, &stat); - if (err) - goto out; - - if (stat.size > INT_MAX) { - err = -EFBIG; - goto out; - } - - /* Don't hand 0 to vmalloc, it whines. */ - if (stat.size == 0) { - err = -EINVAL; - goto out; - } - - info->hdr = vmalloc(stat.size); - if (!info->hdr) { - err = -ENOMEM; - goto out; - } - - pos = 0; - while (pos < stat.size) { - bytes = kernel_read(f.file, pos, (char *)(info->hdr) + pos, - stat.size - pos); - if (bytes < 0) { - vfree(info->hdr); - err = bytes; - goto out; - } - if (bytes == 0) - break; - pos += bytes; - } - info->len = pos; - -out: - fdput(f); - return err; -} - static void free_copy(struct load_info *info) { vfree(info->hdr); @@ -3589,8 +3532,10 @@ SYSCALL_DEFINE3(init_module, void __user *, umod, SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) { - int err; struct load_info info = { }; + loff_t size; + void *hdr; + int err; err = may_init_module(); if (err) @@ -3602,9 +3547,12 @@ SYSCALL_DEFINE3(finit_module, int, fd, const char __user *, uargs, int, flags) |MODULE_INIT_IGNORE_VERMAGIC)) return -EINVAL; - err = copy_module_from_fd(fd, &info); + err = kernel_read_file_from_fd(fd, &hdr, &size, INT_MAX, + READING_MODULE); if (err) return err; + info.hdr = hdr; + info.len = size; return load_module(&info, uargs, flags); } diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 8f1ab37f2897..345b75997e4c 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -77,7 +77,7 @@ static void iint_free(struct integrity_iint_cache *iint) iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_mmap_status = INTEGRITY_UNKNOWN; iint->ima_bprm_status = INTEGRITY_UNKNOWN; - iint->ima_module_status = INTEGRITY_UNKNOWN; + iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; kmem_cache_free(iint_cache, iint); } @@ -157,7 +157,7 @@ static void init_once(void *foo) iint->ima_file_status = INTEGRITY_UNKNOWN; iint->ima_mmap_status = INTEGRITY_UNKNOWN; iint->ima_bprm_status = INTEGRITY_UNKNOWN; - iint->ima_module_status = INTEGRITY_UNKNOWN; + iint->ima_read_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 585af61ed399..5d0f61163d98 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -19,10 +19,12 @@ #include <linux/types.h> #include <linux/crypto.h> +#include <linux/fs.h> #include <linux/security.h> #include <linux/hash.h> #include <linux/tpm.h> #include <linux/audit.h> +#include <crypto/hash_info.h> #include "../integrity.h" @@ -106,6 +108,8 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, const unsigned char *filename); int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); +int ima_calc_buffer_hash(const void *buf, loff_t len, + struct ima_digest_data *hash); int ima_calc_field_array_hash(struct ima_field_data *field_data, struct ima_template_desc *desc, int num_fields, struct ima_digest_data *hash); @@ -136,13 +140,25 @@ static inline unsigned long ima_hash_key(u8 *digest) return hash_long(*digest, IMA_HASH_BITS); } +enum ima_hooks { + FILE_CHECK = 1, + MMAP_CHECK, + BPRM_CHECK, + POST_SETATTR, + MODULE_CHECK, + FIRMWARE_CHECK, + KEXEC_KERNEL_CHECK, + KEXEC_INITRAMFS_CHECK, + POLICY_CHECK, + MAX_CHECK +}; + /* LIM API function definitions */ -int ima_get_action(struct inode *inode, int mask, int function); -int ima_must_measure(struct inode *inode, int mask, int function); +int ima_get_action(struct inode *inode, int mask, enum ima_hooks func); +int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func); int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file, - struct evm_ima_xattr_data **xattr_value, - int *xattr_len); + struct file *file, void *buf, loff_t size, + enum hash_algo algo); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, @@ -157,8 +173,6 @@ void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(struct path *path, char **pathbuf); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; - int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); void ima_init_policy(void); @@ -178,23 +192,25 @@ int ima_policy_show(struct seq_file *m, void *v); #define IMA_APPRAISE_LOG 0x04 #define IMA_APPRAISE_MODULES 0x08 #define IMA_APPRAISE_FIRMWARE 0x10 +#define IMA_APPRAISE_POLICY 0x20 #ifdef CONFIG_IMA_APPRAISE -int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, +int ima_appraise_measurement(enum ima_hooks func, + struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, int opened); int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, - int func); -void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, - struct ima_digest_data *hash); + enum ima_hooks func); +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len); int ima_read_xattr(struct dentry *dentry, struct evm_ima_xattr_data **xattr_value); #else -static inline int ima_appraise_measurement(int func, +static inline int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, @@ -216,15 +232,16 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint, } static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache - *iint, int func) + *iint, + enum ima_hooks func) { return INTEGRITY_UNKNOWN; } -static inline void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, - int xattr_len, - struct ima_digest_data *hash) +static inline enum hash_algo +ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) { + return ima_hash_algo; } static inline int ima_read_xattr(struct dentry *dentry, diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 1d950fbb2aec..370e42dfc5c5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -18,7 +18,7 @@ #include <linux/fs.h> #include <linux/xattr.h> #include <linux/evm.h> -#include <crypto/hash_info.h> + #include "ima.h" /* @@ -156,7 +156,7 @@ err_out: * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) - * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK) + * @func: caller identifier * * The policy is defined in terms of keypairs: * subj=, obj=, type=, func=, mask=, fsmagic= @@ -168,13 +168,13 @@ err_out: * Returns IMA_MEASURE, IMA_APPRAISE mask. * */ -int ima_get_action(struct inode *inode, int mask, int function) +int ima_get_action(struct inode *inode, int mask, enum ima_hooks func) { int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; flags &= ima_policy_flag; - return ima_match_policy(inode, function, mask, flags); + return ima_match_policy(inode, func, mask, flags); } /* @@ -188,9 +188,8 @@ int ima_get_action(struct inode *inode, int mask, int function) * Return 0 on success, error code otherwise */ int ima_collect_measurement(struct integrity_iint_cache *iint, - struct file *file, - struct evm_ima_xattr_data **xattr_value, - int *xattr_len) + struct file *file, void *buf, loff_t size, + enum hash_algo algo) { const char *audit_cause = "failed"; struct inode *inode = file_inode(file); @@ -201,9 +200,6 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, char digest[IMA_MAX_DIGEST_SIZE]; } hash; - if (xattr_value) - *xattr_len = ima_read_xattr(file->f_path.dentry, xattr_value); - if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file_inode(file)->i_version; @@ -213,13 +209,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, goto out; } - /* use default hash algorithm */ - hash.hdr.algo = ima_hash_algo; - - if (xattr_value) - ima_get_hash_algo(*xattr_value, *xattr_len, &hash.hdr); + hash.hdr.algo = algo; - result = ima_calc_file_hash(file, &hash.hdr); + result = (!buf) ? ima_calc_file_hash(file, &hash.hdr) : + ima_calc_buffer_hash(buf, size, &hash.hdr); if (!result) { int length = sizeof(hash.hdr) + hash.hdr.length; void *tmpbuf = krealloc(iint->ima_hash, length, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 1873b5536f80..6b4694aedae8 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,7 +15,6 @@ #include <linux/magic.h> #include <linux/ima.h> #include <linux/evm.h> -#include <crypto/hash_info.h> #include "ima.h" @@ -68,25 +67,25 @@ static int ima_fix_xattr(struct dentry *dentry, /* Return specific func appraised cached result */ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, - int func) + enum ima_hooks func) { switch (func) { case MMAP_CHECK: return iint->ima_mmap_status; case BPRM_CHECK: return iint->ima_bprm_status; - case MODULE_CHECK: - return iint->ima_module_status; - case FIRMWARE_CHECK: - return iint->ima_firmware_status; case FILE_CHECK: - default: + case POST_SETATTR: return iint->ima_file_status; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + return iint->ima_read_status; } } static void ima_set_cache_status(struct integrity_iint_cache *iint, - int func, enum integrity_status status) + enum ima_hooks func, + enum integrity_status status) { switch (func) { case MMAP_CHECK: @@ -95,20 +94,19 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, case BPRM_CHECK: iint->ima_bprm_status = status; break; - case MODULE_CHECK: - iint->ima_module_status = status; - break; - case FIRMWARE_CHECK: - iint->ima_firmware_status = status; - break; case FILE_CHECK: - default: + case POST_SETATTR: iint->ima_file_status = status; break; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + iint->ima_read_status = status; + break; } } -static void ima_cache_flags(struct integrity_iint_cache *iint, int func) +static void ima_cache_flags(struct integrity_iint_cache *iint, + enum ima_hooks func) { switch (func) { case MMAP_CHECK: @@ -117,49 +115,51 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) case BPRM_CHECK: iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED); break; - case MODULE_CHECK: - iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); - break; - case FIRMWARE_CHECK: - iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); - break; case FILE_CHECK: - default: + case POST_SETATTR: iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); break; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + iint->flags |= (IMA_READ_APPRAISED | IMA_APPRAISED); + break; } } -void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, - struct ima_digest_data *hash) +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len) { struct signature_v2_hdr *sig; if (!xattr_value || xattr_len < 2) - return; + /* return default hash algo */ + return ima_hash_algo; switch (xattr_value->type) { case EVM_IMA_XATTR_DIGSIG: sig = (typeof(sig))xattr_value; if (sig->version != 2 || xattr_len <= sizeof(*sig)) - return; - hash->algo = sig->hash_algo; + return ima_hash_algo; + return sig->hash_algo; break; case IMA_XATTR_DIGEST_NG: - hash->algo = xattr_value->digest[0]; + return xattr_value->digest[0]; break; case IMA_XATTR_DIGEST: /* this is for backward compatibility */ if (xattr_len == 21) { unsigned int zero = 0; if (!memcmp(&xattr_value->digest[16], &zero, 4)) - hash->algo = HASH_ALGO_MD5; + return HASH_ALGO_MD5; else - hash->algo = HASH_ALGO_SHA1; + return HASH_ALGO_SHA1; } else if (xattr_len == 17) - hash->algo = HASH_ALGO_MD5; + return HASH_ALGO_MD5; break; } + + /* return default hash algo */ + return ima_hash_algo; } int ima_read_xattr(struct dentry *dentry, @@ -182,7 +182,8 @@ int ima_read_xattr(struct dentry *dentry, * * Return 0 on success, error code otherwise */ -int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, +int ima_appraise_measurement(enum ima_hooks func, + struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, int opened) @@ -296,7 +297,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) if (iint->flags & IMA_DIGSIG) return; - rc = ima_collect_measurement(iint, file, NULL, NULL); + rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo); if (rc < 0) return; diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 6eb62936c672..38f2ed830dd6 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -24,7 +24,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <crypto/hash.h> -#include <crypto/hash_info.h> + #include "ima.h" struct ahash_completion { @@ -519,6 +519,124 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, return rc; } +static int calc_buffer_ahash_atfm(const void *buf, loff_t len, + struct ima_digest_data *hash, + struct crypto_ahash *tfm) +{ + struct ahash_request *req; + struct scatterlist sg; + struct ahash_completion res; + int rc, ahash_rc = 0; + + hash->length = crypto_ahash_digestsize(tfm); + + req = ahash_request_alloc(tfm, GFP_KERNEL); + if (!req) + return -ENOMEM; + + init_completion(&res.completion); + ahash_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG | + CRYPTO_TFM_REQ_MAY_SLEEP, + ahash_complete, &res); + + rc = ahash_wait(crypto_ahash_init(req), &res); + if (rc) + goto out; + + sg_init_one(&sg, buf, len); + ahash_request_set_crypt(req, &sg, NULL, len); + + ahash_rc = crypto_ahash_update(req); + + /* wait for the update request to complete */ + rc = ahash_wait(ahash_rc, &res); + if (!rc) { + ahash_request_set_crypt(req, NULL, hash->digest, 0); + rc = ahash_wait(crypto_ahash_final(req), &res); + } +out: + ahash_request_free(req); + return rc; +} + +static int calc_buffer_ahash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + struct crypto_ahash *tfm; + int rc; + + tfm = ima_alloc_atfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = calc_buffer_ahash_atfm(buf, len, hash, tfm); + + ima_free_atfm(tfm); + + return rc; +} + +static int calc_buffer_shash_tfm(const void *buf, loff_t size, + struct ima_digest_data *hash, + struct crypto_shash *tfm) +{ + SHASH_DESC_ON_STACK(shash, tfm); + unsigned int len; + int rc; + + shash->tfm = tfm; + shash->flags = 0; + + hash->length = crypto_shash_digestsize(tfm); + + rc = crypto_shash_init(shash); + if (rc != 0) + return rc; + + while (size) { + len = size < PAGE_SIZE ? size : PAGE_SIZE; + rc = crypto_shash_update(shash, buf, len); + if (rc) + break; + buf += len; + size -= len; + } + + if (!rc) + rc = crypto_shash_final(shash, hash->digest); + return rc; +} + +static int calc_buffer_shash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + struct crypto_shash *tfm; + int rc; + + tfm = ima_alloc_tfm(hash->algo); + if (IS_ERR(tfm)) + return PTR_ERR(tfm); + + rc = calc_buffer_shash_tfm(buf, len, hash, tfm); + + ima_free_tfm(tfm); + return rc; +} + +int ima_calc_buffer_hash(const void *buf, loff_t len, + struct ima_digest_data *hash) +{ + int rc; + + if (ima_ahash_minsize && len >= ima_ahash_minsize) { + rc = calc_buffer_ahash(buf, len, hash); + if (!rc) + return 0; + } + + return calc_buffer_shash(buf, len, hash); +} + static void __init ima_pcrread(int idx, u8 *pcr) { if (!ima_used_chip) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index f355231997b4..60d011aaec38 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -22,6 +22,7 @@ #include <linux/rculist.h> #include <linux/rcupdate.h> #include <linux/parser.h> +#include <linux/vmalloc.h> #include "ima.h" @@ -258,6 +259,43 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, }; +static ssize_t ima_read_policy(char *path) +{ + void *data; + char *datap; + loff_t size; + int rc, pathlen = strlen(path); + + char *p; + + /* remove \n */ + datap = path; + strsep(&datap, "\n"); + + rc = kernel_read_file_from_path(path, &data, &size, 0, READING_POLICY); + if (rc < 0) { + pr_err("Unable to open file: %s (%d)", path, rc); + return rc; + } + + datap = data; + while (size > 0 && (p = strsep(&datap, "\n"))) { + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + if (rc < 0) + break; + size -= rc; + } + + vfree(data); + if (rc < 0) + return rc; + else if (size) + return -EINVAL; + else + return pathlen; +} + static ssize_t ima_write_policy(struct file *file, const char __user *buf, size_t datalen, loff_t *ppos) { @@ -286,9 +324,20 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf, result = mutex_lock_interruptible(&ima_write_mutex); if (result < 0) goto out_free; - result = ima_parse_add_rule(data); - mutex_unlock(&ima_write_mutex); + if (data[0] == '/') { + result = ima_read_policy(data); + } else if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("IMA: signed policy file (specified as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", "signed policy required", + 1, 0); + if (ima_appraise & IMA_APPRAISE_ENFORCE) + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } + mutex_unlock(&ima_write_mutex); out_free: kfree(data); out: diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index bd79f254d204..5d679a685616 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -21,7 +21,7 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/err.h> -#include <crypto/hash_info.h> + #include "ima.h" /* name for boot aggregate entry */ diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 9d96551d0196..391f41751021 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -24,7 +24,6 @@ #include <linux/slab.h> #include <linux/xattr.h> #include <linux/ima.h> -#include <crypto/hash_info.h> #include "ima.h" @@ -154,8 +153,8 @@ void ima_file_free(struct file *file) ima_check_last_writer(iint, inode, file); } -static int process_measurement(struct file *file, int mask, int function, - int opened) +static int process_measurement(struct file *file, char *buf, loff_t size, + int mask, enum ima_hooks func, int opened) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint = NULL; @@ -163,9 +162,10 @@ static int process_measurement(struct file *file, int mask, int function, char *pathbuf = NULL; const char *pathname = NULL; int rc = -ENOMEM, action, must_appraise; - struct evm_ima_xattr_data *xattr_value = NULL, **xattr_ptr = NULL; + struct evm_ima_xattr_data *xattr_value = NULL; int xattr_len = 0; bool violation_check; + enum hash_algo hash_algo; if (!ima_policy_flag || !S_ISREG(inode->i_mode)) return 0; @@ -174,8 +174,8 @@ static int process_measurement(struct file *file, int mask, int function, * bitmask based on the appraise/audit/measurement policy. * Included is the appraise submask. */ - action = ima_get_action(inode, mask, function); - violation_check = ((function == FILE_CHECK || function == MMAP_CHECK) && + action = ima_get_action(inode, mask, func); + violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) && (ima_policy_flag & IMA_MEASURE)); if (!action && !violation_check) return 0; @@ -184,7 +184,7 @@ static int process_measurement(struct file *file, int mask, int function, /* Is the appraise rule hook specific? */ if (action & IMA_FILE_APPRAISE) - function = FILE_CHECK; + func = FILE_CHECK; inode_lock(inode); @@ -214,16 +214,19 @@ static int process_measurement(struct file *file, int mask, int function, /* Nothing to do, just return existing appraised status */ if (!action) { if (must_appraise) - rc = ima_get_cache_status(iint, function); + rc = ima_get_cache_status(iint, func); goto out_digsig; } template_desc = ima_template_desc_current(); if ((action & IMA_APPRAISE_SUBMASK) || strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0) - xattr_ptr = &xattr_value; + /* read 'security.ima' */ + xattr_len = ima_read_xattr(file->f_path.dentry, &xattr_value); - rc = ima_collect_measurement(iint, file, xattr_ptr, &xattr_len); + hash_algo = ima_get_hash_algo(xattr_value, xattr_len); + + rc = ima_collect_measurement(iint, file, buf, size, hash_algo); if (rc != 0) { if (file->f_flags & O_DIRECT) rc = (iint->flags & IMA_PERMIT_DIRECTIO) ? 0 : -EACCES; @@ -237,7 +240,7 @@ static int process_measurement(struct file *file, int mask, int function, ima_store_measurement(iint, file, pathname, xattr_value, xattr_len); if (action & IMA_APPRAISE_SUBMASK) - rc = ima_appraise_measurement(function, iint, file, pathname, + rc = ima_appraise_measurement(func, iint, file, pathname, xattr_value, xattr_len, opened); if (action & IMA_AUDIT) ima_audit_measurement(iint, pathname); @@ -270,7 +273,8 @@ out: int ima_file_mmap(struct file *file, unsigned long prot) { if (file && (prot & PROT_EXEC)) - return process_measurement(file, MAY_EXEC, MMAP_CHECK, 0); + return process_measurement(file, NULL, 0, MAY_EXEC, + MMAP_CHECK, 0); return 0; } @@ -289,7 +293,8 @@ int ima_file_mmap(struct file *file, unsigned long prot) */ int ima_bprm_check(struct linux_binprm *bprm) { - return process_measurement(bprm->file, MAY_EXEC, BPRM_CHECK, 0); + return process_measurement(bprm->file, NULL, 0, MAY_EXEC, + BPRM_CHECK, 0); } /** @@ -304,24 +309,26 @@ int ima_bprm_check(struct linux_binprm *bprm) */ int ima_file_check(struct file *file, int mask, int opened) { - return process_measurement(file, + return process_measurement(file, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK, opened); } EXPORT_SYMBOL_GPL(ima_file_check); /** - * ima_module_check - based on policy, collect/store/appraise measurement. - * @file: pointer to the file to be measured/appraised + * ima_read_file - pre-measure/appraise hook decision based on policy + * @file: pointer to the file to be measured/appraised/audit + * @read_id: caller identifier * - * Measure/appraise kernel modules based on policy. + * Permit reading a file based on policy. The policy rules are written + * in terms of the policy identifier. Appraising the integrity of + * a file requires a file descriptor. * - * On success return 0. On integrity appraisal error, assuming the file - * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. + * For permission return 0, otherwise return -EACCES. */ -int ima_module_check(struct file *file) +int ima_read_file(struct file *file, enum kernel_read_file_id read_id) { - if (!file) { + if (!file && read_id == READING_MODULE) { #ifndef CONFIG_MODULE_SIG_FORCE if ((ima_appraise & IMA_APPRAISE_MODULES) && (ima_appraise & IMA_APPRAISE_ENFORCE)) @@ -329,18 +336,53 @@ int ima_module_check(struct file *file) #endif return 0; /* We rely on module signature checking */ } - return process_measurement(file, MAY_EXEC, MODULE_CHECK, 0); + return 0; } -int ima_fw_from_file(struct file *file, char *buf, size_t size) +static int read_idmap[READING_MAX_ID] = { + [READING_FIRMWARE] = FIRMWARE_CHECK, + [READING_MODULE] = MODULE_CHECK, + [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, + [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, + [READING_POLICY] = POLICY_CHECK +}; + +/** + * ima_post_read_file - in memory collect/appraise/audit measurement + * @file: pointer to the file to be measured/appraised/audit + * @buf: pointer to in memory file contents + * @size: size of in memory file contents + * @read_id: caller identifier + * + * Measure/appraise/audit in memory file based on policy. Policy rules + * are written in terms of a policy identifier. + * + * On success return 0. On integrity appraisal error, assuming the file + * is in policy and IMA-appraisal is in enforcing mode, return -EACCES. + */ +int ima_post_read_file(struct file *file, void *buf, loff_t size, + enum kernel_read_file_id read_id) { - if (!file) { + enum ima_hooks func; + + if (!file && read_id == READING_FIRMWARE) { if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && (ima_appraise & IMA_APPRAISE_ENFORCE)) return -EACCES; /* INTEGRITY_UNKNOWN */ return 0; } - return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0); + + if (!file && read_id == READING_MODULE) /* MODULE_SIG_FORCE enabled */ + return 0; + + if (!file || !buf || size == 0) { /* should never happen */ + if (ima_appraise & IMA_APPRAISE_ENFORCE) + return -EACCES; + return 0; + } + + func = read_idmap[read_id] ?: FILE_CHECK; + return process_measurement(file, buf, size, MAY_READ, func, 0); } static int __init init_ima(void) diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0a3b781f18e5..be09e2cacf82 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -12,6 +12,7 @@ */ #include <linux/module.h> #include <linux/list.h> +#include <linux/fs.h> #include <linux/security.h> #include <linux/magic.h> #include <linux/parser.h> @@ -113,6 +114,7 @@ static struct ima_rule_entry default_measurement_rules[] = { .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -127,6 +129,10 @@ static struct ima_rule_entry default_appraise_rules[] = { {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, +#ifdef CONFIG_IMA_WRITE_POLICY + {.action = APPRAISE, .func = POLICY_CHECK, + .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, +#endif #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, #else @@ -207,8 +213,8 @@ static void ima_lsm_update_rules(void) * * Returns true on rule match, false on failure. */ -static bool ima_match_rules(struct ima_rule_entry *rule, - struct inode *inode, enum ima_hooks func, int mask) +static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, + enum ima_hooks func, int mask) { struct task_struct *tsk = current; const struct cred *cred = current_cred(); @@ -289,7 +295,7 @@ retry: * In addition to knowing that we need to appraise the file in general, * we need to differentiate between calling hooks, for hook specific rules. */ -static int get_subaction(struct ima_rule_entry *rule, int func) +static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func) { if (!(rule->flags & IMA_FUNC)) return IMA_FILE_APPRAISE; @@ -299,13 +305,12 @@ static int get_subaction(struct ima_rule_entry *rule, int func) return IMA_MMAP_APPRAISE; case BPRM_CHECK: return IMA_BPRM_APPRAISE; - case MODULE_CHECK: - return IMA_MODULE_APPRAISE; - case FIRMWARE_CHECK: - return IMA_FIRMWARE_APPRAISE; case FILE_CHECK: - default: + case POST_SETATTR: return IMA_FILE_APPRAISE; + case MODULE_CHECK ... MAX_CHECK - 1: + default: + return IMA_READ_APPRAISE; } } @@ -411,13 +416,16 @@ void __init ima_init_policy(void) for (i = 0; i < appraise_entries; i++) { list_add_tail(&default_appraise_rules[i].list, &ima_default_rules); + if (default_appraise_rules[i].func == POLICY_CHECK) + temp_ima_appraise |= IMA_APPRAISE_POLICY; } ima_rules = &ima_default_rules; + ima_update_policy_flag(); } /* Make sure we have a valid policy, at least containing some rules. */ -int ima_check_policy() +int ima_check_policy(void) { if (list_empty(&ima_temp_rules)) return -EINVAL; @@ -612,6 +620,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = MMAP_CHECK; else if (strcmp(args[0].from, "BPRM_CHECK") == 0) entry->func = BPRM_CHECK; + else if (strcmp(args[0].from, "KEXEC_KERNEL_CHECK") == + 0) + entry->func = KEXEC_KERNEL_CHECK; + else if (strcmp(args[0].from, "KEXEC_INITRAMFS_CHECK") + == 0) + entry->func = KEXEC_INITRAMFS_CHECK; + else if (strcmp(args[0].from, "POLICY_CHECK") == 0) + entry->func = POLICY_CHECK; else result = -EINVAL; if (!result) @@ -770,6 +786,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) temp_ima_appraise |= IMA_APPRAISE_MODULES; else if (entry->func == FIRMWARE_CHECK) temp_ima_appraise |= IMA_APPRAISE_FIRMWARE; + else if (entry->func == POLICY_CHECK) + temp_ima_appraise |= IMA_APPRAISE_POLICY; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; @@ -855,7 +873,9 @@ static char *mask_tokens[] = { enum { func_file = 0, func_mmap, func_bprm, - func_module, func_firmware, func_post + func_module, func_firmware, func_post, + func_kexec_kernel, func_kexec_initramfs, + func_policy }; static char *func_tokens[] = { @@ -864,6 +884,9 @@ static char *func_tokens[] = { "BPRM_CHECK", "MODULE_CHECK", "FIRMWARE_CHECK", + "KEXEC_KERNEL_CHECK", + "KEXEC_INITRAMFS_CHECK", + "POLICY_CHECK", "POST_SETATTR" }; @@ -903,6 +926,49 @@ void ima_policy_stop(struct seq_file *m, void *v) #define mt(token) mask_tokens[token] #define ft(token) func_tokens[token] +/* + * policy_func_show - display the ima_hooks policy rule + */ +static void policy_func_show(struct seq_file *m, enum ima_hooks func) +{ + char tbuf[64] = {0,}; + + switch (func) { + case FILE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_file)); + break; + case MMAP_CHECK: + seq_printf(m, pt(Opt_func), ft(func_mmap)); + break; + case BPRM_CHECK: + seq_printf(m, pt(Opt_func), ft(func_bprm)); + break; + case MODULE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_module)); + break; + case FIRMWARE_CHECK: + seq_printf(m, pt(Opt_func), ft(func_firmware)); + break; + case POST_SETATTR: + seq_printf(m, pt(Opt_func), ft(func_post)); + break; + case KEXEC_KERNEL_CHECK: + seq_printf(m, pt(Opt_func), ft(func_kexec_kernel)); + break; + case KEXEC_INITRAMFS_CHECK: + seq_printf(m, pt(Opt_func), ft(func_kexec_initramfs)); + break; + case POLICY_CHECK: + seq_printf(m, pt(Opt_func), ft(func_policy)); + break; + default: + snprintf(tbuf, sizeof(tbuf), "%d", func); + seq_printf(m, pt(Opt_func), tbuf); + break; + } + seq_puts(m, " "); +} + int ima_policy_show(struct seq_file *m, void *v) { struct ima_rule_entry *entry = v; @@ -924,33 +990,8 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); - if (entry->flags & IMA_FUNC) { - switch (entry->func) { - case FILE_CHECK: - seq_printf(m, pt(Opt_func), ft(func_file)); - break; - case MMAP_CHECK: - seq_printf(m, pt(Opt_func), ft(func_mmap)); - break; - case BPRM_CHECK: - seq_printf(m, pt(Opt_func), ft(func_bprm)); - break; - case MODULE_CHECK: - seq_printf(m, pt(Opt_func), ft(func_module)); - break; - case FIRMWARE_CHECK: - seq_printf(m, pt(Opt_func), ft(func_firmware)); - break; - case POST_SETATTR: - seq_printf(m, pt(Opt_func), ft(func_post)); - break; - default: - snprintf(tbuf, sizeof(tbuf), "%d", entry->func); - seq_printf(m, pt(Opt_func), tbuf); - break; - } - seq_puts(m, " "); - } + if (entry->flags & IMA_FUNC) + policy_func_show(m, entry->func); if (entry->flags & IMA_MASK) { if (entry->mask & MAY_EXEC) diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 0b7404ebfa80..febd12ed9b55 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -15,8 +15,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#include <crypto/hash_info.h> - #include "ima.h" #include "ima_template_lib.h" diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 2934e3d377f1..f9bae04ba176 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -12,7 +12,6 @@ * File: ima_template_lib.c * Library of supported template fields. */ -#include <crypto/hash_info.h> #include "ima_template_lib.h" diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 5efe2ecc538d..c7a111cc7d89 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -45,16 +45,12 @@ #define IMA_MMAP_APPRAISED 0x00000800 #define IMA_BPRM_APPRAISE 0x00001000 #define IMA_BPRM_APPRAISED 0x00002000 -#define IMA_MODULE_APPRAISE 0x00004000 -#define IMA_MODULE_APPRAISED 0x00008000 -#define IMA_FIRMWARE_APPRAISE 0x00010000 -#define IMA_FIRMWARE_APPRAISED 0x00020000 +#define IMA_READ_APPRAISE 0x00004000 +#define IMA_READ_APPRAISED 0x00008000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ - IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ - IMA_FIRMWARE_APPRAISE) + IMA_BPRM_APPRAISE | IMA_READ_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ - IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ - IMA_FIRMWARE_APPRAISED) + IMA_BPRM_APPRAISED | IMA_READ_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -109,8 +105,7 @@ struct integrity_iint_cache { enum integrity_status ima_file_status:4; enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; - enum integrity_status ima_module_status:4; - enum integrity_status ima_firmware_status:4; + enum integrity_status ima_read_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; }; diff --git a/security/security.c b/security/security.c index e8ffd92ae2eb..3644b0344d29 100644 --- a/security/security.c +++ b/security/security.c @@ -884,31 +884,33 @@ 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 security_kernel_module_request(char *kmod_name) +{ + return call_int_hook(kernel_module_request, 0, kmod_name); +} + +int security_kernel_read_file(struct file *file, enum kernel_read_file_id id) { int ret; - ret = call_int_hook(kernel_fw_from_file, 0, file, buf, size); + ret = call_int_hook(kernel_read_file, 0, file, id); 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); + return ima_read_file(file, id); } +EXPORT_SYMBOL_GPL(security_kernel_read_file); -int security_kernel_module_from_file(struct file *file) +int security_kernel_post_read_file(struct file *file, char *buf, loff_t size, + enum kernel_read_file_id id) { int ret; - ret = call_int_hook(kernel_module_from_file, 0, file); + ret = call_int_hook(kernel_post_read_file, 0, file, buf, size, id); if (ret) return ret; - return ima_module_check(file); + return ima_post_read_file(file, buf, size, id); } +EXPORT_SYMBOL_GPL(security_kernel_post_read_file); int security_task_fix_setuid(struct cred *new, const struct cred *old, int flags) @@ -1691,12 +1693,12 @@ 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 = - LIST_HEAD_INIT(security_hook_heads.kernel_module_from_file), + .kernel_read_file = + LIST_HEAD_INIT(security_hook_heads.kernel_read_file), + .kernel_post_read_file = + LIST_HEAD_INIT(security_hook_heads.kernel_post_read_file), .task_fix_setuid = LIST_HEAD_INIT(security_hook_heads.task_fix_setuid), .task_setpgid = LIST_HEAD_INIT(security_hook_heads.task_setpgid), |