blob: 40a9931a13e2859eaaf256ed2740acacd7ba5048 [file] [log] [blame]
David Woodhousefe7752b2005-12-15 18:33:52 +00001/* auditfilter.c -- filtering of audit events
2 *
3 * Copyright 2003-2004 Red Hat, Inc.
4 * Copyright 2005 Hewlett-Packard Development Company, L.P.
5 * Copyright 2005 IBM Corporation
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/kernel.h>
23#include <linux/audit.h>
24#include <linux/kthread.h>
Amy Griffisf368c07d2006-04-07 16:55:56 -040025#include <linux/mutex.h>
26#include <linux/fs.h>
27#include <linux/namei.h>
David Woodhousefe7752b2005-12-15 18:33:52 +000028#include <linux/netlink.h>
Amy Griffisf368c07d2006-04-07 16:55:56 -040029#include <linux/sched.h>
30#include <linux/inotify.h>
Darrel Goeddel3dc7e312006-03-10 18:14:06 -060031#include <linux/selinux.h>
David Woodhousefe7752b2005-12-15 18:33:52 +000032#include "audit.h"
33
Amy Griffisf368c07d2006-04-07 16:55:56 -040034/*
35 * Locking model:
36 *
37 * audit_filter_mutex:
38 * Synchronizes writes and blocking reads of audit's filterlist
39 * data. Rcu is used to traverse the filterlist and access
40 * contents of structs audit_entry, audit_watch and opaque
41 * selinux rules during filtering. If modified, these structures
42 * must be copied and replace their counterparts in the filterlist.
43 * An audit_parent struct is not accessed during filtering, so may
44 * be written directly provided audit_filter_mutex is held.
45 */
46
47/*
48 * Reference counting:
49 *
50 * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED
51 * event. Each audit_watch holds a reference to its associated parent.
52 *
53 * audit_watch: if added to lists, lifetime is from audit_init_watch() to
54 * audit_remove_watch(). Additionally, an audit_watch may exist
55 * temporarily to assist in searching existing filter data. Each
56 * audit_krule holds a reference to its associated watch.
57 */
58
59struct audit_parent {
60 struct list_head ilist; /* entry in inotify registration list */
61 struct list_head watches; /* associated watches */
62 struct inotify_watch wdata; /* inotify watch data */
63 unsigned flags; /* status flags */
64};
65
66/*
67 * audit_parent status flags:
68 *
69 * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
70 * a filesystem event to ensure we're adding audit watches to a valid parent.
71 * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot
72 * receive them while we have nameidata, but must be used for IN_MOVE_SELF which
73 * we can receive while holding nameidata.
74 */
75#define AUDIT_PARENT_INVALID 0x001
76
77/* Audit filter lists, defined in <linux/audit.h> */
David Woodhousefe7752b2005-12-15 18:33:52 +000078struct list_head audit_filter_list[AUDIT_NR_FILTERS] = {
79 LIST_HEAD_INIT(audit_filter_list[0]),
80 LIST_HEAD_INIT(audit_filter_list[1]),
81 LIST_HEAD_INIT(audit_filter_list[2]),
82 LIST_HEAD_INIT(audit_filter_list[3]),
83 LIST_HEAD_INIT(audit_filter_list[4]),
84 LIST_HEAD_INIT(audit_filter_list[5]),
85#if AUDIT_NR_FILTERS != 6
86#error Fix audit_filter_list initialiser
87#endif
88};
89
Amy Griffisf368c07d2006-04-07 16:55:56 -040090static DEFINE_MUTEX(audit_filter_mutex);
91
92/* Inotify handle */
93extern struct inotify_handle *audit_ih;
94
95/* Inotify events we care about. */
96#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF
97
98void audit_free_parent(struct inotify_watch *i_watch)
99{
100 struct audit_parent *parent;
101
102 parent = container_of(i_watch, struct audit_parent, wdata);
103 WARN_ON(!list_empty(&parent->watches));
104 kfree(parent);
105}
106
107static inline void audit_get_watch(struct audit_watch *watch)
108{
109 atomic_inc(&watch->count);
110}
111
112static void audit_put_watch(struct audit_watch *watch)
113{
114 if (atomic_dec_and_test(&watch->count)) {
115 WARN_ON(watch->parent);
116 WARN_ON(!list_empty(&watch->rules));
117 kfree(watch->path);
118 kfree(watch);
119 }
120}
121
122static void audit_remove_watch(struct audit_watch *watch)
123{
124 list_del(&watch->wlist);
125 put_inotify_watch(&watch->parent->wdata);
126 watch->parent = NULL;
127 audit_put_watch(watch); /* match initial get */
128}
129
Amy Griffis93315ed2006-02-07 12:05:27 -0500130static inline void audit_free_rule(struct audit_entry *e)
David Woodhousefe7752b2005-12-15 18:33:52 +0000131{
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600132 int i;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400133
134 /* some rules don't have associated watches */
135 if (e->rule.watch)
136 audit_put_watch(e->rule.watch);
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600137 if (e->rule.fields)
138 for (i = 0; i < e->rule.field_count; i++) {
139 struct audit_field *f = &e->rule.fields[i];
140 kfree(f->se_str);
141 selinux_audit_rule_free(f->se_rule);
142 }
Amy Griffis93315ed2006-02-07 12:05:27 -0500143 kfree(e->rule.fields);
Amy Griffis5adc8a62006-06-14 18:45:21 -0400144 kfree(e->rule.filterkey);
Amy Griffis93315ed2006-02-07 12:05:27 -0500145 kfree(e);
David Woodhousefe7752b2005-12-15 18:33:52 +0000146}
147
Amy Griffis93315ed2006-02-07 12:05:27 -0500148static inline void audit_free_rule_rcu(struct rcu_head *head)
149{
150 struct audit_entry *e = container_of(head, struct audit_entry, rcu);
151 audit_free_rule(e);
152}
153
Amy Griffisf368c07d2006-04-07 16:55:56 -0400154/* Initialize a parent watch entry. */
155static struct audit_parent *audit_init_parent(struct nameidata *ndp)
156{
157 struct audit_parent *parent;
158 s32 wd;
159
160 parent = kzalloc(sizeof(*parent), GFP_KERNEL);
161 if (unlikely(!parent))
162 return ERR_PTR(-ENOMEM);
163
164 INIT_LIST_HEAD(&parent->watches);
165 parent->flags = 0;
166
167 inotify_init_watch(&parent->wdata);
168 /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */
169 get_inotify_watch(&parent->wdata);
170 wd = inotify_add_watch(audit_ih, &parent->wdata, ndp->dentry->d_inode,
171 AUDIT_IN_WATCH);
172 if (wd < 0) {
173 audit_free_parent(&parent->wdata);
174 return ERR_PTR(wd);
175 }
176
177 return parent;
178}
179
180/* Initialize a watch entry. */
181static struct audit_watch *audit_init_watch(char *path)
182{
183 struct audit_watch *watch;
184
185 watch = kzalloc(sizeof(*watch), GFP_KERNEL);
186 if (unlikely(!watch))
187 return ERR_PTR(-ENOMEM);
188
189 INIT_LIST_HEAD(&watch->rules);
190 atomic_set(&watch->count, 1);
191 watch->path = path;
192 watch->dev = (dev_t)-1;
193 watch->ino = (unsigned long)-1;
194
195 return watch;
196}
197
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600198/* Initialize an audit filterlist entry. */
199static inline struct audit_entry *audit_init_entry(u32 field_count)
200{
201 struct audit_entry *entry;
202 struct audit_field *fields;
203
204 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
205 if (unlikely(!entry))
206 return NULL;
207
208 fields = kzalloc(sizeof(*fields) * field_count, GFP_KERNEL);
209 if (unlikely(!fields)) {
210 kfree(entry);
211 return NULL;
212 }
213 entry->rule.fields = fields;
214
215 return entry;
216}
217
Amy Griffis93315ed2006-02-07 12:05:27 -0500218/* Unpack a filter field's string representation from user-space
219 * buffer. */
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600220static char *audit_unpack_string(void **bufp, size_t *remain, size_t len)
Amy Griffis93315ed2006-02-07 12:05:27 -0500221{
222 char *str;
223
224 if (!*bufp || (len == 0) || (len > *remain))
225 return ERR_PTR(-EINVAL);
226
227 /* Of the currently implemented string fields, PATH_MAX
228 * defines the longest valid length.
229 */
230 if (len > PATH_MAX)
231 return ERR_PTR(-ENAMETOOLONG);
232
233 str = kmalloc(len + 1, GFP_KERNEL);
234 if (unlikely(!str))
235 return ERR_PTR(-ENOMEM);
236
237 memcpy(str, *bufp, len);
238 str[len] = 0;
239 *bufp += len;
240 *remain -= len;
241
242 return str;
243}
244
Amy Griffisf368c07d2006-04-07 16:55:56 -0400245/* Translate an inode field to kernel respresentation. */
246static inline int audit_to_inode(struct audit_krule *krule,
247 struct audit_field *f)
248{
249 if (krule->listnr != AUDIT_FILTER_EXIT ||
250 krule->watch || krule->inode_f)
251 return -EINVAL;
252
253 krule->inode_f = f;
254 return 0;
255}
256
257/* Translate a watch string to kernel respresentation. */
258static int audit_to_watch(struct audit_krule *krule, char *path, int len,
259 u32 op)
260{
261 struct audit_watch *watch;
262
263 if (!audit_ih)
264 return -EOPNOTSUPP;
265
266 if (path[0] != '/' || path[len-1] == '/' ||
267 krule->listnr != AUDIT_FILTER_EXIT ||
268 op & ~AUDIT_EQUAL ||
269 krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */
270 return -EINVAL;
271
272 watch = audit_init_watch(path);
273 if (unlikely(IS_ERR(watch)))
274 return PTR_ERR(watch);
275
276 audit_get_watch(watch);
277 krule->watch = watch;
278
279 return 0;
280}
281
Amy Griffis93315ed2006-02-07 12:05:27 -0500282/* Common user-space to kernel rule translation. */
283static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
284{
285 unsigned listnr;
286 struct audit_entry *entry;
Amy Griffis93315ed2006-02-07 12:05:27 -0500287 int i, err;
288
289 err = -EINVAL;
290 listnr = rule->flags & ~AUDIT_FILTER_PREPEND;
291 switch(listnr) {
292 default:
293 goto exit_err;
294 case AUDIT_FILTER_USER:
295 case AUDIT_FILTER_TYPE:
296#ifdef CONFIG_AUDITSYSCALL
297 case AUDIT_FILTER_ENTRY:
298 case AUDIT_FILTER_EXIT:
299 case AUDIT_FILTER_TASK:
300#endif
301 ;
302 }
Al Viro014149c2006-05-23 01:36:13 -0400303 if (unlikely(rule->action == AUDIT_POSSIBLE)) {
304 printk(KERN_ERR "AUDIT_POSSIBLE is deprecated\n");
305 goto exit_err;
306 }
307 if (rule->action != AUDIT_NEVER && rule->action != AUDIT_ALWAYS)
Amy Griffis93315ed2006-02-07 12:05:27 -0500308 goto exit_err;
309 if (rule->field_count > AUDIT_MAX_FIELDS)
310 goto exit_err;
311
312 err = -ENOMEM;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600313 entry = audit_init_entry(rule->field_count);
314 if (!entry)
Amy Griffis93315ed2006-02-07 12:05:27 -0500315 goto exit_err;
Amy Griffis93315ed2006-02-07 12:05:27 -0500316
317 entry->rule.flags = rule->flags & AUDIT_FILTER_PREPEND;
318 entry->rule.listnr = listnr;
319 entry->rule.action = rule->action;
320 entry->rule.field_count = rule->field_count;
Amy Griffis93315ed2006-02-07 12:05:27 -0500321
322 for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
323 entry->rule.mask[i] = rule->mask[i];
324
325 return entry;
326
327exit_err:
328 return ERR_PTR(err);
329}
330
331/* Translate struct audit_rule to kernel's rule respresentation.
332 * Exists for backward compatibility with userspace. */
333static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
334{
335 struct audit_entry *entry;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400336 struct audit_field *f;
Amy Griffis93315ed2006-02-07 12:05:27 -0500337 int err = 0;
338 int i;
339
340 entry = audit_to_entry_common(rule);
341 if (IS_ERR(entry))
342 goto exit_nofree;
343
344 for (i = 0; i < rule->field_count; i++) {
345 struct audit_field *f = &entry->rule.fields[i];
346
Amy Griffis93315ed2006-02-07 12:05:27 -0500347 f->op = rule->fields[i] & (AUDIT_NEGATE|AUDIT_OPERATORS);
348 f->type = rule->fields[i] & ~(AUDIT_NEGATE|AUDIT_OPERATORS);
349 f->val = rule->values[i];
350
Amy Griffisf368c07d2006-04-07 16:55:56 -0400351 err = -EINVAL;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400352 switch(f->type) {
Al Viro0a73dcc2006-06-05 08:15:59 -0400353 default:
Amy Griffisf368c07d2006-04-07 16:55:56 -0400354 goto exit_free;
Al Viro0a73dcc2006-06-05 08:15:59 -0400355 case AUDIT_PID:
356 case AUDIT_UID:
357 case AUDIT_EUID:
358 case AUDIT_SUID:
359 case AUDIT_FSUID:
360 case AUDIT_GID:
361 case AUDIT_EGID:
362 case AUDIT_SGID:
363 case AUDIT_FSGID:
364 case AUDIT_LOGINUID:
365 case AUDIT_PERS:
366 case AUDIT_ARCH:
367 case AUDIT_MSGTYPE:
368 case AUDIT_DEVMAJOR:
369 case AUDIT_DEVMINOR:
370 case AUDIT_EXIT:
371 case AUDIT_SUCCESS:
372 case AUDIT_ARG0:
373 case AUDIT_ARG1:
374 case AUDIT_ARG2:
375 case AUDIT_ARG3:
376 break;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400377 case AUDIT_INODE:
378 err = audit_to_inode(&entry->rule, f);
379 if (err)
380 goto exit_free;
381 break;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600382 }
383
Amy Griffis93315ed2006-02-07 12:05:27 -0500384 entry->rule.vers_ops = (f->op & AUDIT_OPERATORS) ? 2 : 1;
Dustin Kirklandd9d9ec62006-02-16 13:40:01 -0600385
386 /* Support for legacy operators where
387 * AUDIT_NEGATE bit signifies != and otherwise assumes == */
Amy Griffis93315ed2006-02-07 12:05:27 -0500388 if (f->op & AUDIT_NEGATE)
Dustin Kirklandd9d9ec62006-02-16 13:40:01 -0600389 f->op = AUDIT_NOT_EQUAL;
390 else if (!f->op)
391 f->op = AUDIT_EQUAL;
392 else if (f->op == AUDIT_OPERATORS) {
393 err = -EINVAL;
394 goto exit_free;
395 }
Amy Griffis93315ed2006-02-07 12:05:27 -0500396 }
397
Amy Griffisf368c07d2006-04-07 16:55:56 -0400398 f = entry->rule.inode_f;
399 if (f) {
400 switch(f->op) {
401 case AUDIT_NOT_EQUAL:
402 entry->rule.inode_f = NULL;
403 case AUDIT_EQUAL:
404 break;
405 default:
406 goto exit_free;
407 }
408 }
409
Amy Griffis93315ed2006-02-07 12:05:27 -0500410exit_nofree:
411 return entry;
412
413exit_free:
414 audit_free_rule(entry);
415 return ERR_PTR(err);
416}
417
418/* Translate struct audit_rule_data to kernel's rule respresentation. */
419static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
420 size_t datasz)
421{
422 int err = 0;
423 struct audit_entry *entry;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400424 struct audit_field *f;
Amy Griffis93315ed2006-02-07 12:05:27 -0500425 void *bufp;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600426 size_t remain = datasz - sizeof(struct audit_rule_data);
Amy Griffis93315ed2006-02-07 12:05:27 -0500427 int i;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600428 char *str;
Amy Griffis93315ed2006-02-07 12:05:27 -0500429
430 entry = audit_to_entry_common((struct audit_rule *)data);
431 if (IS_ERR(entry))
432 goto exit_nofree;
433
434 bufp = data->buf;
435 entry->rule.vers_ops = 2;
436 for (i = 0; i < data->field_count; i++) {
437 struct audit_field *f = &entry->rule.fields[i];
438
439 err = -EINVAL;
440 if (!(data->fieldflags[i] & AUDIT_OPERATORS) ||
441 data->fieldflags[i] & ~AUDIT_OPERATORS)
442 goto exit_free;
443
444 f->op = data->fieldflags[i] & AUDIT_OPERATORS;
445 f->type = data->fields[i];
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600446 f->val = data->values[i];
447 f->se_str = NULL;
448 f->se_rule = NULL;
Amy Griffis93315ed2006-02-07 12:05:27 -0500449 switch(f->type) {
Al Viro0a73dcc2006-06-05 08:15:59 -0400450 case AUDIT_PID:
451 case AUDIT_UID:
452 case AUDIT_EUID:
453 case AUDIT_SUID:
454 case AUDIT_FSUID:
455 case AUDIT_GID:
456 case AUDIT_EGID:
457 case AUDIT_SGID:
458 case AUDIT_FSGID:
459 case AUDIT_LOGINUID:
460 case AUDIT_PERS:
461 case AUDIT_ARCH:
462 case AUDIT_MSGTYPE:
463 case AUDIT_PPID:
464 case AUDIT_DEVMAJOR:
465 case AUDIT_DEVMINOR:
466 case AUDIT_EXIT:
467 case AUDIT_SUCCESS:
468 case AUDIT_ARG0:
469 case AUDIT_ARG1:
470 case AUDIT_ARG2:
471 case AUDIT_ARG3:
472 break;
Darrel Goeddel3a6b9f82006-06-29 16:56:39 -0500473 case AUDIT_SUBJ_USER:
474 case AUDIT_SUBJ_ROLE:
475 case AUDIT_SUBJ_TYPE:
476 case AUDIT_SUBJ_SEN:
477 case AUDIT_SUBJ_CLR:
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600478 str = audit_unpack_string(&bufp, &remain, f->val);
479 if (IS_ERR(str))
480 goto exit_free;
481 entry->rule.buflen += f->val;
482
483 err = selinux_audit_rule_init(f->type, f->op, str,
484 &f->se_rule);
485 /* Keep currently invalid fields around in case they
486 * become valid after a policy reload. */
487 if (err == -EINVAL) {
488 printk(KERN_WARNING "audit rule for selinux "
489 "\'%s\' is invalid\n", str);
490 err = 0;
491 }
492 if (err) {
493 kfree(str);
494 goto exit_free;
495 } else
496 f->se_str = str;
497 break;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400498 case AUDIT_WATCH:
499 str = audit_unpack_string(&bufp, &remain, f->val);
500 if (IS_ERR(str))
501 goto exit_free;
502 entry->rule.buflen += f->val;
503
504 err = audit_to_watch(&entry->rule, str, f->val, f->op);
505 if (err) {
506 kfree(str);
507 goto exit_free;
508 }
509 break;
510 case AUDIT_INODE:
511 err = audit_to_inode(&entry->rule, f);
512 if (err)
513 goto exit_free;
514 break;
Amy Griffis5adc8a62006-06-14 18:45:21 -0400515 case AUDIT_FILTERKEY:
516 err = -EINVAL;
517 if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
518 goto exit_free;
519 str = audit_unpack_string(&bufp, &remain, f->val);
520 if (IS_ERR(str))
521 goto exit_free;
522 entry->rule.buflen += f->val;
523 entry->rule.filterkey = str;
524 break;
Al Viro0a73dcc2006-06-05 08:15:59 -0400525 default:
526 goto exit_free;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400527 }
528 }
529
530 f = entry->rule.inode_f;
531 if (f) {
532 switch(f->op) {
533 case AUDIT_NOT_EQUAL:
534 entry->rule.inode_f = NULL;
535 case AUDIT_EQUAL:
536 break;
537 default:
538 goto exit_free;
Amy Griffis93315ed2006-02-07 12:05:27 -0500539 }
540 }
541
542exit_nofree:
543 return entry;
544
545exit_free:
546 audit_free_rule(entry);
547 return ERR_PTR(err);
548}
549
550/* Pack a filter field's string representation into data block. */
551static inline size_t audit_pack_string(void **bufp, char *str)
552{
553 size_t len = strlen(str);
554
555 memcpy(*bufp, str, len);
556 *bufp += len;
557
558 return len;
559}
560
561/* Translate kernel rule respresentation to struct audit_rule.
562 * Exists for backward compatibility with userspace. */
563static struct audit_rule *audit_krule_to_rule(struct audit_krule *krule)
564{
565 struct audit_rule *rule;
566 int i;
567
568 rule = kmalloc(sizeof(*rule), GFP_KERNEL);
569 if (unlikely(!rule))
Amy Griffis0a3b4832006-05-02 15:06:01 -0400570 return NULL;
Amy Griffis93315ed2006-02-07 12:05:27 -0500571 memset(rule, 0, sizeof(*rule));
572
573 rule->flags = krule->flags | krule->listnr;
574 rule->action = krule->action;
575 rule->field_count = krule->field_count;
576 for (i = 0; i < rule->field_count; i++) {
577 rule->values[i] = krule->fields[i].val;
578 rule->fields[i] = krule->fields[i].type;
579
580 if (krule->vers_ops == 1) {
581 if (krule->fields[i].op & AUDIT_NOT_EQUAL)
582 rule->fields[i] |= AUDIT_NEGATE;
583 } else {
584 rule->fields[i] |= krule->fields[i].op;
585 }
586 }
587 for (i = 0; i < AUDIT_BITMASK_SIZE; i++) rule->mask[i] = krule->mask[i];
588
589 return rule;
590}
591
592/* Translate kernel rule respresentation to struct audit_rule_data. */
593static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
594{
595 struct audit_rule_data *data;
596 void *bufp;
597 int i;
598
599 data = kmalloc(sizeof(*data) + krule->buflen, GFP_KERNEL);
600 if (unlikely(!data))
Amy Griffis0a3b4832006-05-02 15:06:01 -0400601 return NULL;
Amy Griffis93315ed2006-02-07 12:05:27 -0500602 memset(data, 0, sizeof(*data));
603
604 data->flags = krule->flags | krule->listnr;
605 data->action = krule->action;
606 data->field_count = krule->field_count;
607 bufp = data->buf;
608 for (i = 0; i < data->field_count; i++) {
609 struct audit_field *f = &krule->fields[i];
610
611 data->fields[i] = f->type;
612 data->fieldflags[i] = f->op;
613 switch(f->type) {
Darrel Goeddel3a6b9f82006-06-29 16:56:39 -0500614 case AUDIT_SUBJ_USER:
615 case AUDIT_SUBJ_ROLE:
616 case AUDIT_SUBJ_TYPE:
617 case AUDIT_SUBJ_SEN:
618 case AUDIT_SUBJ_CLR:
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600619 data->buflen += data->values[i] =
620 audit_pack_string(&bufp, f->se_str);
621 break;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400622 case AUDIT_WATCH:
623 data->buflen += data->values[i] =
624 audit_pack_string(&bufp, krule->watch->path);
625 break;
Amy Griffis5adc8a62006-06-14 18:45:21 -0400626 case AUDIT_FILTERKEY:
627 data->buflen += data->values[i] =
628 audit_pack_string(&bufp, krule->filterkey);
629 break;
Amy Griffis93315ed2006-02-07 12:05:27 -0500630 default:
631 data->values[i] = f->val;
632 }
633 }
634 for (i = 0; i < AUDIT_BITMASK_SIZE; i++) data->mask[i] = krule->mask[i];
635
636 return data;
637}
638
639/* Compare two rules in kernel format. Considered success if rules
640 * don't match. */
641static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b)
David Woodhousefe7752b2005-12-15 18:33:52 +0000642{
643 int i;
644
Amy Griffis93315ed2006-02-07 12:05:27 -0500645 if (a->flags != b->flags ||
646 a->listnr != b->listnr ||
647 a->action != b->action ||
648 a->field_count != b->field_count)
David Woodhousefe7752b2005-12-15 18:33:52 +0000649 return 1;
650
651 for (i = 0; i < a->field_count; i++) {
Amy Griffis93315ed2006-02-07 12:05:27 -0500652 if (a->fields[i].type != b->fields[i].type ||
653 a->fields[i].op != b->fields[i].op)
David Woodhousefe7752b2005-12-15 18:33:52 +0000654 return 1;
Amy Griffis93315ed2006-02-07 12:05:27 -0500655
656 switch(a->fields[i].type) {
Darrel Goeddel3a6b9f82006-06-29 16:56:39 -0500657 case AUDIT_SUBJ_USER:
658 case AUDIT_SUBJ_ROLE:
659 case AUDIT_SUBJ_TYPE:
660 case AUDIT_SUBJ_SEN:
661 case AUDIT_SUBJ_CLR:
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600662 if (strcmp(a->fields[i].se_str, b->fields[i].se_str))
663 return 1;
664 break;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400665 case AUDIT_WATCH:
666 if (strcmp(a->watch->path, b->watch->path))
667 return 1;
668 break;
Amy Griffis5adc8a62006-06-14 18:45:21 -0400669 case AUDIT_FILTERKEY:
670 /* both filterkeys exist based on above type compare */
671 if (strcmp(a->filterkey, b->filterkey))
672 return 1;
673 break;
Amy Griffis93315ed2006-02-07 12:05:27 -0500674 default:
675 if (a->fields[i].val != b->fields[i].val)
676 return 1;
677 }
David Woodhousefe7752b2005-12-15 18:33:52 +0000678 }
679
680 for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
681 if (a->mask[i] != b->mask[i])
682 return 1;
683
684 return 0;
685}
686
Amy Griffisf368c07d2006-04-07 16:55:56 -0400687/* Duplicate the given audit watch. The new watch's rules list is initialized
688 * to an empty list and wlist is undefined. */
689static struct audit_watch *audit_dupe_watch(struct audit_watch *old)
690{
691 char *path;
692 struct audit_watch *new;
693
694 path = kstrdup(old->path, GFP_KERNEL);
695 if (unlikely(!path))
696 return ERR_PTR(-ENOMEM);
697
698 new = audit_init_watch(path);
699 if (unlikely(IS_ERR(new))) {
700 kfree(path);
701 goto out;
702 }
703
704 new->dev = old->dev;
705 new->ino = old->ino;
706 get_inotify_watch(&old->parent->wdata);
707 new->parent = old->parent;
708
709out:
710 return new;
711}
712
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600713/* Duplicate selinux field information. The se_rule is opaque, so must be
714 * re-initialized. */
715static inline int audit_dupe_selinux_field(struct audit_field *df,
716 struct audit_field *sf)
717{
718 int ret = 0;
719 char *se_str;
720
721 /* our own copy of se_str */
722 se_str = kstrdup(sf->se_str, GFP_KERNEL);
723 if (unlikely(IS_ERR(se_str)))
724 return -ENOMEM;
725 df->se_str = se_str;
726
727 /* our own (refreshed) copy of se_rule */
728 ret = selinux_audit_rule_init(df->type, df->op, df->se_str,
729 &df->se_rule);
730 /* Keep currently invalid fields around in case they
731 * become valid after a policy reload. */
732 if (ret == -EINVAL) {
733 printk(KERN_WARNING "audit rule for selinux \'%s\' is "
734 "invalid\n", df->se_str);
735 ret = 0;
736 }
737
738 return ret;
739}
740
741/* Duplicate an audit rule. This will be a deep copy with the exception
742 * of the watch - that pointer is carried over. The selinux specific fields
743 * will be updated in the copy. The point is to be able to replace the old
Amy Griffisf368c07d2006-04-07 16:55:56 -0400744 * rule with the new rule in the filterlist, then free the old rule.
745 * The rlist element is undefined; list manipulations are handled apart from
746 * the initial copy. */
747static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
748 struct audit_watch *watch)
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600749{
750 u32 fcount = old->field_count;
751 struct audit_entry *entry;
752 struct audit_krule *new;
Amy Griffis5adc8a62006-06-14 18:45:21 -0400753 char *fk;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600754 int i, err = 0;
755
756 entry = audit_init_entry(fcount);
757 if (unlikely(!entry))
758 return ERR_PTR(-ENOMEM);
759
760 new = &entry->rule;
761 new->vers_ops = old->vers_ops;
762 new->flags = old->flags;
763 new->listnr = old->listnr;
764 new->action = old->action;
765 for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
766 new->mask[i] = old->mask[i];
767 new->buflen = old->buflen;
Amy Griffisf368c07d2006-04-07 16:55:56 -0400768 new->inode_f = old->inode_f;
769 new->watch = NULL;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600770 new->field_count = old->field_count;
771 memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount);
772
773 /* deep copy this information, updating the se_rule fields, because
774 * the originals will all be freed when the old rule is freed. */
775 for (i = 0; i < fcount; i++) {
776 switch (new->fields[i].type) {
Darrel Goeddel3a6b9f82006-06-29 16:56:39 -0500777 case AUDIT_SUBJ_USER:
778 case AUDIT_SUBJ_ROLE:
779 case AUDIT_SUBJ_TYPE:
780 case AUDIT_SUBJ_SEN:
781 case AUDIT_SUBJ_CLR:
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600782 err = audit_dupe_selinux_field(&new->fields[i],
783 &old->fields[i]);
Amy Griffis5adc8a62006-06-14 18:45:21 -0400784 break;
785 case AUDIT_FILTERKEY:
786 fk = kstrdup(old->filterkey, GFP_KERNEL);
787 if (unlikely(!fk))
788 err = -ENOMEM;
789 else
790 new->filterkey = fk;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600791 }
792 if (err) {
793 audit_free_rule(entry);
794 return ERR_PTR(err);
795 }
796 }
797
Amy Griffisf368c07d2006-04-07 16:55:56 -0400798 if (watch) {
799 audit_get_watch(watch);
800 new->watch = watch;
801 }
802
Darrel Goeddel3dc7e312006-03-10 18:14:06 -0600803 return entry;
804}
805
Amy Griffisf368c07d2006-04-07 16:55:56 -0400806/* Update inode info in audit rules based on filesystem event. */
807static void audit_update_watch(struct audit_parent *parent,
808 const char *dname, dev_t dev,
809 unsigned long ino, unsigned invalidating)
David Woodhousefe7752b2005-12-15 18:33:52 +0000810{
Amy Griffisf368c07d2006-04-07 16:55:56 -0400811 struct audit_watch *owatch, *nwatch, *nextw;
812 struct audit_krule *r, *nextr;
813 struct audit_entry *oentry, *nentry;
814 struct audit_buffer *ab;
815
816 mutex_lock(&audit_filter_mutex);
817 list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
Amy Griffis9c937dc2006-06-08 23:19:31 -0400818 if (audit_compare_dname_path(dname, owatch->path, NULL))
Amy Griffisf368c07d2006-04-07 16:55:56 -0400819 continue;
820
821 /* If the update involves invalidating rules, do the inode-based
822 * filtering now, so we don't omit records. */
823 if (invalidating &&
824 audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
825 audit_set_auditable(current->audit_context);
826
827 nwatch = audit_dupe_watch(owatch);
828 if (unlikely(IS_ERR(nwatch))) {
829 mutex_unlock(&audit_filter_mutex);
830 audit_panic("error updating watch, skipping");
831 return;
832 }
833 nwatch->dev = dev;
834 nwatch->ino = ino;
835
836 list_for_each_entry_safe(r, nextr, &owatch->rules, rlist) {
837
838 oentry = container_of(r, struct audit_entry, rule);
839 list_del(&oentry->rule.rlist);
840 list_del_rcu(&oentry->list);
841
842 nentry = audit_dupe_rule(&oentry->rule, nwatch);
843 if (unlikely(IS_ERR(nentry)))
844 audit_panic("error updating watch, removing");
845 else {
846 int h = audit_hash_ino((u32)ino);
847 list_add(&nentry->rule.rlist, &nwatch->rules);
848 list_add_rcu(&nentry->list, &audit_inode_hash[h]);
849 }
850
851 call_rcu(&oentry->rcu, audit_free_rule_rcu);
852 }
853
854 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
855 audit_log_format(ab, "audit updated rules specifying watch=");
856 audit_log_untrustedstring(ab, owatch->path);
857 audit_log_format(ab, " with dev=%u ino=%lu\n", dev, ino);
858 audit_log_end(ab);
859
860 audit_remove_watch(owatch);
861 goto add_watch_to_parent; /* event applies to a single watch */
862 }
863 mutex_unlock(&audit_filter_mutex);
864 return;
865
866add_watch_to_parent:
867 list_add(&nwatch->wlist, &parent->watches);
868 mutex_unlock(&audit_filter_mutex);
869 return;
870}
871
872/* Remove all watches & rules associated with a parent that is going away. */
873static void audit_remove_parent_watches(struct audit_parent *parent)
874{
875 struct audit_watch *w, *nextw;
876 struct audit_krule *r, *nextr;
Amy Griffis93315ed2006-02-07 12:05:27 -0500877 struct audit_entry *e;
David Woodhousefe7752b2005-12-15 18:33:52 +0000878
Amy Griffisf368c07d2006-04-07 16:55:56 -0400879 mutex_lock(&audit_filter_mutex);
880 parent->flags |= AUDIT_PARENT_INVALID;
881 list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
882 list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
883 e = container_of(r, struct audit_entry, rule);
884 list_del(&r->rlist);
885 list_del_rcu(&e->list);
886 call_rcu(&e->rcu, audit_free_rule_rcu);
887
888 audit_log(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE,
889 "audit implicitly removed rule from list=%d\n",
890 AUDIT_FILTER_EXIT);
891 }
892 audit_remove_watch(w);
893 }
894 mutex_unlock(&audit_filter_mutex);
895}
896
897/* Unregister inotify watches for parents on in_list.
898 * Generates an IN_IGNORED event. */
899static void audit_inotify_unregister(struct list_head *in_list)
900{
901 struct audit_parent *p, *n;
902
903 list_for_each_entry_safe(p, n, in_list, ilist) {
904 list_del(&p->ilist);
905 inotify_rm_watch(audit_ih, &p->wdata);
906 /* the put matching the get in audit_do_del_rule() */
907 put_inotify_watch(&p->wdata);
908 }
909}
910
911/* Find an existing audit rule.
912 * Caller must hold audit_filter_mutex to prevent stale rule data. */
913static struct audit_entry *audit_find_rule(struct audit_entry *entry,
914 struct list_head *list)
915{
916 struct audit_entry *e, *found = NULL;
917 int h;
918
919 if (entry->rule.watch) {
920 /* we don't know the inode number, so must walk entire hash */
921 for (h = 0; h < AUDIT_INODE_BUCKETS; h++) {
922 list = &audit_inode_hash[h];
923 list_for_each_entry(e, list, list)
924 if (!audit_compare_rule(&entry->rule, &e->rule)) {
925 found = e;
926 goto out;
927 }
928 }
929 goto out;
930 }
931
932 list_for_each_entry(e, list, list)
933 if (!audit_compare_rule(&entry->rule, &e->rule)) {
934 found = e;
935 goto out;
936 }
937
938out:
939 return found;
940}
941
942/* Get path information necessary for adding watches. */
943static int audit_get_nd(char *path, struct nameidata **ndp,
944 struct nameidata **ndw)
945{
946 struct nameidata *ndparent, *ndwatch;
947 int err;
948
949 ndparent = kmalloc(sizeof(*ndparent), GFP_KERNEL);
950 if (unlikely(!ndparent))
951 return -ENOMEM;
952
953 ndwatch = kmalloc(sizeof(*ndwatch), GFP_KERNEL);
954 if (unlikely(!ndwatch)) {
955 kfree(ndparent);
956 return -ENOMEM;
957 }
958
959 err = path_lookup(path, LOOKUP_PARENT, ndparent);
960 if (err) {
961 kfree(ndparent);
962 kfree(ndwatch);
963 return err;
964 }
965
966 err = path_lookup(path, 0, ndwatch);
967 if (err) {
968 kfree(ndwatch);
969 ndwatch = NULL;
970 }
971
972 *ndp = ndparent;
973 *ndw = ndwatch;
974
975 return 0;
976}
977
978/* Release resources used for watch path information. */
979static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
980{
981 if (ndp) {
982 path_release(ndp);
983 kfree(ndp);
984 }
985 if (ndw) {
986 path_release(ndw);
987 kfree(ndw);
988 }
989}
990
991/* Associate the given rule with an existing parent inotify_watch.
992 * Caller must hold audit_filter_mutex. */
993static void audit_add_to_parent(struct audit_krule *krule,
994 struct audit_parent *parent)
995{
996 struct audit_watch *w, *watch = krule->watch;
997 int watch_found = 0;
998
999 list_for_each_entry(w, &parent->watches, wlist) {
1000 if (strcmp(watch->path, w->path))
1001 continue;
1002
1003 watch_found = 1;
1004
1005 /* put krule's and initial refs to temporary watch */
1006 audit_put_watch(watch);
1007 audit_put_watch(watch);
1008
1009 audit_get_watch(w);
1010 krule->watch = watch = w;
1011 break;
1012 }
1013
1014 if (!watch_found) {
1015 get_inotify_watch(&parent->wdata);
1016 watch->parent = parent;
1017
1018 list_add(&watch->wlist, &parent->watches);
1019 }
1020 list_add(&krule->rlist, &watch->rules);
1021}
1022
1023/* Find a matching watch entry, or add this one.
1024 * Caller must hold audit_filter_mutex. */
1025static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
1026 struct nameidata *ndw)
1027{
1028 struct audit_watch *watch = krule->watch;
1029 struct inotify_watch *i_watch;
1030 struct audit_parent *parent;
1031 int ret = 0;
1032
1033 /* update watch filter fields */
1034 if (ndw) {
1035 watch->dev = ndw->dentry->d_inode->i_sb->s_dev;
1036 watch->ino = ndw->dentry->d_inode->i_ino;
1037 }
1038
1039 /* The audit_filter_mutex must not be held during inotify calls because
1040 * we hold it during inotify event callback processing. If an existing
1041 * inotify watch is found, inotify_find_watch() grabs a reference before
1042 * returning.
1043 */
1044 mutex_unlock(&audit_filter_mutex);
1045
1046 if (inotify_find_watch(audit_ih, ndp->dentry->d_inode, &i_watch) < 0) {
1047 parent = audit_init_parent(ndp);
1048 if (IS_ERR(parent)) {
1049 /* caller expects mutex locked */
1050 mutex_lock(&audit_filter_mutex);
1051 return PTR_ERR(parent);
1052 }
1053 } else
1054 parent = container_of(i_watch, struct audit_parent, wdata);
1055
1056 mutex_lock(&audit_filter_mutex);
1057
1058 /* parent was moved before we took audit_filter_mutex */
1059 if (parent->flags & AUDIT_PARENT_INVALID)
1060 ret = -ENOENT;
1061 else
1062 audit_add_to_parent(krule, parent);
1063
1064 /* match get in audit_init_parent or inotify_find_watch */
1065 put_inotify_watch(&parent->wdata);
1066 return ret;
1067}
1068
1069/* Add rule to given filterlist if not a duplicate. */
1070static inline int audit_add_rule(struct audit_entry *entry,
1071 struct list_head *list)
1072{
1073 struct audit_entry *e;
1074 struct audit_field *inode_f = entry->rule.inode_f;
1075 struct audit_watch *watch = entry->rule.watch;
1076 struct nameidata *ndp, *ndw;
1077 int h, err, putnd_needed = 0;
1078
1079 if (inode_f) {
1080 h = audit_hash_ino(inode_f->val);
1081 list = &audit_inode_hash[h];
1082 }
1083
1084 mutex_lock(&audit_filter_mutex);
1085 e = audit_find_rule(entry, list);
1086 mutex_unlock(&audit_filter_mutex);
1087 if (e) {
1088 err = -EEXIST;
1089 goto error;
1090 }
1091
1092 /* Avoid calling path_lookup under audit_filter_mutex. */
1093 if (watch) {
1094 err = audit_get_nd(watch->path, &ndp, &ndw);
1095 if (err)
1096 goto error;
1097 putnd_needed = 1;
1098 }
1099
1100 mutex_lock(&audit_filter_mutex);
1101 if (watch) {
1102 /* audit_filter_mutex is dropped and re-taken during this call */
1103 err = audit_add_watch(&entry->rule, ndp, ndw);
1104 if (err) {
1105 mutex_unlock(&audit_filter_mutex);
1106 goto error;
1107 }
1108 h = audit_hash_ino((u32)watch->ino);
1109 list = &audit_inode_hash[h];
David Woodhousefe7752b2005-12-15 18:33:52 +00001110 }
1111
David Woodhousefe7752b2005-12-15 18:33:52 +00001112 if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
David Woodhousefe7752b2005-12-15 18:33:52 +00001113 list_add_rcu(&entry->list, list);
Amy Griffis6a2bcee2006-06-02 13:16:01 -04001114 entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
David Woodhousefe7752b2005-12-15 18:33:52 +00001115 } else {
1116 list_add_tail_rcu(&entry->list, list);
1117 }
Amy Griffisf368c07d2006-04-07 16:55:56 -04001118 mutex_unlock(&audit_filter_mutex);
David Woodhousefe7752b2005-12-15 18:33:52 +00001119
Amy Griffisf368c07d2006-04-07 16:55:56 -04001120 if (putnd_needed)
1121 audit_put_nd(ndp, ndw);
1122
1123 return 0;
1124
1125error:
1126 if (putnd_needed)
1127 audit_put_nd(ndp, ndw);
1128 if (watch)
1129 audit_put_watch(watch); /* tmp watch, matches initial get */
1130 return err;
David Woodhousefe7752b2005-12-15 18:33:52 +00001131}
1132
Amy Griffisf368c07d2006-04-07 16:55:56 -04001133/* Remove an existing rule from filterlist. */
Amy Griffis93315ed2006-02-07 12:05:27 -05001134static inline int audit_del_rule(struct audit_entry *entry,
David Woodhousefe7752b2005-12-15 18:33:52 +00001135 struct list_head *list)
1136{
1137 struct audit_entry *e;
Amy Griffisf368c07d2006-04-07 16:55:56 -04001138 struct audit_field *inode_f = entry->rule.inode_f;
1139 struct audit_watch *watch, *tmp_watch = entry->rule.watch;
1140 LIST_HEAD(inotify_list);
1141 int h, ret = 0;
David Woodhousefe7752b2005-12-15 18:33:52 +00001142
Amy Griffisf368c07d2006-04-07 16:55:56 -04001143 if (inode_f) {
1144 h = audit_hash_ino(inode_f->val);
1145 list = &audit_inode_hash[h];
1146 }
1147
1148 mutex_lock(&audit_filter_mutex);
1149 e = audit_find_rule(entry, list);
1150 if (!e) {
1151 mutex_unlock(&audit_filter_mutex);
1152 ret = -ENOENT;
1153 goto out;
1154 }
1155
1156 watch = e->rule.watch;
1157 if (watch) {
1158 struct audit_parent *parent = watch->parent;
1159
1160 list_del(&e->rule.rlist);
1161
1162 if (list_empty(&watch->rules)) {
1163 audit_remove_watch(watch);
1164
1165 if (list_empty(&parent->watches)) {
1166 /* Put parent on the inotify un-registration
1167 * list. Grab a reference before releasing
1168 * audit_filter_mutex, to be released in
1169 * audit_inotify_unregister(). */
1170 list_add(&parent->ilist, &inotify_list);
1171 get_inotify_watch(&parent->wdata);
1172 }
David Woodhousefe7752b2005-12-15 18:33:52 +00001173 }
1174 }
Amy Griffisf368c07d2006-04-07 16:55:56 -04001175
1176 list_del_rcu(&e->list);
1177 call_rcu(&e->rcu, audit_free_rule_rcu);
1178
1179 mutex_unlock(&audit_filter_mutex);
1180
1181 if (!list_empty(&inotify_list))
1182 audit_inotify_unregister(&inotify_list);
1183
1184out:
1185 if (tmp_watch)
1186 audit_put_watch(tmp_watch); /* match initial get */
1187
1188 return ret;
David Woodhousefe7752b2005-12-15 18:33:52 +00001189}
1190
Amy Griffis93315ed2006-02-07 12:05:27 -05001191/* List rules using struct audit_rule. Exists for backward
1192 * compatibility with userspace. */
Al Viro9044e6b2006-05-22 01:09:24 -04001193static void audit_list(int pid, int seq, struct sk_buff_head *q)
David Woodhousefe7752b2005-12-15 18:33:52 +00001194{
Al Viro9044e6b2006-05-22 01:09:24 -04001195 struct sk_buff *skb;
David Woodhousefe7752b2005-12-15 18:33:52 +00001196 struct audit_entry *entry;
1197 int i;
1198
Amy Griffisf368c07d2006-04-07 16:55:56 -04001199 /* This is a blocking read, so use audit_filter_mutex instead of rcu
1200 * iterator to sync with list writers. */
David Woodhousefe7752b2005-12-15 18:33:52 +00001201 for (i=0; i<AUDIT_NR_FILTERS; i++) {
Amy Griffis93315ed2006-02-07 12:05:27 -05001202 list_for_each_entry(entry, &audit_filter_list[i], list) {
1203 struct audit_rule *rule;
1204
1205 rule = audit_krule_to_rule(&entry->rule);
1206 if (unlikely(!rule))
1207 break;
Al Viro9044e6b2006-05-22 01:09:24 -04001208 skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
Amy Griffis93315ed2006-02-07 12:05:27 -05001209 rule, sizeof(*rule));
Al Viro9044e6b2006-05-22 01:09:24 -04001210 if (skb)
1211 skb_queue_tail(q, skb);
Amy Griffis93315ed2006-02-07 12:05:27 -05001212 kfree(rule);
1213 }
David Woodhousefe7752b2005-12-15 18:33:52 +00001214 }
Amy Griffisf368c07d2006-04-07 16:55:56 -04001215 for (i = 0; i < AUDIT_INODE_BUCKETS; i++) {
1216 list_for_each_entry(entry, &audit_inode_hash[i], list) {
1217 struct audit_rule *rule;
1218
1219 rule = audit_krule_to_rule(&entry->rule);
1220 if (unlikely(!rule))
1221 break;
1222 skb = audit_make_reply(pid, seq, AUDIT_LIST, 0, 1,
1223 rule, sizeof(*rule));
1224 if (skb)
1225 skb_queue_tail(q, skb);
1226 kfree(rule);
1227 }
1228 }
Al Viro9044e6b2006-05-22 01:09:24 -04001229 skb = audit_make_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
1230 if (skb)
1231 skb_queue_tail(q, skb);
David Woodhousefe7752b2005-12-15 18:33:52 +00001232}
1233
Amy Griffis93315ed2006-02-07 12:05:27 -05001234/* List rules using struct audit_rule_data. */
Al Viro9044e6b2006-05-22 01:09:24 -04001235static void audit_list_rules(int pid, int seq, struct sk_buff_head *q)
Amy Griffis93315ed2006-02-07 12:05:27 -05001236{
Al Viro9044e6b2006-05-22 01:09:24 -04001237 struct sk_buff *skb;
Amy Griffis93315ed2006-02-07 12:05:27 -05001238 struct audit_entry *e;
1239 int i;
1240
Amy Griffisf368c07d2006-04-07 16:55:56 -04001241 /* This is a blocking read, so use audit_filter_mutex instead of rcu
1242 * iterator to sync with list writers. */
Amy Griffis93315ed2006-02-07 12:05:27 -05001243 for (i=0; i<AUDIT_NR_FILTERS; i++) {
1244 list_for_each_entry(e, &audit_filter_list[i], list) {
1245 struct audit_rule_data *data;
1246
1247 data = audit_krule_to_data(&e->rule);
1248 if (unlikely(!data))
1249 break;
Al Viro9044e6b2006-05-22 01:09:24 -04001250 skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
Amy Griffisf368c07d2006-04-07 16:55:56 -04001251 data, sizeof(*data) + data->buflen);
1252 if (skb)
1253 skb_queue_tail(q, skb);
1254 kfree(data);
1255 }
1256 }
1257 for (i=0; i< AUDIT_INODE_BUCKETS; i++) {
1258 list_for_each_entry(e, &audit_inode_hash[i], list) {
1259 struct audit_rule_data *data;
1260
1261 data = audit_krule_to_data(&e->rule);
1262 if (unlikely(!data))
1263 break;
1264 skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 0, 1,
1265 data, sizeof(*data) + data->buflen);
Al Viro9044e6b2006-05-22 01:09:24 -04001266 if (skb)
1267 skb_queue_tail(q, skb);
Amy Griffis93315ed2006-02-07 12:05:27 -05001268 kfree(data);
1269 }
1270 }
Al Viro9044e6b2006-05-22 01:09:24 -04001271 skb = audit_make_reply(pid, seq, AUDIT_LIST_RULES, 1, 1, NULL, 0);
1272 if (skb)
1273 skb_queue_tail(q, skb);
Amy Griffis93315ed2006-02-07 12:05:27 -05001274}
1275
Amy Griffis5adc8a62006-06-14 18:45:21 -04001276/* Log rule additions and removals */
1277static void audit_log_rule_change(uid_t loginuid, u32 sid, char *action,
1278 struct audit_krule *rule, int res)
1279{
1280 struct audit_buffer *ab;
1281
1282 ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE);
1283 if (!ab)
1284 return;
1285 audit_log_format(ab, "auid=%u", loginuid);
1286 if (sid) {
1287 char *ctx = NULL;
1288 u32 len;
1289 if (selinux_ctxid_to_string(sid, &ctx, &len))
1290 audit_log_format(ab, " ssid=%u", sid);
1291 else
1292 audit_log_format(ab, " subj=%s", ctx);
1293 kfree(ctx);
1294 }
1295 audit_log_format(ab, " %s rule key=", action);
1296 if (rule->filterkey)
1297 audit_log_untrustedstring(ab, rule->filterkey);
1298 else
1299 audit_log_format(ab, "(null)");
1300 audit_log_format(ab, " list=%d res=%d", rule->listnr, res);
1301 audit_log_end(ab);
1302}
1303
David Woodhousefe7752b2005-12-15 18:33:52 +00001304/**
1305 * audit_receive_filter - apply all rules to the specified message type
1306 * @type: audit message type
1307 * @pid: target pid for netlink audit messages
1308 * @uid: target uid for netlink audit messages
1309 * @seq: netlink audit message sequence (serial) number
1310 * @data: payload data
Amy Griffis93315ed2006-02-07 12:05:27 -05001311 * @datasz: size of payload data
David Woodhousefe7752b2005-12-15 18:33:52 +00001312 * @loginuid: loginuid of sender
Steve Grubbce29b682006-04-01 18:29:34 -05001313 * @sid: SE Linux Security ID of sender
David Woodhousefe7752b2005-12-15 18:33:52 +00001314 */
1315int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
Steve Grubbce29b682006-04-01 18:29:34 -05001316 size_t datasz, uid_t loginuid, u32 sid)
David Woodhousefe7752b2005-12-15 18:33:52 +00001317{
1318 struct task_struct *tsk;
Al Viro9044e6b2006-05-22 01:09:24 -04001319 struct audit_netlink_list *dest;
Amy Griffis93315ed2006-02-07 12:05:27 -05001320 int err = 0;
1321 struct audit_entry *entry;
David Woodhousefe7752b2005-12-15 18:33:52 +00001322
1323 switch (type) {
1324 case AUDIT_LIST:
Amy Griffis93315ed2006-02-07 12:05:27 -05001325 case AUDIT_LIST_RULES:
David Woodhousefe7752b2005-12-15 18:33:52 +00001326 /* We can't just spew out the rules here because we might fill
1327 * the available socket buffer space and deadlock waiting for
1328 * auditctl to read from it... which isn't ever going to
1329 * happen if we're actually running in the context of auditctl
1330 * trying to _send_ the stuff */
1331
Al Viro9044e6b2006-05-22 01:09:24 -04001332 dest = kmalloc(sizeof(struct audit_netlink_list), GFP_KERNEL);
David Woodhousefe7752b2005-12-15 18:33:52 +00001333 if (!dest)
1334 return -ENOMEM;
Al Viro9044e6b2006-05-22 01:09:24 -04001335 dest->pid = pid;
1336 skb_queue_head_init(&dest->q);
David Woodhousefe7752b2005-12-15 18:33:52 +00001337
Amy Griffisf368c07d2006-04-07 16:55:56 -04001338 mutex_lock(&audit_filter_mutex);
Amy Griffis93315ed2006-02-07 12:05:27 -05001339 if (type == AUDIT_LIST)
Al Viro9044e6b2006-05-22 01:09:24 -04001340 audit_list(pid, seq, &dest->q);
Amy Griffis93315ed2006-02-07 12:05:27 -05001341 else
Al Viro9044e6b2006-05-22 01:09:24 -04001342 audit_list_rules(pid, seq, &dest->q);
Amy Griffisf368c07d2006-04-07 16:55:56 -04001343 mutex_unlock(&audit_filter_mutex);
Al Viro9044e6b2006-05-22 01:09:24 -04001344
1345 tsk = kthread_run(audit_send_list, dest, "audit_send_list");
David Woodhousefe7752b2005-12-15 18:33:52 +00001346 if (IS_ERR(tsk)) {
Al Viro9044e6b2006-05-22 01:09:24 -04001347 skb_queue_purge(&dest->q);
David Woodhousefe7752b2005-12-15 18:33:52 +00001348 kfree(dest);
1349 err = PTR_ERR(tsk);
1350 }
1351 break;
1352 case AUDIT_ADD:
Amy Griffis93315ed2006-02-07 12:05:27 -05001353 case AUDIT_ADD_RULE:
1354 if (type == AUDIT_ADD)
1355 entry = audit_rule_to_entry(data);
1356 else
1357 entry = audit_data_to_entry(data, datasz);
1358 if (IS_ERR(entry))
1359 return PTR_ERR(entry);
David Woodhousefe7752b2005-12-15 18:33:52 +00001360
Amy Griffis93315ed2006-02-07 12:05:27 -05001361 err = audit_add_rule(entry,
1362 &audit_filter_list[entry->rule.listnr]);
Amy Griffis5adc8a62006-06-14 18:45:21 -04001363 audit_log_rule_change(loginuid, sid, "add", &entry->rule, !err);
Steve Grubb5d330102006-01-09 09:48:17 -05001364
1365 if (err)
Amy Griffis93315ed2006-02-07 12:05:27 -05001366 audit_free_rule(entry);
David Woodhousefe7752b2005-12-15 18:33:52 +00001367 break;
1368 case AUDIT_DEL:
Amy Griffis93315ed2006-02-07 12:05:27 -05001369 case AUDIT_DEL_RULE:
1370 if (type == AUDIT_DEL)
1371 entry = audit_rule_to_entry(data);
1372 else
1373 entry = audit_data_to_entry(data, datasz);
1374 if (IS_ERR(entry))
1375 return PTR_ERR(entry);
David Woodhousefe7752b2005-12-15 18:33:52 +00001376
Amy Griffis93315ed2006-02-07 12:05:27 -05001377 err = audit_del_rule(entry,
1378 &audit_filter_list[entry->rule.listnr]);
Amy Griffis5adc8a62006-06-14 18:45:21 -04001379 audit_log_rule_change(loginuid, sid, "remove", &entry->rule,
1380 !err);
Steve Grubb5d330102006-01-09 09:48:17 -05001381
Amy Griffis93315ed2006-02-07 12:05:27 -05001382 audit_free_rule(entry);
David Woodhousefe7752b2005-12-15 18:33:52 +00001383 break;
1384 default:
1385 return -EINVAL;
1386 }
1387
1388 return err;
1389}
1390
1391int audit_comparator(const u32 left, const u32 op, const u32 right)
1392{
1393 switch (op) {
1394 case AUDIT_EQUAL:
1395 return (left == right);
1396 case AUDIT_NOT_EQUAL:
1397 return (left != right);
1398 case AUDIT_LESS_THAN:
1399 return (left < right);
1400 case AUDIT_LESS_THAN_OR_EQUAL:
1401 return (left <= right);
1402 case AUDIT_GREATER_THAN:
1403 return (left > right);
1404 case AUDIT_GREATER_THAN_OR_EQUAL:
1405 return (left >= right);
David Woodhousefe7752b2005-12-15 18:33:52 +00001406 }
Dustin Kirklandd9d9ec62006-02-16 13:40:01 -06001407 BUG();
1408 return 0;
David Woodhousefe7752b2005-12-15 18:33:52 +00001409}
1410
Amy Griffisf368c07d2006-04-07 16:55:56 -04001411/* Compare given dentry name with last component in given path,
1412 * return of 0 indicates a match. */
Amy Griffis9c937dc2006-06-08 23:19:31 -04001413int audit_compare_dname_path(const char *dname, const char *path,
1414 int *dirlen)
Amy Griffisf368c07d2006-04-07 16:55:56 -04001415{
1416 int dlen, plen;
1417 const char *p;
David Woodhousefe7752b2005-12-15 18:33:52 +00001418
Amy Griffisf368c07d2006-04-07 16:55:56 -04001419 if (!dname || !path)
1420 return 1;
1421
1422 dlen = strlen(dname);
1423 plen = strlen(path);
1424 if (plen < dlen)
1425 return 1;
1426
1427 /* disregard trailing slashes */
1428 p = path + plen - 1;
1429 while ((*p == '/') && (p > path))
1430 p--;
1431
1432 /* find last path component */
1433 p = p - dlen + 1;
1434 if (p < path)
1435 return 1;
1436 else if (p > path) {
1437 if (*--p != '/')
1438 return 1;
1439 else
1440 p++;
1441 }
1442
Amy Griffis9c937dc2006-06-08 23:19:31 -04001443 /* return length of path's directory component */
1444 if (dirlen)
1445 *dirlen = p - path;
Amy Griffisf368c07d2006-04-07 16:55:56 -04001446 return strncmp(p, dname, dlen);
1447}
David Woodhousefe7752b2005-12-15 18:33:52 +00001448
1449static int audit_filter_user_rules(struct netlink_skb_parms *cb,
Amy Griffis93315ed2006-02-07 12:05:27 -05001450 struct audit_krule *rule,
David Woodhousefe7752b2005-12-15 18:33:52 +00001451 enum audit_state *state)
1452{
1453 int i;
1454
1455 for (i = 0; i < rule->field_count; i++) {
Amy Griffis93315ed2006-02-07 12:05:27 -05001456 struct audit_field *f = &rule->fields[i];
David Woodhousefe7752b2005-12-15 18:33:52 +00001457 int result = 0;
1458
Amy Griffis93315ed2006-02-07 12:05:27 -05001459 switch (f->type) {
David Woodhousefe7752b2005-12-15 18:33:52 +00001460 case AUDIT_PID:
Amy Griffis93315ed2006-02-07 12:05:27 -05001461 result = audit_comparator(cb->creds.pid, f->op, f->val);
David Woodhousefe7752b2005-12-15 18:33:52 +00001462 break;
1463 case AUDIT_UID:
Amy Griffis93315ed2006-02-07 12:05:27 -05001464 result = audit_comparator(cb->creds.uid, f->op, f->val);
David Woodhousefe7752b2005-12-15 18:33:52 +00001465 break;
1466 case AUDIT_GID:
Amy Griffis93315ed2006-02-07 12:05:27 -05001467 result = audit_comparator(cb->creds.gid, f->op, f->val);
David Woodhousefe7752b2005-12-15 18:33:52 +00001468 break;
1469 case AUDIT_LOGINUID:
Amy Griffis93315ed2006-02-07 12:05:27 -05001470 result = audit_comparator(cb->loginuid, f->op, f->val);
David Woodhousefe7752b2005-12-15 18:33:52 +00001471 break;
1472 }
1473
1474 if (!result)
1475 return 0;
1476 }
1477 switch (rule->action) {
1478 case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
David Woodhousefe7752b2005-12-15 18:33:52 +00001479 case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
1480 }
1481 return 1;
1482}
1483
1484int audit_filter_user(struct netlink_skb_parms *cb, int type)
1485{
1486 struct audit_entry *e;
1487 enum audit_state state;
1488 int ret = 1;
1489
1490 rcu_read_lock();
1491 list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_USER], list) {
1492 if (audit_filter_user_rules(cb, &e->rule, &state)) {
1493 if (state == AUDIT_DISABLED)
1494 ret = 0;
1495 break;
1496 }
1497 }
1498 rcu_read_unlock();
1499
1500 return ret; /* Audit by default */
1501}
1502
1503int audit_filter_type(int type)
1504{
1505 struct audit_entry *e;
1506 int result = 0;
1507
1508 rcu_read_lock();
1509 if (list_empty(&audit_filter_list[AUDIT_FILTER_TYPE]))
1510 goto unlock_and_return;
1511
1512 list_for_each_entry_rcu(e, &audit_filter_list[AUDIT_FILTER_TYPE],
1513 list) {
David Woodhousefe7752b2005-12-15 18:33:52 +00001514 int i;
Amy Griffis93315ed2006-02-07 12:05:27 -05001515 for (i = 0; i < e->rule.field_count; i++) {
1516 struct audit_field *f = &e->rule.fields[i];
1517 if (f->type == AUDIT_MSGTYPE) {
1518 result = audit_comparator(type, f->op, f->val);
David Woodhousefe7752b2005-12-15 18:33:52 +00001519 if (!result)
1520 break;
1521 }
1522 }
1523 if (result)
1524 goto unlock_and_return;
1525 }
1526unlock_and_return:
1527 rcu_read_unlock();
1528 return result;
1529}
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001530
1531/* Check to see if the rule contains any selinux fields. Returns 1 if there
1532 are selinux fields specified in the rule, 0 otherwise. */
1533static inline int audit_rule_has_selinux(struct audit_krule *rule)
1534{
1535 int i;
1536
1537 for (i = 0; i < rule->field_count; i++) {
1538 struct audit_field *f = &rule->fields[i];
1539 switch (f->type) {
Darrel Goeddel3a6b9f82006-06-29 16:56:39 -05001540 case AUDIT_SUBJ_USER:
1541 case AUDIT_SUBJ_ROLE:
1542 case AUDIT_SUBJ_TYPE:
1543 case AUDIT_SUBJ_SEN:
1544 case AUDIT_SUBJ_CLR:
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001545 return 1;
1546 }
1547 }
1548
1549 return 0;
1550}
1551
1552/* This function will re-initialize the se_rule field of all applicable rules.
1553 * It will traverse the filter lists serarching for rules that contain selinux
1554 * specific filter fields. When such a rule is found, it is copied, the
1555 * selinux field is re-initialized, and the old rule is replaced with the
1556 * updated rule. */
1557int selinux_audit_rule_update(void)
1558{
1559 struct audit_entry *entry, *n, *nentry;
Amy Griffisf368c07d2006-04-07 16:55:56 -04001560 struct audit_watch *watch;
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001561 int i, err = 0;
1562
Amy Griffisf368c07d2006-04-07 16:55:56 -04001563 /* audit_filter_mutex synchronizes the writers */
1564 mutex_lock(&audit_filter_mutex);
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001565
1566 for (i = 0; i < AUDIT_NR_FILTERS; i++) {
1567 list_for_each_entry_safe(entry, n, &audit_filter_list[i], list) {
1568 if (!audit_rule_has_selinux(&entry->rule))
1569 continue;
1570
Amy Griffisf368c07d2006-04-07 16:55:56 -04001571 watch = entry->rule.watch;
1572 nentry = audit_dupe_rule(&entry->rule, watch);
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001573 if (unlikely(IS_ERR(nentry))) {
1574 /* save the first error encountered for the
1575 * return value */
1576 if (!err)
1577 err = PTR_ERR(nentry);
1578 audit_panic("error updating selinux filters");
Amy Griffisf368c07d2006-04-07 16:55:56 -04001579 if (watch)
1580 list_del(&entry->rule.rlist);
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001581 list_del_rcu(&entry->list);
1582 } else {
Amy Griffisf368c07d2006-04-07 16:55:56 -04001583 if (watch) {
1584 list_add(&nentry->rule.rlist,
1585 &watch->rules);
1586 list_del(&entry->rule.rlist);
1587 }
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001588 list_replace_rcu(&entry->list, &nentry->list);
1589 }
1590 call_rcu(&entry->rcu, audit_free_rule_rcu);
1591 }
1592 }
1593
Amy Griffisf368c07d2006-04-07 16:55:56 -04001594 mutex_unlock(&audit_filter_mutex);
Darrel Goeddel3dc7e312006-03-10 18:14:06 -06001595
1596 return err;
1597}
Amy Griffisf368c07d2006-04-07 16:55:56 -04001598
1599/* Update watch data in audit rules based on inotify events. */
1600void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,
1601 u32 cookie, const char *dname, struct inode *inode)
1602{
1603 struct audit_parent *parent;
1604
1605 parent = container_of(i_watch, struct audit_parent, wdata);
1606
1607 if (mask & (IN_CREATE|IN_MOVED_TO) && inode)
1608 audit_update_watch(parent, dname, inode->i_sb->s_dev,
1609 inode->i_ino, 0);
1610 else if (mask & (IN_DELETE|IN_MOVED_FROM))
1611 audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
1612 /* inotify automatically removes the watch and sends IN_IGNORED */
1613 else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))
1614 audit_remove_parent_watches(parent);
1615 /* inotify does not remove the watch, so remove it manually */
1616 else if(mask & IN_MOVE_SELF) {
1617 audit_remove_parent_watches(parent);
1618 inotify_remove_watch_locked(audit_ih, i_watch);
1619 } else if (mask & IN_IGNORED)
1620 put_inotify_watch(i_watch);
1621}