Commit 4de2f084 authored by Roberto Sassu's avatar Roberto Sassu Committed by Paul Moore
Browse files

ima: Make it independent from 'integrity' LSM



Make the 'ima' LSM independent from the 'integrity' LSM by introducing IMA
own integrity metadata (ima_iint_cache structure, with IMA-specific fields
from the integrity_iint_cache structure), and by managing it directly from
the 'ima' LSM.

Create ima_iint.c and introduce the same integrity metadata management
functions found in iint.c (renamed with ima_). However, instead of putting
metadata in an rbtree, reserve space from IMA in the inode security blob
for a pointer, and introduce the ima_inode_set_iint()/ima_inode_get_iint()
primitives to store/retrieve that pointer. This improves search time from
logarithmic to constant.

Consequently, don't include the inode pointer as field in the
ima_iint_cache structure, since the association with the inode is clear.
Since the inode field is missing in ima_iint_cache, pass the extra inode
parameter to ima_get_verity_digest().

Prefer storing the pointer instead of the entire ima_iint_cache structure,
to avoid too much memory pressure. Use the same mechanism as before, a
cache named ima_iint_cache (renamed from iint_cache), to quickly allocate
a new ima_iint_cache structure when requested by the IMA policy.

Create the new ima_iint_cache in ima_iintcache_init(),
called by init_ima_lsm(), during the initialization of the 'ima' LSM. And,
register ima_inode_free_security() to free the ima_iint_cache structure, if
exists.

Replace integrity_iint_cache with ima_iint_cache in various places of the
IMA code. Also, replace integrity_inode_get() and integrity_iint_find(),
respectively with ima_inode_get() and ima_iint_find().

Finally, move the remaining IMA-specific flags
to security/integrity/ima/ima.h, since they are now unnecessary in the
common integrity layer.

Signed-off-by: default avatarRoberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: default avatarCasey Schaufler <casey@schaufler-ca.com>
Reviewed-by: default avatarStefan Berger <stefanb@linux.ibm.com>
Reviewed-by: default avatarMimi Zohar <zohar@linux.ibm.com>
Acked-by: default avatarMimi Zohar <zohar@linux.ibm.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 75a323e6
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -4,7 +4,7 @@
# Measurement Architecture(IMA).
#

obj-$(CONFIG_IMA) += ima.o
obj-$(CONFIG_IMA) += ima.o ima_iint.o

ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
	 ima_policy.o ima_template.o ima_template_lib.o
+118 −19
Original line number Diff line number Diff line
@@ -60,7 +60,7 @@ extern const char boot_aggregate_name[];

/* IMA event related data */
struct ima_event_data {
	struct integrity_iint_cache *iint;
	struct ima_iint_cache *iint;
	struct file *file;
	const unsigned char *filename;
	struct evm_ima_xattr_data *xattr_value;
@@ -119,6 +119,107 @@ struct ima_kexec_hdr {
	u64 count;
};

/* IMA iint action cache flags */
#define IMA_MEASURE		0x00000001
#define IMA_MEASURED		0x00000002
#define IMA_APPRAISE		0x00000004
#define IMA_APPRAISED		0x00000008
/*#define IMA_COLLECT		0x00000010  do not use this flag */
#define IMA_COLLECTED		0x00000020
#define IMA_AUDIT		0x00000040
#define IMA_AUDITED		0x00000080
#define IMA_HASH		0x00000100
#define IMA_HASHED		0x00000200

/* IMA iint policy rule cache flags */
#define IMA_NONACTION_FLAGS	0xff000000
#define IMA_DIGSIG_REQUIRED	0x01000000
#define IMA_PERMIT_DIRECTIO	0x02000000
#define IMA_NEW_FILE		0x04000000
#define IMA_FAIL_UNVERIFIABLE_SIGS	0x10000000
#define IMA_MODSIG_ALLOWED	0x20000000
#define IMA_CHECK_BLACKLIST	0x40000000
#define IMA_VERITY_REQUIRED	0x80000000

#define IMA_DO_MASK		(IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
				 IMA_HASH | IMA_APPRAISE_SUBMASK)
#define IMA_DONE_MASK		(IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
				 IMA_HASHED | IMA_COLLECTED | \
				 IMA_APPRAISED_SUBMASK)

