Merge tag 'idmapped-mounts-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / security / integrity / ima / ima_policy.c
index 823a0c1..4f8cb15 100644 (file)
@@ -34,6 +34,7 @@
 #define IMA_PCR                0x0100
 #define IMA_FSNAME     0x0200
 #define IMA_KEYRINGS   0x0400
+#define IMA_LABEL      0x0800
 
 #define UNKNOWN                0
 #define MEASURE                0x0001  /* same as IMA_MEASURE */
@@ -85,6 +86,7 @@ struct ima_rule_entry {
        } lsm[MAX_LSM_RULES];
        char *fsname;
        struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */
+       struct ima_rule_opt_list *label; /* Measure data grouped under this label */
        struct ima_template_desc *template;
 };
 
@@ -204,6 +206,10 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
         .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
 };
 
+static struct ima_rule_entry critical_data_rules[] __ro_after_init = {
+       {.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC},
+};
+
 /* An array of architecture specific rules */
 static struct ima_rule_entry *arch_policy_entry __ro_after_init;
 
@@ -226,6 +232,7 @@ __setup("ima_tcb", default_measure_policy_setup);
 
 static bool ima_use_appraise_tcb __initdata;
 static bool ima_use_secure_boot __initdata;
+static bool ima_use_critical_data __initdata;
 static bool ima_fail_unverifiable_sigs __ro_after_init;
 static int __init policy_setup(char *str)
 {
@@ -240,6 +247,8 @@ static int __init policy_setup(char *str)
                        ima_use_appraise_tcb = true;
                else if (strcmp(p, "secure_boot") == 0)
                        ima_use_secure_boot = true;
+               else if (strcmp(p, "critical_data") == 0)
+                       ima_use_critical_data = true;
                else if (strcmp(p, "fail_securely") == 0)
                        ima_fail_unverifiable_sigs = true;
                else
@@ -453,30 +462,46 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 }
 
 /**
- * ima_match_keyring - determine whether the keyring matches the measure rule
+ * ima_match_rule_data - determine whether func_data matches the policy rule
  * @rule: a pointer to a rule
- * @keyring: name of the keyring to match against the measure rule
+ * @func_data: data to match against the measure rule data
  * @cred: a pointer to a credentials structure for user validation
  *
- * Returns true if keyring matches one in the rule, false otherwise.
+ * Returns true if func_data matches one in the rule, false otherwise.
  */
-static bool ima_match_keyring(struct ima_rule_entry *rule,
-                             const char *keyring, const struct cred *cred)
+static bool ima_match_rule_data(struct ima_rule_entry *rule,
+                               const char *func_data,
+                               const struct cred *cred)
 {
+       const struct ima_rule_opt_list *opt_list = NULL;
        bool matched = false;
        size_t i;
 
        if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid))
                return false;
 
-       if (!rule->keyrings)
-               return true;
+       switch (rule->func) {
+       case KEY_CHECK:
+               if (!rule->keyrings)
+                       return true;
+
+               opt_list = rule->keyrings;
+               break;
+       case CRITICAL_DATA:
+               if (!rule->label)
+                       return true;
+
+               opt_list = rule->label;
+               break;
+       default:
+               return false;
+       }
 
-       if (!keyring)
+       if (!func_data)
                return false;
 
