Isolate the IMA digest list code by using macros.
Signed-off-by: Zhou Shuiqing zhoushuiqing2@huawei.com --- fs/xattr.c | 4 + include/linux/ima.h | 8 + security/integrity/digsig_asymmetric.c | 4 + security/integrity/evm/evm.h | 2 + security/integrity/evm/evm_crypto.c | 38 +++- security/integrity/evm/evm_main.c | 146 +++++++++++++ security/integrity/iint.c | 4 + security/integrity/ima/ima.h | 80 ++++++- security/integrity/ima/ima_api.c | 39 ++++ security/integrity/ima/ima_appraise.c | 128 ++++++++++- security/integrity/ima/ima_fs.c | 247 +++++++++++++++++++++- security/integrity/ima/ima_init.c | 8 + security/integrity/ima/ima_main.c | 36 +++- security/integrity/ima/ima_policy.c | 80 +++++++ security/integrity/ima/ima_template.c | 2 + security/integrity/ima/ima_template_lib.c | 8 + security/integrity/ima/ima_template_lib.h | 2 + security/integrity/integrity.h | 15 +- security/security.c | 2 + 19 files changed, 840 insertions(+), 13 deletions(-)
diff --git a/fs/xattr.c b/fs/xattr.c index 149b8cf5f99f..c31266a83391 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -16,7 +16,9 @@ #include <linux/namei.h> #include <linux/security.h> #include <linux/evm.h> +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/ima.h> +#endif #include <linux/syscalls.h> #include <linux/export.h> #include <linux/fsnotify.h> @@ -475,7 +477,9 @@ __vfs_removexattr_locked(struct dentry *dentry, const char *name,
if (!error) { fsnotify_xattr(dentry); +#ifdef CONFIG_IMA_DIGEST_LIST ima_inode_post_removexattr(dentry, name); +#endif evm_inode_post_removexattr(dentry, name); }
diff --git a/include/linux/ima.h b/include/linux/ima.h index f7a088b2579e..713c6f9696cb 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -144,13 +144,17 @@ extern bool is_ima_appraise_enabled(void); extern void ima_inode_post_setattr(struct dentry *dentry); extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len); +#ifdef CONFIG_IMA_DIGEST_LIST extern void ima_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len); +#endif extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); +#ifdef CONFIG_IMA_DIGEST_LIST extern void ima_inode_post_removexattr(struct dentry *dentry, const char *xattr_name); +#endif #else static inline bool is_ima_appraise_enabled(void) { @@ -170,12 +174,14 @@ static inline int ima_inode_setxattr(struct dentry *dentry, return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST static inline void ima_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { } +#endif
static inline int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) @@ -183,10 +189,12 @@ static inline int ima_inode_removexattr(struct dentry *dentry, return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST static inline void ima_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { } +#endif #endif /* CONFIG_IMA_APPRAISE */
#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING) diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 92dc64755e53..72941f9b1b99 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -9,7 +9,9 @@ #include <linux/err.h> #include <linux/ratelimit.h> #include <linux/key-type.h> +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/verification.h> +#endif #include <crypto/public_key.h> #include <crypto/hash_info.h> #include <keys/asymmetric-type.h> @@ -55,6 +57,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) key = request_key(&key_type_asymmetric, name, NULL); }
+#ifdef CONFIG_IMA_DIGEST_LIST if (IS_ERR(key)) { #ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY keyring = VERIFY_USE_SECONDARY_KEYRING; @@ -63,6 +66,7 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) #endif key = search_trusted_key(keyring, &key_type_asymmetric, name); } +#endif
if (IS_ERR(key)) { if (keyring) diff --git a/security/integrity/evm/evm.h b/security/integrity/evm/evm.h index ca7ed2e532dc..f8b1627708a1 100644 --- a/security/integrity/evm/evm.h +++ b/security/integrity/evm/evm.h @@ -32,7 +32,9 @@ struct xattr_list { };
extern int evm_initialized; +#ifdef CONFIG_IMA_DIGEST_LIST extern enum hash_algo evm_hash_algo; +#endif
#define EVM_ATTR_FSUUID 0x0001
diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 7c36dbb96d24..fa8147c2294e 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -33,7 +33,11 @@ static DEFINE_MUTEX(mutex);
static unsigned long evm_set_key_flags;
+#ifdef CONFIG_IMA_DIGEST_LIST enum hash_algo evm_hash_algo __ro_after_init = HASH_ALGO_SHA1; +#else +static const char evm_hmac[] = "hmac(sha1)"; +#endif
/** * evm_set_key() - set EVM HMAC key from the kernel @@ -74,11 +78,13 @@ static struct shash_desc *init_desc(char type, uint8_t hash_algo) long rc; const char *algo; struct crypto_shash **tfm, *tmp_tfm; - char evm_hmac[CRYPTO_MAX_ALG_NAME]; struct shash_desc *desc; +#ifdef CONFIG_IMA_DIGEST_LIST + char evm_hmac[CRYPTO_MAX_ALG_NAME];
snprintf(evm_hmac, sizeof(evm_hmac), "hmac(%s)", CONFIG_EVM_DEFAULT_HASH); +#endif
if (type == EVM_XATTR_HMAC) { if (!(evm_initialized & EVM_INIT_HMAC)) { @@ -156,8 +162,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; } @@ -174,8 +184,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); } @@ -288,8 +302,12 @@ static int evm_is_immutable(struct dentry *dentry, struct inode *inode) return 0; return rc; } +#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; @@ -321,15 +339,23 @@ int evm_update_evmxattr(struct dentry *dentry, const char *xattr_name, if (rc) return -EPERM;
+#ifdef CONFIG_IMA_DIGEST_LIST data.hdr.algo = evm_hash_algo; +#else + data.hdr.algo = HASH_ALGO_SHA1; +#endif rc = evm_calc_hmac(dentry, xattr_name, xattr_value, xattr_value_len, &data); if (rc == 0) { data.hdr.xattr.sha1.type = EVM_XATTR_HMAC; rc = __vfs_setxattr_noperm(dentry, XATTR_NAME_EVM, &data.hdr.xattr.data[1], +#ifdef CONFIG_IMA_DIGEST_LIST hash_digest_size[evm_hash_algo] + 1, 0); +#else + SHA1_DIGEST_SIZE + 1, 0); +#endif } else if (rc == -ENODATA && (inode->i_opflags & IOP_XATTR)) { rc = __vfs_removexattr(dentry, XATTR_NAME_EVM); } @@ -341,7 +367,11 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, { struct shash_desc *desc;
+#ifdef CONFIG_IMA_DIGEST_LIST desc = init_desc(EVM_XATTR_HMAC, evm_hash_algo); +#else + desc = init_desc(EVM_XATTR_HMAC, HASH_ALGO_SHA1); +#endif if (IS_ERR(desc)) { pr_info("init_desc failed\n"); return PTR_ERR(desc); @@ -353,9 +383,15 @@ int evm_init_hmac(struct inode *inode, const struct xattr *lsm_xattr, return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * Get the key from the TPM for the HMAC */ +#else +/* + * Get the key from the TPM for the SHA1-HMAC + */ +#endif int evm_init_key(void) { struct key *evm_key; diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index cddfc0e43a80..fe767feb722d 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -18,7 +18,9 @@ #include <linux/integrity.h> #include <linux/evm.h> #include <linux/magic.h> +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/posix_acl_xattr.h> +#endif
#include <crypto/hash.h> #include <crypto/hash_info.h> @@ -28,8 +30,12 @@ int evm_initialized;
static const char * const integrity_status_msg[] = { +#ifdef CONFIG_IMA_DIGEST_LIST "pass", "pass_immutable", "fail", "fail_immutable", "no_label", "no_xattrs", "unknown" +#else + "pass", "pass_immutable", "fail", "no_label", "no_xattrs", "unknown" +#endif }; int evm_hmac_attrs;
@@ -57,6 +63,7 @@ static struct xattr_list evm_config_default_xattrnames[] = { LIST_HEAD(evm_config_xattrnames);
static int evm_fixmode __ro_after_init; +#ifdef CONFIG_IMA_DIGEST_LIST static int __init evm_set_param(char *str) { if (strncmp(str, "fix", 3) == 0) @@ -73,6 +80,18 @@ static int __init evm_set_param(char *str) return 1; } __setup("evm=", evm_set_param); +#else +static int __init evm_set_fixmode(char *str) +{ + if (strncmp(str, "fix", 3) == 0) + evm_fixmode = 1; + else + pr_err("invalid "%s" mode", str); + + return 0; +} +__setup("evm=", evm_set_fixmode); +#endif
static void __init evm_init_config(void) { @@ -98,6 +117,7 @@ static bool evm_key_loaded(void) return (bool)(evm_initialized & EVM_KEY_MASK); }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * Ignoring INTEGRITY_NOLABEL/INTEGRITY_NOXATTRS is safe if no HMAC key * is loaded and the EVM_SETUP_COMPLETE initialization flag is set. @@ -115,8 +135,13 @@ static bool evm_ignore_error_safe(enum integrity_status evm_status)
return true; } +#endif
+#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; @@ -133,8 +158,10 @@ static int evm_find_protected_xattrs(struct dentry *dentry, int *ima_present) continue; return error; } +#ifdef CONFIG_IMA_DIGEST_LIST if (!strcmp(xattr->name, XATTR_NAME_IMA)) *ima_present = 1; +#endif count++; }
@@ -163,6 +190,7 @@ 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; @@ -172,6 +200,12 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, .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; +#endif + if (iint && (iint->evm_status == INTEGRITY_PASS || iint->evm_status == INTEGRITY_PASS_IMMUTABLE)) return iint->evm_status; @@ -184,7 +218,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) @@ -192,6 +230,7 @@ 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) && @@ -206,6 +245,9 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, goto out;
saved_evm_status = evm_status; +#else + goto out; +#endif }
xattr_len = rc; @@ -213,6 +255,7 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, /* check value type */ switch (xattr_data->type) { case EVM_XATTR_HMAC: +#ifdef CONFIG_IMA_DIGEST_LIST if (xattr_len != hash_digest_size[evm_hash_algo] + 1) { evm_status = INTEGRITY_FAIL; goto out; @@ -231,6 +274,25 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, evm_immutable = 1; fallthrough; case EVM_IMA_XATTR_DIGSIG: +#else + if (xattr_len != sizeof(struct evm_xattr)) { + evm_status = INTEGRITY_FAIL; + goto out; + } + + digest.hdr.algo = HASH_ALGO_SHA1; + rc = evm_calc_hmac(dentry, xattr_name, xattr_value, + xattr_value_len, &digest); + if (rc) + break; + rc = crypto_memneq(xattr_data->data, digest.digest, + SHA1_DIGEST_SIZE); + if (rc) + rc = -EINVAL; + break; + case EVM_IMA_XATTR_DIGSIG: + case EVM_XATTR_PORTABLE_DIGSIG: +#endif /* accept xattr with non-empty signature field */ if (xattr_len <= sizeof(struct signature_v2_hdr)) { evm_status = INTEGRITY_FAIL; @@ -262,6 +324,7 @@ 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 @@ -302,11 +365,13 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, evm_status = INTEGRITY_PASS; } break; +#endif 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) { @@ -315,10 +380,17 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, evm_status = evm_immutable ? INTEGRITY_FAIL_IMMUTABLE : INTEGRITY_FAIL; } +#else + if (rc) + evm_status = (rc == -ENODATA) ? + INTEGRITY_NOXATTRS : INTEGRITY_FAIL; +#endif 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; } @@ -397,6 +469,7 @@ static enum integrity_status evm_verify_current_integrity(struct dentry *dentry) return evm_verify_hmac(dentry, NULL, NULL, 0, NULL); }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * evm_xattr_acl_change - check if passed ACL changes the inode mode * @dentry: pointer to the affected dentry @@ -468,6 +541,7 @@ static int evm_xattr_change(struct dentry *dentry, const char *xattr_name, kfree(xattr_data); return rc; } +#endif
/* * evm_protect_xattr - protect the EVM extended attribute @@ -519,6 +593,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, -EPERM, 0); } out: +#ifdef CONFIG_IMA_DIGEST_LIST if (evm_ignore_error_safe(evm_status)) return 0;
@@ -532,6 +607,7 @@ static int evm_protect_xattr(struct dentry *dentry, const char *xattr_name, if (evm_status == INTEGRITY_PASS_IMMUTABLE && !evm_xattr_change(dentry, xattr_name, xattr_value, xattr_value_len)) return 0; +#endif
if (evm_status != INTEGRITY_PASS) integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry), @@ -569,8 +645,12 @@ int evm_inode_setxattr(struct dentry *dentry, const char *xattr_name, 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(dentry, xattr_name, xattr_value, @@ -605,6 +685,7 @@ static void evm_reset_status(struct inode *inode) iint->evm_status = INTEGRITY_UNKNOWN; }
+#ifdef CONFIG_IMA_DIGEST_LIST /** * evm_status_revalidate - report whether EVM status re-validation is necessary * @xattr_name: pointer to the affected extended attribute name @@ -629,6 +710,7 @@ bool evm_status_revalidate(const char *xattr_name)
return true; } +#endif
/** * evm_inode_post_setxattr - update 'security.evm' to reflect the changes @@ -646,13 +728,20 @@ bool evm_status_revalidate(const char *xattr_name) void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { +#ifdef CONFIG_IMA_DIGEST_LIST if (!evm_status_revalidate(xattr_name)) +#else + if (!evm_key_loaded() || (!evm_protected_xattr(xattr_name) + && !posix_xattr_acl(xattr_name))) +#endif return;
evm_reset_status(dentry->d_inode);
+#ifdef CONFIG_IMA_DIGEST_LIST if (!strcmp(xattr_name, XATTR_NAME_EVM)) return; +#endif
evm_update_evmxattr(dentry, xattr_name, xattr_value, xattr_value_len); } @@ -669,17 +758,24 @@ void evm_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, */ void evm_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { +#ifdef CONFIG_IMA_DIGEST_LIST if (!evm_status_revalidate(xattr_name)) +#else + if (!evm_key_loaded() || !evm_protected_xattr(xattr_name)) +#endif return;
evm_reset_status(dentry->d_inode);
+#ifdef CONFIG_IMA_DIGEST_LIST if (!strcmp(xattr_name, XATTR_NAME_EVM)) return; +#endif
evm_update_evmxattr(dentry, xattr_name, NULL, 0); }
+#ifdef CONFIG_IMA_DIGEST_LIST static int evm_attr_change(struct dentry *dentry, struct iattr *attr) { struct inode *inode = d_backing_inode(dentry); @@ -692,6 +788,7 @@ static int evm_attr_change(struct dentry *dentry, struct iattr *attr)
return 1; } +#endif
/** * evm_inode_setattr - prevent updating an invalid EVM extended attribute @@ -714,6 +811,7 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) if (!(ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))) return 0; evm_status = evm_verify_current_integrity(dentry); +#ifdef CONFIG_IMA_DIGEST_LIST /* * Writing attrs is safe for portable signatures, as portable signatures * are immutable and can never be updated. @@ -727,6 +825,11 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) if (evm_status == INTEGRITY_PASS_IMMUTABLE && !evm_attr_change(dentry, attr)) return 0; +#else + if ((evm_status == INTEGRITY_PASS) || + (evm_status == INTEGRITY_NOXATTRS)) + return 0; +#endif
integrity_audit_msg(AUDIT_INTEGRITY_METADATA, d_backing_inode(dentry), dentry->d_name.name, "appraise_metadata", @@ -747,15 +850,21 @@ int evm_inode_setattr(struct dentry *dentry, struct iattr *attr) */ void evm_inode_post_setattr(struct dentry *dentry, int ia_valid) { +#ifdef CONFIG_IMA_DIGEST_LIST if (!evm_status_revalidate(NULL)) return;
evm_reset_status(dentry->d_inode); +#else + if (!evm_key_loaded()) + return; +#endif
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)) evm_update_evmxattr(dentry, NULL, NULL, 0); }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * evm_inode_init_security - initializes security.evm HMAC value */ @@ -787,6 +896,38 @@ int evm_inode_init_security(struct inode *inode, kfree(xattr_data); return rc; } +#else +/* + * evm_inode_init_security - initializes security.evm + */ +int evm_inode_init_security(struct inode *inode, + const struct xattr *lsm_xattr, + struct xattr *evm_xattr) +{ + struct evm_xattr *xattr_data; + int rc; + + if (!evm_key_loaded() || !evm_protected_xattr(lsm_xattr->name)) + return 0; + + xattr_data = kzalloc(sizeof(*xattr_data), GFP_NOFS); + if (!xattr_data) + return -ENOMEM; + + xattr_data->data.type = EVM_XATTR_HMAC; + rc = evm_init_hmac(inode, lsm_xattr, xattr_data->digest); + if (rc < 0) + goto out; + + evm_xattr->value = xattr_data; + evm_xattr->value_len = sizeof(*xattr_data); + evm_xattr->name = XATTR_EVM_SUFFIX; + return 0; +out: + kfree(xattr_data); + return rc; +} +#endif EXPORT_SYMBOL_GPL(evm_inode_init_security);
#ifdef CONFIG_EVM_LOAD_X509 @@ -802,6 +943,7 @@ void __init evm_load_x509(void)
static int __init init_evm(void) { +#ifdef CONFIG_IMA_DIGEST_LIST int error, i; struct list_head *pos, *q;
@@ -809,6 +951,10 @@ static int __init init_evm(void) CONFIG_EVM_DEFAULT_HASH); if (i >= 0) evm_hash_algo = i; +#else + int error; + struct list_head *pos, *q; +#endif
evm_init_config();
diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 8953ac6412c3..df994202ac0f 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -209,10 +209,14 @@ void __init integrity_load_keys(void) { ima_load_x509();
+#ifdef CONFIG_IMA_DIGEST_LIST if (!IS_ENABLED(CONFIG_IMA_LOAD_X509)) evm_load_x509();
ima_load_digest_lists(); +#else + evm_load_x509(); +#endif }
static int __init integrity_fs_init(void) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 46afb6bef45b..e512a17b09dd 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -53,11 +53,11 @@ extern int ima_hash_algo_idx __ro_after_init; extern int ima_extra_slots __ro_after_init; extern int ima_appraise; extern struct tpm_chip *ima_tpm_chip; +extern const char boot_aggregate_name[]; +#ifdef CONFIG_IMA_DIGEST_LIST extern int ima_digest_list_pcr; extern bool ima_plus_standard_pcr; -extern const char boot_aggregate_name[]; extern int ima_digest_list_actions; -#ifdef CONFIG_IMA_DIGEST_LIST extern int ima_digest_db_max_size __ro_after_init; extern int ima_digest_db_size; #endif @@ -189,6 +189,7 @@ static inline unsigned int ima_hash_key(u8 *digest) return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE; }
+#ifdef CONFIG_IMA_DIGEST_LIST #define __ima_hooks(hook) \ hook(NONE, none) \ hook(FILE_CHECK, file) \ @@ -205,6 +206,23 @@ static inline unsigned int ima_hash_key(u8 *digest) hook(KEY_CHECK, key) \ hook(DIGEST_LIST_CHECK, digest_list) \ hook(MAX_CHECK, none) +#else +#define __ima_hooks(hook) \ + hook(NONE, none) \ + hook(FILE_CHECK, file) \ + hook(MMAP_CHECK, mmap) \ + hook(BPRM_CHECK, bprm) \ + hook(CREDS_CHECK, creds) \ + hook(POST_SETATTR, post_setattr) \ + hook(MODULE_CHECK, module) \ + hook(FIRMWARE_CHECK, firmware) \ + hook(KEXEC_KERNEL_CHECK, kexec_kernel) \ + hook(KEXEC_INITRAMFS_CHECK, kexec_initramfs) \ + hook(POLICY_CHECK, policy) \ + hook(KEXEC_CMDLINE, kexec_cmdline) \ + hook(KEY_CHECK, key) \ + hook(MAX_CHECK, none) +#endif
#define __ima_hook_enumify(ENUM, str) ENUM, #define __ima_stringify(arg) (#arg) @@ -264,12 +282,20 @@ int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func); int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file, void *buf, loff_t size, enum hash_algo algo, struct modsig *modsig); +#ifdef CONFIG_IMA_DIGEST_LIST void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc, struct ima_digest *digest); +#else +void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, + const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len, const struct modsig *modsig, int pcr, + struct ima_template_desc *template_desc); +#endif void process_buffer_measurement(struct inode *inode, const void *buf, int size, const char *eventname, enum ima_hooks func, int pcr, const char *keyring); @@ -278,9 +304,15 @@ void ima_audit_measurement(struct integrity_iint_cache *iint, int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_entry **entry, struct ima_template_desc *template_desc); +#ifdef CONFIG_IMA_DIGEST_LIST int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename, int pcr, struct ima_digest *digest); +#else +int ima_store_template(struct ima_template_entry *entry, int violation, + struct inode *inode, + const unsigned char *filename, int pcr); +#endif void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
@@ -308,21 +340,41 @@ int ima_policy_show(struct seq_file *m, void *v); #define IMA_APPRAISE_FIRMWARE 0x10 #define IMA_APPRAISE_POLICY 0x20 #define IMA_APPRAISE_KEXEC 0x40 +#ifdef CONFIG_IMA_DIGEST_LIST #define IMA_APPRAISE_DIGEST_LIST 0x80 +#endif
#ifdef CONFIG_IMA_APPRAISE int ima_check_blacklist(struct integrity_iint_cache *iint, const struct modsig *modsig, int pcr); + +#ifdef CONFIG_IMA_DIGEST_LIST 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, int xattr_len, const struct modsig *modsig, struct ima_digest *found_digest); +#else +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, + int xattr_len, const struct modsig *modsig); +#endif + int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func); void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, enum ima_hooks func); + +#ifndef CONFIG_IMA_DIGEST_LIST +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len); +int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value); +#endif + #else static inline int ima_check_blacklist(struct integrity_iint_cache *iint, const struct modsig *modsig, int pcr) @@ -330,6 +382,7 @@ static inline int ima_check_blacklist(struct integrity_iint_cache *iint, return 0; }
+#ifndef CONFIG_IMA_DIGEST_LIST static inline int ima_appraise_measurement(enum ima_hooks func, struct integrity_iint_cache *iint, struct file *file, @@ -338,6 +391,15 @@ static inline int ima_appraise_measurement(enum ima_hooks func, int xattr_len, const struct modsig *modsig, struct ima_digest *found_digest) +#else +static inline 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, + int xattr_len, + const struct modsig *modsig) +#endif { return INTEGRITY_UNKNOWN; } @@ -360,6 +422,20 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c return INTEGRITY_UNKNOWN; }
+#ifndef CONFIG_IMA_DIGEST_LIST +static inline enum hash_algo +ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) +{ + return ima_hash_algo; +} + +static inline int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value) +{ + return 0; +} +#endif + #endif /* CONFIG_IMA_APPRAISE */
#ifdef CONFIG_IMA_APPRAISE_MODSIG diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 6ecaf6834844..1b8d3696d873 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -99,15 +99,23 @@ int ima_alloc_init_template(struct ima_event_data *event_data, * * Returns 0 on success, error code otherwise */ +#ifdef CONFIG_IMA_DIGEST_LIST int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename, int pcr, struct ima_digest *digest) +#else +int ima_store_template(struct ima_template_entry *entry, + int violation, struct inode *inode, + const unsigned char *filename, int pcr) +#endif { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; char *template_name = entry->template_desc->name; +#ifdef CONFIG_IMA_DIGEST_LIST struct ima_template_entry *duplicated_entry = NULL; +#endif int result;
if (!violation) { @@ -121,6 +129,7 @@ int ima_store_template(struct ima_template_entry *entry, } }
+#ifdef CONFIG_IMA_DIGEST_LIST if (ima_plus_standard_pcr && !digest) { duplicated_entry = kmemdup(entry, sizeof(*entry) + entry->template_desc->num_fields * @@ -130,9 +139,11 @@ int ima_store_template(struct ima_template_entry *entry, } else if (!ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { pcr = ima_digest_list_pcr; } +#endif
entry->pcr = pcr; result = ima_add_template_entry(entry, violation, op, inode, filename); +#ifdef CONFIG_IMA_DIGEST_LIST if (result) { kfree(duplicated_entry); } else if (duplicated_entry) { @@ -141,6 +152,7 @@ int ima_store_template(struct ima_template_entry *entry, if (result < 0) kfree(duplicated_entry); } +#endif
return result; } @@ -173,8 +185,13 @@ void ima_add_violation(struct file *file, const unsigned char *filename, result = -ENOMEM; goto err_out; } +#ifdef CONFIG_IMA_DIGEST_LIST result = ima_store_template(entry, violation, inode, filename, CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else + result = ima_store_template(entry, violation, inode, + filename, CONFIG_IMA_MEASURE_PCR_IDX); +#endif if (result < 0) ima_free_template_entry(entry); err_out: @@ -315,18 +332,30 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, * * Must be called with iint->mutex held. */ +#ifdef CONFIG_IMA_DIGEST_LIST void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc, struct ima_digest *digest) +#else +void ima_store_measurement(struct integrity_iint_cache *iint, + struct file *file, const unsigned char *filename, + struct evm_ima_xattr_data *xattr_value, + int xattr_len, const struct modsig *modsig, int pcr, + struct ima_template_desc *template_desc) +#endif { static const char op[] = "add_template_measure"; static const char audit_cause[] = "ENOMEM"; int result = -ENOMEM; struct inode *inode = file_inode(file); +#ifdef CONFIG_IMA_DIGEST_LIST struct ima_template_entry *entry = NULL; +#else + struct ima_template_entry *entry; +#endif struct ima_event_data event_data = { .iint = iint, .file = file, .filename = filename, @@ -344,10 +373,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (iint->measured_pcrs & (0x1 << pcr) && !modsig) return;
+#ifdef CONFIG_IMA_DIGEST_LIST if (digest && !ima_plus_standard_pcr && ima_digest_list_pcr >= 0) { result = -EEXIST; goto out; } +#endif
result = ima_alloc_init_template(&event_data, &entry, template_desc); if (result < 0) { @@ -356,14 +387,22 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; }
+#ifdef CONFIG_IMA_DIGEST_LIST result = ima_store_template(entry, violation, inode, filename, pcr, digest); out: +#else + result = ima_store_template(entry, violation, inode, filename, pcr); +#endif if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { iint->flags |= IMA_MEASURED; iint->measured_pcrs |= (0x1 << pcr); } +#ifdef CONFIG_IMA_DIGEST_LIST if (result < 0 && entry) +#else + if (result < 0) +#endif ima_free_template_entry(entry); }
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 956fb0f4c006..e5e071fa8565 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -15,9 +15,11 @@ #include <keys/system_keyring.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST #include "ima_digest_list.h"
static bool ima_appraise_req_evm __ro_after_init; +#endif static int __init default_appraise_setup(char *str) { #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM @@ -45,16 +47,18 @@ static int __init default_appraise_setup(char *str) ima_appraise = appraisal_state; } #endif +#ifdef CONFIG_IMA_DIGEST_LIST if (strcmp(str, "enforce-evm") == 0 || strcmp(str, "log-evm") == 0) ima_appraise_req_evm = true; +#endif return 1; }
__setup("ima_appraise=", default_appraise_setup);
-static bool ima_appraise_no_metadata __ro_after_init; #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)) { @@ -108,9 +112,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(dentry, XATTR_NAME_IMA, @@ -189,6 +195,60 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, } }
+#ifndef CONFIG_IMA_DIGEST_LIST +enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, + int xattr_len) +{ + struct signature_v2_hdr *sig; + enum hash_algo ret; + + if (!xattr_value || xattr_len < 2) + /* return default hash algo */ + return ima_hash_algo; + + switch (xattr_value->type) { + case EVM_IMA_XATTR_DIGSIG: + sig = (typeof(sig))xattr_value; + if (sig->version != 2 || xattr_len <= sizeof(*sig)) + return ima_hash_algo; + return sig->hash_algo; + break; + case IMA_XATTR_DIGEST_NG: + /* first byte contains algorithm id */ + ret = xattr_value->data[0]; + if (ret < HASH_ALGO__LAST) + return ret; + break; + case IMA_XATTR_DIGEST: + /* this is for backward compatibility */ + if (xattr_len == 21) { + unsigned int zero = 0; + if (!memcmp(&xattr_value->data[16], &zero, 4)) + return HASH_ALGO_MD5; + else + return HASH_ALGO_SHA1; + } else if (xattr_len == 17) + return HASH_ALGO_MD5; + break; + } + + /* return default hash algo */ + return ima_hash_algo; +} + +int ima_read_xattr(struct dentry *dentry, + struct evm_ima_xattr_data **xattr_value) +{ + ssize_t ret; + + ret = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)xattr_value, + 0, GFP_NOFS); + if (ret == -EOPNOTSUPP) + ret = 0; + return ret; +} +#endif + /* * xattr_verify - verify xattr digest or signature * @@ -196,18 +256,27 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, * * Return 0 on success, error code otherwise. */ +#ifdef CONFIG_IMA_DIGEST_LIST static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, enum integrity_status *status, const char **cause, struct ima_digest *found_digest) +#else +static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, + struct evm_ima_xattr_data *xattr_value, int xattr_len, + enum integrity_status *status, const char **cause) +#endif { int rc = -EINVAL, hash_start = 0;
+#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);
@@ -217,22 +286,27 @@ static int xattr_verify(enum ima_hooks func, struct integrity_iint_cache *iint, 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))) { +#endif if (iint->flags & IMA_DIGSIG_REQUIRED) { *cause = "IMA-signature-required"; *status = INTEGRITY_FAIL; break; } clear_bit(IMA_DIGSIG, &iint->atomic_flags); +#ifdef CONFIG_IMA_DIGEST_LIST } else { set_bit(IMA_DIGSIG, &iint->atomic_flags); } +#endif if (xattr_len - sizeof(xattr_value->type) - hash_start >= iint->ima_hash->length) /* @@ -352,26 +426,39 @@ int ima_check_blacklist(struct integrity_iint_cache *iint, * * Return 0 on success, error code otherwise */ +#ifdef CONFIG_IMA_DIGEST_LIST 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, int xattr_len, const struct modsig *modsig, struct ima_digest *found_digest) +#else +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, + 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. */ if (!(inode->i_opflags & IOP_XATTR) && !try_modsig) return INTEGRITY_UNKNOWN;
+#ifdef CONFIG_IMA_DIGEST_LIST if (xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG && xattr_len == sizeof(struct signature_v2_hdr)) rc = -ENODATA; @@ -394,6 +481,7 @@ int ima_appraise_measurement(enum ima_hooks func, rc = xattr_len; } } +#endif
/* If reading the xattr failed and there's no modsig, error out. */ if (rc <= 0 && !try_modsig) { @@ -417,11 +505,15 @@ int ima_appraise_measurement(enum ima_hooks func, switch (status) { case INTEGRITY_PASS: case INTEGRITY_PASS_IMMUTABLE: +#ifdef CONFIG_IMA_DIGEST_LIST break; case INTEGRITY_UNKNOWN: if (ima_appraise_req_evm && xattr_value->type != EVM_IMA_XATTR_DIGSIG && !found_digest) goto out; +#else + case INTEGRITY_UNKNOWN: +#endif break; case INTEGRITY_NOXATTRS: /* No EVM protected xattrs. */ /* It's fine not to have xattrs when using a modsig. */ @@ -429,6 +521,7 @@ 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 @@ -446,11 +539,14 @@ int ima_appraise_measurement(enum ima_hooks func, ima_digest_is_immutable(found_digest)) break; } +#endif cause = "missing-HMAC"; goto out; +#ifdef CONFIG_IMA_DIGEST_LIST case INTEGRITY_FAIL_IMMUTABLE: set_bit(IMA_DIGSIG, &iint->atomic_flags); fallthrough; +#endif case INTEGRITY_FAIL: /* Invalid HMAC/signature. */ cause = "invalid-HMAC"; goto out; @@ -458,6 +554,7 @@ int ima_appraise_measurement(enum ima_hooks func, WARN_ONCE(true, "Unexpected integrity status %d\n", status); }
+#ifdef CONFIG_IMA_DIGEST_LIST if ((iint->flags & IMA_META_IMMUTABLE_REQUIRED) && status != INTEGRITY_PASS_IMMUTABLE) { status = INTEGRITY_FAIL; @@ -466,10 +563,16 @@ int ima_appraise_measurement(enum ima_hooks func, filename, op, cause, rc, 0); goto out; } +#endif
if (xattr_value) +#ifdef CONFIG_IMA_DIGEST_LIST rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, &cause, found_digest); +#else + rc = xattr_verify(func, iint, xattr_value, xattr_len, &status, + &cause); +#endif
/* * If we have a modsig and either no imasig or the imasig's key isn't @@ -503,6 +606,7 @@ int ima_appraise_measurement(enum ima_hooks func, status = INTEGRITY_PASS; }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * Permit new files with file/EVM portable signatures, but * without data. @@ -511,6 +615,13 @@ int ima_appraise_measurement(enum ima_hooks func, test_bit(IMA_DIGSIG, &iint->atomic_flags)) { status = INTEGRITY_PASS; } +#else + /* Permit new files with file signatures, but without data. */ + if (inode->i_size == 0 && iint->flags & IMA_NEW_FILE && + xattr_value && xattr_value->type == EVM_IMA_XATTR_DIGSIG) { + status = INTEGRITY_PASS; + } +#endif
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); @@ -567,6 +678,10 @@ void ima_inode_post_setattr(struct dentry *dentry) return;
action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); +#ifndef CONFIG_IMA_DIGEST_LIST + if (!action) + __vfs_removexattr(dentry, XATTR_NAME_IMA); +#endif iint = integrity_iint_find(inode); if (iint) { set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); @@ -620,11 +735,16 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, if (result == 1) { if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) return -EINVAL; +#ifndef CONFIG_IMA_DIGEST_LIST + ima_reset_appraise_flags(d_backing_inode(dentry), + xvalue->type == EVM_IMA_XATTR_DIGSIG); +#endif result = 0; } return result; }
+#ifdef CONFIG_IMA_DIGEST_LIST void ima_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, const void *xattr_value, size_t xattr_value_len) { @@ -641,6 +761,7 @@ void ima_inode_post_setxattr(struct dentry *dentry, const char *xattr_name, if (result == 1 || evm_status_revalidate(xattr_name)) ima_reset_appraise_flags(d_backing_inode(dentry), digsig); } +#endif
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) { @@ -648,11 +769,15 @@ int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
result = ima_protect_xattr(dentry, xattr_name, NULL, 0); if (result == 1) { +#ifndef CONFIG_IMA_DIGEST_LIST + ima_reset_appraise_flags(d_backing_inode(dentry), 0); +#endif result = 0; } return result; }
+#ifdef CONFIG_IMA_DIGEST_LIST void ima_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) { int result; @@ -661,3 +786,4 @@ void ima_inode_post_removexattr(struct dentry *dentry, const char *xattr_name) if (result == 1 || evm_status_revalidate(xattr_name)) ima_reset_appraise_flags(d_backing_inode(dentry), 0); } +#endif diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index f1bc3e201bd8..4ceee381d58b 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -21,11 +21,15 @@ #include <linux/rcupdate.h> #include <linux/parser.h> #include <linux/vmalloc.h> +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/file.h> +#endif #include <linux/ctype.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST #include "ima_digest_list.h" +#endif
static DEFINE_MUTEX(ima_write_mutex);
@@ -36,9 +40,11 @@ static struct dentry *ascii_runtime_measurements; static struct dentry *runtime_measurements_count; static struct dentry *violations; static struct dentry *ima_policy; +#ifdef CONFIG_IMA_DIGEST_LIST static struct dentry *digests_count; static struct dentry *digest_list_data; static struct dentry *digest_list_data_del; +#endif
bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -52,6 +58,7 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
+#ifdef CONFIG_IMA_DIGEST_LIST static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { @@ -63,19 +70,54 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, val = &ima_htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) val = &ima_htable.len; -#ifdef CONFIG_IMA_DIGEST_LIST else if (filp->f_path.dentry == digests_count) val = &ima_digests_htable.len; -#endif
len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val)); return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); } +#else +static ssize_t ima_show_htable_value(char __user *buf, size_t count, + loff_t *ppos, atomic_long_t *val) +{ + char tmpbuf[32]; /* greater than largest 'long' string value */ + ssize_t len; + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", atomic_long_read(val)); + return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); +} +#endif
+#ifdef CONFIG_IMA_DIGEST_LIST static const struct file_operations ima_htable_value_ops = { .read = ima_show_htable_value, .llseek = generic_file_llseek, }; +#else +static ssize_t ima_show_htable_violations(struct file *filp, + char __user *buf, + size_t count, loff_t *ppos) +{ + return ima_show_htable_value(buf, count, ppos, &ima_htable.violations); +} + +static const struct file_operations ima_htable_violations_ops = { + .read = ima_show_htable_violations, + .llseek = generic_file_llseek, +}; + +static ssize_t ima_show_measurements_count(struct file *filp, + char __user *buf, + size_t count, loff_t *ppos) +{ + return ima_show_htable_value(buf, count, ppos, &ima_htable.len); + +} + +static const struct file_operations ima_measurements_count_ops = { + .read = ima_show_measurements_count, + .llseek = generic_file_llseek, +}; +#endif
/* returns pointer to hlist_node */ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) @@ -275,6 +317,7 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, };
+#ifdef CONFIG_IMA_DIGEST_LIST static ssize_t ima_read_file(char *path, struct dentry *dentry) { void *data = NULL; @@ -320,7 +363,6 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) rc = ima_parse_add_rule(p); } else if (dentry == digest_list_data || dentry == digest_list_data_del) { -#ifdef CONFIG_IMA_DIGEST_LIST /* Only check size when adding digest lists */ if (dentry == digest_list_data && size > ima_digest_db_max_size - ima_digest_db_size) { @@ -328,7 +370,6 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry) rc = -ENOMEM; break; } -#endif /* * Disable usage of digest lists if not measured * or appraised. @@ -343,12 +384,10 @@ static ssize_t ima_read_file(char *path, struct dentry *dentry)
if (rc < 0) break; -#ifdef CONFIG_IMA_DIGEST_LIST else if (dentry == digest_list_data) pr_debug("digest imported, current DB size: %d\n", ima_digest_db_size); else if (dentry == digest_list_data_del) pr_debug("digest deleted, current DB size: %d\n", ima_digest_db_size); -#endif size -= rc; }
@@ -461,6 +500,104 @@ static enum ima_fs_flags ima_get_dentry_flag(struct dentry *dentry)
return flag; } +#else +static ssize_t ima_read_policy(char *path) +{ + void *data = NULL; + char *datap; + size_t size; + int rc, pathlen = strlen(path); + + char *p; + + /* remove \n */ + datap = path; + strsep(&datap, "\n"); + + rc = kernel_read_file_from_path(path, 0, &data, INT_MAX, NULL, + READING_POLICY); + if (rc < 0) { + pr_err("Unable to open file: %s (%d)", path, rc); + return rc; + } + size = rc; + rc = 0; + + datap = data; + while (size > 0 && (p = strsep(&datap, "\n"))) { + pr_debug("rule: %s\n", p); + rc = ima_parse_add_rule(p); + if (rc < 0) + break; + size -= rc; + } + + vfree(data); + if (rc < 0) + return rc; + else if (size) + return -EINVAL; + else + return pathlen; +} + +static ssize_t ima_write_policy(struct file *file, const char __user *buf, + size_t datalen, loff_t *ppos) +{ + char *data; + ssize_t result; + + if (datalen >= PAGE_SIZE) + datalen = PAGE_SIZE - 1; + + /* No partial writes. */ + result = -EINVAL; + if (*ppos != 0) + goto out; + + data = memdup_user_nul(buf, datalen); + if (IS_ERR(data)) { + result = PTR_ERR(data); + goto out; + } + + result = mutex_lock_interruptible(&ima_write_mutex); + if (result < 0) + goto out_free; + + if (data[0] == '/') { + result = ima_read_policy(data); + } else if (ima_appraise & IMA_APPRAISE_POLICY) { + pr_err("signed policy file (specified as an absolute pathname) required\n"); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", "signed policy required", + 1, 0); + result = -EACCES; + } else { + result = ima_parse_add_rule(data); + } + mutex_unlock(&ima_write_mutex); +out_free: + kfree(data); +out: + if (result < 0) + valid_policy = 0; + + return result; +} + +static struct dentry *ima_dir; +static struct dentry *ima_symlink; +static struct dentry *binary_runtime_measurements; +static struct dentry *ascii_runtime_measurements; +static struct dentry *runtime_measurements_count; +static struct dentry *violations; +static struct dentry *ima_policy; + +enum ima_fs_flags { + IMA_FS_BUSY, +}; +#endif
static unsigned long ima_fs_flags;
@@ -473,6 +610,7 @@ static const struct seq_operations ima_policy_seqops = { }; #endif
+#ifdef CONFIG_IMA_DIGEST_LIST /* * ima_open_data_upload: sequentialize access to the data upload interface */ @@ -568,6 +706,79 @@ static const struct file_operations ima_data_upload_ops = { .release = ima_release_data_upload, .llseek = generic_file_llseek, }; +#else +/* + * ima_open_policy: sequentialize access to the policy file + */ +static int ima_open_policy(struct inode *inode, struct file *filp) +{ + if (!(filp->f_flags & O_WRONLY)) { +#ifndef CONFIG_IMA_READ_POLICY + return -EACCES; +#else + if ((filp->f_flags & O_ACCMODE) != O_RDONLY) + return -EACCES; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return seq_open(filp, &ima_policy_seqops); +#endif + } + if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags)) + return -EBUSY; + return 0; +} + +/* + * ima_release_policy - start using the new measure policy rules. + * + * Initially, ima_measure points to the default policy rules, now + * point to the new policy rules, and remove the securityfs policy file, + * assuming a valid policy. + */ +static int ima_release_policy(struct inode *inode, struct file *file) +{ + const char *cause = valid_policy ? "completed" : "failed"; + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) + return seq_release(inode, file); + + if (valid_policy && ima_check_policy() < 0) { + cause = "failed"; + valid_policy = 0; + } + + pr_info("policy update %s\n", cause); + integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, + "policy_update", cause, !valid_policy, 0); + + if (!valid_policy) { + ima_delete_rules(); + valid_policy = 1; + clear_bit(IMA_FS_BUSY, &ima_fs_flags); + return 0; + } + + ima_update_policy(); +#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) + securityfs_remove(ima_policy); + ima_policy = NULL; +#elif defined(CONFIG_IMA_WRITE_POLICY) + clear_bit(IMA_FS_BUSY, &ima_fs_flags); +#elif defined(CONFIG_IMA_READ_POLICY) + inode->i_mode &= ~S_IWUSR; +#endif + return 0; +} + +static const struct file_operations ima_measure_policy_ops = { + .open = ima_open_policy, + .write = ima_write_policy, + .read = seq_read, + .release = ima_release_policy, + .llseek = generic_file_llseek, +}; + +#endif
int __init ima_fs_init(void) { @@ -594,6 +805,7 @@ int __init ima_fs_init(void) if (IS_ERR(ascii_runtime_measurements)) goto out;
+#ifdef CONFIG_IMA_DIGEST_LIST runtime_measurements_count = securityfs_create_file("runtime_measurements_count", S_IRUSR | S_IRGRP, ima_dir, NULL, @@ -613,7 +825,6 @@ int __init ima_fs_init(void) if (IS_ERR(ima_policy)) goto out;
-#ifdef CONFIG_IMA_DIGEST_LIST digests_count = securityfs_create_file("digests_count", S_IRUSR | S_IRGRP, ima_dir, NULL, &ima_htable_value_ops); @@ -631,12 +842,34 @@ int __init ima_fs_init(void) &ima_data_upload_ops); if (IS_ERR(digest_list_data_del)) goto out; +#else + runtime_measurements_count = + securityfs_create_file("runtime_measurements_count", + S_IRUSR | S_IRGRP, ima_dir, NULL, + &ima_measurements_count_ops); + if (IS_ERR(runtime_measurements_count)) + goto out; + + violations = + securityfs_create_file("violations", S_IRUSR | S_IRGRP, + ima_dir, NULL, &ima_htable_violations_ops); + if (IS_ERR(violations)) + goto out; + + ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS, + ima_dir, NULL, + &ima_measure_policy_ops); + if (IS_ERR(ima_policy)) + goto out; + #endif return 0; out: +#ifdef CONFIG_IMA_DIGEST_LIST securityfs_remove(digest_list_data_del); securityfs_remove(digest_list_data); securityfs_remove(digests_count); +#endif securityfs_remove(ima_policy); securityfs_remove(violations); securityfs_remove(runtime_measurements_count); diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 913d6b879b0b..085e8a049f51 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -84,9 +84,15 @@ static int __init ima_add_boot_aggregate(void) goto err_out; }
+#ifdef CONFIG_IMA_DIGEST_LIST result = ima_store_template(entry, violation, NULL, boot_aggregate_name, CONFIG_IMA_MEASURE_PCR_IDX, NULL); +#else + result = ima_store_template(entry, violation, NULL, + boot_aggregate_name, + CONFIG_IMA_MEASURE_PCR_IDX); +#endif if (result < 0) { ima_free_template_entry(entry); audit_cause = "store_entry"; @@ -107,8 +113,10 @@ void __init ima_load_x509(void) ima_policy_flag &= ~unset_flags; integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
+#ifdef CONFIG_IMA_DIGEST_LIST /* load also EVM key to avoid appraisal */ evm_load_x509(); +#endif
ima_policy_flag |= unset_flags; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 999d5904cce0..c4121330a647 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -28,7 +28,9 @@ #include <linux/fs.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST #include "ima_digest_list.h" +#endif
#ifdef CONFIG_IMA_APPRAISE int ima_appraise = IMA_APPRAISE_ENFORCE; @@ -38,12 +40,14 @@ int ima_appraise;
int ima_hash_algo = HASH_ALGO_SHA1;
+#ifdef CONFIG_IMA_DIGEST_LIST /* Actions (measure/appraisal) for which digest lists can be used */ int ima_digest_list_actions; /* PCR used for digest list measurements */ int ima_digest_list_pcr = -1; /* Flag to include standard measurement if digest list PCR is specified */ bool ima_plus_standard_pcr; +#endif
static int hash_setup_done;
@@ -156,6 +160,7 @@ static void ima_rdwr_violation_check(struct file *file, "invalid_pcr", "open_writers"); }
+#ifdef CONFIG_IMA_DIGEST_LIST static enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, int xattr_len) { @@ -209,6 +214,7 @@ static int ima_read_xattr(struct dentry *dentry, ret = 0; return ret; } +#endif
static void ima_check_last_writer(struct integrity_iint_cache *iint, struct inode *inode, struct file *file) @@ -268,7 +274,9 @@ static int process_measurement(struct file *file, const struct cred *cred, const char *pathname = NULL; int rc = 0, action, must_appraise = 0; int pcr = CONFIG_IMA_MEASURE_PCR_IDX; +#ifdef CONFIG_IMA_DIGEST_LIST struct ima_digest *found_digest; +#endif struct evm_ima_xattr_data *xattr_value = NULL; struct modsig *modsig = NULL; int xattr_len = 0; @@ -398,28 +406,42 @@ static int process_measurement(struct file *file, const struct cred *cred, if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */ pathname = ima_d_path(&file->f_path, &pathbuf, filename);
+#ifdef CONFIG_IMA_DIGEST_LIST if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX) pathname = file->f_path.dentry->d_name.name;
found_digest = ima_lookup_digest(iint->ima_hash->digest, hash_algo, COMPACT_FILE); +#endif
if (action & IMA_MEASURE) +#ifdef CONFIG_IMA_DIGEST_LIST ima_store_measurement(iint, file, pathname, xattr_value, xattr_len, modsig, pcr, template_desc, ima_digest_allow(found_digest, IMA_MEASURE)); +#else + ima_store_measurement(iint, file, pathname, + xattr_value, xattr_len, modsig, pcr, + template_desc); +#endif
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) { rc = ima_check_blacklist(iint, modsig, pcr); if (rc != -EPERM) { inode_lock(inode); +#ifdef CONFIG_IMA_DIGEST_LIST rc = ima_appraise_measurement(func, iint, file, pathname, xattr_value, xattr_len, modsig, ima_digest_allow(found_digest, IMA_APPRAISE)); +#else + rc = ima_appraise_measurement(func, iint, file, + pathname, xattr_value, + xattr_len, modsig); +#endif inode_unlock(inode); } if (!rc) @@ -568,15 +590,21 @@ int ima_bprm_check(struct linux_binprm *bprm) int ima_file_check(struct file *file, int mask) { u32 secid; - int rc;
security_task_getsecid(current, &secid); +#ifdef CONFIG_IMA_DIGEST_LIST + int rc; rc = process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND), FILE_CHECK); if (ima_current_is_parser() && !rc) ima_check_measured_appraised(file); return rc; +#else + return process_measurement(file, current_cred(), secid, NULL, 0, + mask & (MAY_READ | MAY_WRITE | MAY_EXEC | + MAY_APPEND), FILE_CHECK); +#endif } EXPORT_SYMBOL_GPL(ima_file_check);
@@ -739,7 +767,9 @@ const int read_idmap[READING_MAX_ID] = { [READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK, [READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK, [READING_POLICY] = POLICY_CHECK, +#ifdef CONFIG_IMA_DIGEST_LIST [READING_DIGEST_LIST] = DIGEST_LIST_CHECK +#endif };
/** @@ -941,7 +971,11 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, goto out; }
+#ifdef CONFIG_IMA_DIGEST_LIST ret = ima_store_template(entry, violation, NULL, buf, pcr, NULL); +#else + ret = ima_store_template(entry, violation, NULL, buf, pcr); +#endif if (ret < 0) { audit_cause = "store_entry"; ima_free_template_entry(entry); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 274f4c7c99f4..3e14b55ecf0f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -21,7 +21,9 @@ #include <linux/ima.h>
#include "ima.h" +#ifdef CONFIG_IMA_DIGEST_LIST #include "ima_digest_list.h" +#endif
/* flags definitions */ #define IMA_FUNC 0x0001 @@ -35,7 +37,9 @@ #define IMA_PCR 0x0100 #define IMA_FSNAME 0x0200 #define IMA_KEYRINGS 0x0400 +#ifdef CONFIG_IMA_DIGEST_LIST #define IMA_PARSER 0x0800 +#endif
#define UNKNOWN 0 #define MEASURE 0x0001 /* same as IMA_MEASURE */ @@ -58,7 +62,11 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE };
+#ifdef CONFIG_IMA_DIGEST_LIST enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB, EXEC_TCB }; +#else +enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; +#endif
enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY };
@@ -145,11 +153,13 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, +#ifdef CONFIG_IMA_DIGEST_LIST {.action = MEASURE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC}, };
static struct ima_rule_entry ima_parser_measure_rule __ro_after_init = { .action = MEASURE, .flags = IMA_PARSER +#endif };
static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { @@ -181,12 +191,14 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { #endif };
+#ifdef CONFIG_IMA_DIGEST_LIST static struct ima_rule_entry appraise_exec_rules[] __ro_after_init = { {.action = APPRAISE, .func = BPRM_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, {.action = APPRAISE, .func = MMAP_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, }; +#endif
static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { #ifdef CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS @@ -216,6 +228,7 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, {.action = APPRAISE, .func = POLICY_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, +#ifdef CONFIG_IMA_DIGEST_LIST {.action = APPRAISE, .func = DIGEST_LIST_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, }; @@ -223,6 +236,7 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { static struct ima_rule_entry ima_parser_appraise_rule __ro_after_init = { .action = APPRAISE, .flags = IMA_PARSER | IMA_DIGSIG_REQUIRED +#endif };
/* An array of architecture specific rules */ @@ -246,8 +260,10 @@ static int __init default_measure_policy_setup(char *str) __setup("ima_tcb", default_measure_policy_setup);
static bool ima_use_appraise_tcb __initdata; +#ifdef CONFIG_IMA_DIGEST_LIST static bool ima_use_appraise_exec_tcb __initdata; static bool ima_use_appraise_exec_immutable __initdata; +#endif static bool ima_use_secure_boot __initdata; static bool ima_fail_unverifiable_sigs __ro_after_init; static int __init policy_setup(char *str) @@ -259,14 +275,18 @@ static int __init policy_setup(char *str) continue; if ((strcmp(p, "tcb") == 0) && !ima_policy) ima_policy = DEFAULT_TCB; +#ifdef CONFIG_IMA_DIGEST_LIST else if ((strcmp(p, "exec_tcb") == 0) && !ima_policy) ima_policy = EXEC_TCB; +#endif else if (strcmp(p, "appraise_tcb") == 0) ima_use_appraise_tcb = true; +#ifdef CONFIG_IMA_DIGEST_LIST else if (strcmp(p, "appraise_exec_tcb") == 0) ima_use_appraise_exec_tcb = true; else if (strcmp(p, "appraise_exec_immutable") == 0) ima_use_appraise_exec_immutable = true; +#endif else if (strcmp(p, "secure_boot") == 0) ima_use_secure_boot = true; else if (strcmp(p, "fail_securely") == 0) @@ -569,9 +589,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, if ((rule->flags & IMA_FOWNER) && !rule->fowner_op(inode->i_uid, rule->fowner)) return false; +#ifdef CONFIG_IMA_DIGEST_LIST if ((rule->flags & IMA_PARSER) && !ima_current_is_parser()) return false; +#endif for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; @@ -752,19 +774,27 @@ static int ima_appraise_flag(enum ima_hooks func) return IMA_APPRAISE_POLICY; else if (func == KEXEC_KERNEL_CHECK) return IMA_APPRAISE_KEXEC; +#ifdef CONFIG_IMA_DIGEST_LIST else if (func == DIGEST_LIST_CHECK) return IMA_APPRAISE_DIGEST_LIST; +#endif return 0; }
+#ifdef CONFIG_IMA_DIGEST_LIST static void __init add_rules(struct ima_rule_entry *entries, int count, enum policy_rule_list policy_rule) +#else +static void add_rules(struct ima_rule_entry *entries, int count, + enum policy_rule_list policy_rule) +#endif { int i = 0;
for (i = 0; i < count; i++) { struct ima_rule_entry *entry;
+#ifdef CONFIG_IMA_DIGEST_LIST if (ima_policy == EXEC_TCB) { if (entries == dont_measure_rules) if ((entries[i].flags & IMA_FSMAGIC) && @@ -792,6 +822,7 @@ static void __init add_rules(struct ima_rule_entry *entries, int count, (entries[i].flags & IMA_FUNC) && entries[i].func == BPRM_CHECK) entries[i].flags |= IMA_META_IMMUTABLE_REQUIRED; +#endif
if (policy_rule & IMA_DEFAULT_POLICY) list_add_tail(&entries[i].list, &ima_default_rules); @@ -879,8 +910,10 @@ void __init ima_init_policy(void) ARRAY_SIZE(original_measurement_rules), IMA_DEFAULT_POLICY); break; +#ifdef CONFIG_IMA_DIGEST_LIST case EXEC_TCB: fallthrough; +#endif case DEFAULT_TCB: add_rules(default_measurement_rules, ARRAY_SIZE(default_measurement_rules), @@ -889,8 +922,10 @@ void __init ima_init_policy(void) break; }
+#ifdef CONFIG_IMA_DIGEST_LIST if (ima_policy) add_rules(&ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY); +#endif
/* * Based on runtime secure boot flags, insert arch specific measurement @@ -909,7 +944,11 @@ void __init ima_init_policy(void) * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to other appraise rules. */ +#ifdef CONFIG_IMA_DIGEST_LIST if (ima_use_secure_boot || ima_use_appraise_exec_tcb) +#else + if (ima_use_secure_boot) +#endif add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), IMA_DEFAULT_POLICY);
@@ -929,11 +968,16 @@ void __init ima_init_policy(void) IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); }
+#ifdef CONFIG_IMA_DIGEST_LIST if (ima_use_appraise_tcb || ima_use_appraise_exec_tcb) +#else + if (ima_use_appraise_tcb) +#endif add_rules(default_appraise_rules, ARRAY_SIZE(default_appraise_rules), IMA_DEFAULT_POLICY);
+#ifdef CONFIG_IMA_DIGEST_LIST if (ima_use_appraise_exec_tcb) add_rules(appraise_exec_rules, ARRAY_SIZE(appraise_exec_rules), @@ -942,6 +986,7 @@ void __init ima_init_policy(void) if (ima_use_secure_boot || ima_use_appraise_tcb || ima_use_appraise_exec_tcb) add_rules(&ima_parser_appraise_rule, 1, IMA_DEFAULT_POLICY); +#endif
ima_update_policy_flag(); } @@ -1002,7 +1047,11 @@ enum { Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, Opt_appraise_type, Opt_appraise_flag, Opt_permit_directio, Opt_pcr, Opt_template, Opt_keyrings, +#ifdef CONFIG_IMA_DIGEST_LIST Opt_parser, Opt_err +#else + Opt_err +#endif };
static const match_table_t policy_tokens = { @@ -1039,7 +1088,9 @@ static const match_table_t policy_tokens = { {Opt_pcr, "pcr=%s"}, {Opt_template, "template=%s"}, {Opt_keyrings, "keyrings=%s"}, +#ifdef CONFIG_IMA_DIGEST_LIST {Opt_parser, "parser"}, +#endif {Opt_err, NULL} };
@@ -1134,9 +1185,14 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (entry->action != MEASURE && entry->flags & IMA_PCR) return false;
+#ifdef CONFIG_IMA_DIGEST_LIST if (entry->action != APPRAISE && entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST | IMA_META_IMMUTABLE_REQUIRED)) +#else + if (entry->action != APPRAISE && + entry->flags & (IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED | IMA_CHECK_BLACKLIST)) +#endif return false;
/* @@ -1162,13 +1218,19 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) case POST_SETATTR: case FIRMWARE_CHECK: case POLICY_CHECK: +#ifdef CONFIG_IMA_DIGEST_LIST case DIGEST_LIST_CHECK: +#endif if (entry->flags & ~(IMA_FUNC | IMA_MASK | IMA_FSMAGIC | IMA_UID | IMA_FOWNER | IMA_FSUUID | IMA_INMASK | IMA_EUID | IMA_PCR | IMA_FSNAME | IMA_DIGSIG_REQUIRED | +#ifdef CONFIG_IMA_DIGEST_LIST IMA_PERMIT_DIRECTIO | IMA_META_IMMUTABLE_REQUIRED | IMA_PARSER)) +#else + IMA_PERMIT_DIRECTIO)) +#endif return false;
break; @@ -1180,8 +1242,12 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) IMA_INMASK | IMA_EUID | IMA_PCR | IMA_FSNAME | IMA_DIGSIG_REQUIRED | IMA_PERMIT_DIRECTIO | IMA_MODSIG_ALLOWED | +#ifdef CONFIG_IMA_DIGEST_LIST IMA_CHECK_BLACKLIST | IMA_META_IMMUTABLE_REQUIRED)) +#else + IMA_CHECK_BLACKLIST)) +#endif return false;
break; @@ -1338,8 +1404,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) else if (IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) && strcmp(args[0].from, "KEY_CHECK") == 0) entry->func = KEY_CHECK; +#ifdef CONFIG_IMA_DIGEST_LIST else if (strcmp(args[0].from, "DIGEST_LIST_CHECK") == 0) entry->func = DIGEST_LIST_CHECK; +#endif else result = -EINVAL; if (!result) @@ -1526,8 +1594,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) strcmp(args[0].from, "imasig|modsig") == 0) entry->flags |= IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED; +#ifdef CONFIG_IMA_DIGEST_LIST else if (strcmp(args[0].from, "meta_immutable") == 0) entry->flags |= IMA_META_IMMUTABLE_REQUIRED; +#endif else result = -EINVAL; break; @@ -1546,8 +1616,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr); +#ifdef CONFIG_IMA_DIGEST_LIST if (result || INVALID_PCR(entry->pcr) || entry->pcr == ima_digest_list_pcr) +#else + if (result || INVALID_PCR(entry->pcr)) +#endif result = -EINVAL; else entry->flags |= IMA_PCR; @@ -1574,10 +1648,12 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) &(template_desc->fields), &(template_desc->num_fields)); entry->template = template_desc; +#ifdef CONFIG_IMA_DIGEST_LIST break; case Opt_parser: audit_log_format(ab, "parser "); entry->flags |= IMA_PARSER; +#endif break; case Opt_err: ima_log_string(ab, "UNKNOWN", p); @@ -1849,8 +1925,10 @@ int ima_policy_show(struct seq_file *m, void *v) seq_puts(m, " "); }
+#ifdef CONFIG_IMA_DIGEST_LIST if (entry->flags & IMA_PARSER) seq_puts(m, "parser "); +#endif
for (i = 0; i < MAX_LSM_RULES; i++) { if (entry->lsm[i].rule) { @@ -1893,8 +1971,10 @@ int ima_policy_show(struct seq_file *m, void *v) } if (entry->flags & IMA_CHECK_BLACKLIST) seq_puts(m, "appraise_flag=check_blacklist "); +#ifdef CONFIG_IMA_DIGEST_LIST if (entry->flags & IMA_META_IMMUTABLE_REQUIRED) seq_puts(m, "appraise_type=meta_immutable "); +#endif if (entry->flags & IMA_PERMIT_DIRECTIO) seq_puts(m, "permit_directio "); rcu_read_unlock(); diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 6f126dbd2b39..a40098347f81 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -45,8 +45,10 @@ static const struct ima_template_field supported_fields[] = { {.field_id = "d-modsig", .field_init = ima_eventdigest_modsig_init, .field_show = ima_show_template_digest_ng}, {.field_id = "modsig", .field_init = ima_eventmodsig_init, +#ifdef CONFIG_IMA_DIGEST_LIST .field_show = ima_show_template_sig}, {.field_id = "evmsig", .field_init = ima_eventevmsig_init, +#endif .field_show = ima_show_template_sig}, };
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 90040fac150b..7308ee587314 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -10,7 +10,9 @@ */
#include "ima_template_lib.h" +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/xattr.h> +#endif
static bool ima_template_hash_algo_allowed(u8 algo) { @@ -439,7 +441,11 @@ int ima_eventsig_init(struct ima_event_data *event_data, struct evm_ima_xattr_data *xattr_value = event_data->xattr_value;
if ((!xattr_value) || (xattr_value->type != EVM_IMA_XATTR_DIGSIG)) +#ifdef CONFIG_IMA_DIGEST_LIST return ima_eventevmsig_init(event_data, field_data); +#else + return 0; +#endif
return ima_write_template_field_data(xattr_value, event_data->xattr_len, DATA_FMT_HEX, field_data); @@ -486,6 +492,7 @@ int ima_eventmodsig_init(struct ima_event_data *event_data, field_data); }
+#ifdef CONFIG_IMA_DIGEST_LIST /* * ima_eventevmsig_init - include the EVM portable signature as part of the * template data @@ -514,3 +521,4 @@ int ima_eventevmsig_init(struct ima_event_data *event_data, kfree(xattr_data); return rc; } +#endif diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h index f4b2a2056d1d..5313a8a22e45 100644 --- a/security/integrity/ima/ima_template_lib.h +++ b/security/integrity/ima/ima_template_lib.h @@ -46,6 +46,8 @@ int ima_eventbuf_init(struct ima_event_data *event_data, struct ima_field_data *field_data); int ima_eventmodsig_init(struct ima_event_data *event_data, struct ima_field_data *field_data); +#ifdef CONFIG_IMA_DIGEST_LIST int ima_eventevmsig_init(struct ima_event_data *event_data, struct ima_field_data *field_data); +#endif #endif /* __LINUX_IMA_TEMPLATE_LIB_H */ diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 77e6819e8db8..d9cf29e286a9 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -14,10 +14,13 @@
#include <linux/types.h> #include <linux/integrity.h> +#include <crypto/sha1.h> #include <crypto/sha2.h> #include <linux/key.h> #include <linux/audit.h> +#ifdef CONFIG_IMA_DIGEST_LIST #include <linux/hash_info.h> +#endif
/* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -40,7 +43,9 @@ #define IMA_FAIL_UNVERIFIABLE_SIGS 0x10000000 #define IMA_MODSIG_ALLOWED 0x20000000 #define IMA_CHECK_BLACKLIST 0x40000000 +#ifdef CONFIG_IMA_DIGEST_LIST #define IMA_META_IMMUTABLE_REQUIRED 0x80000000 +#endif
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \ IMA_HASH | IMA_APPRAISE_SUBMASK) @@ -72,7 +77,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, @@ -80,7 +87,9 @@ enum evm_ima_xattr_type { EVM_IMA_XATTR_DIGSIG, IMA_XATTR_DIGEST_NG, EVM_XATTR_PORTABLE_DIGSIG, +#ifdef CONFIG_IMA_DIGEST_LIST EVM_IMA_XATTR_DIGEST_LIST, +#endif IMA_XATTR_LAST };
@@ -92,7 +101,11 @@ struct evm_ima_xattr_data { /* Only used in the EVM HMAC code. */ struct evm_xattr { struct evm_ima_xattr_data data; +#ifdef CONFIG_IMA_DIGEST_LIST u8 digest[SHA512_DIGEST_SIZE]; +#else + u8 digest[SHA1_DIGEST_SIZE]; +#endif } __packed;
#define IMA_MAX_DIGEST_SIZE 64 @@ -144,6 +157,7 @@ struct integrity_iint_cache { struct ima_digest_data *ima_hash; };
+#ifdef CONFIG_IMA_DIGEST_LIST enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE, COMPACT_METADATA, COMPACT__LAST }; enum compact_modifiers { COMPACT_MOD_IMMUTABLE, COMPACT_MOD__LAST }; @@ -162,7 +176,6 @@ static inline bool ima_digest_is_immutable(struct ima_digest *digest) return (digest->modifiers & (1 << COMPACT_MOD_IMMUTABLE)); }
-#ifdef CONFIG_IMA_DIGEST_LIST struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, enum compact_types type); struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action); diff --git a/security/security.c b/security/security.c index 5678d4e334fb..11c859a9b8ed 100644 --- a/security/security.c +++ b/security/security.c @@ -1362,7 +1362,9 @@ void security_inode_post_setxattr(struct dentry *dentry, const char *name, if (unlikely(IS_PRIVATE(d_backing_inode(dentry)))) return; call_void_hook(inode_post_setxattr, dentry, name, value, size, flags); +#ifdef CONFIG_IMA_DIGEST_LIST ima_inode_post_setxattr(dentry, name, value, size); +#endif evm_inode_post_setxattr(dentry, name, value, size); }