From: Roberto Sassu roberto.sassu@huawei.com
euleros inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I7QZ2M CVE: NA
-------------------------------------------------
IMA-Appraise grants access to files with a valid signature or with actual file digest equal to the digest included in security.ima.
This patch adds support for appraisal based on digest lists. Instead of using the reference value from security.ima, this patch checks if the calculated file digest is included in the uploaded digest lists.
This functionality must be explicitly enabled by providing one of the following values for the ima_appraise_digest_list= kernel option:
- digest: this mode enables appraisal verification with digest lists until EVM is initialized; after that, EVM verification must be successful even if the file digest is found in a digest list;
- digest-nometadata: this mode enables appraisal verification with digest lists even after EVM has been initialized; files without security.evm are allowed if the digest of the content is found in the digest list, and security.evm is created with current values of xattrs (trust at first use); all files created in this way will have the new security.ima type EVM_IMA_XATTR_DIGEST_LIST; they can be accessed later only if this mode has been selected.
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 --- .../admin-guide/kernel-parameters.txt | 9 ++ security/integrity/ima/ima.h | 10 ++ security/integrity/ima/ima_appraise.c | 106 +++++++++++++++++- security/integrity/ima/ima_main.c | 8 +- security/integrity/integrity.h | 3 + 5 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4394c25a0..eb4718bb9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1954,6 +1954,15 @@ "enforce-evm" | "log-evm" } default: "enforce"
+ ima_appraise_digest_list= [IMA] + Format: { "digest" | "digest-nometadata" } + + digest: enables appraisal of files with digest lists + until EVM is initialized. + + digest-nometadata: enables appraisal of files with + digest lists even after EVM is initialized. + ima_appraise_tcb [IMA] Deprecated. Use ima_policy= instead. The builtin appraise policy appraises all files owned by uid=0. diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 41879cb5b..9ba7bff50 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -358,7 +358,12 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, +#ifdef CONFIG_IMA_DIGEST_LIST + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest); +#else int xattr_len, const struct modsig *modsig); +#endif int ima_must_appraise(struct mnt_idmap *idmap, struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); @@ -384,7 +389,12 @@ static inline int ima_appraise_measurement(enum ima_hooks func, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, +#ifdef CONFIG_IMA_DIGEST_LIST + const struct modsig *modsig, + struct ima_digest *found_digest) +#else const struct modsig *modsig) +#endif { return INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index d133c13ef..a4178ed86 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -18,6 +18,9 @@ #include <uapi/linux/fsverity.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST +#include "ima_digest_list.h" +#endif
#ifdef CONFIG_IMA_DIGEST_LIST static bool ima_appraise_req_evm __ro_after_init; @@ -63,6 +66,22 @@ void __init ima_appraise_parse_cmdline(void) } #endif
+#ifdef CONFIG_IMA_DIGEST_LIST +static bool ima_appraise_no_metadata __ro_after_init; +static int __init appraise_digest_list_setup(char *str) +{ + if (!strncmp(str, "digest", 6)) { + ima_digest_list_actions |= IMA_APPRAISE; + + if (!strcmp(str + 6, "-nometadata")) + ima_appraise_no_metadata = true; + } + + return 1; +} +__setup("ima_appraise_digest_list=", appraise_digest_list_setup); +#endif + /* * is_ima_appraise_enabled - return appraise status * @@ -104,6 +123,11 @@ static int ima_fix_xattr(struct dentry *dentry, } else { offset = 0; iint->ima_hash->xattr.ng.type = IMA_XATTR_DIGEST_NG; +#ifdef CONFIG_IMA_DIGEST_LIST + if (test_bit(IMA_DIGEST_LIST, &iint->atomic_flags)) + iint->ima_hash->xattr.ng.type = + EVM_IMA_XATTR_DIGEST_LIST; +#endif iint->ima_hash->xattr.ng.algo = algo; } rc = __vfs_setxattr_noperm(&nop_mnt_idmap, dentry, XATTR_NAME_IMA, @@ -288,20 +312,46 @@ static int calc_file_id_hash(enum evm_ima_xattr_type type, */ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, +#ifdef CONFIG_IMA_DIGEST_LIST + enum integrity_status *status, const char **cause, + struct ima_digest *found_digest) +#else enum integrity_status *status, const char **cause) +#endif { struct ima_max_digest_data hash; struct signature_v2_hdr *sig; int rc = -EINVAL, hash_start = 0; int mask;
+#ifdef CONFIG_IMA_DIGEST_LIST + if (found_digest && *status != INTEGRITY_PASS && + *status != INTEGRITY_PASS_IMMUTABLE) + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); +#endif switch (xattr_value->type) { +#ifdef CONFIG_IMA_DIGEST_LIST + case EVM_IMA_XATTR_DIGEST_LIST: + set_bit(IMA_DIGEST_LIST, &iint->atomic_flags); + + if (!ima_appraise_no_metadata) { + *cause = "IMA-xattr-untrusted"; + *status = INTEGRITY_FAIL; + break; + } + fallthrough; +#endif case IMA_XATTR_DIGEST_NG: /* first byte contains algorithm id */ hash_start = 1; fallthrough; case IMA_XATTR_DIGEST: +#ifdef CONFIG_IMA_DIGEST_LIST + if (*status != INTEGRITY_PASS_IMMUTABLE && + (!found_digest || !ima_digest_is_immutable(found_digest))) { +#else if (*status != INTEGRITY_PASS_IMMUTABLE) { +#endif if (iint->flags & IMA_DIGSIG_REQUIRED) { if (iint->flags & IMA_VERITY_REQUIRED) *cause = "verity-signature-required"; @@ -489,14 +539,25 @@ int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, +#ifdef CONFIG_IMA_DIGEST_LIST + int xattr_len, const struct modsig *modsig, + struct ima_digest *found_digest) +#else int xattr_len, const struct modsig *modsig) + +#endif { static const char op[] = "appraise_data"; const char *cause = "unknown"; struct dentry *dentry = file_dentry(file); struct inode *inode = d_backing_inode(dentry); enum integrity_status status = INTEGRITY_UNKNOWN; +#ifdef CONFIG_IMA_DIGEST_LIST + int rc = xattr_len, rc_evm; + char _buf[sizeof(struct evm_ima_xattr_data) + 1 + SHA512_DIGEST_SIZE]; +#else int rc = xattr_len; +#endif bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
/* If not appraising a modsig, we need an xattr. */ @@ -507,6 +568,26 @@ int ima_appraise_measurement(enum ima_hooks func, if (xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG && xattr_len == sizeof(struct signature_v2_hdr)) rc = -ENODATA; + + if (rc == -ENODATA && found_digest && + !(file->f_mode & FMODE_CREATED)) { + struct evm_ima_xattr_data *xattr_data = NULL; + + rc_evm = vfs_getxattr_alloc(&nop_mnt_idmap, dentry, XATTR_NAME_EVM, + (char **)&xattr_data, 0, GFP_NOFS); + if (rc_evm > 0) { + kfree(xattr_data); + } else { + xattr_value = (struct evm_ima_xattr_data *)_buf; + xattr_value->type = IMA_XATTR_DIGEST_NG; + xattr_value->data[0] = found_digest->algo; + memcpy(&xattr_value->data[1], found_digest->digest, + hash_digest_size[found_digest->algo]); + xattr_len = hash_digest_size[found_digest->algo] + 2; + rc = xattr_len; + } + } + #endif /* If reading the xattr failed and there's no modsig, error out. */ if (rc <= 0 && !try_modsig) { @@ -543,7 +624,7 @@ int ima_appraise_measurement(enum ima_hooks func, case INTEGRITY_UNKNOWN: #ifdef CONFIG_IMA_DIGEST_LIST if (ima_appraise_req_evm && - xattr_value->type != EVM_IMA_XATTR_DIGSIG) + xattr_value->type != EVM_IMA_XATTR_DIGSIG && !found_digest) goto out; #endif break; @@ -553,6 +634,25 @@ int ima_appraise_measurement(enum ima_hooks func, break; fallthrough; case INTEGRITY_NOLABEL: /* No security.evm xattr. */ +#ifdef CONFIG_IMA_DIGEST_LIST + /* + * If the digest-nometadata mode is selected, allow access + * without metadata check. EVM will eventually create an HMAC + * based on current xattr values. + */ + if (ima_appraise_no_metadata && found_digest) + break; + /* Allow access to digest lists without metadata, only if they + * are signed or found in a digest list (immutable) + */ + if (func == DIGEST_LIST_CHECK) { + if (xattr_value->type == EVM_IMA_XATTR_DIGSIG) + break; + if (found_digest && + ima_digest_is_immutable(found_digest)) + break; + } +#endif cause = "missing-HMAC"; goto out; case INTEGRITY_FAIL_IMMUTABLE: @@ -568,7 +668,11 @@ int ima_appraise_measurement(enum ima_hooks func,
if (xattr_value) rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, +#ifdef CONFIG_IMA_DIGEST_LIST + &cause, found_digest); +#else &cause); +#endif
/* * If we have a modsig and either no imasig or the imasig's key isn't diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 800441224..d431fb7ec 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -441,8 +441,14 @@ static int process_measurement(struct file *file, const struct cred *cred, if (rc != -EPERM) { inode_lock(inode); rc = ima_appraise_measurement(func, iint, file, - pathname, xattr_value, + pathname, xattr_value, +#ifdef CONFIG_IMA_DIGEST_LIST + xattr_len, modsig, + ima_digest_allow(found_digest, + IMA_APPRAISE)); +#else xattr_len, modsig); +#endif inode_unlock(inode); } if (!rc) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 00688b5bd..65d776e2e 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -76,6 +76,9 @@ #define IMA_CHANGE_ATTR 2 #define IMA_DIGSIG 3 #define IMA_MUST_MEASURE 4 +#ifdef CONFIG_IMA_DIGEST_LIST +#define IMA_DIGEST_LIST 5 +#endif
enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01,