IMA: use vfs_getattr_nosec to get the i_version

IMA currently accesses the i_version out of the inode directly when it
does a measurement. This is fine for most simple filesystems, but can be
problematic with more complex setups (e.g. overlayfs).

Make IMA instead call vfs_getattr_nosec to get this info. This allows
the filesystem to determine whether and how to report the i_version, and
should allow IMA to work properly with a broader class of filesystems in
the future.

Reported-and-Tested-by: Stefan Berger <stefanb@linux.ibm.com>
Reviewed-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
This commit is contained in:
Jeff Layton 2023-04-17 12:55:51 -04:00 committed by Mimi Zohar
parent f1fcbaa18b
commit db1d1e8b98
2 changed files with 14 additions and 7 deletions

View File

@ -13,7 +13,6 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/evm.h> #include <linux/evm.h>
#include <linux/iversion.h>
#include <linux/fsverity.h> #include <linux/fsverity.h>
#include "ima.h" #include "ima.h"
@ -246,10 +245,11 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
const char *filename = file->f_path.dentry->d_name.name; const char *filename = file->f_path.dentry->d_name.name;
struct ima_max_digest_data hash; struct ima_max_digest_data hash;
struct kstat stat;
int result = 0; int result = 0;
int length; int length;
void *tmpbuf; void *tmpbuf;
u64 i_version; u64 i_version = 0;
/* /*
* Always collect the modsig, because IMA might have already collected * Always collect the modsig, because IMA might have already collected
@ -268,7 +268,10 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
* to an initial measurement/appraisal/audit, but was modified to * to an initial measurement/appraisal/audit, but was modified to
* assume the file changed. * assume the file changed.
*/ */
i_version = inode_query_iversion(inode); result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT);
if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
i_version = stat.change_cookie;
hash.hdr.algo = algo; hash.hdr.algo = algo;
hash.hdr.length = hash_digest_size[algo]; hash.hdr.length = hash_digest_size[algo];

View File

@ -24,7 +24,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <linux/ima.h> #include <linux/ima.h>
#include <linux/iversion.h>
#include <linux/fs.h> #include <linux/fs.h>
#include "ima.h" #include "ima.h"
@ -164,11 +163,16 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
mutex_lock(&iint->mutex); mutex_lock(&iint->mutex);
if (atomic_read(&inode->i_writecount) == 1) { if (atomic_read(&inode->i_writecount) == 1) {
struct kstat stat;
update = test_and_clear_bit(IMA_UPDATE_XATTR, update = test_and_clear_bit(IMA_UPDATE_XATTR,
&iint->atomic_flags); &iint->atomic_flags);
if (!IS_I_VERSION(inode) || if ((iint->flags & IMA_NEW_FILE) ||
!inode_eq_iversion(inode, iint->version) || vfs_getattr_nosec(&file->f_path, &stat,
(iint->flags & IMA_NEW_FILE)) { STATX_CHANGE_COOKIE,
AT_STATX_SYNC_AS_STAT) ||
!(stat.result_mask & STATX_CHANGE_COOKIE) ||
stat.change_cookie != iint->version) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0; iint->measured_pcrs = 0;
if (update) if (update)