Commit cc87071e authored by Roberto Sassu's avatar Roberto Sassu Committed by zgzxx
Browse files

evm: Add support for digest lists of metadata

euleros inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I91FSN


CVE: NA

-------------------------------------------------

This patch adds support in EVM to verify file metadata digest with digest
lists. Metadata digest, calculated in the same way as for portable
signatures, is searched in the digest lists only if the file has the
security.evm xattr with type EVM_IMA_XATTR_DIGEST_LIST.

If the found digest is marked as immutable, content and xattr/attr updates
are not allowed. Otherwise, after verification, the existing security.evm
with the new type will be replaced with an HMAC, similarly to non-portable
signatures.

Signed-off-by: default avatarRoberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: default avatarTianxing Zhang <zhangtianxing3@huawei.com>
Reviewed-by: default avatarJason Yan <yanaijie@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
Signed-off-by: default avatarzhoushuiqing <zhoushuiqing2@huawei.com>
Signed-off-by: default avatarzhangguangzhi <zhangguangzhi3@huawei.com>
parent 0ed603d4
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -154,7 +154,12 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
	/* Don't include the inode or generation number in portable
	 * signatures
	 */
#ifdef CONFIG_IMA_DIGEST_LIST
	if (type != EVM_XATTR_PORTABLE_DIGSIG &&
	    type != EVM_IMA_XATTR_DIGEST_LIST) {
#else
	if (type != EVM_XATTR_PORTABLE_DIGSIG) {
#endif
		hmac_misc.ino = inode->i_ino;
		hmac_misc.generation = inode->i_generation;
	}
@@ -171,7 +176,12 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
	hmac_misc.mode = inode->i_mode;
	crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof(hmac_misc));
	if ((evm_hmac_attrs & EVM_ATTR_FSUUID) &&
#ifdef CONFIG_IMA_DIGEST_LIST
	    type != EVM_XATTR_PORTABLE_DIGSIG &&
	    type != EVM_IMA_XATTR_DIGEST_LIST)
#else
	    type != EVM_XATTR_PORTABLE_DIGSIG)
#endif
		crypto_shash_update(desc, (u8 *)&inode->i_sb->s_uuid, UUID_SIZE);
	crypto_shash_final(desc, digest);

@@ -337,7 +347,12 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode)
			rc = 0;
		goto out;
	}
#ifdef CONFIG_IMA_DIGEST_LIST
	if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG ||
		xattr_data->type == EVM_IMA_XATTR_DIGEST_LIST)
#else
	if (xattr_data->type == EVM_XATTR_PORTABLE_DIGSIG)
#endif
		rc = 1;
	else
		rc = 0;
+97 −2
Original line number Diff line number Diff line
@@ -129,7 +129,11 @@ static bool evm_hmac_disabled(void)
	return true;
}

#ifdef CONFIG_IMA_DIGEST_LIST
static int evm_find_protected_xattrs(struct dentry *dentry, int *ima_present)
#else
static int evm_find_protected_xattrs(struct dentry *dentry)
#endif
{
	struct inode *inode = d_backing_inode(dentry);
	struct xattr_list *xattr;
@@ -146,6 +150,10 @@ static int evm_find_protected_xattrs(struct dentry *dentry)
				continue;
			return error;
		}
#ifdef CONFIG_IMA_DIGEST_LIST
		if (!strcmp(xattr->name, XATTR_NAME_IMA))
			*ima_present = 1;
#endif
		count++;
	}

@@ -174,9 +182,20 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
	struct evm_ima_xattr_data *xattr_data = NULL;
	struct signature_v2_hdr *hdr;
	enum integrity_status evm_status = INTEGRITY_PASS;
#ifdef CONFIG_IMA_DIGEST_LIST
	enum integrity_status saved_evm_status = INTEGRITY_UNKNOWN;
	struct evm_digest digest;
	struct ima_digest *found_digest;
	struct inode *inode;
	struct signature_v2_hdr evm_fake_xattr = {
				.type = EVM_IMA_XATTR_DIGEST_LIST,
				.version = 2, .hash_algo = HASH_ALGO_SHA256 };
	int rc, xattr_len, evm_immutable = 0, ima_present = 0;
#else
	struct evm_digest digest;
	struct inode *inode;
	int rc, xattr_len, evm_immutable = 0;
