ima_process_measurements() determines whether or not a file is in policy
before calculating the file hash.  Instead of reading the file once for
calculating the file hash and possibly again for loading the file into
memory, this patch defines a new IMA hook named ima_read_file_from_fd()
to read, and at the same time hash, a kexec'ed image.  After reading and
hashing the file, ima_read_file_from_fd() calls ima_process_measurement()
to measure and appraise the file.

This patch defines a new policy "func" named KEXEC_CHECK to measure
and/or appraise the kexec image.

Signed-off-by: Mimi Zohar <zo...@linux.vnet.ibm.com>
---
 Documentation/ABI/testing/ima_policy      |  2 +-
 include/linux/ima.h                       |  6 ++++
 kernel/kexec_file.c                       |  9 ++++--
 security/integrity/ima/ima.h              |  7 ++--
 security/integrity/ima/ima_api.c          | 35 +++++++++++++++++++-
 security/integrity/ima/ima_appraise.c     |  8 +++++
 security/integrity/ima/ima_crypto.c       | 42 ++++++++++++++++++------
 security/integrity/ima/ima_main.c         | 54 +++++++++++++++++++++++++++----
 security/integrity/ima/ima_policy.c       |  4 +++
 security/integrity/ima/ima_template_lib.c |  2 +-
 security/integrity/integrity.h            |  7 ++--
 11 files changed, 150 insertions(+), 26 deletions(-)

diff --git a/Documentation/ABI/testing/ima_policy 
b/Documentation/ABI/testing/ima_policy
index 0a378a8..5ae0be1 100644
--- a/Documentation/ABI/testing/ima_policy
+++ b/Documentation/ABI/testing/ima_policy
@@ -26,7 +26,7 @@ Description:
                        option: [[appraise_type=]] [permit_directio]
 
                base:   func:= 
[BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
-                               [FIRMWARE_CHECK]
+                               [FIRMWARE_CHECK] [KEXEC_CHECK]
                        mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
                               [[^]MAY_EXEC]
                        fsmagic:= hex value
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 120ccc5..244452b 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -20,6 +20,7 @@ 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_from_fd(int fd, void **buf, size_t *size);
 
 #else
 static inline int ima_bprm_check(struct linux_binprm *bprm)
@@ -52,6 +53,11 @@ static inline int ima_fw_from_file(struct file *file, char 
*buf, size_t size)
        return 0;
 }
 
+static inline int ima_read_file_from_fd(int fd, void **buf, size_t *size)
+{
+       return -EOPNOTSUPP;
+}
+
 #endif /* CONFIG_IMA */
 
 #ifdef CONFIG_IMA_APPRAISE
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b70ada0..fd703b8 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/ima.h>
 #include <crypto/hash.h>
 #include <crypto/sha.h>
 #include <linux/syscalls.h>
@@ -181,15 +182,17 @@ kimage_file_prepare_segments(struct kimage *image, int 
kernel_fd, int initrd_fd,
        int ret = 0;
        void *ldata;
 
-       ret = copy_file_from_fd(kernel_fd, &image->kernel_buf,
-                               &image->kernel_buf_len);
+       ret = ima_read_file_from_fd(kernel_fd, &image->kernel_buf,
+                                   &image->kernel_buf_len);
+       if (ret == -EOPNOTSUPP)
+               ret = copy_file_from_fd(kernel_fd, &image->kernel_buf,
+                                       &image->kernel_buf_len);
        if (ret)
                return ret;
 
        /* Call arch image probe handlers */
        ret = arch_kexec_kernel_image_probe(image, image->kernel_buf,
                                            image->kernel_buf_len);
-
        if (ret)
                goto out;
 
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index d46bc6b..3389bb3 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -106,7 +106,8 @@ int ima_fs_init(void);
 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_file_hash(struct file *file, struct ima_digest_data *hash,
+                      void **buf, unsigned long *buf_len);
 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);
@@ -142,6 +143,8 @@ int ima_get_action(struct inode *inode, int mask, int 
function);
 int ima_must_measure(struct inode *inode, int mask, int function);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file, enum hash_algo algo);
