diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-15 15:58:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-10-15 15:58:18 -0700 |
commit | 840e5bb326bbcb16ce82dd2416d2769de4839aea (patch) | |
tree | 0db7a077c3ae35dd99a89f0128b760951d95db72 /security/integrity/ima/ima_policy.c | |
parent | fefa636d815975b34afc45f50852a2810fb23ba9 (diff) | |
parent | aa662fc04f5b290b3979332588bf8d812b189962 (diff) |
Merge tag 'integrity-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity
Pull integrity updates from Mimi Zohar:
"Continuing IMA policy rule cleanup and validation in particular for
measuring keys, adding/removing/updating informational and error
messages (e.g. "ima_appraise" boot command line option), and other bug
fixes (e.g. minimal data size validation before use, return code and
NULL pointer checking)"
* tag 'integrity-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity:
ima: Fix NULL pointer dereference in ima_file_hash
evm: Check size of security.evm before using it
ima: Remove semicolon at the end of ima_get_binary_runtime_size()
ima: Don't ignore errors from crypto_shash_update()
ima: Use kmemdup rather than kmalloc+memcpy
integrity: include keyring name for unknown key request
ima: limit secure boot feedback scope for appraise
integrity: invalid kernel parameters feedback
ima: add check for enforced appraise option
integrity: Use current_uid() in integrity_audit_message()
ima: Fail rule parsing when asymmetric key measurement isn't supportable
ima: Pre-parse the list of keyrings in a KEY_CHECK rule
Diffstat (limited to 'security/integrity/ima/ima_policy.c')
-rw-r--r-- | security/integrity/ima/ima_policy.c | 153 |
1 files changed, 102 insertions, 51 deletions
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3b0b43e18ecf..9b5adeaa47fc 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -60,6 +60,11 @@ enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY }; +struct ima_rule_opt_list { + size_t count; + char *items[]; +}; + struct ima_rule_entry { struct list_head list; int action; @@ -79,7 +84,7 @@ struct ima_rule_entry { int type; /* audit type */ } lsm[MAX_LSM_RULES]; char *fsname; - char *keyrings; /* Measure keys added to these keyrings */ + struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */ struct ima_template_desc *template; }; @@ -207,10 +212,6 @@ static LIST_HEAD(ima_policy_rules); static LIST_HEAD(ima_temp_rules); static struct list_head *ima_rules = &ima_default_rules; -/* Pre-allocated buffer used for matching keyrings. */ -static char *ima_keyrings; -static size_t ima_keyrings_len; - static int ima_policy __initdata; static int __init default_measure_policy_setup(char *str) @@ -241,6 +242,8 @@ static int __init policy_setup(char *str) ima_use_secure_boot = true; else if (strcmp(p, "fail_securely") == 0) ima_fail_unverifiable_sigs = true; + else + pr_err("policy \"%s\" not found", p); } return 1; @@ -254,6 +257,72 @@ static int __init default_appraise_policy_setup(char *str) } __setup("ima_appraise_tcb", default_appraise_policy_setup); +static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src) +{ + struct ima_rule_opt_list *opt_list; + size_t count = 0; + char *src_copy; + char *cur, *next; + size_t i; + + src_copy = match_strdup(src); + if (!src_copy) + return ERR_PTR(-ENOMEM); + + next = src_copy; + while ((cur = strsep(&next, "|"))) { + /* Don't accept an empty list item */ + if (!(*cur)) { + kfree(src_copy); + return ERR_PTR(-EINVAL); + } + count++; + } + + /* Don't accept an empty list */ + if (!count) { + kfree(src_copy); + return ERR_PTR(-EINVAL); + } + + opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL); + if (!opt_list) { + kfree(src_copy); + return ERR_PTR(-ENOMEM); + } + + /* + * strsep() has already replaced all instances of '|' with '\0', + * leaving a byte sequence of NUL-terminated strings. Reference each + * string with the array of items. + * + * IMPORTANT: Ownership of the allocated buffer is transferred from + * src_copy to the first element in the items array. To free the + * buffer, kfree() must only be called on the first element of the + * array. + */ + for (i = 0, cur = src_copy; i < count; i++) { + opt_list->items[i] = cur; + cur = strchr(cur, '\0') + 1; + } + opt_list->count = count; + + return opt_list; +} + +static void ima_free_rule_opt_list(struct ima_rule_opt_list *opt_list) +{ + if (!opt_list) + return; + + if (opt_list->count) { + kfree(opt_list->items[0]); + opt_list->count = 0; + } + + kfree(opt_list); +} + static void ima_lsm_free_rule(struct ima_rule_entry *entry) { int i; @@ -275,7 +344,7 @@ static void ima_free_rule(struct ima_rule_entry *entry) * the defined_templates list and cannot be freed here */ kfree(entry->fsname); - kfree(entry->keyrings); + ima_free_rule_opt_list(entry->keyrings); ima_lsm_free_rule(entry); kfree(entry); } @@ -285,15 +354,14 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry) struct ima_rule_entry *nentry; int i; - nentry = kmalloc(sizeof(*nentry), GFP_KERNEL); - if (!nentry) - return NULL; - /* * Immutable elements are copied over as pointers and data; only * lsm rules can change */ - memcpy(nentry, entry, sizeof(*nentry)); + nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL); + if (!nentry) + return NULL; + memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm)); for (i = 0; i < MAX_LSM_RULES; i++) { @@ -395,8 +463,8 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, static bool ima_match_keyring(struct ima_rule_entry *rule, const char *keyring, const struct cred *cred) { - char *next_keyring, *keyrings_ptr; bool matched = false; + size_t i; if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) return false; @@ -407,15 +475,8 @@ static bool ima_match_keyring(struct ima_rule_entry *rule, if (!keyring) return false; - strcpy(ima_keyrings, rule->keyrings); - - /* - * "keyrings=" is specified in the policy in the format below: - * keyrings=.builtin_trusted_keys|.ima|.evm - */ - keyrings_ptr = ima_keyrings; - while ((next_keyring = strsep(&keyrings_ptr, "|")) != NULL) { - if (!strcmp(next_keyring, keyring)) { + for (i = 0; i < rule->keyrings->count; i++) { + if (!strcmp(rule->keyrings->items[i], keyring)) { matched = true; break; } @@ -1066,7 +1127,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) bool uid_token; struct ima_template_desc *template_desc; int result = 0; - size_t keyrings_len; ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, AUDIT_INTEGRITY_POLICY_RULE); @@ -1175,7 +1235,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = POLICY_CHECK; else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0) entry->func = KEXEC_CMDLINE; - else if (strcmp(args[0].from, "KEY_CHECK") == 0) + else if (IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) && + strcmp(args[0].from, "KEY_CHECK") == 0) entry->func = KEY_CHECK; else result = -EINVAL; @@ -1232,37 +1293,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) case Opt_keyrings: ima_log_string(ab, "keyrings", args[0].from); - keyrings_len = strlen(args[0].from) + 1; - - if ((entry->keyrings) || - (keyrings_len < 2)) { + if (!IS_ENABLED(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) || + entry->keyrings) { result = -EINVAL; break; } - if (keyrings_len > ima_keyrings_len) { - char *tmpbuf; - - tmpbuf = krealloc(ima_keyrings, keyrings_len, - GFP_KERNEL); - if (!tmpbuf) { - result = -ENOMEM; - break; - } - - ima_keyrings = tmpbuf; - ima_keyrings_len = keyrings_len; - } - - entry->keyrings = kstrdup(args[0].from, GFP_KERNEL); - if (!entry->keyrings) { - kfree(ima_keyrings); - ima_keyrings = NULL; - ima_keyrings_len = 0; - result = -ENOMEM; + entry->keyrings = ima_alloc_rule_opt_list(args); + if (IS_ERR(entry->keyrings)) { + result = PTR_ERR(entry->keyrings); + entry->keyrings = NULL; break; } - result = 0; + entry->flags |= IMA_KEYRINGS; break; case Opt_fsuuid: @@ -1575,6 +1618,15 @@ static void policy_func_show(struct seq_file *m, enum ima_hooks func) seq_printf(m, "func=%d ", func); } +static void ima_show_rule_opt_list(struct seq_file *m, + const struct ima_rule_opt_list *opt_list) +{ + size_t i; + + for (i = 0; i < opt_list->count; i++) + seq_printf(m, "%s%s", i ? "|" : "", opt_list->items[i]); +} + int ima_policy_show(struct seq_file *m, void *v) { struct ima_rule_entry *entry = v; @@ -1631,9 +1683,8 @@ int ima_policy_show(struct seq_file *m, void *v) } if (entry->flags & IMA_KEYRINGS) { - if (entry->keyrings != NULL) - snprintf(tbuf, sizeof(tbuf), "%s", entry->keyrings); - seq_printf(m, pt(Opt_keyrings), tbuf); + seq_puts(m, "keyrings="); + ima_show_rule_opt_list(m, entry->keyrings); seq_puts(m, " "); } |