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

Commit 80eae209 authored by Petko Manolov's avatar Petko Manolov Committed by Mimi Zohar
Browse files

IMA: allow reading back the current IMA policy



It is often useful to be able to read back the IMA policy.  It is
even more important after introducing CONFIG_IMA_WRITE_POLICY.
This option allows the root user to see the current policy rules.

Signed-off-by: default avatarZbigniew Jasinski <z.jasinski@samsung.com>
Signed-off-by: default avatarPetko Manolov <petkan@mip-labs.com>
Signed-off-by: default avatarMimi Zohar <zohar@linux.vnet.ibm.com>
parent 41c89b64
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -118,6 +118,16 @@ config IMA_WRITE_POLICY

	  If unsure, say N.

config IMA_READ_POLICY
	bool "Enable reading back the current IMA policy"
	depends on IMA
	default y if IMA_WRITE_POLICY
	default n if !IMA_WRITE_POLICY
	help
	   It is often useful to be able to read back the IMA policy.  It is
	   even more important after introducing CONFIG_IMA_WRITE_POLICY.
	   This option allows the root user to see the current policy rules.

config IMA_APPRAISE
	bool "Appraise integrity measurements"
	depends on IMA
+13 −2
Original line number Diff line number Diff line
@@ -166,6 +166,10 @@ void ima_update_policy(void);
void ima_update_policy_flag(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
void *ima_policy_start(struct seq_file *m, loff_t *pos);
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
int ima_policy_show(struct seq_file *m, void *v);

/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE	0x01
@@ -250,5 +254,12 @@ static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
{
	return -EINVAL;
}
#endif /* CONFIG_IMA_LSM_RULES */
#endif
#endif /* CONFIG_IMA_TRUSTED_KEYRING */

#ifdef	CONFIG_IMA_READ_POLICY
#define	POLICY_FILE_FLAGS	(S_IWUSR | S_IRUSR)
#else
#define	POLICY_FILE_FLAGS	S_IWUSR
#endif /* CONFIG_IMA_WRITE_POLICY */

#endif /* __LINUX_IMA_H */
+25 −4
Original line number Diff line number Diff line
@@ -311,14 +311,31 @@ enum ima_fs_flags {

static unsigned long ima_fs_flags;

#ifdef	CONFIG_IMA_READ_POLICY
static const struct seq_operations ima_policy_seqops = {
		.start = ima_policy_start,
		.next = ima_policy_next,
		.stop = ima_policy_stop,
		.show = ima_policy_show,
};
#endif

/*
 * ima_open_policy: sequentialize access to the policy file
 */
static int ima_open_policy(struct inode *inode, struct file *filp)
{
	/* No point in being allowed to open it if you aren't going to write */
	if (!(filp->f_flags & O_WRONLY))
	if (!(filp->f_flags & O_WRONLY)) {
#ifndef	CONFIG_IMA_READ_POLICY
		return -EACCES;
#else
		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
			return -EACCES;
		if (!capable(CAP_SYS_ADMIN))
			return -EPERM;
		return seq_open(filp, &ima_policy_seqops);
#endif
	}
	if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
		return -EBUSY;
	return 0;
@@ -335,6 +352,9 @@ static int ima_release_policy(struct inode *inode, struct file *file)
{
	const char *cause = valid_policy ? "completed" : "failed";

	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
		return 0;

	pr_info("IMA: policy update %s\n", cause);
	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
			    "policy_update", cause, !valid_policy, 0);
@@ -345,6 +365,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
		clear_bit(IMA_FS_BUSY, &ima_fs_flags);
		return 0;
	}

	ima_update_policy();
#ifndef	CONFIG_IMA_WRITE_POLICY
	securityfs_remove(ima_policy);
@@ -358,6 +379,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
static const struct file_operations ima_measure_policy_ops = {
	.open = ima_open_policy,
	.write = ima_write_policy,
	.read = seq_read,
	.release = ima_release_policy,
	.llseek = generic_file_llseek,
};
@@ -395,8 +417,7 @@ int __init ima_fs_init(void)
	if (IS_ERR(violations))
		goto out;

	ima_policy = securityfs_create_file("policy",
					    S_IWUSR,
	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
					    ima_dir, NULL,
					    &ima_measure_policy_ops);
	if (IS_ERR(ima_policy))
+205 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/slab.h>
#include <linux/rculist.h>
#include <linux/genhd.h>
#include <linux/seq_file.h>

#include "ima.h"

@@ -458,8 +459,8 @@ enum {
	Opt_obj_user, Opt_obj_role, Opt_obj_type,
	Opt_subj_user, Opt_subj_role, Opt_subj_type,
	Opt_func, Opt_mask, Opt_fsmagic,
	Opt_uid, Opt_euid, Opt_fowner,
	Opt_appraise_type, Opt_fsuuid, Opt_permit_directio
	Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner,
	Opt_appraise_type, Opt_permit_directio
};

static match_table_t policy_tokens = {
@@ -828,3 +829,205 @@ void ima_delete_rules(void)
		kfree(entry);
	}
}

#ifdef	CONFIG_IMA_READ_POLICY
enum {
	mask_exec = 0, mask_write, mask_read, mask_append
};

static char *mask_tokens[] = {
	"MAY_EXEC",
	"MAY_WRITE",
	"MAY_READ",
	"MAY_APPEND"
};

enum {
	func_file = 0, func_mmap, func_bprm,
	func_module, func_firmware, func_post
};

static char *func_tokens[] = {
	"FILE_CHECK",
	"MMAP_CHECK",
	"BPRM_CHECK",
	"MODULE_CHECK",
	"FIRMWARE_CHECK",
	"POST_SETATTR"
};

void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
	loff_t l = *pos;
	struct ima_rule_entry *entry;

	rcu_read_lock();
	list_for_each_entry_rcu(entry, ima_rules, list) {
		if (!l--) {
			rcu_read_unlock();
			return entry;
		}
	}
	rcu_read_unlock();
	return NULL;
}