+int ima_collected_measurement(struct integrity_iint_cache *iint,
+                           struct file *file, struct ima_digest_data *hash);
 void ima_store_measurement(struct integrity_iint_cache *iint, struct file 
*file,
                           const unsigned char *filename,
                           struct evm_ima_xattr_data *xattr_value,
@@ -156,7 +159,7 @@ 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 };
+enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, 
FIRMWARE_CHECK, KEXEC_CHECK, POST_SETATTR };
 
 int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                     int flags);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index e7c7a5d..2187cb4 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -210,7 +210,7 @@ int ima_collect_measurement(struct integrity_iint_cache 
*iint,
 
                hash.hdr.algo = algo;
 
-               result = ima_calc_file_hash(file, &hash.hdr);
+               result = ima_calc_file_hash(file, &hash.hdr, NULL, NULL);
                if (!result) {
                        int length = sizeof(hash.hdr) + hash.hdr.length;
                        void *tmpbuf = krealloc(iint->ima_hash, length,
@@ -233,6 +233,39 @@ out:
 }
 
 /*
+ * ima_collected_measurement - save the "collected" file hash
+ *
+ * The file hash has already been "collected".  Save the file hash
+ * in the iint and clear the measured/appraised flags.
+ */
+int ima_collected_measurement(struct integrity_iint_cache *iint,
+                             struct file *file, struct ima_digest_data *hash)
+{
+       const char *audit_cause = "failed";
+       const char *filename = file->f_path.dentry->d_name.name;
+       int length = sizeof(struct ima_digest_data) + hash->length;
+       int result = -ENOMEM;
+       void *tmpbuf = krealloc(iint->ima_hash, length, GFP_KERNEL);
+
+       if (!tmpbuf)
+               goto out;
+
+       iint->ima_hash = tmpbuf;
+       memcpy(iint->ima_hash, hash, length);
+       iint->version = file_inode(file)->i_version;
+       iint->flags |= IMA_COLLECTED;
+       result = 0;
+
+out:
+       if (result)
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA,
+                                   d_inode(file->f_path.dentry),
+                                   filename, "collected_data", audit_cause,
+                                   result, 0);
+       return result;
+}
+
+/*
  * ima_store_measurement - store file measurement
  *
  * Create an "ima" template and then store the template by calling
diff --git a/security/integrity/ima/ima_appraise.c 
b/security/integrity/ima/ima_appraise.c
index 9c2b46b..c74c1de 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -78,6 +78,8 @@ enum integrity_status ima_get_cache_status(struct 
integrity_iint_cache *iint,
                return iint->ima_module_status;
        case FIRMWARE_CHECK:
                return iint->ima_firmware_status;
+       case KEXEC_CHECK:
+               return iint->ima_kexec_status;
        case FILE_CHECK:
        default:
                return iint->ima_file_status;
@@ -100,6 +102,9 @@ static void ima_set_cache_status(struct 
integrity_iint_cache *iint,
        case FIRMWARE_CHECK:
                iint->ima_firmware_status = status;
                break;
+       case KEXEC_CHECK:
+               iint->ima_kexec_status = status;
+               break;
        case FILE_CHECK:
        default:
                iint->ima_file_status = status;
@@ -122,6 +127,9 @@ static void ima_cache_flags(struct integrity_iint_cache 
*iint, int func)
        case FIRMWARE_CHECK:
                iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED);
                break;
+       case KEXEC_CHECK:
+               iint->flags |= (IMA_KEXEC_APPRAISED | IMA_APPRAISED);
+               break;
        case FILE_CHECK:
        default:
                iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
diff --git a/security/integrity/ima/ima_crypto.c 
b/security/integrity/ima/ima_crypto.c
index fb30ce4..fe1ed2e2 100644
--- a/security/integrity/ima/ima_crypto.c
+++ b/security/integrity/ima/ima_crypto.c
@@ -23,6 +23,7 @@
 #include <linux/scatterlist.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <crypto/hash.h>
 
 #include "ima.h"
@@ -352,6 +353,7 @@ static int ima_calc_file_ahash(struct file *file, struct 
ima_digest_data *hash)
 
 static int ima_calc_file_hash_tfm(struct file *file,
                                  struct ima_digest_data *hash,
+                                 void **buf, unsigned long *buf_len,
                                  struct crypto_shash *tfm)
 {
        loff_t i_size, offset = 0;
@@ -373,7 +375,7 @@ static int ima_calc_file_hash_tfm(struct file *file,
        if (i_size == 0)
                goto out;
 
-       rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+       rbuf = !buf ? kzalloc(PAGE_SIZE, GFP_KERNEL) : vmalloc(i_size);
        if (!rbuf)
                return -ENOMEM;
 
@@ -385,29 +387,45 @@ static int ima_calc_file_hash_tfm(struct file *file,
        while (offset < i_size) {
                int rbuf_len;
 
-               rbuf_len = integrity_kernel_read(file, offset, rbuf, PAGE_SIZE);
+               rbuf_len = integrity_kernel_read(file, offset,
+                                                !buf ? rbuf : rbuf + offset,
+                                                PAGE_SIZE);
                if (rbuf_len < 0) {
                        rc = rbuf_len;
                        break;
                }
                if (rbuf_len == 0)
                        break;
-               offset += rbuf_len;
 
-               rc = crypto_shash_update(shash, rbuf, rbuf_len);
+               rc = crypto_shash_update(shash, !buf ? rbuf : rbuf + offset,
+                                        rbuf_len);
+               offset += rbuf_len;
                if (rc)
                        break;
        }
        if (read)
                file->f_mode &= ~FMODE_READ;
-       kfree(rbuf);
+       
+       if (!buf)
+               kfree(rbuf);
+       else {
+               *buf = rbuf;
+               *buf_len = i_size;
+       }
+
 out:
        if (!rc)
                rc = crypto_shash_final(shash, hash->digest);
        return rc;
 }
 
-static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash)
+/*
+ * ima_calc_file_shash calculates a file hash.  When the caller specfies
+ * a buffer, read the file data into memory, calculating the file hash,
+ * and return the buffer.
+ */
+static int ima_calc_file_shash(struct file *file, struct ima_digest_data *hash,
+                              void **buf, unsigned long *buf_len)
 {
        struct crypto_shash *tfm;
        int rc;
@@ -416,7 +434,7 @@ static int ima_calc_file_shash(struct file *file, struct 
ima_digest_data *hash)
        if (IS_ERR(tfm))
                return PTR_ERR(tfm);
 
-       rc = ima_calc_file_hash_tfm(file, hash, tfm);
+       rc = ima_calc_file_hash_tfm(file, hash, buf, buf_len, tfm);
 
        ima_free_tfm(tfm);
 
@@ -435,21 +453,25 @@ static int ima_calc_file_shash(struct file *file, struct 
ima_digest_data *hash)
  * If the ima.ahash_minsize parameter is not specified, this function uses
  * shash for the hash calculation.  If ahash fails, it falls back to using
  * shash.
+ * 
+ * When returning the file data to the caller, use shash to calculate the
+ * file hash.
  */
-int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash)
+int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash,
+                      void **buf, unsigned long *buf_len)
 {
        loff_t i_size;
        int rc;
 
        i_size = i_size_read(file_inode(file));
 
-       if (ima_ahash_minsize && i_size >= ima_ahash_minsize) {
+       if (ima_ahash_minsize && i_size >= ima_ahash_minsize && !buf) {
                rc = ima_calc_file_ahash(file, hash);
                if (!rc)
                        return 0;
        }
 
-       return ima_calc_file_shash(file, hash);
+       return ima_calc_file_shash(file, hash, buf, buf_len);
 }
 
 /*
diff --git a/security/integrity/ima/ima_main.c 
b/security/integrity/ima/ima_main.c
index d9fc463..d7e04bb 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -154,7 +154,7 @@ void ima_file_free(struct file *file)
 }
 
 static int process_measurement(struct file *file, int mask, int function,
-                              int opened)
+                              struct ima_digest_data *hash, int opened)
 {
        struct inode *inode = file_inode(file);
        struct integrity_iint_cache *iint = NULL;
@@ -193,6 +193,11 @@ static int process_measurement(struct file *file, int 
mask, int function,
                if (!iint)
                        goto out;
        }
+       if (hash) {
+               rc = ima_collected_measurement(iint, file, hash);
+               if (rc)
+                       goto out;
+       }
 
        if (violation_check) {
                ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
@@ -273,7 +278,7 @@ 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, MAY_EXEC, MMAP_CHECK, NULL, 0);
        return 0;
 }
 
@@ -292,7 +297,7 @@ 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, MAY_EXEC, BPRM_CHECK, NULL, 0);
 }
 
 /**
@@ -309,7 +314,7 @@ int ima_file_check(struct file *file, int mask, int opened)
 {
        return process_measurement(file,
                                   mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
-                                  FILE_CHECK, opened);
+                                  FILE_CHECK, NULL, opened);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
@@ -332,7 +337,31 @@ 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 process_measurement(file, MAY_EXEC, MODULE_CHECK, NULL, 0);
+}
+
+static int ima_read_and_process_file(struct file *file, enum ima_hooks func,
+                                    void **buf, size_t *buf_len)
+{
+       struct evm_ima_xattr_data *xattr_value = NULL;
+       struct ima_template_desc *template_desc = ima_template_desc_current();
+       int xattr_len = 0;
+       struct {
+               struct ima_digest_data hdr;
+               char digest[IMA_MAX_DIGEST_SIZE];
+       } hash;
+       int ret;
+
+       template_desc = ima_template_desc_current();
+       if (strcmp(template_desc->name, IMA_TEMPLATE_IMA_NAME) != 0)
+               xattr_len = ima_read_xattr(file->f_path.dentry, &xattr_value);
+
+       hash.hdr.algo = ima_get_hash_algo(xattr_value, xattr_len);
+       ret = ima_calc_file_hash(file, &hash.hdr, buf, buf_len);
+       if (!ret)
+               ret = process_measurement(file, MAY_READ, func, &hash.hdr, 0);
+       kfree(xattr_value);
+       return ret;
 }
 
 int ima_fw_from_file(struct file *file, char *buf, size_t size)
@@ -343,7 +372,20 @@ int ima_fw_from_file(struct file *file, char *buf, size_t 
size)
                        return -EACCES; /* INTEGRITY_UNKNOWN */
                return 0;
        }
