Merge tag 'integrity-v5.10' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[platform/kernel/linux-starfive.git] / security / integrity / ima / ima_policy.c
index 3b0b43e..9b5adea 100644 (file)
@@ -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, " ");
        }