aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/audit_watch.c
diff options
context:
space:
mode:
authorEric Paris <eparis@redhat.com>2009-12-17 20:12:04 -0500
committerEric Paris <eparis@redhat.com>2010-07-28 09:58:16 -0400
commite9fd702a58c49dbb14481dca88dad44758da393a (patch)
treec944771328e35ea36cf4e0e332b8402113878b3c /kernel/audit_watch.c
parentae7b8f4108bcffb42173f867ce845268c7202d48 (diff)
audit: convert audit watches to use fsnotify instead of inotify
Audit currently uses inotify to pin inodes in core and to detect when watched inodes are deleted or unmounted. This patch uses fsnotify instead of inotify. Signed-off-by: Eric Paris <eparis@redhat.com>
Diffstat (limited to 'kernel/audit_watch.c')
-rw-r--r--kernel/audit_watch.c208
1 files changed, 148 insertions, 60 deletions
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c
index c2ca7168bfd1..ff5be849473d 100644
--- a/kernel/audit_watch.c
+++ b/kernel/audit_watch.c
@@ -24,18 +24,18 @@
24#include <linux/kthread.h> 24#include <linux/kthread.h>
25#include <linux/mutex.h> 25#include <linux/mutex.h>
26#include <linux/fs.h> 26#include <linux/fs.h>
27#include <linux/fsnotify_backend.h>
27#include <linux/namei.h> 28#include <linux/namei.h>
28#include <linux/netlink.h> 29#include <linux/netlink.h>
29#include <linux/sched.h> 30#include <linux/sched.h>
30#include <linux/slab.h> 31#include <linux/slab.h>
31#include <linux/inotify.h>
32#include <linux/security.h> 32#include <linux/security.h>
33#include "audit.h" 33#include "audit.h"
34 34
35/* 35/*
36 * Reference counting: 36 * Reference counting:
37 * 37 *
38 * audit_parent: lifetime is from audit_init_parent() to receipt of an IN_IGNORED 38 * audit_parent: lifetime is from audit_init_parent() to receipt of an FS_IGNORED
39 * event. Each audit_watch holds a reference to its associated parent. 39 * event. Each audit_watch holds a reference to its associated parent.
40 * 40 *
41 * audit_watch: if added to lists, lifetime is from audit_init_watch() to 41 * audit_watch: if added to lists, lifetime is from audit_init_watch() to
@@ -57,26 +57,27 @@ struct audit_watch {
57struct audit_parent { 57struct audit_parent {
58 struct list_head ilist; /* tmp list used to free parents */ 58 struct list_head ilist; /* tmp list used to free parents */
59 struct list_head watches; /* anchor for audit_watch->wlist */ 59 struct list_head watches; /* anchor for audit_watch->wlist */
60 struct inotify_watch wdata; /* inotify watch data */ 60 struct fsnotify_mark_entry mark; /* fsnotify mark on the inode */
61 unsigned flags; /* status flags */ 61 unsigned flags; /* status flags */
62}; 62};
63 63
64/* Inotify handle. */ 64/* fsnotify handle. */
65struct inotify_handle *audit_ih; 65struct fsnotify_group *audit_watch_group;
66 66
67/* 67/*
68 * audit_parent status flags: 68 * audit_parent status flags:
69 * 69 *
70 * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to 70 * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
71 * a filesystem event to ensure we're adding audit watches to a valid parent. 71 * a filesystem event to ensure we're adding audit watches to a valid parent.
72 * Technically not needed for IN_DELETE_SELF or IN_UNMOUNT events, as we cannot 72 * Technically not needed for FS_DELETE_SELF or FS_UNMOUNT events, as we cannot
73 * receive them while we have nameidata, but must be used for IN_MOVE_SELF which 73 * receive them while we have nameidata, but must be used for FS_MOVE_SELF which
74 * we can receive while holding nameidata. 74 * we can receive while holding nameidata.
75 */ 75 */
76#define AUDIT_PARENT_INVALID 0x001 76#define AUDIT_PARENT_INVALID 0x001
77 77
78/* Inotify events we care about. */ 78/* fsnotify events we care about. */
79#define AUDIT_IN_WATCH IN_MOVE|IN_CREATE|IN_DELETE|IN_DELETE_SELF|IN_MOVE_SELF 79#define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
80 FS_MOVE_SELF | FS_EVENT_ON_CHILD)
80 81
81static void audit_free_parent(struct audit_parent *parent) 82static void audit_free_parent(struct audit_parent *parent)
82{ 83{
@@ -84,14 +85,45 @@ static void audit_free_parent(struct audit_parent *parent)
84 kfree(parent); 85 kfree(parent);
85} 86}
86 87
87static void audit_destroy_watch(struct inotify_watch *i_watch) 88static void audit_watch_free_mark(struct fsnotify_mark_entry *entry)
88{ 89{
89 struct audit_parent *parent; 90 struct audit_parent *parent;
90 91
91 parent = container_of(i_watch, struct audit_parent, wdata); 92 parent = container_of(entry, struct audit_parent, mark);
92 audit_free_parent(parent); 93 audit_free_parent(parent);
93} 94}
94 95
96static void audit_get_parent(struct audit_parent *parent)
97{
98 if (likely(parent))
99 fsnotify_get_mark(&parent->mark);
100}
101
102static void audit_put_parent(struct audit_parent *parent)
103{
104 if (likely(parent))
105 fsnotify_put_mark(&parent->mark);
106}
107
108/*
109 * Find and return the audit_parent on the given inode. If found a reference
110 * is taken on this parent.
111 */
112static inline struct audit_parent *audit_find_parent(struct inode *inode)
113{
114 struct audit_parent *parent = NULL;
115 struct fsnotify_mark_entry *entry;
116
117 spin_lock(&inode->i_lock);
118 entry = fsnotify_find_mark_entry(audit_watch_group, inode);
119 spin_unlock(&inode->i_lock);
120
121 if (entry)
122 parent = container_of(entry, struct audit_parent, mark);
123
124 return parent;
125}
126
95void audit_get_watch(struct audit_watch *watch) 127void audit_get_watch(struct audit_watch *watch)
96{ 128{
97 atomic_inc(&watch->count); 129 atomic_inc(&watch->count);
@@ -110,7 +142,7 @@ void audit_put_watch(struct audit_watch *watch)
110void audit_remove_watch(struct audit_watch *watch) 142void audit_remove_watch(struct audit_watch *watch)
111{ 143{
112 list_del(&watch->wlist); 144 list_del(&watch->wlist);
113 put_inotify_watch(&watch->parent->wdata); 145 audit_put_parent(watch->parent);
114 watch->parent = NULL; 146 watch->parent = NULL;
115 audit_put_watch(watch); /* match initial get */ 147 audit_put_watch(watch); /* match initial get */
116} 148}
@@ -130,8 +162,9 @@ int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev)
130/* Initialize a parent watch entry. */ 162/* Initialize a parent watch entry. */
131static struct audit_parent *audit_init_parent(struct nameidata *ndp) 163static struct audit_parent *audit_init_parent(struct nameidata *ndp)
132{ 164{
165 struct inode *inode = ndp->path.dentry->d_inode;
133 struct audit_parent *parent; 166 struct audit_parent *parent;
134 s32 wd; 167 int ret;
135 168
136 parent = kzalloc(sizeof(*parent), GFP_KERNEL); 169 parent = kzalloc(sizeof(*parent), GFP_KERNEL);
137 if (unlikely(!parent)) 170 if (unlikely(!parent))
@@ -140,14 +173,14 @@ static struct audit_parent *audit_init_parent(struct nameidata *ndp)
140 INIT_LIST_HEAD(&parent->watches); 173 INIT_LIST_HEAD(&parent->watches);
141 parent->flags = 0; 174 parent->flags = 0;
142 175
143 inotify_init_watch(&parent->wdata); 176 fsnotify_init_mark(&parent->mark, audit_watch_free_mark);
144 /* grab a ref so inotify watch hangs around until we take audit_filter_mutex */ 177 parent->mark.mask = AUDIT_FS_WATCH;
145 get_inotify_watch(&parent->wdata); 178 /* grab a ref so fsnotify mark hangs around until we take audit_filter_mutex */
146 wd = inotify_add_watch(audit_ih, &parent->wdata, 179 audit_get_parent(parent);
147 ndp->path.dentry->d_inode, AUDIT_IN_WATCH); 180 ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode);
148 if (wd < 0) { 181 if (ret < 0) {
149 audit_free_parent(parent); 182 audit_free_parent(parent);
150 return ERR_PTR(wd); 183 return ERR_PTR(ret);
151 } 184 }
152 185
153 return parent; 186 return parent;
@@ -176,7 +209,7 @@ int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op)
176{ 209{
177 struct audit_watch *watch; 210 struct audit_watch *watch;
178 211
179 if (!audit_ih) 212 if (!audit_watch_group)
180 return -EOPNOTSUPP; 213 return -EOPNOTSUPP;
181 214
182 if (path[0] != '/' || path[len-1] == '/' || 215 if (path[0] != '/' || path[len-1] == '/' ||
@@ -214,7 +247,7 @@ static struct audit_watch *audit_dupe_watch(struct audit_watch *old)
214 247
215 new->dev = old->dev; 248 new->dev = old->dev;
216 new->ino = old->ino; 249 new->ino = old->ino;
217 get_inotify_watch(&old->parent->wdata); 250 audit_get_parent(old->parent);
218 new->parent = old->parent; 251 new->parent = old->parent;
219 252
220out: 253out:
@@ -335,19 +368,21 @@ static void audit_remove_parent_watches(struct audit_parent *parent)
335 audit_remove_watch(w); 368 audit_remove_watch(w);
336 } 369 }
337 mutex_unlock(&audit_filter_mutex); 370 mutex_unlock(&audit_filter_mutex);
371
372 fsnotify_destroy_mark_by_entry(&parent->mark);
338} 373}
339 374
340/* Unregister inotify watches for parents on in_list. 375/* Unregister inotify watches for parents on in_list.
341 * Generates an IN_IGNORED event. */ 376 * Generates an FS_IGNORED event. */
342void audit_watch_inotify_unregister(struct list_head *in_list) 377void audit_watch_inotify_unregister(struct list_head *in_list)
343{ 378{
344 struct audit_parent *p, *n; 379 struct audit_parent *p, *n;
345 380
346 list_for_each_entry_safe(p, n, in_list, ilist) { 381 list_for_each_entry_safe(p, n, in_list, ilist) {
347 list_del(&p->ilist); 382 list_del(&p->ilist);
348 inotify_rm_watch(audit_ih, &p->wdata); 383 fsnotify_destroy_mark_by_entry(&p->mark);
349 /* the unpin matching the pin in audit_remove_watch_rule() */ 384 /* matches the get in audit_remove_watch_rule() */
350 unpin_inotify_watch(&p->wdata); 385 audit_put_parent(p);
351 } 386 }
352} 387}
353 388
@@ -399,7 +434,7 @@ static void audit_put_nd(struct nameidata *ndp, struct nameidata *ndw)
399 } 434 }
400} 435}
401 436
402/* Associate the given rule with an existing parent inotify_watch. 437/* Associate the given rule with an existing parent.
403 * Caller must hold audit_filter_mutex. */ 438 * Caller must hold audit_filter_mutex. */
404static void audit_add_to_parent(struct audit_krule *krule, 439static void audit_add_to_parent(struct audit_krule *krule,
405 struct audit_parent *parent) 440 struct audit_parent *parent)
@@ -407,6 +442,8 @@ static void audit_add_to_parent(struct audit_krule *krule,
407 struct audit_watch *w, *watch = krule->watch; 442 struct audit_watch *w, *watch = krule->watch;
408 int watch_found = 0; 443 int watch_found = 0;
409 444
445 BUG_ON(!mutex_is_locked(&audit_filter_mutex));
446
410 list_for_each_entry(w, &parent->watches, wlist) { 447 list_for_each_entry(w, &parent->watches, wlist) {
411 if (strcmp(watch->path, w->path)) 448 if (strcmp(watch->path, w->path))
412 continue; 449 continue;
@@ -423,7 +460,7 @@ static void audit_add_to_parent(struct audit_krule *krule,
423 } 460 }
424 461
425 if (!watch_found) { 462 if (!watch_found) {
426 get_inotify_watch(&parent->wdata); 463 audit_get_parent(parent);
427 watch->parent = parent; 464 watch->parent = parent;
428 465
429 list_add(&watch->wlist, &parent->watches); 466 list_add(&watch->wlist, &parent->watches);
@@ -436,7 +473,6 @@ static void audit_add_to_parent(struct audit_krule *krule,
436int audit_add_watch(struct audit_krule *krule, struct list_head **list) 473int audit_add_watch(struct audit_krule *krule, struct list_head **list)
437{ 474{
438 struct audit_watch *watch = krule->watch; 475 struct audit_watch *watch = krule->watch;
439 struct inotify_watch *i_watch;
440 struct audit_parent *parent; 476 struct audit_parent *parent;
441 struct nameidata *ndp = NULL, *ndw = NULL; 477 struct nameidata *ndp = NULL, *ndw = NULL;
442 int h, ret = 0; 478 int h, ret = 0;
@@ -462,8 +498,8 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
462 * inotify watch is found, inotify_find_watch() grabs a reference before 498 * inotify watch is found, inotify_find_watch() grabs a reference before
463 * returning. 499 * returning.
464 */ 500 */
465 if (inotify_find_watch(audit_ih, ndp->path.dentry->d_inode, 501 parent = audit_find_parent(ndp->path.dentry->d_inode);
466 &i_watch) < 0) { 502 if (!parent) {
467 parent = audit_init_parent(ndp); 503 parent = audit_init_parent(ndp);
468 if (IS_ERR(parent)) { 504 if (IS_ERR(parent)) {
469 /* caller expects mutex locked */ 505 /* caller expects mutex locked */
@@ -471,8 +507,7 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
471 ret = PTR_ERR(parent); 507 ret = PTR_ERR(parent);
472 goto error; 508 goto error;
473 } 509 }
474 } else 510 }
475 parent = container_of(i_watch, struct audit_parent, wdata);
476 511
477 mutex_lock(&audit_filter_mutex); 512 mutex_lock(&audit_filter_mutex);
478 513
@@ -482,8 +517,8 @@ int audit_add_watch(struct audit_krule *krule, struct list_head **list)
482 else 517 else
483 audit_add_to_parent(krule, parent); 518 audit_add_to_parent(krule, parent);
484 519
485 /* match get in audit_init_parent or inotify_find_watch */ 520 /* match get in audit_find_parent or audit_init_parent */
486 put_inotify_watch(&parent->wdata); 521 audit_put_parent(parent);
487 522
488 h = audit_hash_ino((u32)watch->ino); 523 h = audit_hash_ino((u32)watch->ino);
489 *list = &audit_inode_hash[h]; 524 *list = &audit_inode_hash[h];
@@ -504,52 +539,105 @@ void audit_remove_watch_rule(struct audit_krule *krule, struct list_head *list)
504 audit_remove_watch(watch); 539 audit_remove_watch(watch);
505 540
506 if (list_empty(&parent->watches)) { 541 if (list_empty(&parent->watches)) {
507 /* Put parent on the inotify un-registration 542 /* Put parent on the un-registration list.
508 * list. Grab a reference before releasing 543 * Grab a reference before releasing
509 * audit_filter_mutex, to be released in 544 * audit_filter_mutex, to be released in
510 * audit_inotify_unregister(). 545 * audit_watch_inotify_unregister().
511 * If filesystem is going away, just leave 546 * If filesystem is going away, just leave
512 * the sucker alone, eviction will take 547 * the sucker alone, eviction will take
513 * care of it. */ 548 * care of it. */
514 if (pin_inotify_watch(&parent->wdata)) 549 audit_get_parent(parent);
515 list_add(&parent->ilist, list); 550 list_add(&parent->ilist, list);
516 } 551 }
517 } 552 }
518} 553}
519 554
520/* Update watch data in audit rules based on inotify events. */ 555static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask)
521static void audit_handle_ievent(struct inotify_watch *i_watch, u32 wd, u32 mask,
522 u32 cookie, const char *dname, struct inode *inode)
523{ 556{
557 struct fsnotify_mark_entry *entry;
558 bool send;
559
560 spin_lock(&inode->i_lock);
561 entry = fsnotify_find_mark_entry(group, inode);
562 spin_unlock(&inode->i_lock);
563 if (!entry)
564 return false;
565
566 mask = (mask & ~FS_EVENT_ON_CHILD);
567 send = (entry->mask & mask);
568
569 /* find took a reference */
570 fsnotify_put_mark(entry);
571
572 return send;
573}
574
575/* Update watch data in audit rules based on fsnotify events. */
576static int audit_watch_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
577{
578 struct inode *inode;
579 __u32 mask = event->mask;
580 const char *dname = event->file_name;
524 struct audit_parent *parent; 581 struct audit_parent *parent;
525 582
526 parent = container_of(i_watch, struct audit_parent, wdata); 583 BUG_ON(group != audit_watch_group);
584
585 parent = audit_find_parent(event->to_tell);
586 if (unlikely(!parent))
587 return 0;
588
589 switch (event->data_type) {
590 case (FSNOTIFY_EVENT_PATH):
591 inode = event->path.dentry->d_inode;
592 break;
593 case (FSNOTIFY_EVENT_INODE):
594 inode = event->inode;
595 break;
596 default:
597 BUG();
598 inode = NULL;
599 break;
600 };
527 601
528 if (mask & (IN_CREATE|IN_MOVED_TO) && inode) 602 if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
529 audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0); 603 audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
530 else if (mask & (IN_DELETE|IN_MOVED_FROM)) 604 else if (mask & (FS_DELETE|FS_MOVED_FROM))
531 audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1); 605 audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
532 /* inotify automatically removes the watch and sends IN_IGNORED */ 606 else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
533 else if (mask & (IN_DELETE_SELF|IN_UNMOUNT))
534 audit_remove_parent_watches(parent); 607 audit_remove_parent_watches(parent);
535 /* inotify does not remove the watch, so remove it manually */ 608 /* moved put_inotify_watch to freeing mark */
536 else if(mask & IN_MOVE_SELF) { 609
537 audit_remove_parent_watches(parent); 610 /* matched the ref taken by audit_find_parent */
538 inotify_remove_watch_locked(audit_ih, i_watch); 611 audit_put_parent(parent);
539 } else if (mask & IN_IGNORED) 612
540 put_inotify_watch(i_watch); 613 return 0;
614}
615
616static void audit_watch_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group)
617{
618 struct audit_parent *parent;
619
620 parent = container_of(entry, struct audit_parent, mark);
621 /* taken from audit_handle_ievent & FS_IGNORED please figure out what I match... */
622 audit_put_parent(parent);
541} 623}
542 624
543static const struct inotify_operations audit_inotify_ops = { 625static const struct fsnotify_ops audit_watch_fsnotify_ops = {
544 .handle_event = audit_handle_ievent, 626 .should_send_event = audit_watch_should_send_event,
545 .destroy_watch = audit_destroy_watch, 627 .handle_event = audit_watch_handle_event,
628 .free_group_priv = NULL,
629 .freeing_mark = audit_watch_freeing_mark,
630 .free_event_priv = NULL,
546}; 631};
547 632
548static int __init audit_watch_init(void) 633static int __init audit_watch_init(void)
549{ 634{
550 audit_ih = inotify_init(&audit_inotify_ops); 635 audit_watch_group = fsnotify_obtain_group(AUDIT_WATCH_GROUP_NUM, AUDIT_FS_WATCH,
551 if (IS_ERR(audit_ih)) 636 &audit_watch_fsnotify_ops);
552 audit_panic("cannot initialize inotify handle"); 637 if (IS_ERR(audit_watch_group)) {
638 audit_watch_group = NULL;
639 audit_panic("cannot create audit fsnotify group");
640 }
553 return 0; 641 return 0;
554} 642}
555subsys_initcall(audit_watch_init); 643subsys_initcall(audit_watch_init);