diff options
author | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-19 14:35:50 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@shinybook.infradead.org> | 2005-06-19 14:35:50 -0400 |
commit | 0f45aa18e65cf3d768082d7d86054a0d2a20bb18 (patch) | |
tree | 6e94dfcd813061f5a710b5621e1b2f5a01a95533 | |
parent | 0107b3cf3225aed6ddde4fa8dbcd4ed643b34f4d (diff) |
AUDIT: Allow filtering of user messages
Turn the field from a bitmask to an enumeration and add a list to allow
filtering of messages generated by userspace. We also define a list for
file system watches in anticipation of that feature.
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r-- | include/linux/audit.h | 15 | ||||
-rw-r--r-- | kernel/audit.c | 36 | ||||
-rw-r--r-- | kernel/auditsc.c | 92 |
3 files changed, 85 insertions, 58 deletions
diff --git a/include/linux/audit.h b/include/linux/audit.h index bf2ad3ba72eb..b5bda24f7365 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h | |||
@@ -75,10 +75,15 @@ | |||
75 | #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ | 75 | #define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ |
76 | 76 | ||
77 | /* Rule flags */ | 77 | /* Rule flags */ |
78 | #define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */ | 78 | #define AUDIT_FILTER_USER 0x00 /* Apply rule to user-generated messages */ |
79 | #define AUDIT_AT_ENTRY 0x02 /* Apply rule at syscall entry */ | 79 | #define AUDIT_FILTER_TASK 0x01 /* Apply rule at task creation (not syscall) */ |
80 | #define AUDIT_AT_EXIT 0x04 /* Apply rule at syscall exit */ | 80 | #define AUDIT_FILTER_ENTRY 0x02 /* Apply rule at syscall entry */ |
81 | #define AUDIT_PREPEND 0x10 /* Prepend to front of list */ | 81 | #define AUDIT_FILTER_WATCH 0x03 /* Apply rule to file system watches */ |
82 | #define AUDIT_FILTER_EXIT 0x04 /* Apply rule at syscall exit */ | ||
83 | |||
84 | #define AUDIT_NR_FILTERS 5 | ||
85 | |||
86 | #define AUDIT_FILTER_PREPEND 0x10 /* Prepend to front of list */ | ||
82 | 87 | ||
83 | /* Rule actions */ | 88 | /* Rule actions */ |
84 | #define AUDIT_NEVER 0 /* Do not build context if rule matches */ | 89 | #define AUDIT_NEVER 0 /* Do not build context if rule matches */ |
@@ -230,6 +235,7 @@ extern int audit_socketcall(int nargs, unsigned long *args); | |||
230 | extern int audit_sockaddr(int len, void *addr); | 235 | extern int audit_sockaddr(int len, void *addr); |
231 | extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); | 236 | extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt); |
232 | extern void audit_signal_info(int sig, struct task_struct *t); | 237 | extern void audit_signal_info(int sig, struct task_struct *t); |
238 | extern int audit_filter_user(struct task_struct *tsk, int type); | ||
233 | #else | 239 | #else |
234 | #define audit_alloc(t) ({ 0; }) | 240 | #define audit_alloc(t) ({ 0; }) |
235 | #define audit_free(t) do { ; } while (0) | 241 | #define audit_free(t) do { ; } while (0) |
@@ -246,6 +252,7 @@ extern void audit_signal_info(int sig, struct task_struct *t); | |||
246 | #define audit_sockaddr(len, addr) ({ 0; }) | 252 | #define audit_sockaddr(len, addr) ({ 0; }) |
247 | #define audit_avc_path(dentry, mnt) ({ 0; }) | 253 | #define audit_avc_path(dentry, mnt) ({ 0; }) |
248 | #define audit_signal_info(s,t) do { ; } while (0) | 254 | #define audit_signal_info(s,t) do { ; } while (0) |
255 | #define audit_filter_user(struct ({ 1; }) | ||
249 | #endif | 256 | #endif |
250 | 257 | ||
251 | #ifdef CONFIG_AUDIT | 258 | #ifdef CONFIG_AUDIT |
diff --git a/kernel/audit.c b/kernel/audit.c index ef35166fdc29..f0bbfe073136 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -107,13 +107,6 @@ static struct sk_buff_head audit_skb_queue; | |||
107 | static struct task_struct *kauditd_task; | 107 | static struct task_struct *kauditd_task; |
108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); | 108 | static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait); |
109 | 109 | ||
110 | /* There are three lists of rules -- one to search at task creation | ||
111 | * time, one to search at syscall entry time, and another to search at | ||
112 | * syscall exit time. */ | ||
113 | static LIST_HEAD(audit_tsklist); | ||
114 | static LIST_HEAD(audit_entlist); | ||
115 | static LIST_HEAD(audit_extlist); | ||
116 | |||
117 | /* The netlink socket is only to be read by 1 CPU, which lets us assume | 110 | /* The netlink socket is only to be read by 1 CPU, which lets us assume |
118 | * that list additions and deletions never happen simultaneously in | 111 | * that list additions and deletions never happen simultaneously in |
119 | * auditsc.c */ | 112 | * auditsc.c */ |
@@ -376,6 +369,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
376 | u16 msg_type = nlh->nlmsg_type; | 369 | u16 msg_type = nlh->nlmsg_type; |
377 | uid_t loginuid; /* loginuid of sender */ | 370 | uid_t loginuid; /* loginuid of sender */ |
378 | struct audit_sig_info sig_data; | 371 | struct audit_sig_info sig_data; |
372 | struct task_struct *tsk; | ||
379 | 373 | ||
380 | err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); | 374 | err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); |
381 | if (err) | 375 | if (err) |
@@ -435,15 +429,25 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
435 | break; | 429 | break; |
436 | case AUDIT_USER: | 430 | case AUDIT_USER: |
437 | case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: | 431 | case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG: |
438 | ab = audit_log_start(NULL, msg_type); | 432 | read_lock(&tasklist_lock); |
439 | if (!ab) | 433 | tsk = find_task_by_pid(pid); |
440 | break; /* audit_panic has been called */ | 434 | if (tsk) |
441 | audit_log_format(ab, | 435 | get_task_struct(tsk); |
442 | "user pid=%d uid=%u auid=%u" | 436 | read_unlock(&tasklist_lock); |
443 | " msg='%.1024s'", | 437 | if (!tsk) |
444 | pid, uid, loginuid, (char *)data); | 438 | return -ESRCH; |
445 | audit_set_pid(ab, pid); | 439 | |
446 | audit_log_end(ab); | 440 | if (audit_filter_user(tsk, msg_type)) { |
441 | ab = audit_log_start(NULL, msg_type); | ||
442 | if (ab) { | ||
443 | audit_log_format(ab, | ||
444 | "user pid=%d uid=%u auid=%u msg='%.1024s'", | ||
445 | pid, uid, loginuid, (char *)data); | ||
446 | audit_set_pid(ab, pid); | ||
447 | audit_log_end(ab); | ||
448 | } | ||
449 | } | ||
450 | put_task_struct(tsk); | ||
447 | break; | 451 | break; |
448 | case AUDIT_ADD: | 452 | case AUDIT_ADD: |
449 | case AUDIT_DEL: | 453 | case AUDIT_DEL: |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e75f84e1a1a0..6b4fbb1c0129 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -167,9 +167,16 @@ struct audit_context { | |||
167 | /* There are three lists of rules -- one to search at task creation | 167 | /* There are three lists of rules -- one to search at task creation |
168 | * time, one to search at syscall entry time, and another to search at | 168 | * time, one to search at syscall entry time, and another to search at |
169 | * syscall exit time. */ | 169 | * syscall exit time. */ |
170 | static LIST_HEAD(audit_tsklist); | 170 | static struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { |
171 | static LIST_HEAD(audit_entlist); | 171 | LIST_HEAD_INIT(audit_filter_list[0]), |
172 | static LIST_HEAD(audit_extlist); | 172 | LIST_HEAD_INIT(audit_filter_list[1]), |
173 | LIST_HEAD_INIT(audit_filter_list[2]), | ||
174 | LIST_HEAD_INIT(audit_filter_list[3]), | ||
175 | LIST_HEAD_INIT(audit_filter_list[4]), | ||
176 | #if AUDIT_NR_FILTERS != 5 | ||
177 | #error Fix audit_filter_list initialiser | ||
178 | #endif | ||
179 | }; | ||
173 | 180 | ||
174 | struct audit_entry { | 181 | struct audit_entry { |
175 | struct list_head list; | 182 | struct list_head list; |
@@ -210,16 +217,15 @@ static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) | |||
210 | /* Note that audit_add_rule and audit_del_rule are called via | 217 | /* Note that audit_add_rule and audit_del_rule are called via |
211 | * audit_receive() in audit.c, and are protected by | 218 | * audit_receive() in audit.c, and are protected by |
212 | * audit_netlink_sem. */ | 219 | * audit_netlink_sem. */ |
213 | static inline int audit_add_rule(struct audit_entry *entry, | 220 | static inline void audit_add_rule(struct audit_entry *entry, |
214 | struct list_head *list) | 221 | struct list_head *list) |
215 | { | 222 | { |
216 | if (entry->rule.flags & AUDIT_PREPEND) { | 223 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
217 | entry->rule.flags &= ~AUDIT_PREPEND; | 224 | entry->rule.flags &= ~AUDIT_FILTER_PREPEND; |
218 | list_add_rcu(&entry->list, list); | 225 | list_add_rcu(&entry->list, list); |
219 | } else { | 226 | } else { |
220 | list_add_tail_rcu(&entry->list, list); | 227 | list_add_tail_rcu(&entry->list, list); |
221 | } | 228 | } |
222 | return 0; | ||
223 | } | 229 | } |
224 | 230 | ||
225 | static void audit_free_rule(struct rcu_head *head) | 231 | static void audit_free_rule(struct rcu_head *head) |
@@ -245,7 +251,7 @@ static inline int audit_del_rule(struct audit_rule *rule, | |||
245 | return 0; | 251 | return 0; |
246 | } | 252 | } |
247 | } | 253 | } |
248 | return -EFAULT; /* No matching rule */ | 254 | return -ENOENT; /* No matching rule */ |
249 | } | 255 | } |
250 | 256 | ||
251 | /* Copy rule from user-space to kernel-space. Called during | 257 | /* Copy rule from user-space to kernel-space. Called during |
@@ -260,6 +266,8 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) | |||
260 | return -1; | 266 | return -1; |
261 | if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) | 267 | if (s->field_count < 0 || s->field_count > AUDIT_MAX_FIELDS) |
262 | return -1; | 268 | return -1; |
269 | if ((s->flags & ~AUDIT_FILTER_PREPEND) >= AUDIT_NR_FILTERS) | ||
270 | return -1; | ||
263 | 271 | ||
264 | d->flags = s->flags; | 272 | d->flags = s->flags; |
265 | d->action = s->action; | 273 | d->action = s->action; |
@@ -275,23 +283,20 @@ static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) | |||
275 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | 283 | int audit_receive_filter(int type, int pid, int uid, int seq, void *data, |
276 | uid_t loginuid) | 284 | uid_t loginuid) |
277 | { | 285 | { |
278 | u32 flags; | ||
279 | struct audit_entry *entry; | 286 | struct audit_entry *entry; |
280 | int err = 0; | 287 | int err = 0; |
288 | int i; | ||
289 | unsigned listnr; | ||
281 | 290 | ||
282 | switch (type) { | 291 | switch (type) { |
283 | case AUDIT_LIST: | 292 | case AUDIT_LIST: |
284 | /* The *_rcu iterators not needed here because we are | 293 | /* The *_rcu iterators not needed here because we are |
285 | always called with audit_netlink_sem held. */ | 294 | always called with audit_netlink_sem held. */ |
286 | list_for_each_entry(entry, &audit_tsklist, list) | 295 | for (i=0; i<AUDIT_NR_FILTERS; i++) { |
287 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | 296 | list_for_each_entry(entry, &audit_filter_list[i], list) |
288 | &entry->rule, sizeof(entry->rule)); | 297 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, |
289 | list_for_each_entry(entry, &audit_entlist, list) | 298 | &entry->rule, sizeof(entry->rule)); |
290 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | 299 | } |
291 | &entry->rule, sizeof(entry->rule)); | ||
292 | list_for_each_entry(entry, &audit_extlist, list) | ||
293 | audit_send_reply(pid, seq, AUDIT_LIST, 0, 1, | ||
294 | &entry->rule, sizeof(entry->rule)); | ||
295 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); | 300 | audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0); |
296 | break; | 301 | break; |
297 | case AUDIT_ADD: | 302 | case AUDIT_ADD: |
@@ -301,26 +306,20 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, | |||
301 | kfree(entry); | 306 | kfree(entry); |
302 | return -EINVAL; | 307 | return -EINVAL; |
303 | } | 308 | } |
304 | flags = entry->rule.flags; | 309 | listnr = entry->rule.flags & ~AUDIT_FILTER_PREPEND; |
305 | if (!err && (flags & AUDIT_PER_TASK)) | 310 | audit_add_rule(entry, &audit_filter_list[listnr]); |
306 | err = audit_add_rule(entry, &audit_tsklist); | ||
307 | if (!err && (flags & AUDIT_AT_ENTRY)) | ||
308 | err = audit_add_rule(entry, &audit_entlist); | ||
309 | if (!err && (flags & AUDIT_AT_EXIT)) | ||
310 | err = audit_add_rule(entry, &audit_extlist); | ||
311 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 311 | audit_log(NULL, AUDIT_CONFIG_CHANGE, |
312 | "auid=%u added an audit rule\n", loginuid); | 312 | "auid=%u added an audit rule\n", loginuid); |
313 | break; | 313 | break; |
314 | case AUDIT_DEL: | 314 | case AUDIT_DEL: |
315 | flags =((struct audit_rule *)data)->flags; | 315 | listnr =((struct audit_rule *)data)->flags & ~AUDIT_FILTER_PREPEND; |
316 | if (!err && (flags & AUDIT_PER_TASK)) | 316 | if (listnr >= AUDIT_NR_FILTERS) |
317 | err = audit_del_rule(data, &audit_tsklist); | 317 | return -EINVAL; |
318 | if (!err && (flags & AUDIT_AT_ENTRY)) | 318 | |
319 | err = audit_del_rule(data, &audit_entlist); | 319 | err = audit_del_rule(data, &audit_filter_list[listnr]); |
320 | if (!err && (flags & AUDIT_AT_EXIT)) | 320 | if (!err) |
321 | err = audit_del_rule(data, &audit_extlist); | 321 | audit_log(NULL, AUDIT_CONFIG_CHANGE, |
322 | audit_log(NULL, AUDIT_CONFIG_CHANGE, | 322 | "auid=%u removed an audit rule\n", loginuid); |
323 | "auid=%u removed an audit rule\n", loginuid); | ||
324 | break; | 323 | break; |
325 | default: | 324 | default: |
326 | return -EINVAL; | 325 | return -EINVAL; |
@@ -454,7 +453,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk) | |||
454 | enum audit_state state; | 453 | enum audit_state state; |
455 | 454 | ||
456 | rcu_read_lock(); | 455 | rcu_read_lock(); |
457 | list_for_each_entry_rcu(e, &audit_tsklist, list) { | 456 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TASK], list) { |
458 | if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { | 457 | if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { |
459 | rcu_read_unlock(); | 458 | rcu_read_unlock(); |
460 | return state; | 459 | return state; |
@@ -490,6 +489,23 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk, | |||
490 | return AUDIT_BUILD_CONTEXT; | 489 | return AUDIT_BUILD_CONTEXT; |
491 | } | 490 | } |
492 | 491 | ||
492 | int audit_filter_user(struct task_struct *tsk, int type) | ||
493 | { | ||
494 | struct audit_entry *e; | ||
495 | enum audit_state state; | ||
496 | |||
497 | rcu_read_lock(); | ||
498 | list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) { | ||
499 | if (audit_filter_rules(tsk, &e->rule, NULL, &state)) { | ||
500 | rcu_read_unlock(); | ||
501 | return state != AUDIT_DISABLED; | ||
502 | } | ||
503 | } | ||
504 | rcu_read_unlock(); | ||
505 | return 1; /* Audit by default */ | ||
506 | |||
507 | } | ||
508 | |||
493 | /* This should be called with task_lock() held. */ | 509 | /* This should be called with task_lock() held. */ |
494 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, | 510 | static inline struct audit_context *audit_get_context(struct task_struct *tsk, |
495 | int return_valid, | 511 | int return_valid, |
@@ -504,7 +520,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk, | |||
504 | 520 | ||
505 | if (context->in_syscall && !context->auditable) { | 521 | if (context->in_syscall && !context->auditable) { |
506 | enum audit_state state; | 522 | enum audit_state state; |
507 | state = audit_filter_syscall(tsk, context, &audit_extlist); | 523 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]); |
508 | if (state == AUDIT_RECORD_CONTEXT) | 524 | if (state == AUDIT_RECORD_CONTEXT) |
509 | context->auditable = 1; | 525 | context->auditable = 1; |
510 | } | 526 | } |
@@ -876,7 +892,7 @@ void audit_syscall_entry(struct task_struct *tsk, int arch, int major, | |||
876 | 892 | ||
877 | state = context->state; | 893 | state = context->state; |
878 | if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT) | 894 | if (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT) |
879 | state = audit_filter_syscall(tsk, context, &audit_entlist); | 895 | state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]); |
880 | if (likely(state == AUDIT_DISABLED)) | 896 | if (likely(state == AUDIT_DISABLED)) |
881 | return; | 897 | return; |
882 | 898 | ||