22 #include <linux/kernel.h>
23 #include <linux/audit.h>
29 #include <linux/netlink.h>
30 #include <linux/sched.h>
31 #include <linux/slab.h>
63 static struct fsnotify_group *audit_watch_group;
66 #define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
67 FS_MOVE_SELF | FS_EVENT_ON_CHILD)
69 static void audit_free_parent(
struct audit_parent *parent)
75 static void audit_watch_free_mark(
struct fsnotify_mark *
entry)
80 audit_free_parent(parent);
83 static void audit_get_parent(
struct audit_parent *parent)
89 static void audit_put_parent(
struct audit_parent *parent)
102 struct fsnotify_mark *
entry;
129 audit_put_parent(watch->
parent);
141 return (watch->
ino != (
unsigned long)-1) &&
149 struct inode *inode = path->
dentry->d_inode;
153 parent = kzalloc(
sizeof(*parent),
GFP_KERNEL);
157 INIT_LIST_HEAD(&parent->
watches);
163 audit_free_parent(parent);
171 static struct audit_watch *audit_init_watch(
char *path)
179 INIT_LIST_HEAD(&watch->
rules);
183 watch->
ino = (
unsigned long)-1;
193 if (!audit_watch_group)
196 if (path[0] !=
'/' || path[len-1] ==
'/' ||
202 watch = audit_init_watch(path);
204 return PTR_ERR(watch);
223 new = audit_init_watch(path);
231 audit_get_parent(old->
parent);
232 new->parent = old->
parent;
246 audit_log_string(ab, op);
256 static void audit_update_watch(
struct audit_parent *parent,
258 unsigned long ino,
unsigned invalidating)
274 if (invalidating && !audit_dummy_context())
279 nwatch = audit_dupe_watch(owatch);
280 if (IS_ERR(nwatch)) {
292 list_del_rcu(&oentry->
list);
295 if (IS_ERR(nentry)) {
299 int h = audit_hash_ino((
u32)ino);
308 nentry->
rule.watch = nwatch;
309 list_add(&nentry->
rule.rlist, &nwatch->
rules);
311 list_replace(&oentry->
rule.list,
315 audit_watch_log_rule_change(r, owatch,
"updated rules");
320 audit_remove_watch(owatch);
321 goto add_watch_to_parent;
333 static void audit_remove_parent_watches(
struct audit_parent *parent)
343 audit_watch_log_rule_change(r, w,
"remove rule");
346 list_del_rcu(&e->
list);
349 audit_remove_watch(w);
357 static int audit_get_nd(
struct audit_watch *watch,
struct path *parent)
374 static void audit_add_to_parent(
struct audit_krule *krule,
380 BUG_ON(!mutex_is_locked(&audit_filter_mutex));
398 audit_get_parent(parent);
412 struct path parent_path;
418 ret = audit_get_nd(watch, &parent_path);
427 parent = audit_find_parent(parent_path.
dentry->d_inode);
429 parent = audit_init_parent(&parent_path);
430 if (IS_ERR(parent)) {
431 ret = PTR_ERR(parent);
436 audit_add_to_parent(krule, parent);
439 audit_put_parent(parent);
441 h = audit_hash_ino((
u32)watch->
ino);
455 if (list_empty(&watch->
rules)) {
456 audit_remove_watch(watch);
458 if (list_empty(&parent->
watches)) {
459 audit_get_parent(parent);
461 audit_put_parent(parent);
466 static bool audit_watch_should_send_event(
struct fsnotify_group *
group,
struct inode *inode,
467 struct fsnotify_mark *inode_mark,
468 struct fsnotify_mark *vfsmount_mark,
475 static int audit_watch_handle_event(
struct fsnotify_group *
group,
476 struct fsnotify_mark *inode_mark,
477 struct fsnotify_mark *vfsmount_mark,
478 struct fsnotify_event *
event)
482 const char *dname =
event->file_name;
487 BUG_ON(group != audit_watch_group);
489 switch (event->data_type) {
490 case (FSNOTIFY_EVENT_PATH):
491 inode =
event->path.dentry->d_inode;
493 case (FSNOTIFY_EVENT_INODE):
494 inode =
event->inode;
502 if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
503 audit_update_watch(parent, dname, inode->
i_sb->s_dev, inode->
i_ino, 0);
504 else if (mask & (FS_DELETE|FS_MOVED_FROM))
505 audit_update_watch(parent, dname, (
dev_t)-1, (
unsigned long)-1, 1);
506 else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
507 audit_remove_parent_watches(parent);
512 static const struct fsnotify_ops audit_watch_fsnotify_ops = {
513 .should_send_event = audit_watch_should_send_event,
514 .handle_event = audit_watch_handle_event,
515 .free_group_priv =
NULL,
516 .freeing_mark =
NULL,
517 .free_event_priv =
NULL,
520 static int __init audit_watch_init(
void)
523 if (IS_ERR(audit_watch_group)) {
524 audit_watch_group =
NULL;