Commit 777f8171 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull integrity updates from Mimi Zohar:
 "Bug fixes:

   - defer credentials checking from the bprm_check_security hook to the
     bprm_creds_from_file security hook

   - properly ignore IMA policy rules based on undefined SELinux labels

  IMA policy rule extensions:

   - extend IMA to limit including file hashes in the audit logs
     (dont_audit action)

   - define a new filesystem subtype policy option (fs_subtype)

  Misc:

   - extend IMA to support in-kernel module decompression by deferring
     the IMA signature verification in kernel_read_file() to after the
     kernel module is decompressed"

* tag 'integrity-v6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
  ima: Handle error code returned by ima_filter_rule_match()
  ima: Access decompressed kernel module to verify appended signature
  ima: add fs_subtype condition for distinguishing FUSE instances
  ima: add dont_audit action to suppress audit actions
  ima: Attach CREDS_CHECK IMA hook to bprm_creds_from_file LSM hook
parents 204a920f 738c9738
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -20,9 +20,10 @@ Description:
		  rule format: action [condition ...]

		  action: measure | dont_measure | appraise | dont_appraise |
			  audit | hash | dont_hash
			  audit | dont_audit | hash | dont_hash
		  condition:= base | lsm  [option]
			base:	[[func=] [mask=] [fsmagic=] [fsuuid=] [fsname=]
				[fs_subtype=]
				[uid=] [euid=] [gid=] [egid=]
				[fowner=] [fgroup=]]
			lsm:	[[subj_user=] [subj_role=] [subj_type=]
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
	id(KEXEC_INITRAMFS, kexec-initramfs)	\
	id(POLICY, security-policy)		\
	id(X509_CERTIFICATE, x509-certificate)	\
	id(MODULE_COMPRESSED, kernel-module-compressed) \
	id(MAX_ID, )

#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
+14 −3
Original line number Diff line number Diff line
@@ -3675,24 +3675,35 @@ static int idempotent_wait_for_completion(struct idempotent *u)

static int init_module_from_file(struct file *f, const char __user * uargs, int flags)
{
	bool compressed = !!(flags & MODULE_INIT_COMPRESSED_FILE);
	struct load_info info = { };
	void *buf = NULL;
	int len;
	int err;

	len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
	len = kernel_read_file(f, 0, &buf, INT_MAX, NULL,
			       compressed ? READING_MODULE_COMPRESSED :
					    READING_MODULE);
	if (len < 0) {
		mod_stat_inc(&failed_kreads);
		return len;
	}

	if (flags & MODULE_INIT_COMPRESSED_FILE) {
		int err = module_decompress(&info, buf, len);
	if (compressed) {
		err = module_decompress(&info, buf, len);
		vfree(buf); /* compressed data is no longer needed */
		if (err) {
			mod_stat_inc(&failed_decompress);
			mod_stat_add_long(len, &invalid_decompress_bytes);
			return err;
		}
		err = security_kernel_post_read_file(f, (char *)info.hdr, info.len,
						     READING_MODULE);
		if (err) {
			mod_stat_inc(&failed_kreads);
			free_copy(&info, flags);
			return err;
		}
	} else {
		info.hdr = buf;
		info.len = len;
+47 −15
Original line number Diff line number Diff line
@@ -235,7 +235,8 @@ static void ima_file_free(struct file *file)

static int process_measurement(struct file *file, const struct cred *cred,
			       struct lsm_prop *prop, char *buf, loff_t size,
			       int mask, enum ima_hooks func)
			       int mask, enum ima_hooks func,
			       enum kernel_read_file_id read_id)
{
	struct inode *real_inode, *inode = file_inode(file);
	struct ima_iint_cache *iint = NULL;
@@ -406,6 +407,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
	if (rc != 0 && rc != -EBADF && rc != -EINVAL)
		goto out_locked;

	/* Defer measuring/appraising kernel modules to READING_MODULE */
	if (read_id == READING_MODULE_COMPRESSED) {
		must_appraise = 0;
		goto out_locked;
	}

	if (!pathbuf)	/* ima_rdwr_violation possibly pre-fetched */
		pathname = ima_d_path(&file->f_path, &pathbuf, filename);

@@ -486,14 +493,14 @@ static int ima_file_mmap(struct file *file, unsigned long reqprot,

	if (reqprot & PROT_EXEC) {
		ret = process_measurement(file, current_cred(), &prop, NULL,
					  0, MAY_EXEC, MMAP_CHECK_REQPROT);
					  0, MAY_EXEC, MMAP_CHECK_REQPROT, 0);
		if (ret)
			return ret;
	}

	if (prot & PROT_EXEC)
		return process_measurement(file, current_cred(), &prop, NULL,
					   0, MAY_EXEC, MMAP_CHECK);
					   0, MAY_EXEC, MMAP_CHECK, 0);

	return 0;
}
@@ -573,18 +580,41 @@ static int ima_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
 */
static int ima_bprm_check(struct linux_binprm *bprm)
{
	int ret;
	struct lsm_prop prop;

	security_current_getlsmprop_subj(&prop);
	ret = process_measurement(bprm->file, current_cred(),
				  &prop, NULL, 0, MAY_EXEC, BPRM_CHECK);
	if (ret)
		return ret;
	return process_measurement(bprm->file, current_cred(),
				   &prop, NULL, 0, MAY_EXEC, BPRM_CHECK, 0);
}

	security_cred_getlsmprop(bprm->cred, &prop);
	return process_measurement(bprm->file, bprm->cred, &prop, NULL, 0,
				   MAY_EXEC, CREDS_CHECK);
/**
 * ima_creds_check - based on policy, collect/store measurement.
 * @bprm: contains the linux_binprm structure
 * @file: contains the file descriptor of the binary being executed
 *
 * The OS protects against an executable file, already open for write,
 * from being executed in deny_write_access() and an executable file,
 * already open for execute, from being modified in get_write_access().
 * So we can be certain that what we verify and measure here is actually
 * what is being executed.
 *
 * The difference from ima_bprm_check() is that ima_creds_check() is invoked
 * only after determining the final binary to be executed without interpreter,
 * and not when searching for intermediate binaries. The reason is that since
 * commit 56305aa9b6fab ("exec: Compute file based creds only once"), the
 * credentials to be applied to the process are calculated only at that stage
 * (bprm_creds_from_file security hook instead of bprm_check_security).
 *
 * On success return 0.  On integrity appraisal error, assuming the file
 * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
 */
static int ima_creds_check(struct linux_binprm *bprm, const struct file *file)
{
	struct lsm_prop prop;

	security_current_getlsmprop_subj(&prop);
	return process_measurement((struct file *)file, bprm->cred, &prop, NULL,
				   0, MAY_EXEC, CREDS_CHECK, 0);
}

/**
@@ -632,7 +662,7 @@ static int ima_file_check(struct file *file, int mask)
	security_current_getlsmprop_subj(&prop);
	return process_measurement(file, current_cred(), &prop, NULL, 0,
				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
					   MAY_APPEND), FILE_CHECK);
					   MAY_APPEND), FILE_CHECK, 0);
}

static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
@@ -851,12 +881,13 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
	func = read_idmap[read_id] ?: FILE_CHECK;
	security_current_getlsmprop_subj(&prop);
	return process_measurement(file, current_cred(), &prop, NULL, 0,
				   MAY_READ, func);
				   MAY_READ, func, 0);
}

const int read_idmap[READING_MAX_ID] = {
	[READING_FIRMWARE] = FIRMWARE_CHECK,
	[READING_MODULE] = MODULE_CHECK,
	[READING_MODULE_COMPRESSED] = MODULE_CHECK,
	[READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK,
	[READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK,
	[READING_POLICY] = POLICY_CHECK
@@ -894,7 +925,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size,
	func = read_idmap[read_id] ?: FILE_CHECK;
	security_current_getlsmprop_subj(&prop);
	return process_measurement(file, current_cred(), &prop, buf, size,
				   MAY_READ, func);
				   MAY_READ, func, read_id);
}

/**
@@ -1242,6 +1273,7 @@ static int __init init_ima(void)
static struct security_hook_list ima_hooks[] __ro_after_init = {
	LSM_HOOK_INIT(bprm_check_security, ima_bprm_check),
	LSM_HOOK_INIT(bprm_creds_for_exec, ima_bprm_creds_for_exec),
	LSM_HOOK_INIT(bprm_creds_from_file, ima_creds_check),
	LSM_HOOK_INIT(file_post_open, ima_file_check),
	LSM_HOOK_INIT(inode_post_create_tmpfile, ima_post_create_tmpfile),
	LSM_HOOK_INIT(file_release, ima_file_free),
+55 −7
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@
#define IMA_GID		0x2000
#define IMA_EGID	0x4000
#define IMA_FGROUP	0x8000
#define IMA_FS_SUBTYPE	0x10000

#define UNKNOWN		0
#define MEASURE		0x0001	/* same as IMA_MEASURE */
@@ -45,6 +46,7 @@
#define APPRAISE	0x0004	/* same as IMA_APPRAISE */
#define DONT_APPRAISE	0x0008
#define AUDIT		0x0040
#define DONT_AUDIT	0x0080
#define HASH		0x0100
#define DONT_HASH	0x0200

@@ -119,6 +121,7 @@ struct ima_rule_entry {
		int type;	/* audit type */
	} lsm[MAX_LSM_RULES];
	char *fsname;
	char *fs_subtype;
	struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
	struct ima_rule_opt_list *label; /* Measure data grouped under this label */
	struct ima_template_desc *template;
@@ -241,7 +244,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = {

static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
	{.action = APPRAISE, .func = MODULE_CHECK,
	 .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
	 .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
		  IMA_CHECK_BLACKLIST},
	{.action = APPRAISE, .func = FIRMWARE_CHECK,
	 .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
	{.action = APPRAISE, .func = KEXEC_KERNEL_CHECK,
@@ -397,6 +401,7 @@ static void ima_free_rule(struct ima_rule_entry *entry)
	 * the defined_templates list and cannot be freed here
	 */
	kfree(entry->fsname);
	kfree(entry->fs_subtype);
	ima_free_rule_opt_list(entry->keyrings);
	ima_lsm_free_rule(entry);
	kfree(entry);
@@ -601,6 +606,12 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
	if ((rule->flags & IMA_FSNAME)
	    && strcmp(rule->fsname, inode->i_sb->s_type->name))
		return false;
	if (rule->flags & IMA_FS_SUBTYPE) {
		if (!inode->i_sb->s_subtype)
			return false;
		if (strcmp(rule->fs_subtype, inode->i_sb->s_subtype))
			return false;
	}
	if ((rule->flags & IMA_FSUUID) &&
	    !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid))
		return false;
@@ -674,7 +685,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
				goto retry;
			}
		}
		if (!rc) {
		if (rc <= 0) {
			result = false;
			goto out;
		}
@@ -1064,10 +1075,10 @@ void ima_update_policy(void)
enum policy_opt {
	Opt_measure, Opt_dont_measure,
	Opt_appraise, Opt_dont_appraise,
	Opt_audit, Opt_hash, Opt_dont_hash,
	Opt_audit, Opt_dont_audit, Opt_hash, Opt_dont_hash,
	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_fsname, Opt_fsuuid,
	Opt_func, Opt_mask, Opt_fsmagic, Opt_fsname, Opt_fs_subtype, Opt_fsuuid,
	Opt_uid_eq, Opt_euid_eq, Opt_gid_eq, Opt_egid_eq,
	Opt_fowner_eq, Opt_fgroup_eq,
	Opt_uid_gt, Opt_euid_gt, Opt_gid_gt, Opt_egid_gt,
@@ -1086,6 +1097,7 @@ static const match_table_t policy_tokens = {
	{Opt_appraise, "appraise"},
	{Opt_dont_appraise, "dont_appraise"},
	{Opt_audit, "audit"},
	{Opt_dont_audit, "dont_audit"},
	{Opt_hash, "hash"},
	{Opt_dont_hash, "dont_hash"},
	{Opt_obj_user, "obj_user=%s"},
@@ -1098,6 +1110,7 @@ static const match_table_t policy_tokens = {
	{Opt_mask, "mask=%s"},
	{Opt_fsmagic, "fsmagic=%s"},
	{Opt_fsname, "fsname=%s"},
	{Opt_fs_subtype, "fs_subtype=%s"},
	{Opt_fsuuid, "fsuuid=%s"},
	{Opt_uid_eq, "uid=%s"},
	{Opt_euid_eq, "euid=%s"},
@@ -1282,7 +1295,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
		if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
				     IMA_UID | IMA_FOWNER | IMA_FSUUID |
				     IMA_INMASK | IMA_EUID | IMA_PCR |
				     IMA_FSNAME | IMA_GID | IMA_EGID |
				     IMA_FSNAME | IMA_FS_SUBTYPE |
				     IMA_GID | IMA_EGID |
				     IMA_FGROUP | IMA_DIGSIG_REQUIRED |
				     IMA_PERMIT_DIRECTIO | IMA_VALIDATE_ALGOS |
				     IMA_CHECK_BLACKLIST | IMA_VERITY_REQUIRED))
@@ -1295,7 +1309,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
		if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC |
				     IMA_UID | IMA_FOWNER | IMA_FSUUID |
				     IMA_INMASK | IMA_EUID | IMA_PCR |
				     IMA_FSNAME | IMA_GID | IMA_EGID |
				     IMA_FSNAME | IMA_FS_SUBTYPE |
				     IMA_GID | IMA_EGID |
				     IMA_FGROUP | IMA_DIGSIG_REQUIRED |
				     IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED |
				     IMA_CHECK_BLACKLIST | IMA_VALIDATE_ALGOS))
@@ -1308,7 +1323,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)

		if (entry->flags & ~(IMA_FUNC | IMA_FSMAGIC | IMA_UID |
				     IMA_FOWNER | IMA_FSUUID | IMA_EUID |
				     IMA_PCR | IMA_FSNAME | IMA_GID | IMA_EGID |
				     IMA_PCR | IMA_FSNAME | IMA_FS_SUBTYPE |
				     IMA_GID | IMA_EGID |
				     IMA_FGROUP))
			return false;

@@ -1478,6 +1494,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)

			entry->action = AUDIT;
			break;
		case Opt_dont_audit:
			ima_log_string(ab, "action", "dont_audit");

			if (entry->action != UNKNOWN)
				result = -EINVAL;

			entry->action = DONT_AUDIT;
			break;
		case Opt_hash:
			ima_log_string(ab, "action", "hash");

