Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar...
[linux-2.6-block.git] / security / integrity / ima / ima_policy.c
index 7b53f2ca58e285f75c21286e7e405f118117fe7b..6df7f641ff66658e7793185a51c04862e7100728 100644 (file)
@@ -76,6 +76,7 @@ struct ima_rule_entry {
                int type;       /* audit type */
        } lsm[MAX_LSM_RULES];
        char *fsname;
+       struct ima_template_desc *template;
 };
 
 /*
@@ -195,7 +196,7 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
 };
 
 /* An array of architecture specific rules */
-struct ima_rule_entry *arch_policy_entry __ro_after_init;
+static struct ima_rule_entry *arch_policy_entry __ro_after_init;
 
 static LIST_HEAD(ima_default_rules);
 static LIST_HEAD(ima_policy_rules);
@@ -245,31 +246,113 @@ static int __init default_appraise_policy_setup(char *str)
 }
 __setup("ima_appraise_tcb", default_appraise_policy_setup);
 
+static void ima_lsm_free_rule(struct ima_rule_entry *entry)
+{
+       int i;
+
+       for (i = 0; i < MAX_LSM_RULES; i++) {
+               kfree(entry->lsm[i].rule);
+               kfree(entry->lsm[i].args_p);
+       }
+       kfree(entry);
+}
+
+static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
+{
+       struct ima_rule_entry *nentry;
+       int i, result;
+
+       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));
+       memset(nentry->lsm, 0, FIELD_SIZEOF(struct ima_rule_entry, lsm));
+
+       for (i = 0; i < MAX_LSM_RULES; i++) {
+               if (!entry->lsm[i].rule)
+                       continue;
+
+               nentry->lsm[i].type = entry->lsm[i].type;
+               nentry->lsm[i].args_p = kstrdup(entry->lsm[i].args_p,
+                                               GFP_KERNEL);
+               if (!nentry->lsm[i].args_p)
+                       goto out_err;
+
+               result = security_filter_rule_init(nentry->lsm[i].type,
+                                                  Audit_equal,
+                                                  nentry->lsm[i].args_p,
+                                                  &nentry->lsm[i].rule);
+               if (result == -EINVAL)
+                       pr_warn("ima: rule for LSM \'%d\' is undefined\n",
+                               entry->lsm[i].type);
+       }
+       return nentry;
+
+out_err:
+       ima_lsm_free_rule(nentry);
+       return NULL;
+}
+
+static int ima_lsm_update_rule(struct ima_rule_entry *entry)
+{
+       struct ima_rule_entry *nentry;
+
+       nentry = ima_lsm_copy_rule(entry);
+       if (!nentry)
+               return -ENOMEM;
+
+       list_replace_rcu(&entry->list, &nentry->list);
+       synchronize_rcu();
+       ima_lsm_free_rule(entry);
+
+       return 0;
+}
+
 /*
  * The LSM policy can be reloaded, leaving the IMA LSM based rules referring
  * to the old, stale LSM policy.  Update the IMA LSM based rules to reflect
- * the reloaded LSM policy.  We assume the rules still exist; and BUG_ON() if
- * they don't.
+ * the reloaded LSM policy.
  */
 static void ima_lsm_update_rules(void)
 {
-       struct ima_rule_entry *entry;
-       int result;
-       int i;
+       struct ima_rule_entry *entry, *e;
+       int i, result, needs_update;
 
-       list_for_each_entry(entry, &ima_policy_rules, list) {
+       list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+               needs_update = 0;
                for (i = 0; i < MAX_LSM_RULES; i++) {
-                       if (!entry->lsm[i].rule)
-                               continue;
-                       result = security_filter_rule_init(entry->lsm[i].type,
-                                                          Audit_equal,
-                                                          entry->lsm[i].args_p,
-                                                          &entry->lsm[i].rule);
-                       BUG_ON(!entry->lsm[i].rule);
+                       if (entry->lsm[i].rule) {
+                               needs_update = 1;
+                               break;
+                       }
+               }
+               if (!needs_update)
+                       continue;
+
+               result = ima_lsm_update_rule(entry);
+               if (result) {
+                       pr_err("ima: lsm rule update error %d\n",
+                               result);
+                       return;
                }
        }
 }
 
