Sorry, I forgot to run checkpatch on this one prior to sending :/ There was a few style issues in this patch, so here is the fixed version: This patch defines a new value for the ima policy option 'func'. That value restricts the hash algorithms accepted when writing the security.ima xattr. When a policy contains a rule of the form appraise func=SETXATTR_CHECK appraise_hash=sha256,sha384,sha512 only values corresponding to one of these three digest algorithms will be accepted for writing the security.ima xattr. Attempting to write the attribute using another algorithm (or "free-form" data) will be denied with an audit log message. In the absence of such a policy rule, the default is still to only accept hash algorithms built in the kernel (with all the limitations that entails). On policy update, the latest SETXATTR_CHECK rule is the only one that apply, and other SETXATTR_CHECK rules are deleted. Signed-off-by: Simon Thoby <simon.thoby@xxxxxxxxxx> --- Documentation/ABI/testing/ima_policy | 9 +++- security/integrity/ima/ima.h | 4 ++ security/integrity/ima/ima_appraise.c | 28 +++++++++--- security/integrity/ima/ima_policy.c | 64 +++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 6 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 365e4c91719e..c05a21007272 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -30,9 +30,10 @@ Description: [appraise_flag=] [keyrings=] [appraise_hash=] base: func:= [BPRM_CHECK][MMAP_CHECK][CREDS_CHECK][FILE_CHECK][MODULE_CHECK] - [FIRMWARE_CHECK] + [FIRMWARE_CHECK] [KEXEC_KERNEL_CHECK] [KEXEC_INITRAMFS_CHECK] [KEXEC_CMDLINE] [KEY_CHECK] [CRITICAL_DATA] + [SETXATTR_CHECK] mask:= [[^]MAY_READ] [[^]MAY_WRITE] [[^]MAY_APPEND] [[^]MAY_EXEC] fsmagic:= hex value @@ -138,3 +139,9 @@ Description: keys added to .builtin_trusted_keys or .ima keyring: measure func=KEY_CHECK keyrings=.builtin_trusted_keys|.ima + + Example of the special SETXATTR_CHECK appraise rule, that + restricts the hash algorithms allowed when writing to the + security.ima xattr of a file: + + appraise func=SETXATTR_CHECK appraise_hash=sha256,sha384,sha512 diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 7ef1b214d358..aeb3bf30c0f9 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -46,6 +46,9 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 }; /* current content of the policy */ extern int ima_policy_flag; +/* bitset of digests algorithms allowed in the setxattr hook */ +extern atomic_t ima_setxattr_allowed_hash_algorithms; + /* set during initialization */ extern int ima_hash_algo __ro_after_init; extern int ima_sha1_idx __ro_after_init; @@ -198,6 +201,7 @@ static inline unsigned int ima_hash_key(u8 *digest) hook(KEXEC_CMDLINE, kexec_cmdline) \ hook(KEY_CHECK, key) \ hook(CRITICAL_DATA, critical_data) \ + hook(SETXATTR_CHECK, setxattr_check) \ hook(MAX_CHECK, none) #define __ima_hook_enumify(ENUM, str) ENUM, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 6d121819ae9e..f3d52bbfdf0f 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -590,14 +590,32 @@ int ima_setxattr_validate_hash_alg(struct dentry *dentry, { int res = -EACCES; char *path = NULL, *pathbuf = NULL; + const char *errmsg = "unavailable-hash-algorithm"; enum hash_algo hash_alg = ima_get_hash_algo((struct evm_ima_xattr_data *)xattr_value, xattr_value_len); + unsigned int allowed_hashes = atomic_read( + &ima_setxattr_allowed_hash_algorithms); - /* disallow xattr writes with algorithms not built in the kernel */ - if (likely(hash_alg == ima_hash_algo - || crypto_has_alg(hash_algo_name[hash_alg], 0, 0))) - return 0; + if (allowed_hashes) { + /* success if the algorithm is whitelisted in the ima policy */ + if (allowed_hashes & (1U << hash_alg)) + return 0; + + /* + * We use a different audit message when the hash algorithm + * is denied by a policy rule, instead of not being built + * in the kernel image + */ + errmsg = "denied-hash-algorithm"; + } else { + if (likely(hash_alg == ima_hash_algo)) + return 0; + + /* allow any xattr using an algorithm built in the kernel */ + if (crypto_has_alg(hash_algo_name[hash_alg], 0, 0)) + return 0; + } pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); /* no memory available ? no file path for you */ @@ -605,7 +623,7 @@ int ima_setxattr_validate_hash_alg(struct dentry *dentry, path = dentry_path(dentry, pathbuf, PATH_MAX); integrity_audit_msg(AUDIT_INTEGRITY_DATA, d_inode(dentry), - path, "collect_data", "unavailable-hash-algorithm", res, 0); + path, "collect_data", errmsg, res, 0); kfree(pathbuf); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index a7f110cbbff0..cfebf8b01cc0 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -53,6 +53,8 @@ int ima_policy_flag; static int temp_ima_appraise; static int build_ima_appraise __ro_after_init; +atomic_t ima_setxattr_allowed_hash_algorithms; + #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE @@ -915,6 +917,50 @@ int ima_check_policy(void) return 0; } +/** ima_update_setxattr_allowed_hash_algorithms - cleanup SETXATTR_CHECK rules + * in the new ruleset + * + * Called when updating the IMA policy. Delete non-applicable rules with + * 'func' set to SETXATTR_CHECK and update the atomic variable to hold + * the list of allowed hash algorithms for the security.ima xattr. + * + * SETXATTR_CHECK rules do not implement a full policy check because of + * the performance impact performing rules checking on setxattr() would + * have. The consequence is that only one SETXATTR_CHECK can be active at + * a time. To prevent confusion, on policy updates, if a new SETXATTR_CHECK + * is defined, other SETXATTR_CHECK rules are remove from the ruleset. + */ +void ima_update_setxattr_allowed_hash_algorithms(struct list_head *policy) +{ + struct ima_rule_entry *entry, *tmp; + bool setxattr_check_already_defined = false; + + list_for_each_entry_safe_reverse(entry, tmp, policy, list) { + if (entry->func != SETXATTR_CHECK) + continue; + + if (setxattr_check_already_defined) { + /* + * delete old SETXATTR_CHECK entries when a newer + * one already exists + */ + list_del(&entry->list); + ima_free_rule(entry); + } else { + /* + * only the last entry with the SETXATTR_CHECK func + * apply: this allows runtime upgrades of the + * digest algorithm policy, unlike the other IMA + * rules + */ + atomic_xchg(&ima_setxattr_allowed_hash_algorithms, + entry->allowed_hashes); + setxattr_check_already_defined = true; + } + } + +} + /** * ima_update_policy - update default_rules with new measure rules * @@ -932,9 +978,12 @@ void ima_update_policy(void) list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu); + ima_update_setxattr_allowed_hash_algorithms(policy); + if (ima_rules != policy) { ima_policy_flag = 0; ima_rules = policy; + atomic_xchg(&ima_setxattr_allowed_hash_algorithms, 0); /* * IMA architecture specific policy rules are specified @@ -1176,6 +1225,19 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) if (ima_rule_contains_lsm_cond(entry)) return false; + break; + case SETXATTR_CHECK: + /* any action other than APPRAISE is unsupported */ + if (entry->action != APPRAISE) + return false; + + /* + * full policies are not supported, they would have too + * much of a performance impact + */ + if (entry->flags & ~(IMA_FUNC | IMA_VALIDATE_HASH)) + return false; + break; default: return false; @@ -1332,6 +1394,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = KEY_CHECK; else if (strcmp(args[0].from, "CRITICAL_DATA") == 0) entry->func = CRITICAL_DATA; + else if (strcmp(args[0].from, "SETXATTR_CHECK") == 0) + entry->func = SETXATTR_CHECK; else result = -EINVAL; if (!result) -- 2.31.1