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

Commit 3c29548f authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'next-integrity' of...

Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull integrity updates from James Morris:
 "This contains a mixture of bug fixes, code cleanup, and new
  functionality. Of note is the integrity cache locking fix, file change
  detection, and support for a new EVM portable and immutable signature
  type.

  The re-introduction of the integrity cache lock (iint) fixes the
  problem of attempting to take the i_rwsem shared a second time, when
  it was previously taken exclusively. Defining atomic flags resolves
  the original iint/i_rwsem circular locking - accessing the file data
  vs. modifying the file metadata. Although it fixes the O_DIRECT
  problem as well, a subsequent patch is needed to remove the explicit
  O_DIRECT prevention.

  For performance reasons, detecting when a file has changed and needs
  to be re-measured, re-appraised, and/or re-audited, was limited to
  after the last writer has closed, and only if the file data has
  changed. Detecting file change is based on i_version. For filesystems
  that do not support i_version, remote filesystems, or userspace
  filesystems, the file was measured, appraised and/or audited once and
  never re-evaluated. Now local filesystems, which do not support
  i_version or are not mounted with the i_version option, assume the
  file has changed and are required to re-evaluate the file. This change
  does not address detecting file change on remote or userspace
  filesystems.

  Unlike file data signatures, which can be included and distributed in
  software packages (eg. rpm, deb), the existing EVM signature, which
  protects the file metadata, could not be included in software
  packages, as it includes file system specific information (eg. i_ino,
  possibly the UUID). This pull request defines a new EVM portable and
  immutable file metadata signature format, which can be included in
  software packages"

* 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security:
  ima/policy: fix parsing of fsuuid
  ima: Use i_version only when filesystem supports it
  integrity: remove unneeded initializations in integrity_iint_cache entries
  ima: log message to module appraisal error
  ima: pass filename to ima_rdwr_violation_check()
  ima: Fix line continuation format
  ima: support new "hash" and "dont_hash" policy actions
  ima: re-introduce own integrity cache lock
  EVM: Add support for portable signature format
  EVM: Allow userland to permit modification of EVM-protected metadata
  ima: relax requiring a file signature for new files with zero length
parents e1c70f32 36447456
Loading
Loading
Loading
Loading
+35 −19
Original line number Diff line number Diff line
@@ -14,30 +14,46 @@ Description:
		generated either locally or remotely using an
		asymmetric key. These keys are loaded onto root's
		keyring using keyctl, and EVM is then enabled by
		echoing a value to <securityfs>/evm:
		echoing a value to <securityfs>/evm made up of the
		following bits:

		1: enable HMAC validation and creation
		2: enable digital signature validation
		3: enable HMAC and digital signature validation and HMAC
		   creation
		Bit	  Effect
		0	  Enable HMAC validation and creation
		1	  Enable digital signature validation
		2	  Permit modification of EVM-protected metadata at
			  runtime. Not supported if HMAC validation and
			  creation is enabled.
		31	  Disable further runtime modification of EVM policy

		Further writes will be blocked if HMAC support is enabled or
		if bit 32 is set:
		For example:

		echo 0x80000002 ><securityfs>/evm
		echo 1 ><securityfs>/evm

		will enable digital signature validation and block
		further writes to <securityfs>/evm.
		will enable HMAC validation and creation

		Until this is done, EVM can not create or validate the
		'security.evm' xattr, but returns INTEGRITY_UNKNOWN.
		Loading keys and signaling EVM should be done as early
		as possible.  Normally this is done in the initramfs,
		which has already been measured as part of the trusted
		boot.  For more information on creating and loading
		existing trusted/encrypted keys, refer to:
		echo 0x80000003 ><securityfs>/evm

		Documentation/security/keys/trusted-encrypted.rst. Both dracut
		(via 97masterkey and 98integrity) and systemd (via
		will enable HMAC and digital signature validation and
		HMAC creation and disable all further modification of policy.

		echo 0x80000006 ><securityfs>/evm

		will enable digital signature validation, permit
		modification of EVM-protected metadata and
		disable all further modification of policy

		Note that once a key has been loaded, it will no longer be
		possible to enable metadata modification.

		Until key loading has been signaled EVM can not create
		or validate the 'security.evm' xattr, but returns
		INTEGRITY_UNKNOWN.  Loading keys and signaling EVM
		should be done as early as possible.  Normally this is
		done in the initramfs, which has already been measured
		as part of the trusted boot.  For more information on
		creating and loading existing trusted/encrypted keys,
		refer to:
		Documentation/security/keys/trusted-encrypted.rst. Both
		dracut (via 97masterkey and 98integrity) and systemd (via
		core/ima-setup) have support for loading keys at boot
		time.
+2 −1
Original line number Diff line number Diff line
@@ -17,7 +17,8 @@ Description:

		rule format: action [condition ...]

		action: measure | dont_measure | appraise | dont_appraise | audit
		action: measure | dont_measure | appraise | dont_appraise |
			audit | hash | dont_hash
		condition:= base | lsm  [option]
			base:	[[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
				[euid=] [fowner=]]
+1 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@

enum integrity_status {
	INTEGRITY_PASS = 0,
	INTEGRITY_PASS_IMMUTABLE,
	INTEGRITY_FAIL,
	INTEGRITY_NOLABEL,
	INTEGRITY_NOXATTRS,
+6 −3
Original line number Diff line number Diff line
@@ -23,9 +23,12 @@

#define EVM_INIT_HMAC	0x0001
#define EVM_INIT_X509	0x0002
#define EVM_SETUP       0x80000000 /* userland has signaled key load */
#define EVM_ALLOW_METADATA_WRITES	0x0004
#define EVM_SETUP_COMPLETE 0x80000000 /* userland has signaled key load */

#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP)
#define EVM_KEY_MASK (EVM_INIT_HMAC | EVM_INIT_X509)
#define EVM_INIT_MASK (EVM_INIT_HMAC | EVM_INIT_X509 | EVM_SETUP_COMPLETE | \
		       EVM_ALLOW_METADATA_WRITES)

extern int evm_initialized;
extern char *evm_hmac;
@@ -51,7 +54,7 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,
		  size_t req_xattr_value_len, char *digest);
int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
		  const char *req_xattr_value,
		  size_t req_xattr_value_len, char *digest);
		  size_t req_xattr_value_len, char type, char *digest);
