Commit d07eeeb8 authored by Mimi Zohar's avatar Mimi Zohar
Browse files

Merge branch 'restrict-digest-alg-v8' into next-integrity

Taken from the cover letter "IMA: restrict the accepted digest
algorithms for the security.ima xattr":

Provide users the ability to restrict the algorithms accepted by
their system, both when writing/updating xattrs, and when appraising
files, while retaining a permissive behavior by default to preserve
backward compatibility.

To provide these features, alter the behavior of setxattr to
only accept hashes built in the kernel, instead of any hash listed
in the kernel (complete list crypto/hash_info.c). In addition, the
user can define in his IMA policy the list of digest algorithms
allowed for writing to the security.ima xattr. In that case,
only algorithms present in that list are accepted for writing.

In addition, users may opt-in to allowlist hash algorithms for
appraising thanks to the new 'appraise_algos' IMA policy option.
By default IMA will keep accepting any hash algorithm, but specifying
that option will make appraisal of files hashed with another algorithm
fail.

Link: https://lore.kernel.org/linux-integrity/20210816081056.24530-1-Simon.THOBY@viveris.fr/
parents e37be534 8ecd39cb
Loading
Loading
Loading
Loading
+13 −2
Original line number Diff line number Diff line
@@ -27,12 +27,13 @@ Description:
			lsm:	[[subj_user=] [subj_role=] [subj_type=]
				 [obj_user=] [obj_role=] [obj_type=]]
			option:	[[appraise_type=]] [template=] [permit_directio]
				[appraise_flag=] [keyrings=]
				[appraise_flag=] [appraise_algos=] [keyrings=]
		  base:
			func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK]
				[FIRMWARE_CHECK]
				[KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK]
				[KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA]
				[SETXATTR_CHECK]
			mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND]
			       [[^]MAY_EXEC]
			fsmagic:= hex value
@@ -55,6 +56,10 @@ Description:
			label:= [selinux]|[kernel_info]|[data_label]
			data_label:= a unique string used for grouping and limiting critical data.
			For example, "selinux" to measure critical data for SELinux.
			appraise_algos:= comma-separated list of hash algorithms
			For example, "sha256,sha512" to only accept to appraise
			files where the security.ima xattr was hashed with one
			of these two algorithms.

		  default policy:
			# PROC_SUPER_MAGIC
@@ -134,3 +139,9 @@ Description:
		keys added to .builtin_trusted_keys or .ima keyring:

			measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima

		Example of the special SETXATTR_CHECK appraise rule, that
		restricts the hash algorithms allowed when writing to the
		security.ima xattr of a file:

			appraise func=SETXATTR_CHECK appraise_algos=sha256,sha384,sha512
+0 −1
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@ config IMA
	select SECURITYFS
	select CRYPTO
	select CRYPTO_HMAC
	select CRYPTO_MD5
	select CRYPTO_SHA1
	select CRYPTO_HASH_INFO
	select TCG_TPM if HAS_IOMEM && !UML
+9 −5
Original line number Diff line number Diff line
@@ -46,8 +46,11 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
/* current content of the policy */
extern int ima_policy_flag;

/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;

/* set during initialization */
extern int ima_hash_algo;
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __ro_after_init;
extern int ima_extra_slots __ro_after_init;
@@ -198,6 +201,7 @@ static inline unsigned int ima_hash_key(u8 *digest)
	hook(KEXEC_CMDLINE, kexec_cmdline)		\
	hook(KEY_CHECK, key)				\
	hook(CRITICAL_DATA, critical_data)		\
	hook(SETXATTR_CHECK, setxattr_check)		\
	hook(MAX_CHECK, none)

#define __ima_hook_enumify(ENUM, str)	ENUM,
@@ -254,7 +258,7 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
		   const struct cred *cred, u32 secid, int mask,
		   enum ima_hooks func, int *pcr,
		   struct ima_template_desc **template_desc,
		   const char *func_data);
		   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,
@@ -285,10 +289,10 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
		     const struct cred *cred, u32 secid, enum ima_hooks func,
		     int mask, int flags, int *pcr,
		     struct ima_template_desc **template_desc,
		     const char *func_data);
		     const char *func_data, unsigned int *allowed_algos);
void ima_init_policy(void);
void ima_update_policy(void);
void ima_update_policy_flag(void);
void ima_update_policy_flags(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
int ima_check_policy(void);
@@ -319,7 +323,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
					   enum ima_hooks func);
enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
				 int xattr_len);
