diff options
Diffstat (limited to 'security/integrity/ima/ima_appraise.c')
| -rw-r--r-- | security/integrity/ima/ima_appraise.c | 120 | 
1 files changed, 95 insertions, 25 deletions
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2d4becab891..d3113d4aaa3 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,6 +15,7 @@  #include <linux/magic.h>  #include <linux/ima.h>  #include <linux/evm.h> +#include <crypto/hash_info.h>  #include "ima.h" @@ -43,19 +44,31 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)  }  static int ima_fix_xattr(struct dentry *dentry, -			  struct integrity_iint_cache *iint) +			 struct integrity_iint_cache *iint)  { -	iint->ima_xattr.type = IMA_XATTR_DIGEST; -	return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, -				     (u8 *)&iint->ima_xattr, -				      sizeof(iint->ima_xattr), 0); +	int rc, offset; +	u8 algo = iint->ima_hash->algo; + +	if (algo <= HASH_ALGO_SHA1) { +		offset = 1; +		iint->ima_hash->xattr.sha1.type = IMA_XATTR_DIGEST; +	} else { +		offset = 0; +		iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; +		iint->ima_hash->xattr.ng.algo = algo; +	} +	rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, +				   &iint->ima_hash->xattr.data[offset], +				   (sizeof(iint->ima_hash->xattr) - offset) + +				   iint->ima_hash->length, 0); +	return rc;  }  /* Return specific func appraised cached result */  enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  					   int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		return iint->ima_mmap_status;  	case BPRM_CHECK: @@ -71,7 +84,7 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,  static void ima_set_cache_status(struct integrity_iint_cache *iint,  				 int func, enum integrity_status status)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->ima_mmap_status = status;  		break; @@ -90,7 +103,7 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint,  static void ima_cache_flags(struct integrity_iint_cache *iint, int func)  { -	switch(func) { +	switch (func) {  	case MMAP_CHECK:  		iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);  		break; @@ -107,6 +120,50 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)  	}  } +void ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len, +		       struct ima_digest_data *hash) +{ +	struct signature_v2_hdr *sig; + +	if (!xattr_value || xattr_len < 2) +		return; + +	switch (xattr_value->type) { +	case EVM_IMA_XATTR_DIGSIG: +		sig = (typeof(sig))xattr_value; +		if (sig->version != 2 || xattr_len <= sizeof(*sig)) +			return; +		hash->algo = sig->hash_algo; +		break; +	case IMA_XATTR_DIGEST_NG: +		hash->algo = xattr_value->digest[0]; +		break; +	case IMA_XATTR_DIGEST: +		/* this is for backward compatibility */ +		if (xattr_len == 21) { +			unsigned int zero = 0; +			if (!memcmp(&xattr_value->digest[16], &zero, 4)) +				hash->algo = HASH_ALGO_MD5; +			else +				hash->algo = HASH_ALGO_SHA1; +		} else if (xattr_len == 17) +			hash->algo = HASH_ALGO_MD5; +		break; +	} +} + +int ima_read_xattr(struct dentry *dentry, +		   struct evm_ima_xattr_data **xattr_value) +{ +	struct inode *inode = dentry->d_inode; + +	if (!inode->i_op->getxattr) +		return 0; + +	return vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, +				  0, GFP_NOFS); +} +  /*   * ima_appraise_measurement - appraise file measurement   * @@ -116,23 +173,22 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func)   * Return 0 on success, error code otherwise   */  int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, -			     struct file *file, const unsigned char *filename) +			     struct file *file, const unsigned char *filename, +			     struct evm_ima_xattr_data *xattr_value, +			     int xattr_len)  { +	static const char op[] = "appraise_data"; +	char *cause = "unknown";  	struct dentry *dentry = file->f_dentry;  	struct inode *inode = dentry->d_inode; -	struct evm_ima_xattr_data *xattr_value = NULL;  	enum integrity_status status = INTEGRITY_UNKNOWN; -	const char *op = "appraise_data"; -	char *cause = "unknown"; -	int rc; +	int rc = xattr_len, hash_start = 0;  	if (!ima_appraise)  		return 0;  	if (!inode->i_op->getxattr)  		return INTEGRITY_UNKNOWN; -	rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, -				0, GFP_NOFS);  	if (rc <= 0) {  		if (rc && rc != -ENODATA)  			goto out; @@ -153,14 +209,25 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  		goto out;  	}  	switch (xattr_value->type) { +	case IMA_XATTR_DIGEST_NG: +		/* first byte contains algorithm id */ +		hash_start = 1;  	case IMA_XATTR_DIGEST:  		if (iint->flags & IMA_DIGSIG_REQUIRED) {  			cause = "IMA signature required";  			status = INTEGRITY_FAIL;  			break;  		} -		rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, -			    IMA_DIGEST_SIZE); +		if (xattr_len - sizeof(xattr_value->type) - hash_start >= +				iint->ima_hash->length) +			/* xattr length may be longer. md5 hash in previous +			   version occupied 20 bytes in xattr, instead of 16 +			 */ +			rc = memcmp(&xattr_value->digest[hash_start], +				    iint->ima_hash->digest, +				    iint->ima_hash->length); +		else +			rc = -EINVAL;  		if (rc) {  			cause = "invalid-hash";  			status = INTEGRITY_FAIL; @@ -171,9 +238,9 @@ int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,  	case EVM_IMA_XATTR_DIGSIG:  		iint->flags |= IMA_DIGSIG;  		rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, -					     xattr_value->digest, rc - 1, -					     iint->ima_xattr.digest, -					     IMA_DIGEST_SIZE); +					     (const char *)xattr_value, rc, +					     iint->ima_hash->digest, +					     iint->ima_hash->length);  		if (rc == -EOPNOTSUPP) {  			status = INTEGRITY_UNKNOWN;  		} else if (rc) { @@ -203,7 +270,6 @@ out:  		ima_cache_flags(iint, func);  	}  	ima_set_cache_status(iint, func, status); -	kfree(xattr_value);  	return status;  } @@ -219,7 +285,7 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)  	if (iint->flags & IMA_DIGSIG)  		return; -	rc = ima_collect_measurement(iint, file); +	rc = ima_collect_measurement(iint, file, NULL, NULL);  	if (rc < 0)  		return; @@ -275,7 +341,7 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,  	return 0;  } -static void ima_reset_appraise_flags(struct inode *inode) +static void ima_reset_appraise_flags(struct inode *inode, int digsig)  {  	struct integrity_iint_cache *iint; @@ -287,18 +353,22 @@ static void ima_reset_appraise_flags(struct inode *inode)  		return;  	iint->flags &= ~IMA_DONE_MASK; +	if (digsig) +		iint->flags |= IMA_DIGSIG;  	return;  }  int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,  		       const void *xattr_value, size_t xattr_value_len)  { +	const struct evm_ima_xattr_data *xvalue = xattr_value;  	int result;  	result = ima_protect_xattr(dentry, xattr_name, xattr_value,  				   xattr_value_len);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, +			 (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);  		result = 0;  	}  	return result; @@ -310,7 +380,7 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)  	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);  	if (result == 1) { -		ima_reset_appraise_flags(dentry->d_inode); +		ima_reset_appraise_flags(dentry->d_inode, 0);  		result = 0;  	}  	return result;  | 