/* IMA iint subaction appraise cache flags */
#define IMA_FILE_APPRAISE	0x00001000
#define IMA_FILE_APPRAISED	0x00002000
#define IMA_MMAP_APPRAISE	0x00004000
#define IMA_MMAP_APPRAISED	0x00008000
#define IMA_BPRM_APPRAISE	0x00010000
#define IMA_BPRM_APPRAISED	0x00020000
#define IMA_READ_APPRAISE	0x00040000
#define IMA_READ_APPRAISED	0x00080000
#define IMA_CREDS_APPRAISE	0x00100000
#define IMA_CREDS_APPRAISED	0x00200000
#define IMA_APPRAISE_SUBMASK	(IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
				 IMA_BPRM_APPRAISE | IMA_READ_APPRAISE | \
				 IMA_CREDS_APPRAISE)
#define IMA_APPRAISED_SUBMASK	(IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
				 IMA_BPRM_APPRAISED | IMA_READ_APPRAISED | \
				 IMA_CREDS_APPRAISED)

/* IMA iint cache atomic_flags */
#define IMA_CHANGE_XATTR	0
#define IMA_UPDATE_XATTR	1
#define IMA_CHANGE_ATTR		2
#define IMA_DIGSIG		3
#define IMA_MUST_MEASURE	4

/* IMA integrity metadata associated with an inode */
struct ima_iint_cache {
	struct mutex mutex;	/* protects: version, flags, digest */
	u64 version;		/* track inode changes */
	unsigned long flags;
	unsigned long measured_pcrs;
	unsigned long atomic_flags;
	unsigned long real_ino;
	dev_t real_dev;
	enum integrity_status ima_file_status:4;
	enum integrity_status ima_mmap_status:4;
	enum integrity_status ima_bprm_status:4;
	enum integrity_status ima_read_status:4;
	enum integrity_status ima_creds_status:4;
	struct ima_digest_data *ima_hash;
};

extern struct lsm_blob_sizes ima_blob_sizes;

static inline struct ima_iint_cache *
ima_inode_get_iint(const struct inode *inode)
{
	struct ima_iint_cache **iint_sec;

	if (unlikely(!inode->i_security))
		return NULL;

	iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
	return *iint_sec;
}

static inline void ima_inode_set_iint(const struct inode *inode,
				      struct ima_iint_cache *iint)
{
	struct ima_iint_cache **iint_sec;

	if (unlikely(!inode->i_security))
		return;

	iint_sec = inode->i_security + ima_blob_sizes.lbs_inode;
	*iint_sec = iint;
}

struct ima_iint_cache *ima_iint_find(struct inode *inode);
struct ima_iint_cache *ima_inode_get(struct inode *inode);
void ima_inode_free(struct inode *inode);
void __init ima_iintcache_init(void);

extern const int read_idmap[];

#ifdef CONFIG_HAVE_IMA_KEXEC
@@ -152,8 +253,8 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data,
			      struct ima_template_entry *entry);
int ima_calc_boot_aggregate(struct ima_digest_data *hash);
void ima_add_violation(struct file *file, const unsigned char *filename,
		       struct integrity_iint_cache *iint,
		       const char *op, const char *cause);
		       struct ima_iint_cache *iint, const char *op,
		       const char *cause);
int ima_init_crypto(void);
void ima_putc(struct seq_file *m, void *data, int datalen);
void ima_print_digest(struct seq_file *m, u8 *digest, u32 size);
@@ -267,10 +368,10 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
		   struct ima_template_desc **template_desc,
		   const char *func_data, unsigned int *allowed_algos);
int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
			    struct file *file, void *buf, loff_t size,
			    enum hash_algo algo, struct modsig *modsig);
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
			    void *buf, loff_t size, enum hash_algo algo,
			    struct modsig *modsig);
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
			   const unsigned char *filename,
			   struct evm_ima_xattr_data *xattr_value,
			   int xattr_len, const struct modsig *modsig, int pcr,
@@ -280,7 +381,7 @@ int process_buffer_measurement(struct mnt_idmap *idmap,
			       const char *eventname, enum ima_hooks func,
			       int pcr, const char *func_data,
			       bool buf_hash, u8 *digest, size_t digest_len);