int ima_read_xattr(struct dentry *dentry,
		   struct evm_ima_xattr_data **xattr_value);
+4 −2
Original line number Diff line number Diff line
@@ -172,6 +172,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 * @pcr: pointer filled in if matched measure policy sets pcr=
 * @template_desc: pointer filled in if matched measure policy sets template=
 * @func_data: func specific data, may be NULL
 * @allowed_algos: allowlist of hash algorithms for the IMA xattr
 *
 * The policy is defined in terms of keypairs:
 *		subj=, obj=, type=, func=, mask=, fsmagic=
@@ -188,14 +189,15 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
		   const struct cred *cred, u32 secid, int mask,
		   enum ima_hooks func, int *pcr,
		   struct ima_template_desc **template_desc,
		   const char *func_data)
		   const char *func_data, unsigned int *allowed_algos)
{
	int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;

	flags &= ima_policy_flag;

	return ima_match_policy(mnt_userns, inode, cred, secid, func, mask,
				flags, pcr, template_desc, func_data);
				flags, pcr, template_desc, func_data,
				allowed_algos);
}

/*
+68 −5
Original line number Diff line number Diff line
@@ -77,8 +77,9 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
		return 0;

	security_task_getsecid_subj(current, &secid);
	return ima_match_policy(mnt_userns, inode, current_cred(), secid, func,
				mask, IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
	return ima_match_policy(mnt_userns, inode, current_cred(), secid,
				func, mask, IMA_APPRAISE | IMA_HASH, NULL,
				NULL, NULL, NULL);
}

static int ima_fix_xattr(struct dentry *dentry,
@@ -171,7 +172,7 @@ static void ima_cache_flags(struct integrity_iint_cache *iint,
	}
}

enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
enum hash_algo ima_get_hash_algo(const struct evm_ima_xattr_data *xattr_value,
				 int xattr_len)
{
	struct signature_v2_hdr *sig;
@@ -575,6 +576,66 @@ static void ima_reset_appraise_flags(struct inode *inode, int digsig)
		clear_bit(IMA_DIGSIG, &iint->atomic_flags);
}

/**
 * validate_hash_algo() - Block setxattr with unsupported hash algorithms
 * @dentry: object of the setxattr()
 * @xattr_value: userland supplied xattr value
 * @xattr_value_len: length of xattr_value
 *
 * The xattr value is mapped to its hash algorithm, and this algorithm
 * must be built in the kernel for the setxattr to be allowed.
 *
 * Emit an audit message when the algorithm is invalid.
 *
 * Return: 0 on success, else an error.
 */
static int validate_hash_algo(struct dentry *dentry,
			      const struct evm_ima_xattr_data *xattr_value,
			      size_t xattr_value_len)
{
	char *path = NULL, *pathbuf = NULL;
	enum hash_algo xattr_hash_algo;
	const char *errmsg = "unavailable-hash-algorithm";
	unsigned int allowed_hashes;

	xattr_hash_algo = ima_get_hash_algo(xattr_value, xattr_value_len);

	allowed_hashes = atomic_read(&ima_setxattr_allowed_hash_algorithms);

	if (allowed_hashes) {
		/* success if the algorithm is allowed in the ima policy */
		if (allowed_hashes & (1U << xattr_hash_algo))
			return 0;

		/*
		 * We use a different audit message when the hash algorithm
		 * is denied by a policy rule, instead of not being built
		 * in the kernel image
		 */
		errmsg = "denied-hash-algorithm";
	} else {
		if (likely(xattr_hash_algo == ima_hash_algo))
			return 0;

		/* allow any xattr using an algorithm built in the kernel */
		if (crypto_has_alg(hash_algo_name[xattr_hash_algo], 0, 0))
			return 0;
	}

	pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
	if (!pathbuf)
		return -EACCES;

	path = dentry_path(dentry, pathbuf, PATH_MAX);

	integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), path,
			    "set_data", errmsg, -EACCES, 0);

	kfree(pathbuf);

	return -EACCES;
}

int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
		       const void *xattr_value, size_t xattr_value_len)
{
@@ -592,9 +653,11 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
		digsig = (xvalue->type == EVM_XATTR_PORTABLE_DIGSIG);
	}
	if (result == 1 || evm_revalidate_status(xattr_name)) {
		result = validate_hash_algo(dentry, xvalue, xattr_value_len);
		if (result)
			return result;

		ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
		if (result == 1)
			result = 0;
	}
	return result;
}
Loading