diff options
Diffstat (limited to 'kernel/auditfilter.c')
-rw-r--r-- | kernel/auditfilter.c | 289 |
1 files changed, 258 insertions, 31 deletions
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index d3a8539f3a8..7c134906d68 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 | ||
43 | static inline void audit_free_rule(struct audit_entry *e) | 44 | static 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. */ | ||
64 | static 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. */ |
57 | static __attribute__((unused)) char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | 85 | static 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. */ | ||
396 | static 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. */ | ||
426 | static 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. */ |
333 | static inline int audit_add_rule(struct audit_entry *entry, | 471 | static inline int audit_add_rule(struct audit_entry *entry, |
@@ -448,9 +586,10 @@ static int audit_list_rules(void *_dest) | |||
448 | * @data: payload data | 586 | * @data: payload data |
449 | * @datasz: size of payload data | 587 | * @datasz: size of payload data |
450 | * @loginuid: loginuid of sender | 588 | * @loginuid: loginuid of sender |
589 | * @sid: SE Linux Security ID of sender | ||
451 | */ | 590 | */ |
452 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | 591 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, |
453 | size_t datasz, uid_t loginuid) | 592 | size_t datasz, uid_t loginuid, u32 sid) |
454 | { | 593 | { |
455 | struct task_struct *tsk; | 594 | struct task_struct *tsk; |
456 | int *dest; | 595 | int *dest; |
@@ -493,9 +632,23 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
493 | 632 | ||
494 | err = audit_add_rule(entry, | 633 | err = audit_add_rule(entry, |
495 | &audit_filter_list[entry->rule.listnr]); | 634 | &audit_filter_list[entry->rule.listnr]); |
496 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 635 | if (sid) { |
497 | "auid=%u add rule to list=%d res=%d\n", | 636 | char *ctx = NULL; |
498 | loginuid, entry->rule.listnr, !err); | 637 | u32 len; |
638 | if (selinux_ctxid_to_string(sid, &ctx, &len)) { | ||
639 | /* Maybe call audit_panic? */ | ||
640 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
641 | "auid=%u ssid=%u add rule to list=%d res=%d", | ||
642 | loginuid, sid, entry->rule.listnr, !err); | ||
643 | } else | ||
644 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
645 | "auid=%u subj=%s add rule to list=%d res=%d", | ||
646 | loginuid, ctx, entry->rule.listnr, !err); | ||
647 | kfree(ctx); | ||
648 | } else | ||
649 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
650 | "auid=%u add rule to list=%d res=%d", | ||
651 | loginuid, entry->rule.listnr, !err); | ||
499 | 652 | ||
500 | if (err) | 653 | if (err) |
501 | audit_free_rule(entry); | 654 | audit_free_rule(entry); |
@@ -511,9 +664,24 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
511 | 664 | ||
512 | err = audit_del_rule(entry, | 665 | err = audit_del_rule(entry, |
513 | &audit_filter_list[entry->rule.listnr]); | 666 | &audit_filter_list[entry->rule.listnr]); |
514 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | 667 | |
515 | "auid=%u remove rule from list=%d res=%d\n", | 668 | if (sid) { |
516 | loginuid, entry->rule.listnr, !err); | 669 | char *ctx = NULL; |
670 | u32 len; | ||
671 | if (selinux_ctxid_to_string(sid, &ctx, &len)) { | ||
672 | /* Maybe call audit_panic? */ | ||
673 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
674 | "auid=%u ssid=%u remove rule from list=%d res=%d", | ||
675 | loginuid, sid, entry->rule.listnr, !err); | ||
676 | } else | ||
677 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
678 | "auid=%u subj=%s remove rule from list=%d res=%d", | ||
679 | loginuid, ctx, entry->rule.listnr, !err); | ||
680 | kfree(ctx); | ||
681 | } else | ||
682 | audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE, | ||
683 | "auid=%u remove rule from list=%d res=%d", | ||
684 | loginuid, entry->rule.listnr, !err); | ||
517 | 685 | ||
518 | audit_free_rule(entry); | 686 | audit_free_rule(entry); |
519 | break; | 687 | break; |
@@ -628,3 +796,62 @@ unlock_and_return: | |||
628 | rcu_read_unlock(); | 796 | rcu_read_unlock(); |
629 | return result; | 797 | return result; |
630 | } | 798 | } |
799 | |||
800 | /* Check to see if the rule contains any selinux fields. Returns 1 if there | ||
801 | are selinux fields specified in the rule, 0 otherwise. */ | ||
802 | static inline int audit_rule_has_selinux(struct audit_krule *rule) | ||
803 | { | ||
804 | int i; | ||
805 | |||
806 | for (i = 0; i < rule->field_count; i++) { | ||
807 | struct audit_field *f = &rule->fields[i]; | ||
808 | switch (f->type) { | ||
809 | case AUDIT_SE_USER: | ||
810 | case AUDIT_SE_ROLE: | ||
811 | case AUDIT_SE_TYPE: | ||
812 | case AUDIT_SE_SEN: | ||
813 | case AUDIT_SE_CLR: | ||
814 | return 1; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | return 0; | ||
819 | } | ||
820 | |||
821 | /* This function will re-initialize the se_rule field of all applicable rules. | ||
822 | * It will traverse the filter lists serarching for rules that contain selinux | ||
823 | * specific filter fields. When such a rule is found, it is copied, the | ||
824 | * selinux field is re-initialized, and the old rule is replaced with the | ||
825 | * updated rule. */ | ||
826 | int selinux_audit_rule_update(void) | ||
827 | { | ||
828 | struct audit_entry *entry, *n, *nentry; | ||
829 | int i, err = 0; | ||
830 | |||
831 | /* audit_netlink_mutex synchronizes the writers */ | ||
832 | mutex_lock(&audit_netlink_mutex); | ||
833 | |||
834 | for (i = 0; i < AUDIT_NR_FILTERS; i++) { | ||
835 | list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) { | ||
836 | if (!audit_rule_has_selinux(&entry->rule)) | ||
837 | continue; | ||
838 | |||
839 | nentry = audit_dupe_rule(&entry->rule); | ||
840 | if (unlikely(IS_ERR(nentry))) { | ||
841 | /* save the first error encountered for the | ||
842 | * return value */ | ||
843 | if (!err) | ||
844 | err = PTR_ERR(nentry); | ||
845 | audit_panic("error updating selinux filters"); | ||
846 | list_del_rcu(&entry->list); | ||
847 | } else { | ||
848 | list_replace_rcu(&entry->list, &nentry->list); | ||
849 | } | ||
850 | call_rcu(&entry->rcu, audit_free_rule_rcu); | ||
851 | } | ||
852 | } | ||
853 | |||
854 | mutex_unlock(&audit_netlink_mutex); | ||
855 | |||
856 | return err; | ||
857 | } | ||