Commit 18b44bc5a672 ("ovl: Always reevaluate the file signature for
IMA") forced signature re-evaulation on every file access.

Instead of always re-evaluating the file's integrity, detect a change
to the backing file, by comparing the cached file metadata with the
backing file's metadata.  Verifying just the i_version has not changed
is insufficient.  In addition save and compare the i_ino and i_rdev
as well.

Signed-off-by: Mimi Zohar <[email protected]>
---
Changelog:
- Changes made based on Amir's review: removal of unnecessary overlay
magic test, verify i_version, i_ino and i_rdev haven't changed.

 fs/overlayfs/super.c              |  2 +-
 security/integrity/ima/ima_api.c  |  4 ++++
 security/integrity/ima/ima_main.c | 16 +++++++++++++++-
 security/integrity/integrity.h    |  2 ++
 4 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c
index 3fa2416264a4..c71d185980c0 100644
--- a/fs/overlayfs/super.c
+++ b/fs/overlayfs/super.c
@@ -1489,7 +1489,7 @@ int ovl_fill_super(struct super_block *sb, struct 
fs_context *fc)
                ovl_trusted_xattr_handlers;
        sb->s_fs_info = ofs;
        sb->s_flags |= SB_POSIXACL;
-       sb->s_iflags |= SB_I_SKIP_SYNC | SB_I_IMA_UNVERIFIABLE_SIGNATURE;
+       sb->s_iflags |= SB_I_SKIP_SYNC;
 
        err = -ENOMEM;
        root_dentry = ovl_get_root(sb, ctx->upper.dentry, oe);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 452e80b541e5..f191bdcceef8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -243,6 +243,7 @@ int ima_collect_measurement(struct integrity_iint_cache 
*iint,
 {
        const char *audit_cause = "failed";
        struct inode *inode = file_inode(file);
+       struct inode *real_inode = d_real_inode(file_dentry(file));
        const char *filename = file->f_path.dentry->d_name.name;
        struct ima_max_digest_data hash;
        struct kstat stat;
@@ -272,6 +273,7 @@ int ima_collect_measurement(struct integrity_iint_cache 
*iint,
                                   AT_STATX_SYNC_AS_STAT);
        if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
                i_version = stat.change_cookie;
+
        hash.hdr.algo = algo;
        hash.hdr.length = hash_digest_size[algo];
 
@@ -302,6 +304,8 @@ int ima_collect_measurement(struct integrity_iint_cache 
*iint,
        iint->ima_hash = tmpbuf;
        memcpy(iint->ima_hash, &hash, length);
        iint->version = i_version;
+       iint->real_ino = real_inode->i_ino;
+       iint->real_rdev = real_inode->i_rdev;
 
        /* Possibly temporary failure due to type of read (eg. O_DIRECT) */
        if (!result)
diff --git a/security/integrity/ima/ima_main.c 
b/security/integrity/ima/ima_main.c
index 365db0e43d7c..4a6a22f8805b 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -25,6 +25,7 @@
 #include <linux/xattr.h>
 #include <linux/ima.h>
 #include <linux/fs.h>
+#include <linux/iversion.h>
 
 #include "ima.h"
 
@@ -207,7 +208,7 @@ static int process_measurement(struct file *file, const 
struct cred *cred,
                               u32 secid, char *buf, loff_t size, int mask,
                               enum ima_hooks func)
 {
-       struct inode *inode = file_inode(file);
+       struct inode *backing_inode, *inode = file_inode(file);
        struct integrity_iint_cache *iint = NULL;
        struct ima_template_desc *template_desc = NULL;
        char *pathbuf = NULL;
@@ -284,6 +285,19 @@ static int process_measurement(struct file *file, const 
struct cred *cred,
                iint->measured_pcrs = 0;
        }
 
+       /* Detect and re-evaluate changes made to the backing file. */
+       backing_inode = d_real_inode(file_dentry(file));
+       if (backing_inode != inode &&
+           (action & IMA_DO_MASK) && (iint->flags & IMA_DONE_MASK)) {
+               if (!IS_I_VERSION(backing_inode) ||
+                   backing_inode->i_rdev != iint->real_rdev ||
+                   backing_inode->i_ino != iint->real_ino ||
+                   !inode_eq_iversion(backing_inode, iint->version)) {
+                       iint->flags &= ~IMA_DONE_MASK;
+                       iint->measured_pcrs = 0;
+               }
+       }
+
        /* Determine if already appraised/measured based on bitmask
         * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
         *  IMA_AUDIT, IMA_AUDITED)
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index d7553c93f5c0..dd2bb2d150f6 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -164,6 +164,8 @@ struct integrity_iint_cache {
        unsigned long flags;
        unsigned long measured_pcrs;
        unsigned long atomic_flags;
+       unsigned long real_ino;
+       dev_t real_rdev;
        enum integrity_status ima_file_status:4;
        enum integrity_status ima_mmap_status:4;
        enum integrity_status ima_bprm_status:4;
-- 
2.39.3

Reply via email to