ima: use the lsm policy update notifier
authorJanne Karhunen <janne.karhunen@gmail.com>
Fri, 14 Jun 2019 12:20:15 +0000 (15:20 +0300)
committerMimi Zohar <zohar@linux.ibm.com>
Fri, 14 Jun 2019 13:02:43 +0000 (09:02 -0400)
Don't do lazy policy updates while running the rule matching,
run the updates as they happen.

Depends on commit f242064c5df3 ("LSM: switch to blocking policy update notifiers")

Signed-off-by: Janne Karhunen <janne.karhunen@gmail.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
security/integrity/ima/ima.h
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c

index d213e83..2203451 100644 (file)
@@ -154,6 +154,8 @@ unsigned long ima_get_binary_runtime_size(void);
 int ima_init_template(void);
 void ima_init_template_list(void);
 int __init ima_init_digests(void);
+int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
+                         void *lsm_data);
 
 /*
  * used to protect h_table and sha_table
index f16353b..9e3ea8a 100644 (file)
@@ -43,6 +43,10 @@ int ima_appraise;
 int ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
+static struct notifier_block ima_lsm_policy_notifier = {
+       .notifier_call = ima_lsm_policy_change,
+};
+
 static int __init hash_setup(char *str)
 {
        struct ima_template_desc *template_desc = ima_template_desc_current();
@@ -621,6 +625,10 @@ static int __init init_ima(void)
                error = ima_init();
        }
 
+       error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
+       if (error)
+               pr_warn("Couldn't register LSM notifier, error %d\n", error);
+
        if (!error)
                ima_update_policy_flag();
 
index cd1b728..ec4efa8 100644 (file)
@@ -249,31 +249,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
@@ -327,11 +409,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:
@@ -352,11 +433,6 @@ retry:
                default:
                        break;
                }
-               if ((rc < 0) && (!retried)) {
-                       retried = 1;
-                       ima_lsm_update_rules();
-                       goto retry;
-               }
                if (!rc)
                        return false;
        }