-       for (i = 0; i < rule->keyrings->count; i++) {
-               if (!strcmp(rule->keyrings->items[i], keyring)) {
+       for (i = 0; i < opt_list->count; i++) {
+               if (!strcmp(opt_list->items[i], func_data)) {
                        matched = true;
                        break;
                }
@@ -488,29 +513,37 @@ static bool ima_match_keyring(struct ima_rule_entry *rule,
 /**
  * ima_match_rules - determine whether an inode matches the policy rule.
  * @rule: a pointer to a rule
+ * @mnt_userns:        user namespace of the mount the inode was found from
  * @inode: a pointer to an inode
  * @cred: a pointer to a credentials structure for user validation
  * @secid: the secid of the task to be validated
  * @func: LIM hook identifier
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
- * @keyring: keyring name to check in policy for KEY_CHECK func
+ * @func_data: func specific data, may be NULL
  *
  * Returns true on rule match, false on failure.
  */
-static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
-                           const struct cred *cred, u32 secid,
-                           enum ima_hooks func, int mask,
-                           const char *keyring)
+static bool ima_match_rules(struct ima_rule_entry *rule,
+                           struct user_namespace *mnt_userns,
+                           struct inode *inode, const struct cred *cred,
+                           u32 secid, enum ima_hooks func, int mask,
+                           const char *func_data)
 {
        int i;
 
-       if (func == KEY_CHECK) {
-               return (rule->flags & IMA_FUNC) && (rule->func == func) &&
-                      ima_match_keyring(rule, keyring, cred);
-       }
        if ((rule->flags & IMA_FUNC) &&
            (rule->func != func && func != POST_SETATTR))
                return false;
+
+       switch (func) {
+       case KEY_CHECK:
+       case CRITICAL_DATA:
+               return ((rule->func == func) &&
+                       ima_match_rule_data(rule, func_data, cred));
+       default:
+               break;
+       }
+
        if ((rule->flags & IMA_MASK) &&
            (rule->mask != mask && func != POST_SETATTR))
                return false;
@@ -539,7 +572,7 @@ 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))
+           !rule->fowner_op(i_uid_into_mnt(mnt_userns, inode), rule->fowner))
                return false;
        for (i = 0; i < MAX_LSM_RULES; i++) {
                int rc = 0;
@@ -602,6 +635,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
 
 /**
  * ima_match_policy - decision based on LSM and other conditions
+ * @mnt_userns:        user namespace of the mount the inode was found from
  * @inode: pointer to an inode for which the policy decision is being made
  * @cred: pointer to a credentials structure for which the policy decision is
  *        being made
@@ -610,8 +644,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
  * @pcr: set the pcr to extend
  * @template_desc: the template that should be used for this rule
- * @keyring: the keyring name, if given, to be used to check in the policy.
- *           keyring can be NULL if func is anything other than KEY_CHECK.
+ * @func_data: func specific data, may be NULL
  *
  * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
  * conditions.
@@ -620,10 +653,11 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * list when walking it.  Reads are many orders of magnitude more numerous
  * than writes so ima_match_policy() is classical RCU candidate.
  */
-int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
-                    enum ima_hooks func, int mask, int flags, int *pcr,
+int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
+                    const struct cred *cred, u32 secid, enum ima_hooks func,
+                    int mask, int flags, int *pcr,
                     struct ima_template_desc **template_desc,
-                    const char *keyring)
+                    const char *func_data)
 {
        struct ima_rule_entry *entry;
        int action = 0, actmask = flags | (flags << 1);
@@ -637,8 +671,8 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
                if (!(entry->action & actmask))
                        continue;
 
-               if (!ima_match_rules(entry, inode, cred, secid, func, mask,
-                                    keyring))
+               if (!ima_match_rules(entry, mnt_userns, inode, cred, secid,
+                                    func, mask, func_data))
                        continue;
 
                action |= entry->flags & IMA_ACTION_FLAGS;
@@ -848,6 +882,11 @@ void __init ima_init_policy(void)
                          ARRAY_SIZE(default_appraise_rules),
                          IMA_DEFAULT_POLICY);
 
+       if (ima_use_critical_data)
+               add_rules(critical_data_rules,
+                         ARRAY_SIZE(critical_data_rules),
+                         IMA_DEFAULT_POLICY);
+
        ima_update_policy_flag();
 }
 
@@ -907,7 +946,7 @@ 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,
-       Opt_err
+       Opt_label, Opt_err
 };
 
 static const match_table_t policy_tokens = {
@@ -944,6 +983,7 @@ static const match_table_t policy_tokens = {
        {Opt_pcr, "pcr=%s"},
        {Opt_template, "template=%s"},
        {Opt_keyrings, "keyrings=%s"},
+       {Opt_label, "label=%s"},
        {Opt_err, NULL}
 };
 
@@ -1106,6 +1146,18 @@ static bool ima_validate_rule(struct ima_rule_entry *entry)
                if (ima_rule_contains_lsm_cond(entry))
                        return false;
 
+               break;
+       case CRITICAL_DATA:
+               if (entry->action & ~(MEASURE | DONT_MEASURE))
+                       return false;
+
+               if (entry->flags & ~(IMA_FUNC | IMA_UID | IMA_PCR |
+                                    IMA_LABEL))
+                       return false;
+
+               if (ima_rule_contains_lsm_cond(entry))
+                       return false;
+
                break;
        default:
                return false;
@@ -1238,6 +1290,8 @@ 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;
+                       else if (strcmp(args[0].from, "CRITICAL_DATA") == 0)
+                               entry->func = CRITICAL_DATA;
                        else
                                result = -EINVAL;
                        if (!result)
@@ -1308,6 +1362,23 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 
                        entry->flags |= IMA_KEYRINGS;
                        break;
+               case Opt_label:
+                       ima_log_string(ab, "label", args[0].from);
+
+                       if (entry->label) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       entry->label = ima_alloc_rule_opt_list(args);
+                       if (IS_ERR(entry->label)) {
+                               result = PTR_ERR(entry->label);
+                               entry->label = NULL;
+                               break;
+                       }
+
+                       entry->flags |= IMA_LABEL;
+                       break;
                case Opt_fsuuid:
                        ima_log_string(ab, "fsuuid", args[0].from);
 
@@ -1688,6 +1759,12 @@ int ima_policy_show(struct seq_file *m, void *v)
                seq_puts(m, " ");
        }
 
+       if (entry->flags & IMA_LABEL) {
+               seq_puts(m, "label=");
+               ima_show_rule_opt_list(m, entry->label);
+               seq_puts(m, " ");
+       }
+
        if (entry->flags & IMA_PCR) {
                snprintf(tbuf, sizeof(tbuf), "%d", entry->pcr);
                seq_printf(m, pt(Opt_pcr), tbuf);