@@ -1587,6 +1611,22 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
			result = 0;
			entry->flags |= IMA_FSNAME;
			break;
		case Opt_fs_subtype:
			ima_log_string(ab, "fs_subtype", args[0].from);

			if (entry->fs_subtype) {
				result = -EINVAL;
				break;
			}

			entry->fs_subtype = kstrdup(args[0].from, GFP_KERNEL);
			if (!entry->fs_subtype) {
				result = -ENOMEM;
				break;
			}
			result = 0;
			entry->flags |= IMA_FS_SUBTYPE;
			break;
		case Opt_keyrings:
			ima_log_string(ab, "keyrings", args[0].from);

@@ -2097,6 +2137,8 @@ int ima_policy_show(struct seq_file *m, void *v)
		seq_puts(m, pt(Opt_dont_appraise));
	if (entry->action & AUDIT)
		seq_puts(m, pt(Opt_audit));
	if (entry->action & DONT_AUDIT)
		seq_puts(m, pt(Opt_dont_audit));
	if (entry->action & HASH)
		seq_puts(m, pt(Opt_hash));
	if (entry->action & DONT_HASH)
@@ -2133,6 +2175,12 @@ int ima_policy_show(struct seq_file *m, void *v)
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_FS_SUBTYPE) {
		snprintf(tbuf, sizeof(tbuf), "%s", entry->fs_subtype);
		seq_printf(m, pt(Opt_fs_subtype), tbuf);
		seq_puts(m, " ");
	}

	if (entry->flags & IMA_KEYRINGS) {
		seq_puts(m, "keyrings=");
		ima_show_rule_opt_list(m, entry->keyrings);
Loading