void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
{
	struct ima_rule_entry *entry = v;

	rcu_read_lock();
	entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
	rcu_read_unlock();
	(*pos)++;

	return (&entry->list == ima_rules) ? NULL : entry;
}

void ima_policy_stop(struct seq_file *m, void *v)
{
}

#define pt(token)	policy_tokens[token + Opt_err].pattern
#define mt(token)	mask_tokens[token]
#define ft(token)	func_tokens[token]

int ima_policy_show(struct seq_file *m, void *v)
{
	struct ima_rule_entry *entry = v;
	int i = 0;
	char tbuf[64] = {0,};

	rcu_read_lock();

	if (entry->action & MEASURE)
		seq_puts(m, pt(Opt_measure));
	if (entry->action & DONT_MEASURE)
		seq_puts(m, pt(Opt_dont_measure));
	if (entry->action & APPRAISE)
		seq_puts(m, pt(Opt_appraise));
	if (entry->action & DONT_APPRAISE)
		seq_puts(m, pt(Opt_dont_appraise));
	if (entry->action & AUDIT)
		seq_puts(m, pt(Opt_audit));

	seq_puts(m, " ");

	if (entry->flags & IMA_FUNC) {
		switch (entry->func) {
		case FILE_CHECK:
			seq_printf(m, pt(Opt_func), ft(func_file));
			break;
		case MMAP_CHECK:
			seq_printf(m, pt(Opt_func), ft(func_mmap));
			break;
		case BPRM_CHECK:
			seq_printf(m, pt(Opt_func), ft(func_bprm));
			break;
		case MODULE_CHECK:
			seq_printf(m, pt(Opt_func), ft(func_module));
			break;
		case FIRMWARE_CHECK:
			seq_printf(m, pt(Opt_func), ft(func_firmware));
			break;
		case POST_SETATTR:
			seq_printf(m, pt(Opt_func), ft(func_post));
			break;
		default:
			snprintf(tbuf, sizeof(tbuf), "%d", entry->func);
			seq_printf(m, pt(Opt_func), tbuf);
			break;
		}
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_MASK) {
		if (entry->mask & MAY_EXEC)
			seq_printf(m, pt(Opt_mask), mt(mask_exec));
		if (entry->mask & MAY_WRITE)
			seq_printf(m, pt(Opt_mask), mt(mask_write));
		if (entry->mask & MAY_READ)
			seq_printf(m, pt(Opt_mask), mt(mask_read));
		if (entry->mask & MAY_APPEND)
			seq_printf(m, pt(Opt_mask), mt(mask_append));
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_FSMAGIC) {
		snprintf(tbuf, sizeof(tbuf), "0x%lx", entry->fsmagic);
		seq_printf(m, pt(Opt_fsmagic), tbuf);
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_FSUUID) {
		seq_puts(m, "fsuuid=");
		for (i = 0; i < ARRAY_SIZE(entry->fsuuid); ++i) {
			switch (i) {
			case 4:
			case 6:
			case 8:
			case 10:
				seq_puts(m, "-");
			}
			seq_printf(m, "%x", entry->fsuuid[i]);
		}
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_UID) {
		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
		seq_printf(m, pt(Opt_uid), tbuf);
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_EUID) {
		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
		seq_printf(m, pt(Opt_euid), tbuf);
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_FOWNER) {
		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
		seq_printf(m, pt(Opt_fowner), tbuf);
		seq_puts(m, " ");
	}

	for (i = 0; i < MAX_LSM_RULES; i++) {
		if (entry->lsm[i].rule) {
			switch (i) {
			case LSM_OBJ_USER:
				seq_printf(m, pt(Opt_obj_user),
					   (char *)entry->lsm[i].args_p);
				break;
			case LSM_OBJ_ROLE:
				seq_printf(m, pt(Opt_obj_role),
					   (char *)entry->lsm[i].args_p);
				break;
			case LSM_OBJ_TYPE:
				seq_printf(m, pt(Opt_obj_type),
					   (char *)entry->lsm[i].args_p);
				break;
			case LSM_SUBJ_USER:
				seq_printf(m, pt(Opt_subj_user),
					   (char *)entry->lsm[i].args_p);
				break;
			case LSM_SUBJ_ROLE:
				seq_printf(m, pt(Opt_subj_role),
					   (char *)entry->lsm[i].args_p);
				break;
			case LSM_SUBJ_TYPE:
				seq_printf(m, pt(Opt_subj_type),
					   (char *)entry->lsm[i].args_p);
				break;
			}
		}
	}
	if (entry->flags & IMA_DIGSIG_REQUIRED)
		seq_puts(m, "appraise_type=imasig ");
	if (entry->flags & IMA_PERMIT_DIRECTIO)
		seq_puts(m, "permit_directio ");
	rcu_read_unlock();
	seq_puts(m, "\n");
	return 0;
}
#endif	/* CONFIG_IMA_READ_POLICY */