]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - security/integrity/ima/ima_main.c
ima: re-introduce own integrity cache lock
[mirror_ubuntu-bionic-kernel.git] / security / integrity / ima / ima_main.c
index 095af9646aff0ce85c839ac17f362de5c9d9c874..7e6b81eb7613579d70656ea74ee7694812ee7e46 100644 (file)
@@ -99,10 +99,13 @@ static void ima_rdwr_violation_check(struct file *file,
                        if (!iint)
                                iint = integrity_iint_find(inode);
                        /* IMA_MEASURE is set from reader side */
-                       if (iint && (iint->flags & IMA_MEASURE))
+                       if (iint && test_bit(IMA_MUST_MEASURE,
+                                               &iint->atomic_flags))
                                send_tomtou = true;
                }
        } else {
+               if (must_measure)
+                       set_bit(IMA_MUST_MEASURE, &iint->atomic_flags);
                if ((atomic_read(&inode->i_writecount) > 0) && must_measure)
                        send_writers = true;
        }
@@ -124,21 +127,24 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
                                  struct inode *inode, struct file *file)
 {
        fmode_t mode = file->f_mode;
+       bool update;
 
        if (!(mode & FMODE_WRITE))
                return;
 
-       inode_lock(inode);
+       mutex_lock(&iint->mutex);
        if (atomic_read(&inode->i_writecount) == 1) {
+               update = test_and_clear_bit(IMA_UPDATE_XATTR,
+                                           &iint->atomic_flags);
                if ((iint->version != inode->i_version) ||
                    (iint->flags & IMA_NEW_FILE)) {
                        iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
                        iint->measured_pcrs = 0;
-                       if (iint->flags & IMA_APPRAISE)
+                       if (update)
                                ima_update_xattr(iint, file);
                }
        }
-       inode_unlock(inode);
+       mutex_unlock(&iint->mutex);
 }
 
 /**
@@ -171,7 +177,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
        char *pathbuf = NULL;
        char filename[NAME_MAX];
        const char *pathname = NULL;
-       int rc = -ENOMEM, action, must_appraise;
+       int rc = 0, action, must_appraise = 0;
        int pcr = CONFIG_IMA_MEASURE_PCR_IDX;
        struct evm_ima_xattr_data *xattr_value = NULL;
        int xattr_len = 0;
@@ -202,17 +208,31 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
        if (action) {
                iint = integrity_inode_get(inode);
                if (!iint)
-                       goto out;
+                       rc = -ENOMEM;
        }
 
-       if (violation_check) {
+       if (!rc && violation_check)
                ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
                                         &pathbuf, &pathname);
-               if (!action) {
-                       rc = 0;
-                       goto out_free;
-               }
-       }
+
+       inode_unlock(inode);
+
+       if (rc)
+               goto out;
+       if (!action)
+               goto out;
+
+       mutex_lock(&iint->mutex);
+
+       if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
+               /* reset appraisal flags if ima_inode_post_setattr was called */
+               iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+                                IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+                                IMA_ACTION_FLAGS);
+
+       if (test_and_clear_bit(IMA_CHANGE_XATTR, &iint->atomic_flags))
+               /* reset all flags if ima_inode_setxattr was called */
+               iint->flags &= ~IMA_DONE_MASK;
 
        /* Determine if already appraised/measured based on bitmask
         * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
@@ -230,7 +250,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
        if (!action) {
                if (must_appraise)
                        rc = ima_get_cache_status(iint, func);
-               goto out_digsig;
+               goto out_locked;
        }
 
        template_desc = ima_template_desc_current();
@@ -243,7 +263,7 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
 
        rc = ima_collect_measurement(iint, file, buf, size, hash_algo);
        if (rc != 0 && rc != -EBADF && rc != -EINVAL)
-               goto out_digsig;
+               goto out_locked;
 
        if (!pathbuf)   /* ima_rdwr_violation possibly pre-fetched */
                pathname = ima_d_path(&file->f_path, &pathbuf, filename);
@@ -251,26 +271,32 @@ static int process_measurement(struct file *file, char *buf, loff_t size,
        if (action & IMA_MEASURE)
                ima_store_measurement(iint, file, pathname,
                                      xattr_value, xattr_len, pcr);
-       if (rc == 0 && (action & IMA_APPRAISE_SUBMASK))
+       if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
+               inode_lock(inode);
                rc = ima_appraise_measurement(func, iint, file, pathname,
                                              xattr_value, xattr_len, opened);
+               inode_unlock(inode);
+       }
        if (action & IMA_AUDIT)
                ima_audit_measurement(iint, pathname);
 
        if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
                rc = 0;
-out_digsig:
-       if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG) &&
+out_locked:
+       if ((mask & MAY_WRITE) && test_bit(IMA_DIGSIG, &iint->atomic_flags) &&
             !(iint->flags & IMA_NEW_FILE))
                rc = -EACCES;
+       mutex_unlock(&iint->mutex);
        kfree(xattr_value);
-out_free:
+out:
        if (pathbuf)
                __putname(pathbuf);
-out:
-       inode_unlock(inode);
-       if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
-               return -EACCES;
+       if (must_appraise) {
+               if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
+                       return -EACCES;
+               if (file->f_mode & FMODE_WRITE)
+                       set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+       }
        return 0;
 }