+int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
+                         void *lsm_data)
+{
+       if (event != LSM_POLICY_CHANGE)
+               return NOTIFY_DONE;
+
+       ima_lsm_update_rules();
+       return NOTIFY_OK;
+}
+
 /**
  * ima_match_rules - determine whether an inode matches the measure rule.
  * @rule: a pointer to a rule
@@ -287,6 +370,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
 {
        int i;
 
+       if (func == KEXEC_CMDLINE) {
+               if ((rule->flags & IMA_FUNC) && (rule->func == func))
+                       return true;
+               return false;
+       }
        if ((rule->flags & IMA_FUNC) &&
            (rule->func != func && func != POST_SETATTR))
                return false;
@@ -323,11 +411,10 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode,
        for (i = 0; i < MAX_LSM_RULES; i++) {
                int rc = 0;
                u32 osid;
-               int retried = 0;
 
                if (!rule->lsm[i].rule)
                        continue;
-retry:
+
                switch (i) {
                case LSM_OBJ_USER:
                case LSM_OBJ_ROLE:
@@ -348,11 +435,6 @@ retry:
                default:
                        break;
                }
-               if ((rc < 0) && (!retried)) {
-                       retried = 1;
-                       ima_lsm_update_rules();
-                       goto retry;
-               }
                if (!rc)
                        return false;
        }
@@ -393,6 +475,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * @func: IMA hook identifier
  * @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
  *
  * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
  * conditions.
@@ -402,7 +485,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * 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)
+                    enum ima_hooks func, int mask, int flags, int *pcr,
+                    struct ima_template_desc **template_desc)
 {
        struct ima_rule_entry *entry;
        int action = 0, actmask = flags | (flags << 1);
@@ -434,6 +518,11 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
                if ((pcr) && (entry->flags & IMA_PCR))
                        *pcr = entry->pcr;
 
+               if (template_desc && entry->template)
+                       *template_desc = entry->template;
+               else if (template_desc)
+                       *template_desc = ima_template_desc_current();
+
                if (!actmask)
                        break;
        }
@@ -672,7 +761,7 @@ enum {
        Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt,
        Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt,
        Opt_appraise_type, Opt_permit_directio,
-       Opt_pcr, Opt_err
+       Opt_pcr, Opt_template, Opt_err
 };
 
 static const match_table_t policy_tokens = {
@@ -706,6 +795,7 @@ static const match_table_t policy_tokens = {
        {Opt_appraise_type, "appraise_type=%s"},
        {Opt_permit_directio, "permit_directio"},
        {Opt_pcr, "pcr=%s"},
+       {Opt_template, "template=%s"},
        {Opt_err, NULL}
 };
 
@@ -759,6 +849,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
        char *from;
        char *p;
        bool uid_token;
+       struct ima_template_desc *template_desc;
        int result = 0;
 
        ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
@@ -866,6 +957,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                                entry->func = KEXEC_INITRAMFS_CHECK;
                        else if (strcmp(args[0].from, "POLICY_CHECK") == 0)
                                entry->func = POLICY_CHECK;
+                       else if (strcmp(args[0].from, "KEXEC_CMDLINE") == 0)
+                               entry->func = KEXEC_CMDLINE;
                        else
                                result = -EINVAL;
                        if (!result)
@@ -1054,6 +1147,28 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                        else
                                entry->flags |= IMA_PCR;
 
+                       break;
+               case Opt_template:
+                       ima_log_string(ab, "template", args[0].from);
+                       if (entry->action != MEASURE) {
+                               result = -EINVAL;
+                               break;
+                       }
+                       template_desc = lookup_template_desc(args[0].from);
+                       if (!template_desc || entry->template) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       /*
+                        * template_desc_init_fields() does nothing if
+                        * the template is already initialised, so
+                        * it's safe to do this unconditionally
+                        */
+                       template_desc_init_fields(template_desc->fmt,
+                                                &(template_desc->fields),
+                                                &(template_desc->num_fields));
+                       entry->template = template_desc;
                        break;
                case Opt_err:
                        ima_log_string(ab, "UNKNOWN", p);
@@ -1330,6 +1445,8 @@ int ima_policy_show(struct seq_file *m, void *v)
                        }
                }
        }
+       if (entry->template)
+               seq_printf(m, "template=%s ", entry->template->name);
        if (entry->flags & IMA_DIGSIG_REQUIRED)
                seq_puts(m, "appraise_type=imasig ");
        if (entry->flags & IMA_PERMIT_DIRECTIO)