diff options
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/audit.c | 8 | ||||
-rw-r--r-- | kernel/audit.h | 10 | ||||
-rw-r--r-- | kernel/auditfilter.c | 245 | ||||
-rw-r--r-- | kernel/auditsc.c | 20 |
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 */ |
56 | struct audit_field { | 56 | struct 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 | ||
62 | struct audit_krule { | 64 | struct audit_krule { |
@@ -86,3 +88,5 @@ extern void audit_send_reply(int pid, int seq, int type, | |||
86 | extern void audit_log_lost(const char *message); | 88 | extern void audit_log_lost(const char *message); |
87 | extern void audit_panic(const char *message); | 89 | extern void audit_panic(const char *message); |
88 | extern struct mutex audit_netlink_mutex; | 90 | extern struct mutex audit_netlink_mutex; |
91 | |||
92 | extern 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 | ||
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, |
@@ -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. */ | ||
772 | static 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. */ | ||
796 | int 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: |