From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M 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: Roberto Sassu roberto.sassu@huawei.com Signed-off-by: Tianxing Zhang zhangtianxing3@huawei.com Reviewed-by: Jason Yan yanaijie@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com Signed-off-by: zhoushuiqing zhoushuiqing2@huawei.com --- security/integrity/evm/evm_crypto.c | 15 +++++ security/integrity/evm/evm_main.c | 99 ++++++++++++++++++++++++++++- 2 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 033804f5a..f3835b477 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -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; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cf24c5255..47ac6e9e7 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -128,7 +128,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; @@ -145,6 +149,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++; }
@@ -173,9 +181,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)) @@ -189,7 +208,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) @@ -197,7 +220,23 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, } else if (rc == -EOPNOTSUPP) { evm_status = INTEGRITY_UNKNOWN; } - goto out; +#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; @@ -255,12 +294,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) @@ -273,7 +360,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, out: if (iint) iint->evm_status = evm_status; - kfree(xattr_data); +#ifdef CONFIG_IMA_DIGEST_LIST + if (xattr_data != (struct evm_ima_xattr_data *)&evm_fake_xattr) +#endif + kfree(xattr_data); return evm_status; }
@@ -581,7 +671,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,