int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
		  char *hmac_val);
int evm_init_secfs(void);
+66 −9
Original line number Diff line number Diff line
@@ -138,7 +138,7 @@ static struct shash_desc *init_desc(char type)
 * protection.)
 */
static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
			  char *digest)
			  char type, char *digest)
{
	struct h_misc {
		unsigned long ino;
@@ -149,8 +149,13 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
	} hmac_misc;

	memset(&hmac_misc, 0, sizeof(hmac_misc));
	/* Don't include the inode or generation number in portable
	 * signatures
	 */
	if (type != EVM_XATTR_PORTABLE_DIGSIG) {
		hmac_misc.ino = inode->i_ino;
		hmac_misc.generation = inode->i_generation;
	}
	/* The hmac uid and gid must be encoded in the initial user
	 * namespace (not the filesystems user namespace) as encoding
	 * them in the filesystems user namespace allows an attack
@@ -163,7 +168,8 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
	hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
	hmac_misc.mode = inode->i_mode;
	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
	if (evm_hmac_attrs & EVM_ATTR_FSUUID)
	if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
	    type != EVM_XATTR_PORTABLE_DIGSIG)
		crypto_shash_update(desc, &inode->i_sb->s_uuid.b[0],
				    sizeof(inode->i_sb->s_uuid));
	crypto_shash_final(desc, digest);
@@ -189,6 +195,7 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
	char *xattr_value = NULL;
	int error;
	int size;
	bool ima_present = false;

	if (!(inode->i_opflags & IOP_XATTR))
		return -EOPNOTSUPP;
@@ -199,11 +206,18 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,

	error = -ENODATA;
	for (xattrname = evm_config_xattrnames; *xattrname != NULL; xattrname++) {
		bool is_ima = false;

		if (strcmp(*xattrname, XATTR_NAME_IMA) == 0)
			is_ima = true;

		if ((req_xattr_name && req_xattr_value)
		    && !strcmp(*xattrname, req_xattr_name)) {
			error = 0;
			crypto_shash_update(desc, (const u8 *)req_xattr_value,
					     req_xattr_value_len);
			if (is_ima)
				ima_present = true;
			continue;
		}
		size = vfs_getxattr_alloc(dentry, *xattrname,
@@ -218,9 +232,14 @@ static int evm_calc_hmac_or_hash(struct dentry *dentry,
		error = 0;
		xattr_size = size;
		crypto_shash_update(desc, (const u8 *)xattr_value, xattr_size);
		if (is_ima)
			ima_present = true;
	}
	hmac_add_misc(desc, inode, digest);
	hmac_add_misc(desc, inode, type, digest);

	/* Portable EVM signatures must include an IMA hash */
	if (type == EVM_XATTR_PORTABLE_DIGSIG && !ima_present)
		return -EPERM;
out:
	kfree(xattr_value);
	kfree(desc);
@@ -237,12 +256,40 @@ int evm_calc_hmac(struct dentry *dentry, const char *req_xattr_name,

int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
		  const char *req_xattr_value, size_t req_xattr_value_len,
		  char *digest)
		  char type, char *digest)
{
	return evm_calc_hmac_or_hash(dentry, req_xattr_name, req_xattr_value,
				req_xattr_value_len, IMA_XATTR_DIGEST, digest);
				     req_xattr_value_len, type, digest);
}

static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
{
	const struct evm_ima_xattr_data *xattr_data = NULL;
	struct integrity_iint_cache *iint;
	int rc = 0;

	iint = integrity_iint_find(inode);
	if (iint && (iint->flags & EVM_IMMUTABLE_DIGSIG))
		return 1;

	/* Do this the hard way */
	rc = vfs_getxattr_alloc(dentry, XATTR_NAME_EVM, (char **)&xattr_data, 0,
				GFP_NOFS);
	if (rc <= 0) {
		if (rc == -ENODATA)
			return 0;
		return rc;
	}
	if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
		rc = 1;
	else
		rc = 0;

	kfree(xattr_data);
	return rc;
}


/*
 * Calculate the hmac and update security.evm xattr
 *
@@ -255,6 +302,16 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name,
	struct evm_ima_xattr_data xattr_data;
	int rc = 0;

	/*
	 * Don't permit any transformation of the EVM xattr if the signature
	 * is of an immutable type
	 */
	rc = evm_is_immutable(dentry, inode);
	if (rc < 0)
		return rc;
	if (rc)
		return -EPERM;

	rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
			   xattr_value_len, xattr_data.digest);
	if (rc == 0) {
@@ -280,7 +337,7 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr,
	}

	crypto_shash_update(desc, lsm_xattr->value, lsm_xattr->value_len);
	hmac_add_misc(desc, inode, hmac_val);
	hmac_add_misc(desc, inode, EVM_XATTR_HMAC, hmac_val);
	kfree(desc);
	return 0;
}
Loading