void ima_audit_measurement(struct integrity_iint_cache *iint,
void ima_audit_measurement(struct ima_iint_cache *iint,
			   const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
			    struct ima_template_entry **entry,
@@ -318,17 +419,16 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_KEXEC	0x40

#ifdef CONFIG_IMA_APPRAISE
int ima_check_blacklist(struct integrity_iint_cache *iint,
int ima_check_blacklist(struct ima_iint_cache *iint,
			const struct modsig *modsig, int pcr);
int ima_appraise_measurement(enum ima_hooks func,
			     struct integrity_iint_cache *iint,
int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
			     struct file *file, const unsigned char *filename,
			     struct evm_ima_xattr_data *xattr_value,
			     int xattr_len, const struct modsig *modsig);
int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
		      int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
void ima_update_xattr(struct ima_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
					   enum ima_hooks func);
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
				 int xattr_len);
@@ -337,14 +437,14 @@ int ima_read_xattr(struct dentry *dentry,
void __init init_ima_appraise_lsm(const struct lsm_id *lsmid);

#else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
static inline int ima_check_blacklist(struct ima_iint_cache *iint,
				      const struct modsig *modsig, int pcr)
{
	return 0;
}

static inline int ima_appraise_measurement(enum ima_hooks func,
					   struct integrity_iint_cache *iint,
					   struct ima_iint_cache *iint,
					   struct file *file,
					   const unsigned char *filename,
					   struct evm_ima_xattr_data *xattr_value,
@@ -361,14 +461,13 @@ static inline int ima_must_appraise(struct mnt_idmap *idmap,
	return 0;
}

static inline void ima_update_xattr(struct integrity_iint_cache *iint,
static inline void ima_update_xattr(struct ima_iint_cache *iint,
				    struct file *file)
{
}

static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
							 *iint,
							 enum ima_hooks func)
static inline enum integrity_status
ima_get_cache_status(struct ima_iint_cache *iint, enum ima_hooks func)
{
	return INTEGRITY_UNKNOWN;
}
+12 −11
Original line number Diff line number Diff line
@@ -131,8 +131,8 @@ int ima_store_template(struct ima_template_entry *entry,
 * value is invalidated.
 */
void ima_add_violation(struct file *file, const unsigned char *filename,
		       struct integrity_iint_cache *iint,
		       const char *op, const char *cause)
		       struct ima_iint_cache *iint, const char *op,
		       const char *cause)
{
	struct ima_template_entry *entry;
	struct inode *inode = file_inode(file);
@@ -201,7 +201,8 @@ int ima_get_action(struct mnt_idmap *idmap, struct inode *inode,
				allowed_algos);
}

static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
static bool ima_get_verity_digest(struct ima_iint_cache *iint,
				  struct inode *inode,
				  struct ima_max_digest_data *hash)
{
	enum hash_algo alg;
@@ -211,7 +212,7 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
	 * On failure, 'measure' policy rules will result in a file data
	 * hash containing 0's.
	 */
	digest_len = fsverity_get_digest(iint->inode, hash->digest, NULL, &alg);
	digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
	if (digest_len == 0)
		return false;

@@ -237,9 +238,9 @@ static bool ima_get_verity_digest(struct integrity_iint_cache *iint,
 *
 * Return 0 on success, error code otherwise
 */
int ima_collect_measurement(struct integrity_iint_cache *iint,
			    struct file *file, void *buf, loff_t size,
			    enum hash_algo algo, struct modsig *modsig)
int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file,
			    void *buf, loff_t size, enum hash_algo algo,
			    struct modsig *modsig)
{
	const char *audit_cause = "failed";
	struct inode *inode = file_inode(file);
@@ -280,7 +281,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
	memset(&hash.digest, 0, sizeof(hash.digest));

	if (iint->flags & IMA_VERITY_REQUIRED) {
		if (!ima_get_verity_digest(iint, &hash)) {
		if (!ima_get_verity_digest(iint, inode, &hash)) {
			audit_cause = "no-verity-digest";
			result = -ENODATA;
		}
@@ -338,8 +339,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
 *
 * Must be called with iint->mutex held.
 */
void ima_store_measurement(struct integrity_iint_cache *iint,
			   struct file *file, const unsigned char *filename,
void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
			   const unsigned char *filename,
			   struct evm_ima_xattr_data *xattr_value,
			   int xattr_len, const struct modsig *modsig, int pcr,
			   struct ima_template_desc *template_desc)
@@ -382,7 +383,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
		ima_free_template_entry(entry);
}

void ima_audit_measurement(struct integrity_iint_cache *iint,
void ima_audit_measurement(struct ima_iint_cache *iint,
			   const unsigned char *filename)
{
	struct audit_buffer *ab;
+12 −15
Original line number Diff line number Diff line
@@ -84,8 +84,7 @@ int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode,
				NULL, NULL, NULL);
}

static int ima_fix_xattr(struct dentry *dentry,
			 struct integrity_iint_cache *iint)
static int ima_fix_xattr(struct dentry *dentry, struct ima_iint_cache *iint)
{
	int rc, offset;
	u8 algo = iint->ima_hash->algo;
@@ -106,7 +105,7 @@ static int ima_fix_xattr(struct dentry *dentry,
}

/* Return specific func appraised cached result */
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
enum integrity_status ima_get_cache_status(struct ima_iint_cache *iint,
					   enum ima_hooks func)
{
	switch (func) {
@@ -126,7 +125,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
	}
}

static void ima_set_cache_status(struct integrity_iint_cache *iint,
static void ima_set_cache_status(struct ima_iint_cache *iint,
				 enum ima_hooks func,
				 enum integrity_status status)
{
@@ -152,8 +151,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,
	}
}

static void ima_cache_flags(struct integrity_iint_cache *iint,
			     enum ima_hooks func)
static void ima_cache_flags(struct ima_iint_cache *iint, enum ima_hooks func)
{
	switch (func) {
	case MMAP_CHECK:
@@ -276,7 +274,7 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type,
 *
 * Return 0 on success, error code otherwise.
 */
static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint,
static int xattr_verify(enum ima_hooks func, struct ima_iint_cache *iint,
			struct evm_ima_xattr_data *xattr_value, int xattr_len,
			enum integrity_status *status, const char **cause)
{
@@ -443,7 +441,7 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
 *
 * Returns -EPERM if the hash is blacklisted.
 */
int ima_check_blacklist(struct integrity_iint_cache *iint,
int ima_check_blacklist(struct ima_iint_cache *iint,
			const struct modsig *modsig, int pcr)
{
	enum hash_algo hash_algo;
@@ -477,8 +475,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
 *
 * Return 0 on success, error code otherwise
 */
int ima_appraise_measurement(enum ima_hooks func,
			     struct integrity_iint_cache *iint,
int ima_appraise_measurement(enum ima_hooks func, struct ima_iint_cache *iint,
			     struct file *file, const unsigned char *filename,
			     struct evm_ima_xattr_data *xattr_value,
			     int xattr_len, const struct modsig *modsig)
@@ -603,7 +600,7 @@ int ima_appraise_measurement(enum ima_hooks func,
/*
 * ima_update_xattr - update 'security.ima' hash value
 */
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
void ima_update_xattr(struct ima_iint_cache *iint, struct file *file)
{
	struct dentry *dentry = file_dentry(file);
	int rc = 0;
@@ -640,7 +637,7 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
				   struct dentry *dentry, int ia_valid)
{
	struct inode *inode = d_backing_inode(dentry);
	struct integrity_iint_cache *iint;
	struct ima_iint_cache *iint;
	int action;

	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
@@ -648,7 +645,7 @@ static void ima_inode_post_setattr(struct mnt_idmap *idmap,
		return;

	action = ima_must_appraise(idmap, inode, MAY_ACCESS, POST_SETATTR);
	iint = integrity_iint_find(inode);
	iint = ima_iint_find(inode);
	if (iint) {
		set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
		if (!action)
@@ -674,12 +671,12 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,

static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
	struct integrity_iint_cache *iint;
	struct ima_iint_cache *iint;

	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
		return;

	iint = integrity_iint_find(inode);
	iint = ima_iint_find(inode);
	if (!iint)
		return;
	iint->measured_pcrs = 0;
+142 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2008 IBM Corporation
 *
 * Authors:
 * Mimi Zohar <zohar@us.ibm.com>
 *
 * File: ima_iint.c
 *	- implements the IMA hook: ima_inode_free
 *	- cache integrity information in the inode security blob
 */
#include <linux/slab.h>

#include "ima.h"

static struct kmem_cache *ima_iint_cache __ro_after_init;

/**
 * ima_iint_find - Return the iint associated with an inode
 * @inode: Pointer to the inode
 *
 * Return the IMA integrity information (iint) associated with an inode, if the
 * inode was processed by IMA.
 *
 * Return: Found iint or NULL.
 */
struct ima_iint_cache *ima_iint_find(struct inode *inode)
{
	if (!IS_IMA(inode))
		return NULL;

	return ima_inode_get_iint(inode);
}

#define IMA_MAX_NESTING (FILESYSTEM_MAX_STACK_DEPTH + 1)

/*
 * It is not clear that IMA should be nested at all, but as long is it measures
 * files both on overlayfs and on underlying fs, we need to annotate the iint
 * mutex to avoid lockdep false positives related to IMA + overlayfs.
 * See ovl_lockdep_annotate_inode_mutex_key() for more details.
 */
static inline void ima_iint_lockdep_annotate(struct ima_iint_cache *iint,
					     struct inode *inode)
{
#ifdef CONFIG_LOCKDEP
	static struct lock_class_key ima_iint_mutex_key[IMA_MAX_NESTING];

	int depth = inode->i_sb->s_stack_depth;

	if (WARN_ON_ONCE(depth < 0 || depth >= IMA_MAX_NESTING))
		depth = 0;

	lockdep_set_class(&iint->mutex, &ima_iint_mutex_key[depth]);
#endif
}

static void ima_iint_init_always(struct ima_iint_cache *iint,
				 struct inode *inode)
{
	iint->ima_hash = NULL;
	iint->version = 0;
	iint->flags = 0UL;
	iint->atomic_flags = 0UL;
	iint->ima_file_status = INTEGRITY_UNKNOWN;
	iint->ima_mmap_status = INTEGRITY_UNKNOWN;
	iint->ima_bprm_status = INTEGRITY_UNKNOWN;
	iint->ima_read_status = INTEGRITY_UNKNOWN;
	iint->ima_creds_status = INTEGRITY_UNKNOWN;
	iint->measured_pcrs = 0;
	mutex_init(&iint->mutex);
	ima_iint_lockdep_annotate(iint, inode);
}

static void ima_iint_free(struct ima_iint_cache *iint)
{
	kfree(iint->ima_hash);
	mutex_destroy(&iint->mutex);
	kmem_cache_free(ima_iint_cache, iint);
}

/**
 * ima_inode_get - Find or allocate an iint associated with an inode
 * @inode: Pointer to the inode
 *
 * Find an iint associated with an inode, and allocate a new one if not found.
 * Caller must lock i_mutex.
 *
 * Return: An iint on success, NULL on error.
 */
struct ima_iint_cache *ima_inode_get(struct inode *inode)
{
	struct ima_iint_cache *iint;

	iint = ima_iint_find(inode);
	if (iint)
		return iint;

	iint = kmem_cache_alloc(ima_iint_cache, GFP_NOFS);
	if (!iint)
		return NULL;

	ima_iint_init_always(iint, inode);

	inode->i_flags |= S_IMA;
	ima_inode_set_iint(inode, iint);

	return iint;
}

/**
 * ima_inode_free - Called on inode free
 * @inode: Pointer to the inode
 *
 * Free the iint associated with an inode.
 */
void ima_inode_free(struct inode *inode)
{
	struct ima_iint_cache *iint;

	if (!IS_IMA(inode))
		return;

	iint = ima_iint_find(inode);
	ima_inode_set_iint(inode, NULL);

	ima_iint_free(iint);
}

static void ima_iint_init_once(void *foo)
{
	struct ima_iint_cache *iint = (struct ima_iint_cache *)foo;

	memset(iint, 0, sizeof(*iint));
}

void __init ima_iintcache_init(void)
{
	ima_iint_cache =
	    kmem_cache_create("ima_iint_cache", sizeof(struct ima_iint_cache),
			      0, SLAB_PANIC, ima_iint_init_once);
}
Loading