#endif

	if (iint && (iint->evm_status == INTEGRITY_PASS ||
		     iint->evm_status == INTEGRITY_PASS_IMMUTABLE))
@@ -190,7 +209,11 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
	if (rc <= 0) {
		evm_status = INTEGRITY_FAIL;
		if (rc == -ENODATA) {
#ifdef CONFIG_IMA_DIGEST_LIST
			rc = evm_find_protected_xattrs(dentry, &ima_present);
#else
			rc = evm_find_protected_xattrs(dentry);
#endif
			if (rc > 0)
				evm_status = INTEGRITY_NOLABEL;
			else if (rc == 0)
@@ -198,7 +221,23 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
		} else if (rc == -EOPNOTSUPP) {
			evm_status = INTEGRITY_UNKNOWN;
		}
#ifdef CONFIG_IMA_DIGEST_LIST
		/* IMA added a fake xattr, set also EVM fake xattr */
		if (!ima_present && xattr_name &&
		    !strcmp(xattr_name, XATTR_NAME_IMA) &&
		    xattr_value_len > 2) {
			evm_fake_xattr.hash_algo =
			  ((struct evm_ima_xattr_data *)xattr_value)->data[0];
			xattr_data =
			  (struct evm_ima_xattr_data *)&evm_fake_xattr;
			rc = sizeof(evm_fake_xattr);
		}
		if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr)
#endif
			goto out;
#ifdef CONFIG_IMA_DIGEST_LIST
		saved_evm_status = evm_status;
#endif
	}

	xattr_len = rc;
@@ -256,12 +295,60 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
			}
		}
		break;
#ifdef CONFIG_IMA_DIGEST_LIST
	case EVM_IMA_XATTR_DIGEST_LIST:
		/* At this point, we cannot determine whether metadata are
		 * immutable or not. However, it is safe to return the
		 * fail_immutable error, as HMAC will not be created for this
		 * security.evm type.
		 */
		evm_immutable = 1;

		if (xattr_len < offsetof(struct signature_v2_hdr, keyid)) {
			evm_status = INTEGRITY_FAIL;
			goto out;
		}

		hdr = (struct signature_v2_hdr *)xattr_data;
		digest.hdr.algo = hdr->hash_algo;
		rc = evm_calc_hash(dentry, xattr_name, xattr_value,
				   xattr_value_len, xattr_data->type, &digest);
		if (rc)
			break;

		found_digest = ima_lookup_digest(digest.digest, hdr->hash_algo,
						 COMPACT_METADATA);
		if (!found_digest) {
			rc = -ENOENT;
			break;
		}

		if (!ima_digest_allow(found_digest, IMA_APPRAISE)) {
			rc = -EACCES;
			break;
		}

		if (ima_digest_is_immutable(found_digest)) {
			if (iint)
				iint->flags |= EVM_IMMUTABLE_DIGSIG;
			evm_status = INTEGRITY_PASS_IMMUTABLE;
		} else {
			evm_status = INTEGRITY_PASS;
		}
		break;
#endif /* CONFIG_IMA_DIGEST_LIST */
	default:
		rc = -EINVAL;
		break;
	}

#ifdef CONFIG_IMA_DIGEST_LIST
	if (rc && xattr_data == (struct evm_ima_xattr_data *)&evm_fake_xattr) {
		evm_status = saved_evm_status;
	} else if (rc) {
#else
	if (rc) {
#endif
		if (rc == -ENODATA)
			evm_status = INTEGRITY_NOXATTRS;
		else if (evm_immutable)
@@ -274,6 +361,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
out:
	if (iint)
		iint->evm_status = evm_status;
#ifdef CONFIG_IMA_DIGEST_LIST
	if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr)
#endif
		kfree(xattr_data);
	return evm_status;
}
@@ -582,7 +672,12 @@ int evm_inode_setxattr(struct mnt_idmap *idmap, struct dentry *dentry,
		if (!xattr_value_len)
			return -EINVAL;
		if (xattr_data->type != EVM_IMA_XATTR_DIGSIG &&
#ifdef CONFIG_IMA_DIGEST_LIST
		    xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG &&
			xattr_data->type != EVM_IMA_XATTR_DIGEST_LIST)
#else
		    xattr_data->type != EVM_XATTR_PORTABLE_DIGSIG)
#endif
			return -EPERM;
	}
	return evm_protect_xattr(idmap, dentry, xattr_name, xattr_value,