@@ -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;
@@ -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 */
@@ -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;
}
@@ -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;
@@ -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),
@@ -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;
}
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 <casey@schaufler-ca.com> Cc: linux-integrity@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(-)