-       return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, 0);
+       return process_measurement(file, MAY_EXEC, FIRMWARE_CHECK, NULL, 0);
+}
+
+int ima_read_file_from_fd(int fd, void **buf, size_t *buf_len)
+{
+       struct fd f = fdget(fd);
+       int ret;
+
+       if (!f.file)
+               return -EBADF;
+
+       ret = ima_read_and_process_file(f.file, KEXEC_CHECK, buf, buf_len);
+       fdput(f);
+       return ret;
 }
 
 static int __init init_ima(void)
diff --git a/security/integrity/ima/ima_policy.c 
b/security/integrity/ima/ima_policy.c
index 3997e20..008693c 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -304,6 +304,8 @@ static int get_subaction(struct ima_rule_entry *rule, int 
func)
                return IMA_MODULE_APPRAISE;
        case FIRMWARE_CHECK:
                return IMA_FIRMWARE_APPRAISE;
+       case KEXEC_CHECK:
+               return IMA_KEXEC_APPRAISE;
        case FILE_CHECK:
        default:
                return IMA_FILE_APPRAISE;
@@ -579,6 +581,8 @@ 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_CHECK") == 0)
+                               entry->func = KEXEC_CHECK;
                        else
                                result = -EINVAL;
                        if (!result)
diff --git a/security/integrity/ima/ima_template_lib.c 
b/security/integrity/ima/ima_template_lib.c
index f9bae04..79e07c7 100644
--- a/security/integrity/ima/ima_template_lib.c
+++ b/security/integrity/ima/ima_template_lib.c
@@ -224,7 +224,7 @@ int ima_eventdigest_init(struct ima_event_data *event_data,
        inode = file_inode(event_data->file);
        hash.hdr.algo = ima_template_hash_algo_allowed(ima_hash_algo) ?
            ima_hash_algo : HASH_ALGO_SHA1;
-       result = ima_calc_file_hash(event_data->file, &hash.hdr);
+       result = ima_calc_file_hash(event_data->file, &hash.hdr, NULL, NULL);
        if (result) {
                integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
                                    event_data->filename, "collect_data",
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 5efe2ec..56c571e 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -49,12 +49,14 @@
 #define IMA_MODULE_APPRAISED   0x00008000
 #define IMA_FIRMWARE_APPRAISE  0x00010000
 #define IMA_FIRMWARE_APPRAISED 0x00020000
+#define IMA_KEXEC_APPRAISE     0x00040000
+#define IMA_KEXEC_APPRAISED    0x00080000
 #define IMA_APPRAISE_SUBMASK   (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
                                 IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \
-                                IMA_FIRMWARE_APPRAISE)
+                                IMA_FIRMWARE_APPRAISE | IMA_KEXEC_APPRAISE)
 #define IMA_APPRAISED_SUBMASK  (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
                                 IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \
-                                IMA_FIRMWARE_APPRAISED)
+                                IMA_FIRMWARE_APPRAISED | IMA_KEXEC_APPRAISED)
 
 enum evm_ima_xattr_type {
        IMA_XATTR_DIGEST = 0x01,
@@ -111,6 +113,7 @@ struct integrity_iint_cache {
        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_kexec_status:4;
        enum integrity_status evm_status:4;
        struct ima_digest_data *ima_hash;
 };
-- 
2.1.0

--
To unsubscribe from this list: send the line "unsubscribe 
linux-security-module" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to