Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8eb988c7 authored by Mimi Zohar's avatar Mimi Zohar Committed by Al Viro
Browse files

fix ima breakage



The "Untangling ima mess, part 2 with counters" patch messed
up the counters.  Based on conversations with Al Viro, this patch
streamlines ima_path_check() by removing the counter maintaince.
The counters are now updated independently, from measuring the file,
in __dentry_open() and alloc_file() by calling ima_counts_get().
ima_path_check() is called from nfsd and do_filp_open().
It also did not measure all files that should have been measured.
Reason: ima_path_check() got bogus value passed as mask.
[AV: mea culpa]
[AV: add missing nfsd bits]

Signed-off-by: default avatarMimi Zohar <zohar@us.ibm.com>
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 1e41568d
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -1736,8 +1736,7 @@ do_last:
		if (nd.root.mnt)
			path_put(&nd.root);
		if (!IS_ERR(filp)) {
			error = ima_path_check(&filp->f_path, filp->f_mode &
				       (MAY_READ | MAY_WRITE | MAY_EXEC));
			error = ima_path_check(filp, acc_mode);
			if (error) {
				fput(filp);
				filp = ERR_PTR(error);
@@ -1797,8 +1796,7 @@ ok:
	}
	filp = nameidata_to_filp(&nd);
	if (!IS_ERR(filp)) {
		error = ima_path_check(&filp->f_path, filp->f_mode &
			       (MAY_READ | MAY_WRITE | MAY_EXEC));
		error = ima_path_check(filp, acc_mode);
		if (error) {
			fput(filp);
			filp = ERR_PTR(error);
+1 −2
Original line number Diff line number Diff line
@@ -752,8 +752,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
			    flags, current_cred());
	if (IS_ERR(*filp))
		host_err = PTR_ERR(*filp);
	host_err = ima_path_check(&(*filp)->f_path,
				  access & (MAY_READ | MAY_WRITE | MAY_EXEC));
	host_err = ima_path_check(*filp, access);
out_nfserr:
	err = nfserrno(host_err);
out:
+2 −2
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@ struct linux_binprm;
extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_inode_alloc(struct inode *inode);
extern void ima_inode_free(struct inode *inode);
extern int ima_path_check(struct path *path, int mask);
extern int ima_path_check(struct file *file, int mask);
extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot);
extern void ima_counts_get(struct file *file);
@@ -38,7 +38,7 @@ static inline void ima_inode_free(struct inode *inode)
	return;
}

static inline int ima_path_check(struct path *path, int mask)
static inline int ima_path_check(struct file *file, int mask)
{
	return 0;
}
+92 −144
Original line number Diff line number Diff line
@@ -84,6 +84,36 @@ out:
	return found;
}

/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
 *
 * When opening a file for read, if the file is already open for write,
 * the file could change, resulting in a file measurement error.
 *
 * Opening a file for write, if the file is already open for read, results
 * in a time of measure, time of use (ToMToU) error.
 *
 * In either case invalidate the PCR.
 */
enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
static void ima_read_write_check(enum iint_pcr_error error,
				 struct ima_iint_cache *iint,
				 struct inode *inode,
				 const unsigned char *filename)
{
	switch (error) {
	case TOMTOU:
		if (iint->readcount > 0)
			ima_add_violation(inode, filename, "invalid_pcr",
					  "ToMToU");
		break;
	case OPEN_WRITERS:
		if (iint->writecount > 0)
			ima_add_violation(inode, filename, "invalid_pcr",
					  "open_writers");
		break;
	}
}

/*
 * Update the counts given an fmode_t
 */
@@ -98,6 +128,47 @@ static void ima_inc_counts(struct ima_iint_cache *iint, fmode_t mode)
		iint->writecount++;
}

/*
 * ima_counts_get - increment file counts
 *
 * Maintain read/write counters for all files, but only
 * invalidate the PCR for measured files:
 * 	- Opening a file for write when already open for read,
 *	  results in a time of measure, time of use (ToMToU) error.
 *	- Opening a file for read when already open for write,
 * 	  could result in a file measurement error.
 *
 */
void ima_counts_get(struct file *file)
{
	struct dentry *dentry = file->f_path.dentry;
	struct inode *inode = dentry->d_inode;
	fmode_t mode = file->f_mode;
	struct ima_iint_cache *iint;
	int rc;

	if (!ima_initialized || !S_ISREG(inode->i_mode))
		return;
	iint = ima_iint_find_get(inode);
	if (!iint)
		return;
	mutex_lock(&iint->mutex);
	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
	if (rc < 0)
		goto out;

	if (mode & FMODE_WRITE) {
		ima_read_write_check(TOMTOU, iint, inode, dentry->d_name.name);
		goto out;
	}
	ima_read_write_check(OPEN_WRITERS, iint, inode, dentry->d_name.name);
out:
	ima_inc_counts(iint, file->f_mode);
	mutex_unlock(&iint->mutex);

	kref_put(&iint->refcount, iint_free);
}

/*
 * Decrement ima counts
 */
@@ -153,123 +224,6 @@ void ima_file_free(struct file *file)
	kref_put(&iint->refcount, iint_free);
}

/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
 *
 * When opening a file for read, if the file is already open for write,
 * the file could change, resulting in a file measurement error.
 *
 * Opening a file for write, if the file is already open for read, results
 * in a time of measure, time of use (ToMToU) error.
 *
 * In either case invalidate the PCR.
 */
enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
static void ima_read_write_check(enum iint_pcr_error error,
				 struct ima_iint_cache *iint,
				 struct inode *inode,
				 const unsigned char *filename)
{
	switch (error) {
	case TOMTOU:
		if (iint->readcount > 0)
			ima_add_violation(inode, filename, "invalid_pcr",
					  "ToMToU");
		break;
	case OPEN_WRITERS:
		if (iint->writecount > 0)
			ima_add_violation(inode, filename, "invalid_pcr",
					  "open_writers");
		break;
	}
}

static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
				const unsigned char *filename)
{
	int rc = 0;

	ima_inc_counts(iint, file->f_mode);

	rc = ima_collect_measurement(iint, file);
	if (!rc)
		ima_store_measurement(iint, file, filename);
	return rc;
}

/**
 * ima_path_check - based on policy, collect/store measurement.
 * @path: contains a pointer to the path to be measured
 * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
 *
 * Measure the file being open for readonly, based on the
 * ima_must_measure() policy decision.
 *
 * Keep read/write counters for all files, but only
 * invalidate the PCR for measured files:
 * 	- Opening a file for write when already open for read,
 *	  results in a time of measure, time of use (ToMToU) error.
 *	- Opening a file for read when already open for write,
 * 	  could result in a file measurement error.
 *
 * Always return 0 and audit dentry_open failures.
 * (Return code will be based upon measurement appraisal.)
 */
int ima_path_check(struct path *path, int mask)
{
	struct inode *inode = path->dentry->d_inode;
	struct ima_iint_cache *iint;
	struct file *file = NULL;
	int rc;

	if (!ima_initialized || !S_ISREG(inode->i_mode))
		return 0;
	iint = ima_iint_find_get(inode);
	if (!iint)
		return 0;

	mutex_lock(&iint->mutex);

	rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
	if (rc < 0)
		goto out;

	if ((mask & MAY_WRITE) || (mask == 0))
		ima_read_write_check(TOMTOU, iint, inode,
				     path->dentry->d_name.name);

	if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
		goto out;

	ima_read_write_check(OPEN_WRITERS, iint, inode,
			     path->dentry->d_name.name);
	if (!(iint->flags & IMA_MEASURED)) {
		struct dentry *dentry = dget(path->dentry);
		struct vfsmount *mnt = mntget(path->mnt);

		file = dentry_open(dentry, mnt, O_RDONLY | O_LARGEFILE,
				   current_cred());
		if (IS_ERR(file)) {
			int audit_info = 0;

			integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
					    dentry->d_name.name,
					    "add_measurement",
					    "dentry_open failed",
					    1, audit_info);
			file = NULL;
			goto out;
		}
		rc = get_path_measurement(iint, file, dentry->d_name.name);
	}
out:
	mutex_unlock(&iint->mutex);
	if (file)
		fput(file);
	kref_put(&iint->refcount, iint_free);
	return 0;
}
EXPORT_SYMBOL_GPL(ima_path_check);

static int process_measurement(struct file *file, const unsigned char *filename,
			       int mask, int function)
{
@@ -297,33 +251,6 @@ out:
	return rc;
}

/*
 * ima_counts_get - increment file counts
 *
 * - for IPC shm and shmat file.
 * - for nfsd exported files.
 *
 * Increment the counts for these files to prevent unnecessary
 * imbalance messages.
 */
void ima_counts_get(struct file *file)
{
	struct inode *inode = file->f_dentry->d_inode;
	struct ima_iint_cache *iint;

	if (!ima_initialized || !S_ISREG(inode->i_mode))
		return;
	iint = ima_iint_find_get(inode);
	if (!iint)
		return;
	mutex_lock(&iint->mutex);
	ima_inc_counts(iint, file->f_mode);
	mutex_unlock(&iint->mutex);

	kref_put(&iint->refcount, iint_free);
}
EXPORT_SYMBOL_GPL(ima_counts_get);

/**
 * ima_file_mmap - based on policy, collect/store measurement.
 * @file: pointer to the file to be measured (May be NULL)
@@ -369,6 +296,27 @@ int ima_bprm_check(struct linux_binprm *bprm)
	return 0;
}

/**
 * ima_path_check - based on policy, collect/store measurement.
 * @file: pointer to the file to be measured
 * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
 *
 * Measure files based on the ima_must_measure() policy decision.
 *
 * Always return 0 and audit dentry_open failures.
 * (Return code will be based upon measurement appraisal.)
 */
int ima_path_check(struct file *file, int mask)
{
	int rc;

	rc = process_measurement(file, file->f_dentry->d_name.name,
				 mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
				 PATH_CHECK);
	return 0;
}
EXPORT_SYMBOL_GPL(ima_path_check);

static int __init init_ima(void)
{
	int error;