With multiple possible security modules supporting audit rule
it is necessary to keep separate data for each module in the
audit rules. This affects IMA as well, as it re-uses the audit
rule list mechanisms.

Signed-off-by: Casey Schaufler <ca...@schaufler-ca.com>
Cc: linux-integr...@vger.kernel.org
Cc: linux-audit@redhat.com
---
 include/linux/audit.h               |  4 +++-
 include/linux/security.h            |  8 +++----
 kernel/auditfilter.c                | 26 +++++++++++----------
 kernel/auditsc.c                    | 12 +++++-----
 security/integrity/ima/ima_policy.c | 36 +++++++++++++++++++----------
 security/security.c                 | 34 +++++++++++++++++++++++----
 6 files changed, 80 insertions(+), 40 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 2ce0e8da3922..d4213c471801 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -11,6 +11,7 @@
 
 #include <linux/sched.h>
 #include <linux/ptrace.h>
+#include <linux/security.h>
 #include <uapi/linux/audit.h>
 
 #define AUDIT_INO_UNSET ((unsigned long)-1)
@@ -64,8 +65,9 @@ struct audit_field {
                kuid_t                  uid;
                kgid_t                  gid;
                struct {
+                       bool            lsm_isset;
                        char            *lsm_str;
-                       void            *lsm_rule;
+                       void            *lsm_rules[LSMBLOB_ENTRIES];
                };
        };
        u32                             op;
diff --git a/include/linux/security.h b/include/linux/security.h
index 26967055a002..0bf71dd74a9c 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1887,8 +1887,8 @@ static inline int security_key_getsecurity(struct key 
*key, char **_buffer)
 int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule);
 int security_audit_rule_known(struct audit_krule *krule);
 int security_audit_rule_match(struct lsmblob *blob, u32 field, u32 op,
-                             void *lsmrule);
-void security_audit_rule_free(void *lsmrule);
+                             void **lsmrule);
+void security_audit_rule_free(void **lsmrule);
 
 #else
 
@@ -1904,12 +1904,12 @@ static inline int security_audit_rule_known(struct 
audit_krule *krule)
 }
 
 static inline int security_audit_rule_match(struct lsmblob *blob, u32 field,
-                                           u32 op, void *lsmrule)
+                                           u32 op, void **lsmrule)
 {
        return 0;
 }
 
-static inline void security_audit_rule_free(void *lsmrule)
+static inline void security_audit_rule_free(void **lsmrule)
 { }
 
 #endif /* CONFIG_SECURITY */
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index bf28bb599b6d..0f351d1f6ef9 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -74,7 +74,7 @@ static void audit_free_lsm_field(struct audit_field *f)
        case AUDIT_OBJ_LEV_LOW:
        case AUDIT_OBJ_LEV_HIGH:
                kfree(f->lsm_str);
-               security_audit_rule_free(f->lsm_rule);
+               security_audit_rule_free(f->lsm_rules);
        }
 }
 
@@ -517,7 +517,7 @@ static struct audit_entry *audit_data_to_entry(struct 
audit_rule_data *data,
                        entry->rule.buflen += f->val;
 
                        err = security_audit_rule_init(f->type, f->op, str,
-                                                      (void **)&f->lsm_rule);
+                                                      f->lsm_rules);
                        /* Keep currently invalid fields around in case they
                         * become valid after a policy reload. */
                        if (err == -EINVAL) {
@@ -528,8 +528,10 @@ static struct audit_entry *audit_data_to_entry(struct 
audit_rule_data *data,
                        if (err) {
                                kfree(str);
                                goto exit_free;
-                       } else
+                       } else {
+                               f->lsm_isset = true;
                                f->lsm_str = str;
+                       }
                        break;
                case AUDIT_WATCH:
                        str = audit_unpack_string(&bufp, &remain, f->val);
@@ -767,7 +769,7 @@ static int audit_compare_rule(struct audit_krule *a, struct 
audit_krule *b)
        return 0;
 }
 
