RE: [RFC] EVM: Add support for portable signature format

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




> -----Original Message-----
> From: Matthew Garrett [mailto:mjg59@xxxxxxxxxx]
> Sent: 26 October 2017 11:32
> To: linux-integrity@xxxxxxxxxxxxxxx
> Cc: zohar@xxxxxxxxxxxxxxxxxx; Matthew Garrett <mjg59@xxxxxxxxxx>;
> Dmitry Kasatkin <dmitry.kasatkin@xxxxxxxxxx>; Mikhail Kurinnoi
> <viewizard@xxxxxxxxxxxxx>
> 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@xxxxxxxxxx>
> Cc: Mikhail Kurinnoi <viewizard@xxxxxxxxxxxxx>
> ---
>  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;
>  	}
>  	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;

Something is wrong here?
When integrity verification pass, this code WILL ALLOW to change attribute.
But it is not possible... next time integrity verification will fail?
Or I miss something?

>  	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





[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux Kernel]     [Linux Kernel Hardening]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux SCSI]

  Powered by Linux