All of lore.kernel.org
 help / color / mirror / Atom feed
From: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
To: Matthew Garrett <mjg59@google.com>,
	"linux-integrity@vger.kernel.org"
	<linux-integrity@vger.kernel.org>
Cc: "zohar@linux.vnet.ibm.com" <zohar@linux.vnet.ibm.com>,
	Mikhail Kurinnoi <viewizard@viewizard.com>
Subject: RE: [RFC] EVM: Add support for portable signature format
Date: Fri, 27 Oct 2017 10:41:25 +0000	[thread overview]
Message-ID: <C2D7A727C393B644B70DF4B4DCFB60B921C471AC@FRAEML521-MBX.china.huawei.com> (raw)
In-Reply-To: <20171026083144.16247-1-mjg59@google.com>



> -----Original Message-----
> From: Matthew Garrett [mailto:mjg59@google.com]
> Sent: 26 October 2017 11:32
> To: linux-integrity@vger.kernel.org
> Cc: zohar@linux.vnet.ibm.com; Matthew Garrett <mjg59@google.com>;
> Dmitry Kasatkin <dmitry.kasatkin@huawei.com>; Mikhail Kurinnoi
> <viewizard@viewizard.com>
> Subject: [RFC] EVM: Add support for portable signature format
> 
> Ok I /think/ I have everything covered here. I'm away from my workstation
> this week so can't test this beyond building it right now, but thought I should
> send it out so people can check my reasoning.
> 
> The EVM signature includes the inode number and (optionally) the filesystem
> UUID, making it impractical to ship EVM signatures in packages. This patch
> adds a new portable format intended to allow distributions to include EVM
> signatures. It is identical to the existing format but hardcodes the inode and
> generation numbers to 0 and does not include the filesystem UUID even if
> the kernel is configured to do so.
> 
> Removing the inode means that the metadata and signature from one file
> could be copied to another file without invalidating it. This is avoided by
> ensuring that an IMA xattr is present during EVM validation.
> 
> Portable signatures are intended to be immutable - ie, they will never be
> transformed into HMACs. This is achieved via the following changes:
> 
> 1) An update is not triggered on initial xattr write
> 2) evm_verifyxattr returns INTEGRITY_PASS_IMMUTABLE for valid portable
> digital signatures and sets the EVM_IMMUTABLE_DIGSIG flag
> 3) ima_appraise_measurement() is updated to treat
> INTEGRITY_PASS_IMMUTABLE as valid, allowing IMA appraisal to pass
> 4) ima_update_xattr() does not update the IMA xattr if
> EVM_IMMUTABLE_DIGSIG is set
> 5) any other codepath that calls evm_update_evmxattr() treats
> INTEGRITY_PASS_IMMUTABLE as a failure, and prevents the write that would
> trigger an HMAC writeout
> 
> The only path that will still result in an update will be if IMA is in "fix" mode
> while a valid HMAC key is loaded.
> 
> Based on earlier work by Dmitry Kasatkin and Mikhail Kurinnoi.
> 
> Cc: Dmitry Kasatkin <dmitry.kasatkin@huawei.com>
> Cc: Mikhail Kurinnoi <viewizard@viewizard.com>
> ---
>  include/linux/integrity.h             |  1 +
>  security/integrity/evm/evm.h          |  2 +-
>  security/integrity/evm/evm_crypto.c   | 37 ++++++++++++++++++++++++++---
> ------
>  security/integrity/evm/evm_main.c     | 23 ++++++++++++++--------
>  security/integrity/ima/ima_appraise.c |  6 ++++--
>  security/integrity/integrity.h        |  2 ++
>  6 files changed, 51 insertions(+), 20 deletions(-)
> 
> diff --git a/include/linux/integrity.h b/include/linux/integrity.h index
> c2d6082a1a4c..858d3f4a2241 100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> @@ -14,6 +14,7 @@
> 
>  enum integrity_status {
>  	INTEGRITY_PASS = 0,
> +	INTEGRITY_PASS_IMMUTABLE,
>  	INTEGRITY_FAIL,
>  	INTEGRITY_NOLABEL,
>  	INTEGRITY_NOXATTRS,
> diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h
> index f5f12727771a..2ff02459fcfd 100644
> --- a/security/integrity/evm/evm.h
> +++ b/security/integrity/evm/evm.h
> @@ -48,7 +48,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);
> diff --git a/security/integrity/evm/evm_crypto.c
> b/security/integrity/evm/evm_crypto.c
> index 1d32cd20009a..8855722529d4 100644
> --- a/security/integrity/evm/evm_crypto.c
> +++ b/security/integrity/evm/evm_crypto.c
> @@ -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));
> -	hmac_misc.ino = inode->i_ino;
> -	hmac_misc.generation = inode->i_generation;
> +	/* 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);
> @@ -232,15 +251,15 @@ int evm_calc_hmac(struct dentry *dentry, const
> char *req_xattr_name,
>  		  char *digest)
>  {
>  	return evm_calc_hmac_or_hash(dentry, req_xattr_name,
> req_xattr_value,
> -				req_xattr_value_len, EVM_XATTR_HMAC,
> digest);
> +			       req_xattr_value_len, EVM_XATTR_HMAC, 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)
> +		  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);
>  }
> 
>  /*
> @@ -280,7 +299,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;
>  }
> diff --git a/security/integrity/evm/evm_main.c
> b/security/integrity/evm/evm_main.c
> index 063d38aef64e..7ab633eab576 100644
> --- a/security/integrity/evm/evm_main.c
> +++ b/security/integrity/evm/evm_main.c
> @@ -120,7 +120,8 @@ static enum integrity_status evm_verify_hmac(struct
> dentry *dentry,
>  	enum integrity_status evm_status = INTEGRITY_PASS;
>  	int rc, xattr_len;
> 
> -	if (iint && iint->evm_status == INTEGRITY_PASS)
> +	if (iint && (iint->evm_status == INTEGRITY_PASS ||
> +		     iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
>  		return iint->evm_status;
> 
>  	/* if status is not PASS, try to check again - against -ENOMEM */ @@
> -161,22 +162,25 @@ static enum integrity_status evm_verify_hmac(struct
> dentry *dentry,
>  			rc = -EINVAL;
>  		break;
>  	case EVM_IMA_XATTR_DIGSIG:
> +	case EVM_XATTR_PORTABLE_DIGSIG:
>  		rc = evm_calc_hash(dentry, xattr_name, xattr_value,
> -				xattr_value_len, calc.digest);
> +				   xattr_value_len, xattr_data->type,
> +				   calc.digest);
>  		if (rc)
>  			break;
>  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_EVM,
>  					(const char *)xattr_data, xattr_len,
>  					calc.digest, sizeof(calc.digest));
>  		if (!rc) {
> -			/* Replace RSA with HMAC if not mounted readonly
> and
> -			 * not immutable
> -			 */
> -			if (!IS_RDONLY(d_backing_inode(dentry)) &&
> -			    !IS_IMMUTABLE(d_backing_inode(dentry)))
> +			if (xattr_data->type ==
> EVM_XATTR_PORTABLE_DIGSIG) {
> +				iint->flags |= EVM_IMMUTABLE_DIGSIG;
> +				evm_status = INTEGRITY_PASS_IMMUTABLE;
> +			} else if (!IS_RDONLY(d_backing_inode(dentry)) &&
> +				   !IS_IMMUTABLE(d_backing_inode(dentry)))
> {
>  				evm_update_evmxattr(dentry, xattr_name,
>  						    xattr_value,
>  						    xattr_value_len);
> +			}
>  		}
>  		break;
>  	default:
> @@ -292,6 +296,7 @@ static int evm_protect_xattr(struct dentry *dentry,
> const char *xattr_name,
>  			return 0;
>  		evm_status = evm_verify_current_integrity(dentry);
>  		if ((evm_status == INTEGRITY_PASS) ||
> +		    (evm_status == INTEGRITY_PASS_IMMUTABLE) ||
>  		    (evm_status == INTEGRITY_NOXATTRS))
>  			return 0;
>  		goto out;
> @@ -345,7 +350,8 @@ int evm_inode_setxattr(struct dentry *dentry, const
> char *xattr_name,
>  	if (strcmp(xattr_name, XATTR_NAME_EVM) == 0) {
>  		if (!xattr_value_len)
>  			return -EINVAL;
> -		if (xattr_data->type != EVM_IMA_XATTR_DIGSIG)
> +		if (xattr_data->type != EVM_IMA_XATTR_DIGSIG &&
> +		    xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
>  			return -EPERM;
>  	}

Also I have an impression that evm_protect_xattr will allow to set security.ima for example,
And it will cause to try to re-calculate hmac over immutable signature...



>  	return evm_protect_xattr(dentry, xattr_name, xattr_value, @@ -
> 432,6 +438,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr
> *attr)
>  		return 0;
>  	evm_status = evm_verify_current_integrity(dentry);
>  	if ((evm_status == INTEGRITY_PASS) ||
> +	    (evm_status == INTEGRITY_PASS_IMMUTABLE) ||
>  	    (evm_status == INTEGRITY_NOXATTRS))
>  		return 0;
>  	integrity_audit_msg(AUDIT_INTEGRITY_METADATA,
> d_backing_inode(dentry), diff --git a/security/integrity/ima/ima_appraise.c
> b/security/integrity/ima/ima_appraise.c
> index 809ba70fbbbf..6e277c5c7829 100644
> --- a/security/integrity/ima/ima_appraise.c
> +++ b/security/integrity/ima/ima_appraise.c
> @@ -229,7 +229,9 @@ int ima_appraise_measurement(enum ima_hooks
> func,
>  	}
> 
>  	status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc,
> iint);
> -	if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN))
> {
> +	if ((status != INTEGRITY_PASS) &&
> +	    (status != INTEGRITY_PASS_IMMUTABLE) &&
> +	    (status != INTEGRITY_UNKNOWN)) {
>  		if ((status == INTEGRITY_NOLABEL)
>  		    || (status == INTEGRITY_NOXATTRS))
>  			cause = "missing-HMAC";
> @@ -317,7 +319,7 @@ void ima_update_xattr(struct integrity_iint_cache
> *iint, struct file *file)
>  	int rc = 0;
> 
>  	/* do not collect and update hash for digital signatures */
> -	if (iint->flags & IMA_DIGSIG)
> +	if (iint->flags & IMA_DIGSIG || iint->flags &
> EVM_IMMUTABLE_DIGSIG)
>  		return;
> 
>  	rc = ima_collect_measurement(iint, file, NULL, 0, ima_hash_algo); diff
> --git a/security/integrity/integrity.h b/security/integrity/integrity.h index
> a53e7e4ab06c..cbc7de33fac7 100644
> --- a/security/integrity/integrity.h
> +++ b/security/integrity/integrity.h
> @@ -33,6 +33,7 @@
>  #define IMA_DIGSIG_REQUIRED	0x02000000
>  #define IMA_PERMIT_DIRECTIO	0x04000000
>  #define IMA_NEW_FILE		0x08000000
> +#define EVM_IMMUTABLE_DIGSIG	0x10000000
> 
>  #define IMA_DO_MASK		(IMA_MEASURE | IMA_APPRAISE |
> IMA_AUDIT | \
>  				 IMA_APPRAISE_SUBMASK)
> @@ -58,6 +59,7 @@ enum evm_ima_xattr_type {
>  	EVM_XATTR_HMAC,
>  	EVM_IMA_XATTR_DIGSIG,
>  	IMA_XATTR_DIGEST_NG,
> +	EVM_XATTR_PORTABLE_DIGSIG,
>  	IMA_XATTR_LAST
>  };
> 
> --
> 2.15.0.rc2.357.g7e34df9404-goog

  parent reply	other threads:[~2017-10-27 10:41 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-10-26  8:31 [RFC] EVM: Add support for portable signature format Matthew Garrett
2017-10-26  9:03 ` Mikhail Kurinnoi
2017-10-26  9:08   ` Matthew Garrett
2017-10-26  9:46     ` Mikhail Kurinnoi
2017-10-26 19:22       ` Mimi Zohar
2017-10-30 10:53         ` Matthew Garrett
2017-10-30 11:36           ` Mimi Zohar
2017-10-30 11:51             ` Matthew Garrett
2017-10-30 12:14               ` Mimi Zohar
2017-10-27 10:27 ` Dmitry Kasatkin
2017-10-30 12:30   ` Mimi Zohar
2017-10-30 13:21     ` Matthew Garrett
2017-10-30 13:58       ` Mimi Zohar
2017-10-30 14:04         ` Matthew Garrett
2017-10-27 10:41 ` Dmitry Kasatkin [this message]
2017-10-30 12:38   ` Mimi Zohar
2017-10-30 13:17     ` Matthew Garrett
2017-10-30 15:31       ` Mimi Zohar
2017-10-30 15:36         ` Matthew Garrett
2017-11-01 17:54           ` Matthew Garrett
2017-11-01 18:25             ` Mimi Zohar

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=C2D7A727C393B644B70DF4B4DCFB60B921C471AC@FRAEML521-MBX.china.huawei.com \
    --to=dmitry.kasatkin@huawei.com \
    --cc=linux-integrity@vger.kernel.org \
    --cc=mjg59@google.com \
    --cc=viewizard@viewizard.com \
    --cc=zohar@linux.vnet.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.