-/* Duplicate LSM field information.  The lsm_rule is opaque, so must be
+/* Duplicate LSM field information.  The lsm_rules is opaque, so must be
  * re-initialized. */
 static inline int audit_dupe_lsm_field(struct audit_field *df,
                                           struct audit_field *sf)
@@ -781,9 +783,9 @@ static inline int audit_dupe_lsm_field(struct audit_field 
*df,
                return -ENOMEM;
        df->lsm_str = lsm_str;
 
-       /* our own (refreshed) copy of lsm_rule */
+       /* our own (refreshed) copy of lsm_rules */
        ret = security_audit_rule_init(df->type, df->op, df->lsm_str,
-                                      (void **)&df->lsm_rule);
+                                      df->lsm_rules);
        /* Keep currently invalid fields around in case they
         * become valid after a policy reload. */
        if (ret == -EINVAL) {
@@ -835,7 +837,7 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old)
        new->tree = old->tree;
        memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
 
-       /* deep copy this information, updating the lsm_rule fields, because
+       /* deep copy this information, updating the lsm_rules fields, because
         * the originals will all be freed when the old rule is freed. */
        for (i = 0; i < fcount; i++) {
                switch (new->fields[i].type) {
@@ -1354,11 +1356,11 @@ int audit_filter(int msgtype, unsigned int listtype)
                        case AUDIT_SUBJ_TYPE:
                        case AUDIT_SUBJ_SEN:
                        case AUDIT_SUBJ_CLR:
-                               if (f->lsm_rule) {
+                               if (f->lsm_isset) {
                                        security_task_getsecid(current, &blob);
                                        result = security_audit_rule_match(
                                                        &blob, f->type,
-                                                       f->op, f->lsm_rule);
+                                                       f->op, f->lsm_rules);
                                }
                                break;
                        case AUDIT_EXE:
@@ -1385,7 +1387,7 @@ int audit_filter(int msgtype, unsigned int listtype)
        return ret;
 }
 
-static int update_lsm_rule(struct audit_krule *r)
+static int update_lsm_rules(struct audit_krule *r)
 {
        struct audit_entry *entry = container_of(r, struct audit_entry, rule);
        struct audit_entry *nentry;
@@ -1417,7 +1419,7 @@ static int update_lsm_rule(struct audit_krule *r)
        return err;
 }
 
-/* This function will re-initialize the lsm_rule field of all applicable rules.
+/* This function will re-initialize the lsm_rules field of all applicable 
rules.
  * It will traverse the filter lists serarching for rules that contain LSM
  * specific filter fields.  When such a rule is found, it is copied, the
  * LSM field is re-initialized, and the old rule is replaced with the
@@ -1432,7 +1434,7 @@ int audit_update_lsm_rules(void)
 
        for (i = 0; i < AUDIT_NR_FILTERS; i++) {
                list_for_each_entry_safe(r, n, &audit_rules_list[i], list) {
-                       int res = update_lsm_rule(r);
+                       int res = update_lsm_rules(r);
                        if (!err)
                                err = res;
                }
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 28fea2e73040..b9f81ef64c39 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -638,7 +638,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                           match for now to avoid losing information that
                           may be wanted.   An error message will also be
                           logged upon error */
-                       if (f->lsm_rule) {
+                       if (f->lsm_isset) {
                                if (need_sid) {
                                        security_task_getsecid(tsk, &blob);
                                        need_sid = 0;
@@ -646,7 +646,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                                result = security_audit_rule_match(&blob,
                                                                   f->type,
                                                                   f->op,
-                                                                  f->lsm_rule);
+                                                                  
f->lsm_rules);
                        }
                        break;
                case AUDIT_OBJ_USER:
@@ -656,21 +656,21 @@ static int audit_filter_rules(struct task_struct *tsk,
                case AUDIT_OBJ_LEV_HIGH:
                        /* The above note for AUDIT_SUBJ_USER...AUDIT_SUBJ_CLR
                           also applies here */
-                       if (f->lsm_rule) {
+                       if (f->lsm_isset) {
                                /* Find files that match */
                                if (name) {
                                        result = security_audit_rule_match(
                                                                &name->oblob,
                                                                f->type,
                                                                f->op,
-                                                               f->lsm_rule);
+                                                               f->lsm_rules);
                                } else if (ctx) {
                                        list_for_each_entry(n, 
&ctx->names_list, list) {
                                                if (security_audit_rule_match(
                                                                &n->oblob,
                                                                f->type,
                                                                f->op,
-                                                               f->lsm_rule)) {
+                                                               f->lsm_rules)) {
                                                        ++result;
                                                        break;
                                                }
@@ -681,7 +681,7 @@ static int audit_filter_rules(struct task_struct *tsk,
                                        break;
                                if (security_audit_rule_match(&ctx->ipc.oblob,
                                                              f->type, f->op,
-                                                             f->lsm_rule))
+                                                             f->lsm_rules))
                                        ++result;
                        }
                        break;
diff --git a/security/integrity/ima/ima_policy.c 
b/security/integrity/ima/ima_policy.c
index 1c617ae74558..227993b8422d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -74,7 +74,7 @@ struct ima_rule_entry {
        bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */
        int pcr;
        struct {
-               void *rule;     /* LSM file metadata specific */
+               void *rules[LSMBLOB_ENTRIES];
                void *args_p;   /* audit value */
                int type;       /* audit type */
        } lsm[MAX_LSM_RULES];
@@ -82,6 +82,16 @@ struct ima_rule_entry {
        struct ima_template_desc *template;
 };
 
+static inline bool ima_lsm_isset(void *rules[])
+{
+       int i;
+
+       for (i = 0; i < LSMBLOB_ENTRIES; i++)
+               if (rules[i])
+                       return true;
+       return false;
+}
+
 /*
  * Without LSM specific knowledge, the default policy can only be
  * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
@@ -252,9 +262,11 @@ __setup("ima_appraise_tcb", default_appraise_policy_setup);
 static void ima_lsm_free_rule(struct ima_rule_entry *entry)
 {
        int i;
+       int r;
 
        for (i = 0; i < MAX_LSM_RULES; i++) {
-               kfree(entry->lsm[i].rule);
+               for (r = 0; r < LSMBLOB_ENTRIES; r++)
+                       kfree(entry->lsm[i].rules[r]);
                kfree(entry->lsm[i].args_p);
        }
        kfree(entry);
@@ -277,7 +289,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct 
ima_rule_entry *entry)
        memset(nentry->lsm, 0, sizeof_field(struct ima_rule_entry, lsm));
 
        for (i = 0; i < MAX_LSM_RULES; i++) {
-               if (!entry->lsm[i].rule)
+               if (!ima_lsm_isset(entry->lsm[i].rules))
                        continue;
 
                nentry->lsm[i].type = entry->lsm[i].type;
@@ -289,7 +301,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct 
ima_rule_entry *entry)
                result = security_filter_rule_init(nentry->lsm[i].type,
                                                   Audit_equal,
                                                   nentry->lsm[i].args_p,
-                                                  &nentry->lsm[i].rule);
+                                                  nentry->lsm[i].rules);
                if (result == -EINVAL)
                        pr_warn("ima: rule for LSM \'%d\' is undefined\n",
                                entry->lsm[i].type);
@@ -329,7 +341,7 @@ static void ima_lsm_update_rules(void)
        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) {
+                       if (ima_lsm_isset(entry->lsm[i].rules)) {
                                needs_update = 1;
                                break;
                        }
@@ -415,7 +427,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, 
struct inode *inode,
                int rc = 0;
                struct lsmblob blob;
 
-               if (!rule->lsm[i].rule)
+               if (!ima_lsm_isset(rule->lsm[i].rules))
                        continue;
 
                switch (i) {
@@ -426,7 +438,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, 
struct inode *inode,
                        rc = security_filter_rule_match(&blob,
                                                        rule->lsm[i].type,
                                                        Audit_equal,
-                                                       rule->lsm[i].rule);
+                                                       rule->lsm[i].rules);
                        break;
                case LSM_SUBJ_USER:
                case LSM_SUBJ_ROLE:
@@ -434,7 +446,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule, 
struct inode *inode,
                        rc = security_filter_rule_match(&blob,
                                                        rule->lsm[i].type,
                                                        Audit_equal,
-                                                       rule->lsm[i].rule);
+                                                       rule->lsm[i].rules);
                default:
                        break;
                }
@@ -811,7 +823,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
 {
        int result;
 
-       if (entry->lsm[lsm_rule].rule)
+       if (ima_lsm_isset(entry->lsm[lsm_rule].rules))
                return -EINVAL;
 
        entry->lsm[lsm_rule].args_p = match_strdup(args);
@@ -822,8 +834,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
        result = security_filter_rule_init(entry->lsm[lsm_rule].type,
                                           Audit_equal,
                                           entry->lsm[lsm_rule].args_p,
-                                          &entry->lsm[lsm_rule].rule);
-       if (!entry->lsm[lsm_rule].rule) {
+                                          entry->lsm[lsm_rule].rules);
+       if (!ima_lsm_isset(entry->lsm[lsm_rule].rules)) {
                kfree(entry->lsm[lsm_rule].args_p);
                return -EINVAL;
        }
@@ -1470,7 +1482,7 @@ int ima_policy_show(struct seq_file *m, void *v)
        }
 
        for (i = 0; i < MAX_LSM_RULES; i++) {
-               if (entry->lsm[i].rule) {
+               if (ima_lsm_isset(entry->lsm[i].rules)) {
                        switch (i) {
                        case LSM_OBJ_USER:
                                seq_printf(m, pt(Opt_obj_user),
diff --git a/security/security.c b/security/security.c
index e94de64e114c..4be490512ed2 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2831,7 +2831,24 @@ int security_key_getsecurity(struct key *key, char 
**_buffer)
 
 int security_audit_rule_init(u32 field, u32 op, char *rulestr, void **lsmrule)
 {
-       return call_int_hook(audit_rule_init, 0, field, op, rulestr, lsmrule);
+       struct security_hook_list *hp;
+       bool one_is_good = false;
+       int rc = 0;
+       int trc;
+
+       hlist_for_each_entry(hp, &security_hook_heads.audit_rule_init, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               trc = hp->hook.audit_rule_init(field, op, rulestr,
+                                              &lsmrule[hp->lsmid->slot]);
+               if (trc == 0)
+                       one_is_good = true;
+               else
+                       rc = trc;
+       }
+       if (one_is_good)
+               return 0;
+       return rc;
 }
 
 int security_audit_rule_known(struct audit_krule *krule)
@@ -2839,13 +2856,19 @@ int security_audit_rule_known(struct audit_krule *krule)
        return call_int_hook(audit_rule_known, 0, krule);
 }
 
-void security_audit_rule_free(void *lsmrule)
+void security_audit_rule_free(void **lsmrule)
 {
-       call_void_hook(audit_rule_free, lsmrule);
+       struct security_hook_list *hp;
+
+       hlist_for_each_entry(hp, &security_hook_heads.audit_rule_free, list) {
+               if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
+                       continue;
+               hp->hook.audit_rule_free(lsmrule[hp->lsmid->slot]);
+       }
 }
 
 int security_audit_rule_match(struct lsmblob *blob, u32 field, u32 op,
-                             void *lsmrule)
+                             void **lsmrule)
 {
        struct security_hook_list *hp;
        int rc;
@@ -2854,7 +2877,8 @@ int security_audit_rule_match(struct lsmblob *blob, u32 
field, u32 op,
                if (WARN_ON(hp->lsmid->slot < 0 || hp->lsmid->slot >= lsm_slot))
                        continue;
                rc = hp->hook.audit_rule_match(blob->secid[hp->lsmid->slot],
-                                              field, op, lsmrule);
+                                              field, op,
+                                              &lsmrule[hp->lsmid->slot]);
                if (rc != 0)
                        return rc;
        }
-- 
2.20.1



--
Linux-audit mailing list
Linux-audit@redhat.com
https://www.redhat.com/mailman/listinfo/linux-audit

Reply via email to