aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDarrel Goeddel <dgoeddel@trustedcs.com>2006-03-10 19:14:06 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2006-05-01 06:09:36 -0400
commit3dc7e3153eddfcf7ba8b50628775ba516e5f759f (patch)
tree926957e904739fc6c29e5125b7c1635b9f77548c
parent376bd9cb357ec945ac893feaeb63af7370a6e70b (diff)
[PATCH] support for context based audit filtering, part 2
This patch provides the ability to filter audit messages based on the elements of the process' SELinux context (user, role, type, mls sensitivity, and mls clearance). It uses the new interfaces from selinux to opaquely store information related to the selinux context and to filter based on that information. It also uses the callback mechanism provided by selinux to refresh the information when a new policy is loaded. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--kernel/audit.c8
-rw-r--r--kernel/audit.h10
-rw-r--r--kernel/auditfilter.c245
-rw-r--r--kernel/auditsc.c20
4 files changed, 256 insertions, 27 deletions
diff --git a/kernel/audit.c b/kernel/audit.c
index c8ccbd09048f..9060be750c48 100644
--- a/kernel/audit.c
+++ b/kernel/audit.c
@@ -55,6 +55,9 @@
55#include <net/netlink.h> 55#include <net/netlink.h>
56#include <linux/skbuff.h> 56#include <linux/skbuff.h>
57#include <linux/netlink.h> 57#include <linux/netlink.h>
58#include <linux/selinux.h>
59
60#include "audit.h"
58 61
59/* No auditing will take place until audit_initialized != 0. 62/* No auditing will take place until audit_initialized != 0.
60 * (Initialization happens after skb_init is called.) */ 63 * (Initialization happens after skb_init is called.) */
@@ -564,6 +567,11 @@ static int __init audit_init(void)
564 skb_queue_head_init(&audit_skb_queue); 567 skb_queue_head_init(&audit_skb_queue);
565 audit_initialized = 1; 568 audit_initialized = 1;
566 audit_enabled = audit_default; 569 audit_enabled = audit_default;
570
571 /* Register the callback with selinux. This callback will be invoked
572 * when a new policy is loaded. */
573 selinux_audit_set_callback(&selinux_audit_rule_update);
574
567 audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized"); 575 audit_log(NULL, GFP_KERNEL, AUDIT_KERNEL, "initialized");
568 return 0; 576 return 0;
569} 577}
diff --git a/kernel/audit.h b/kernel/audit.h
index bc5392076e2b..6f733920fd32 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -54,9 +54,11 @@ enum audit_state {
54 54
55/* Rule lists */ 55/* Rule lists */
56struct audit_field { 56struct audit_field {
57 u32 type; 57 u32 type;
58 u32 val; 58 u32 val;
59 u32 op; 59 u32 op;
60 char *se_str;
61 struct selinux_audit_rule *se_rule;
60}; 62};
61 63
62struct audit_krule { 64struct audit_krule {
@@ -86,3 +88,5 @@ extern void audit_send_reply(int pid, int seq, int type,
86extern void audit_log_lost(const char *message); 88extern void audit_log_lost(const char *message);
87extern void audit_panic(const char *message); 89extern void audit_panic(const char *message);
88extern struct mutex audit_netlink_mutex; 90extern struct mutex audit_netlink_mutex;
91
92extern int selinux_audit_rule_update(void);
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index d3a8539f3a83..85a7862143a1 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -23,6 +23,7 @@
23#include <linux/audit.h> 23#include <linux/audit.h>
24#include <linux/kthread.h> 24#include <linux/kthread.h>
25#include <linux/netlink.h> 25#include <linux/netlink.h>
26#include <linux/selinux.h>
26#include "audit.h" 27#include "audit.h"
27 28
28/* There are three lists of rules -- one to search at task creation 29/* There are three lists of rules -- one to search at task creation
@@ -42,6 +43,13 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
42 43
43static inline void audit_free_rule(struct audit_entry *e) 44static inline void audit_free_rule(struct audit_entry *e)
44{ 45{
46 int i;
47 if (e->rule.fields)
48 for (i = 0; i < e->rule.field_count; i++) {
49 struct audit_field *f = &e->rule.fields[i];
50 kfree(f->se_str);
51 selinux_audit_rule_free(f->se_rule);
52 }
45 kfree(e->rule.fields); 53 kfree(e->rule.fields);
46 kfree(e); 54 kfree(e);
47} 55}
@@ -52,9 +60,29 @@ static inline void audit_free_rule_rcu(struct rcu_head *head)
52 audit_free_rule(e); 60 audit_free_rule(e);
53} 61}
54 62
63/* Initialize an audit filterlist entry. */
64static inline struct audit_entry *audit_init_entry(u32 field_count)
65{
66 struct audit_entry *entry;
67 struct audit_field *fields;
68
69 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
70 if (unlikely(!entry))
71 return NULL;
72
73 fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL);
74 if (unlikely(!fields)) {
75 kfree(entry);
76 return NULL;
77 }
78 entry->rule.fields = fields;
79
80 return entry;
81}
82
55/* Unpack a filter field's string representation from user-space 83/* Unpack a filter field's string representation from user-space
56 * buffer. */ 84 * buffer. */
57static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) 85static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
58{ 86{
59 char *str; 87 char *str;
60 88
@@ -84,7 +112,6 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
84{ 112{
85 unsigned listnr; 113 unsigned listnr;
86 struct audit_entry *entry; 114 struct audit_entry *entry;
87 struct audit_field *fields;
88 int i, err; 115 int i, err;
89 116
90 err = -EINVAL; 117 err = -EINVAL;
@@ -108,23 +135,14 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
108 goto exit_err; 135 goto exit_err;
109 136
110 err = -ENOMEM; 137 err = -ENOMEM;
111 entry = kmalloc(sizeof(*entry), GFP_KERNEL); 138 entry = audit_init_entry(rule->field_count);
112 if (unlikely(!entry)) 139 if (!entry)
113 goto exit_err;
114 fields = kmalloc(sizeof(*fields) * rule->field_count, GFP_KERNEL);
115 if (unlikely(!fields)) {
116 kfree(entry);
117 goto exit_err; 140 goto exit_err;
118 }
119
120 memset(&entry->rule, 0, sizeof(struct audit_krule));
121 memset(fields, 0, sizeof(struct audit_field));
122 141
123 entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND; 142 entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
124 entry->rule.listnr = listnr; 143 entry->rule.listnr = listnr;
125 entry->rule.action = rule->action; 144 entry->rule.action = rule->action;
126 entry->rule.field_count = rule->field_count; 145 entry->rule.field_count = rule->field_count;
127 entry->rule.fields = fields;
128 146
129 for (i = 0; i < AUDIT_BITMASK_SIZE; i++) 147 for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
130 entry->rule.mask[i] = rule->mask[i]; 148 entry->rule.mask[i] = rule->mask[i];
@@ -150,15 +168,20 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
150 for (i = 0; i < rule->field_count; i++) { 168 for (i = 0; i < rule->field_count; i++) {
151 struct audit_field *f = &entry->rule.fields[i]; 169 struct audit_field *f = &entry->rule.fields[i];
152 170
153 if (rule->fields[i] & AUDIT_UNUSED_BITS) {
154 err = -EINVAL;
155 goto exit_free;
156 }
157
158 f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS); 171 f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
159 f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS); 172 f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
160 f->val = rule->values[i]; 173 f->val = rule->values[i];
161 174
175 if (f->type & AUDIT_UNUSED_BITS ||
176 f->type == AUDIT_SE_USER ||
177 f->type == AUDIT_SE_ROLE ||
178 f->type == AUDIT_SE_TYPE ||
179 f->type == AUDIT_SE_SEN ||
180 f->type == AUDIT_SE_CLR) {
181 err = -EINVAL;
182 goto exit_free;
183 }
184
162 entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1; 185 entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
163 186
164 /* Support for legacy operators where 187 /* Support for legacy operators where
@@ -188,8 +211,9 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
188 int err = 0; 211 int err = 0;
189 struct audit_entry *entry; 212 struct audit_entry *entry;
190 void *bufp; 213 void *bufp;
191 /* size_t remain = datasz - sizeof(struct audit_rule_data); */ 214 size_t remain = datasz - sizeof(struct audit_rule_data);
192 int i; 215 int i;
216 char *str;
193 217
194 entry = audit_to_entry_common((struct audit_rule *)data); 218 entry = audit_to_entry_common((struct audit_rule *)data);
195 if (IS_ERR(entry)) 219 if (IS_ERR(entry))
@@ -207,10 +231,35 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
207 231
208 f->op = data->fieldflags[i] & AUDIT_OPERATORS; 232 f->op = data->fieldflags[i] & AUDIT_OPERATORS;
209 f->type = data->fields[i]; 233 f->type = data->fields[i];
234 f->val = data->values[i];
235 f->se_str = NULL;
236 f->se_rule = NULL;
210 switch(f->type) { 237 switch(f->type) {
211 /* call type-specific conversion routines here */ 238 case AUDIT_SE_USER:
212 default: 239 case AUDIT_SE_ROLE:
213 f->val = data->values[i]; 240 case AUDIT_SE_TYPE:
241 case AUDIT_SE_SEN:
242 case AUDIT_SE_CLR:
243 str = audit_unpack_string(&bufp, &remain, f->val);
244 if (IS_ERR(str))
245 goto exit_free;
246 entry->rule.buflen += f->val;
247
248 err = selinux_audit_rule_init(f->type, f->op, str,
249 &f->se_rule);
250 /* Keep currently invalid fields around in case they
251 * become valid after a policy reload. */
252 if (err == -EINVAL) {
253 printk(KERN_WARNING "audit rule for selinux "
254 "\'%s\' is invalid\n", str);
255 err = 0;
256 }
257 if (err) {
258 kfree(str);
259 goto exit_free;
260 } else
261 f->se_str = str;
262 break;
214 } 263 }
215 } 264 }
216 265
@@ -286,7 +335,14 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
286 data->fields[i] = f->type; 335 data->fields[i] = f->type;
287 data->fieldflags[i] = f->op; 336 data->fieldflags[i] = f->op;
288 switch(f->type) { 337 switch(f->type) {
289 /* call type-specific conversion routines here */ 338 case AUDIT_SE_USER:
339 case AUDIT_SE_ROLE:
340 case AUDIT_SE_TYPE:
341 case AUDIT_SE_SEN:
342 case AUDIT_SE_CLR:
343 data->buflen += data->values[i] =
344 audit_pack_string(&bufp, f->se_str);
345 break;
290 default: 346 default:
291 data->values[i] = f->val; 347 data->values[i] = f->val;
292 } 348 }
@@ -314,7 +370,14 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
314 return 1; 370 return 1;
315 371
316 switch(a->fields[i].type) { 372 switch(a->fields[i].type) {
317 /* call type-specific comparison routines here */ 373 case AUDIT_SE_USER:
374 case AUDIT_SE_ROLE:
375 case AUDIT_SE_TYPE:
376 case AUDIT_SE_SEN:
377 case AUDIT_SE_CLR:
378 if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
379 return 1;
380 break;
318 default: 381 default:
319 if (a->fields[i].val != b->fields[i].val) 382 if (a->fields[i].val != b->fields[i].val)
320 return 1; 383 return 1;
@@ -328,6 +391,81 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
328 return 0; 391 return 0;
329} 392}
330 393
394/* Duplicate selinux field information. The se_rule is opaque, so must be
395 * re-initialized. */
396static inline int audit_dupe_selinux_field(struct audit_field *df,
397 struct audit_field *sf)
398{
399 int ret = 0;
400 char *se_str;
401
402 /* our own copy of se_str */
403 se_str = kstrdup(sf->se_str, GFP_KERNEL);
404 if (unlikely(IS_ERR(se_str)))
405 return -ENOMEM;
406 df->se_str = se_str;
407
408 /* our own (refreshed) copy of se_rule */
409 ret = selinux_audit_rule_init(df->type, df->op, df->se_str,
410 &df->se_rule);
411 /* Keep currently invalid fields around in case they
412 * become valid after a policy reload. */
413 if (ret == -EINVAL) {
414 printk(KERN_WARNING "audit rule for selinux \'%s\' is "
415 "invalid\n", df->se_str);
416 ret = 0;
417 }
418
419 return ret;
420}
421
422/* Duplicate an audit rule. This will be a deep copy with the exception
423 * of the watch - that pointer is carried over. The selinux specific fields
424 * will be updated in the copy. The point is to be able to replace the old
425 * rule with the new rule in the filterlist, then free the old rule. */
426static struct audit_entry *audit_dupe_rule(struct audit_krule *old)
427{
428 u32 fcount = old->field_count;
429 struct audit_entry *entry;
430 struct audit_krule *new;
431 int i, err = 0;
432
433 entry = audit_init_entry(fcount);
434 if (unlikely(!entry))
435 return ERR_PTR(-ENOMEM);
436
437 new = &entry->rule;
438 new->vers_ops = old->vers_ops;
439 new->flags = old->flags;
440 new->listnr = old->listnr;
441 new->action = old->action;
442 for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
443 new->mask[i] = old->mask[i];
444 new->buflen = old->buflen;
445 new->field_count = old->field_count;
446 memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
447
448 /* deep copy this information, updating the se_rule fields, because
449 * the originals will all be freed when the old rule is freed. */
450 for (i = 0; i < fcount; i++) {
451 switch (new->fields[i].type) {
452 case AUDIT_SE_USER:
453 case AUDIT_SE_ROLE:
454 case AUDIT_SE_TYPE:
455 case AUDIT_SE_SEN:
456 case AUDIT_SE_CLR:
457 err = audit_dupe_selinux_field(&new->fields[i],
458 &old->fields[i]);
459 }
460 if (err) {
461 audit_free_rule(entry);
462 return ERR_PTR(err);
463 }
464 }
465
466 return entry;
467}
468
331/* Add rule to given filterlist if not a duplicate. Protected by 469/* Add rule to given filterlist if not a duplicate. Protected by
332 * audit_netlink_mutex. */ 470 * audit_netlink_mutex. */
333static inline int audit_add_rule(struct audit_entry *entry, 471static inline int audit_add_rule(struct audit_entry *entry,
@@ -628,3 +766,62 @@ unlock_and_return:
628 rcu_read_unlock(); 766 rcu_read_unlock();
629 return result; 767 return result;
630} 768}
769
770/* Check to see if the rule contains any selinux fields. Returns 1 if there
771 are selinux fields specified in the rule, 0 otherwise. */
772static inline int audit_rule_has_selinux(struct audit_krule *rule)
773{
774 int i;
775
776 for (i = 0; i < rule->field_count; i++) {
777 struct audit_field *f = &rule->fields[i];
778 switch (f->type) {
779 case AUDIT_SE_USER:
780 case AUDIT_SE_ROLE:
781 case AUDIT_SE_TYPE:
782 case AUDIT_SE_SEN:
783 case AUDIT_SE_CLR:
784 return 1;
785 }
786 }
787
788 return 0;
789}
790
791/* This function will re-initialize the se_rule field of all applicable rules.
792 * It will traverse the filter lists serarching for rules that contain selinux
793 * specific filter fields. When such a rule is found, it is copied, the
794 * selinux field is re-initialized, and the old rule is replaced with the
795 * updated rule. */
796int selinux_audit_rule_update(void)
797{
798 struct audit_entry *entry, *n, *nentry;
799 int i, err = 0;
800
801 /* audit_netlink_mutex synchronizes the writers */
802 mutex_lock(&audit_netlink_mutex);
803
804 for (i = 0; i < AUDIT_NR_FILTERS; i++) {
805 list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
806 if (!audit_rule_has_selinux(&entry->rule))
807 continue;
808
809 nentry = audit_dupe_rule(&entry->rule);
810 if (unlikely(IS_ERR(nentry))) {
811 /* save the first error encountered for the
812 * return value */
813 if (!err)
814 err = PTR_ERR(nentry);
815 audit_panic("error updating selinux filters");
816 list_del_rcu(&entry->list);
817 } else {
818 list_replace_rcu(&entry->list, &nentry->list);
819 }
820 call_rcu(&entry->rcu, audit_free_rule_rcu);
821 }
822 }
823
824 mutex_unlock(&audit_netlink_mutex);
825
826 return err;
827}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 8aca4ab4aa27..d3d97d28b69a 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -58,6 +58,7 @@
58#include <linux/security.h> 58#include <linux/security.h>
59#include <linux/list.h> 59#include <linux/list.h>
60#include <linux/tty.h> 60#include <linux/tty.h>
61#include <linux/selinux.h>
61 62
62#include "audit.h" 63#include "audit.h"
63 64
@@ -168,6 +169,9 @@ static int audit_filter_rules(struct task_struct *tsk,
168 enum audit_state *state) 169 enum audit_state *state)
169{ 170{
170 int i, j; 171 int i, j;
172 u32 sid;
173
174 selinux_task_ctxid(tsk, &sid);
171 175
172 for (i = 0; i < rule->field_count; i++) { 176 for (i = 0; i < rule->field_count; i++) {
173 struct audit_field *f = &rule->fields[i]; 177 struct audit_field *f = &rule->fields[i];
@@ -257,6 +261,22 @@ static int audit_filter_rules(struct task_struct *tsk,
257 if (ctx) 261 if (ctx)
258 result = audit_comparator(ctx->loginuid, f->op, f->val); 262 result = audit_comparator(ctx->loginuid, f->op, f->val);
259 break; 263 break;
264 case AUDIT_SE_USER:
265 case AUDIT_SE_ROLE:
266 case AUDIT_SE_TYPE:
267 case AUDIT_SE_SEN:
268 case AUDIT_SE_CLR:
269 /* NOTE: this may return negative values indicating
270 a temporary error. We simply treat this as a
271 match for now to avoid losing information that
272 may be wanted. An error message will also be
273 logged upon error */
274 if (f->se_rule)
275 result = selinux_audit_rule_match(sid, f->type,
276 f->op,
277 f->se_rule,
278 ctx);
279 break;
260 case AUDIT_ARG0: 280 case AUDIT_ARG0:
261 case AUDIT_ARG1: 281 case AUDIT_ARG1:
262 case AUDIT_ARG2: 282 case AUDIT_ARG2: