diff options
author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-21 11:54:32 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-21 11:54:32 -0400 |
commit | 2fb59d623ad85dfdb8ce03a660051743f7361896 (patch) | |
tree | 3991ab2b88cc369444c136089d9c1570c26c6072 | |
parent | efdc31319d43050a5742fb690b1a4beb68092a94 (diff) | |
parent | 74c3cbe33bc077ac1159cadfea608b501e100344 (diff) |
Merge branch 'audit.b43' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current
* 'audit.b43' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current:
[PATCH] audit: watching subtrees
[PATCH] new helper - inotify_evict_watch()
[PATCH] new helper - inotify_clone_watch()
[PATCH] new helpers - collect_mounts() and release_collected_mounts()
[PATCH] pass dentry to audit_inode()/audit_inode_child()
-rw-r--r-- | fs/dcache.c | 2 | ||||
-rw-r--r-- | fs/debugfs/inode.c | 2 | ||||
-rw-r--r-- | fs/inotify.c | 43 | ||||
-rw-r--r-- | fs/namei.c | 10 | ||||
-rw-r--r-- | fs/namespace.c | 22 | ||||
-rw-r--r-- | fs/open.c | 4 | ||||
-rw-r--r-- | fs/pnode.h | 1 | ||||
-rw-r--r-- | fs/xattr.c | 8 | ||||
-rw-r--r-- | include/linux/audit.h | 19 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 | ||||
-rw-r--r-- | include/linux/fs.h | 2 | ||||
-rw-r--r-- | include/linux/fsnotify.h | 9 | ||||
-rw-r--r-- | include/linux/inotify.h | 2 | ||||
-rw-r--r-- | init/Kconfig | 4 | ||||
-rw-r--r-- | ipc/mqueue.c | 8 | ||||
-rw-r--r-- | kernel/Makefile | 1 | ||||
-rw-r--r-- | kernel/audit.c | 87 | ||||
-rw-r--r-- | kernel/audit.h | 34 | ||||
-rw-r--r-- | kernel/audit_tree.c | 903 | ||||
-rw-r--r-- | kernel/auditfilter.c | 64 | ||||
-rw-r--r-- | kernel/auditsc.c | 225 |
21 files changed, 1411 insertions, 40 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 5489b2d98a00..2bb3f7ac683b 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -38,7 +38,7 @@ int sysctl_vfs_cache_pressure __read_mostly = 100; | |||
38 | EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); | 38 | EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); |
39 | 39 | ||
40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock); | 40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(dcache_lock); |
41 | static __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); | 41 | __cacheline_aligned_in_smp DEFINE_SEQLOCK(rename_lock); |
42 | 42 | ||
43 | EXPORT_SYMBOL(dcache_lock); | 43 | EXPORT_SYMBOL(dcache_lock); |
44 | 44 | ||
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 11be8a325e26..6a713b33992f 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c | |||
@@ -413,7 +413,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, | |||
413 | d_move(old_dentry, dentry); | 413 | d_move(old_dentry, dentry); |
414 | fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name, | 414 | fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name, |
415 | old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode), | 415 | old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode), |
416 | NULL, old_dentry->d_inode); | 416 | NULL, old_dentry); |
417 | fsnotify_oldname_free(old_name); | 417 | fsnotify_oldname_free(old_name); |
418 | unlock_rename(new_dir, old_dir); | 418 | unlock_rename(new_dir, old_dir); |
419 | dput(dentry); | 419 | dput(dentry); |
diff --git a/fs/inotify.c b/fs/inotify.c index 7457501b9565..2c5b92152876 100644 --- a/fs/inotify.c +++ b/fs/inotify.c | |||
@@ -667,6 +667,49 @@ out: | |||
667 | EXPORT_SYMBOL_GPL(inotify_add_watch); | 667 | EXPORT_SYMBOL_GPL(inotify_add_watch); |
668 | 668 | ||
669 | /** | 669 | /** |
670 | * inotify_clone_watch - put the watch next to existing one | ||
671 | * @old: already installed watch | ||
672 | * @new: new watch | ||
673 | * | ||
674 | * Caller must hold the inotify_mutex of inode we are dealing with; | ||
675 | * it is expected to remove the old watch before unlocking the inode. | ||
676 | */ | ||
677 | s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new) | ||
678 | { | ||
679 | struct inotify_handle *ih = old->ih; | ||
680 | int ret = 0; | ||
681 | |||
682 | new->mask = old->mask; | ||
683 | new->ih = ih; | ||
684 | |||
685 | mutex_lock(&ih->mutex); | ||
686 | |||
687 | /* Initialize a new watch */ | ||
688 | ret = inotify_handle_get_wd(ih, new); | ||
689 | if (unlikely(ret)) | ||
690 | goto out; | ||
691 | ret = new->wd; | ||
692 | |||
693 | get_inotify_handle(ih); | ||
694 | |||
695 | new->inode = igrab(old->inode); | ||
696 | |||
697 | list_add(&new->h_list, &ih->watches); | ||
698 | list_add(&new->i_list, &old->inode->inotify_watches); | ||
699 | out: | ||
700 | mutex_unlock(&ih->mutex); | ||
701 | return ret; | ||
702 | } | ||
703 | |||
704 | void inotify_evict_watch(struct inotify_watch *watch) | ||
705 | { | ||
706 | get_inotify_watch(watch); | ||
707 | mutex_lock(&watch->ih->mutex); | ||
708 | inotify_remove_watch_locked(watch->ih, watch); | ||
709 | mutex_unlock(&watch->ih->mutex); | ||
710 | } | ||
711 | |||
712 | /** | ||
670 | * inotify_rm_wd - remove a watch from an inotify instance | 713 | * inotify_rm_wd - remove a watch from an inotify instance |
671 | * @ih: inotify handle | 714 | * @ih: inotify handle |
672 | * @wd: watch descriptor to remove | 715 | * @wd: watch descriptor to remove |
diff --git a/fs/namei.c b/fs/namei.c index 1e5c71669164..3b993db26cee 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -1174,7 +1174,7 @@ static int fastcall do_path_lookup(int dfd, const char *name, | |||
1174 | out: | 1174 | out: |
1175 | if (unlikely(!retval && !audit_dummy_context() && nd->dentry && | 1175 | if (unlikely(!retval && !audit_dummy_context() && nd->dentry && |
1176 | nd->dentry->d_inode)) | 1176 | nd->dentry->d_inode)) |
1177 | audit_inode(name, nd->dentry->d_inode); | 1177 | audit_inode(name, nd->dentry); |
1178 | out_fail: | 1178 | out_fail: |
1179 | return retval; | 1179 | return retval; |
1180 | 1180 | ||
@@ -1214,7 +1214,7 @@ int vfs_path_lookup(struct dentry *dentry, struct vfsmount *mnt, | |||
1214 | retval = path_walk(name, nd); | 1214 | retval = path_walk(name, nd); |
1215 | if (unlikely(!retval && !audit_dummy_context() && nd->dentry && | 1215 | if (unlikely(!retval && !audit_dummy_context() && nd->dentry && |
1216 | nd->dentry->d_inode)) | 1216 | nd->dentry->d_inode)) |
1217 | audit_inode(name, nd->dentry->d_inode); | 1217 | audit_inode(name, nd->dentry); |
1218 | 1218 | ||
1219 | return retval; | 1219 | return retval; |
1220 | 1220 | ||
@@ -1469,7 +1469,7 @@ static int may_delete(struct inode *dir,struct dentry *victim,int isdir) | |||
1469 | return -ENOENT; | 1469 | return -ENOENT; |
1470 | 1470 | ||
1471 | BUG_ON(victim->d_parent->d_inode != dir); | 1471 | BUG_ON(victim->d_parent->d_inode != dir); |
1472 | audit_inode_child(victim->d_name.name, victim->d_inode, dir); | 1472 | audit_inode_child(victim->d_name.name, victim, dir); |
1473 | 1473 | ||
1474 | error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); | 1474 | error = permission(dir,MAY_WRITE | MAY_EXEC, NULL); |
1475 | if (error) | 1475 | if (error) |
@@ -1783,7 +1783,7 @@ do_last: | |||
1783 | * It already exists. | 1783 | * It already exists. |
1784 | */ | 1784 | */ |
1785 | mutex_unlock(&dir->d_inode->i_mutex); | 1785 | mutex_unlock(&dir->d_inode->i_mutex); |
1786 | audit_inode(pathname, path.dentry->d_inode); | 1786 | audit_inode(pathname, path.dentry); |
1787 | 1787 | ||
1788 | error = -EEXIST; | 1788 | error = -EEXIST; |
1789 | if (flag & O_EXCL) | 1789 | if (flag & O_EXCL) |
@@ -2562,7 +2562,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
2562 | if (!error) { | 2562 | if (!error) { |
2563 | const char *new_name = old_dentry->d_name.name; | 2563 | const char *new_name = old_dentry->d_name.name; |
2564 | fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir, | 2564 | fsnotify_move(old_dir, new_dir, old_name, new_name, is_dir, |
2565 | new_dentry->d_inode, old_dentry->d_inode); | 2565 | new_dentry->d_inode, old_dentry); |
2566 | } | 2566 | } |
2567 | fsnotify_oldname_free(old_name); | 2567 | fsnotify_oldname_free(old_name); |
2568 | 2568 | ||
diff --git a/fs/namespace.c b/fs/namespace.c index 860752998fb3..06083885b21e 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -246,7 +246,7 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
246 | list_add(&mnt->mnt_slave, &old->mnt_slave_list); | 246 | list_add(&mnt->mnt_slave, &old->mnt_slave_list); |
247 | mnt->mnt_master = old; | 247 | mnt->mnt_master = old; |
248 | CLEAR_MNT_SHARED(mnt); | 248 | CLEAR_MNT_SHARED(mnt); |
249 | } else { | 249 | } else if (!(flag & CL_PRIVATE)) { |
250 | if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) | 250 | if ((flag & CL_PROPAGATION) || IS_MNT_SHARED(old)) |
251 | list_add(&mnt->mnt_share, &old->mnt_share); | 251 | list_add(&mnt->mnt_share, &old->mnt_share); |
252 | if (IS_MNT_SLAVE(old)) | 252 | if (IS_MNT_SLAVE(old)) |
@@ -746,6 +746,26 @@ Enomem: | |||
746 | return NULL; | 746 | return NULL; |
747 | } | 747 | } |
748 | 748 | ||
749 | struct vfsmount *collect_mounts(struct vfsmount *mnt, struct dentry *dentry) | ||
750 | { | ||
751 | struct vfsmount *tree; | ||
752 | down_read(&namespace_sem); | ||
753 | tree = copy_tree(mnt, dentry, CL_COPY_ALL | CL_PRIVATE); | ||
754 | up_read(&namespace_sem); | ||
755 | return tree; | ||
756 | } | ||
757 | |||
758 | void drop_collected_mounts(struct vfsmount *mnt) | ||
759 | { | ||
760 | LIST_HEAD(umount_list); | ||
761 | down_read(&namespace_sem); | ||
762 | spin_lock(&vfsmount_lock); | ||
763 | umount_tree(mnt, 0, &umount_list); | ||
764 | spin_unlock(&vfsmount_lock); | ||
765 | up_read(&namespace_sem); | ||
766 | release_mounts(&umount_list); | ||
767 | } | ||
768 | |||
749 | /* | 769 | /* |
750 | * @source_mnt : mount tree to be attached | 770 | * @source_mnt : mount tree to be attached |
751 | * @nd : place the mount tree @source_mnt is attached | 771 | * @nd : place the mount tree @source_mnt is attached |
@@ -569,7 +569,7 @@ asmlinkage long sys_fchmod(unsigned int fd, mode_t mode) | |||
569 | dentry = file->f_path.dentry; | 569 | dentry = file->f_path.dentry; |
570 | inode = dentry->d_inode; | 570 | inode = dentry->d_inode; |
571 | 571 | ||
572 | audit_inode(NULL, inode); | 572 | audit_inode(NULL, dentry); |
573 | 573 | ||
574 | err = -EROFS; | 574 | err = -EROFS; |
575 | if (IS_RDONLY(inode)) | 575 | if (IS_RDONLY(inode)) |
@@ -727,7 +727,7 @@ asmlinkage long sys_fchown(unsigned int fd, uid_t user, gid_t group) | |||
727 | goto out; | 727 | goto out; |
728 | 728 | ||
729 | dentry = file->f_path.dentry; | 729 | dentry = file->f_path.dentry; |
730 | audit_inode(NULL, dentry->d_inode); | 730 | audit_inode(NULL, dentry); |
731 | error = chown_common(dentry, user, group); | 731 | error = chown_common(dentry, user, group); |
732 | fput(file); | 732 | fput(file); |
733 | out: | 733 | out: |
diff --git a/fs/pnode.h b/fs/pnode.h index d45bd8ec36bf..f249be2fee7a 100644 --- a/fs/pnode.h +++ b/fs/pnode.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #define CL_COPY_ALL 0x04 | 22 | #define CL_COPY_ALL 0x04 |
23 | #define CL_MAKE_SHARED 0x08 | 23 | #define CL_MAKE_SHARED 0x08 |
24 | #define CL_PROPAGATION 0x10 | 24 | #define CL_PROPAGATION 0x10 |
25 | #define CL_PRIVATE 0x20 | ||
25 | 26 | ||
26 | static inline void set_mnt_shared(struct vfsmount *mnt) | 27 | static inline void set_mnt_shared(struct vfsmount *mnt) |
27 | { | 28 | { |
diff --git a/fs/xattr.c b/fs/xattr.c index a44fd92caca3..6645b7313b33 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
@@ -267,7 +267,7 @@ sys_fsetxattr(int fd, char __user *name, void __user *value, | |||
267 | if (!f) | 267 | if (!f) |
268 | return error; | 268 | return error; |
269 | dentry = f->f_path.dentry; | 269 | dentry = f->f_path.dentry; |
270 | audit_inode(NULL, dentry->d_inode); | 270 | audit_inode(NULL, dentry); |
271 | error = setxattr(dentry, name, value, size, flags); | 271 | error = setxattr(dentry, name, value, size, flags); |
272 | fput(f); | 272 | fput(f); |
273 | return error; | 273 | return error; |
@@ -349,7 +349,7 @@ sys_fgetxattr(int fd, char __user *name, void __user *value, size_t size) | |||
349 | f = fget(fd); | 349 | f = fget(fd); |
350 | if (!f) | 350 | if (!f) |
351 | return error; | 351 | return error; |
352 | audit_inode(NULL, f->f_path.dentry->d_inode); | 352 | audit_inode(NULL, f->f_path.dentry); |
353 | error = getxattr(f->f_path.dentry, name, value, size); | 353 | error = getxattr(f->f_path.dentry, name, value, size); |
354 | fput(f); | 354 | fput(f); |
355 | return error; | 355 | return error; |
@@ -422,7 +422,7 @@ sys_flistxattr(int fd, char __user *list, size_t size) | |||
422 | f = fget(fd); | 422 | f = fget(fd); |
423 | if (!f) | 423 | if (!f) |
424 | return error; | 424 | return error; |
425 | audit_inode(NULL, f->f_path.dentry->d_inode); | 425 | audit_inode(NULL, f->f_path.dentry); |
426 | error = listxattr(f->f_path.dentry, list, size); | 426 | error = listxattr(f->f_path.dentry, list, size); |
427 | fput(f); | 427 | fput(f); |
428 | return error; | 428 | return error; |
@@ -485,7 +485,7 @@ sys_fremovexattr(int fd, char __user *name) | |||
485 | if (!f) | 485 | if (!f) |
486 | return error; | 486 | return error; |
487 | dentry = f->f_path.dentry; | 487 | dentry = f->f_path.dentry; |
488 | audit_inode(NULL, dentry->d_inode); | 488 | audit_inode(NULL, dentry); |
489 | error = removexattr(dentry, name); | 489 | error = removexattr(dentry, name); |
490 | fput(f); | 490 | fput(f); |
491 | return error; | 491 | return error; |
diff --git a/include/linux/audit.h b/include/linux/audit.h index 9ae740936a65..c68781692838 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h | |||
@@ -63,6 +63,8 @@ | |||
63 | #define AUDIT_ADD_RULE 1011 /* Add syscall filtering rule */ | 63 | #define AUDIT_ADD_RULE 1011 /* Add syscall filtering rule */ |
64 | #define AUDIT_DEL_RULE 1012 /* Delete syscall filtering rule */ | 64 | #define AUDIT_DEL_RULE 1012 /* Delete syscall filtering rule */ |
65 | #define AUDIT_LIST_RULES 1013 /* List syscall filtering rules */ | 65 | #define AUDIT_LIST_RULES 1013 /* List syscall filtering rules */ |
66 | #define AUDIT_TRIM 1014 /* Trim junk from watched tree */ | ||
67 | #define AUDIT_MAKE_EQUIV 1015 /* Append to watched tree */ | ||
66 | #define AUDIT_TTY_GET 1016 /* Get TTY auditing status */ | 68 | #define AUDIT_TTY_GET 1016 /* Get TTY auditing status */ |
67 | #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */ | 69 | #define AUDIT_TTY_SET 1017 /* Set TTY auditing status */ |
68 | 70 | ||
@@ -203,6 +205,7 @@ | |||
203 | #define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */ | 205 | #define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */ |
204 | #define AUDIT_WATCH 105 | 206 | #define AUDIT_WATCH 105 |
205 | #define AUDIT_PERM 106 | 207 | #define AUDIT_PERM 106 |
208 | #define AUDIT_DIR 107 | ||
206 | 209 | ||
207 | #define AUDIT_ARG0 200 | 210 | #define AUDIT_ARG0 200 |
208 | #define AUDIT_ARG1 (AUDIT_ARG0+1) | 211 | #define AUDIT_ARG1 (AUDIT_ARG0+1) |
@@ -366,8 +369,8 @@ extern void audit_syscall_entry(int arch, | |||
366 | extern void audit_syscall_exit(int failed, long return_code); | 369 | extern void audit_syscall_exit(int failed, long return_code); |
367 | extern void __audit_getname(const char *name); | 370 | extern void __audit_getname(const char *name); |
368 | extern void audit_putname(const char *name); | 371 | extern void audit_putname(const char *name); |
369 | extern void __audit_inode(const char *name, const struct inode *inode); | 372 | extern void __audit_inode(const char *name, const struct dentry *dentry); |
370 | extern void __audit_inode_child(const char *dname, const struct inode *inode, | 373 | extern void __audit_inode_child(const char *dname, const struct dentry *dentry, |
371 | const struct inode *parent); | 374 | const struct inode *parent); |
372 | extern void __audit_ptrace(struct task_struct *t); | 375 | extern void __audit_ptrace(struct task_struct *t); |
373 | 376 | ||
@@ -381,15 +384,15 @@ static inline void audit_getname(const char *name) | |||
381 | if (unlikely(!audit_dummy_context())) | 384 | if (unlikely(!audit_dummy_context())) |
382 | __audit_getname(name); | 385 | __audit_getname(name); |
383 | } | 386 | } |
384 | static inline void audit_inode(const char *name, const struct inode *inode) { | 387 | static inline void audit_inode(const char *name, const struct dentry *dentry) { |
385 | if (unlikely(!audit_dummy_context())) | 388 | if (unlikely(!audit_dummy_context())) |
386 | __audit_inode(name, inode); | 389 | __audit_inode(name, dentry); |
387 | } | 390 | } |
388 | static inline void audit_inode_child(const char *dname, | 391 | static inline void audit_inode_child(const char *dname, |
389 | const struct inode *inode, | 392 | const struct dentry *dentry, |
390 | const struct inode *parent) { | 393 | const struct inode *parent) { |
391 | if (unlikely(!audit_dummy_context())) | 394 | if (unlikely(!audit_dummy_context())) |
392 | __audit_inode_child(dname, inode, parent); | 395 | __audit_inode_child(dname, dentry, parent); |
393 | } | 396 | } |
394 | void audit_core_dumps(long signr); | 397 | void audit_core_dumps(long signr); |
395 | 398 | ||
@@ -477,9 +480,9 @@ extern int audit_signals; | |||
477 | #define audit_dummy_context() 1 | 480 | #define audit_dummy_context() 1 |
478 | #define audit_getname(n) do { ; } while (0) | 481 | #define audit_getname(n) do { ; } while (0) |
479 | #define audit_putname(n) do { ; } while (0) | 482 | #define audit_putname(n) do { ; } while (0) |
480 | #define __audit_inode(n,i) do { ; } while (0) | 483 | #define __audit_inode(n,d) do { ; } while (0) |
481 | #define __audit_inode_child(d,i,p) do { ; } while (0) | 484 | #define __audit_inode_child(d,i,p) do { ; } while (0) |
482 | #define audit_inode(n,i) do { ; } while (0) | 485 | #define audit_inode(n,d) do { ; } while (0) |
483 | #define audit_inode_child(d,i,p) do { ; } while (0) | 486 | #define audit_inode_child(d,i,p) do { ; } while (0) |
484 | #define audit_core_dumps(i) do { ; } while (0) | 487 | #define audit_core_dumps(i) do { ; } while (0) |
485 | #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) | 488 | #define auditsc_get_stamp(c,t,s) do { BUG(); } while (0) |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index aab53df4fafa..c2c153f97e8f 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -178,6 +178,7 @@ d_iput: no no no yes | |||
178 | #define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */ | 178 | #define DCACHE_INOTIFY_PARENT_WATCHED 0x0020 /* Parent inode is watched */ |
179 | 179 | ||
180 | extern spinlock_t dcache_lock; | 180 | extern spinlock_t dcache_lock; |
181 | extern seqlock_t rename_lock; | ||
181 | 182 | ||
182 | /** | 183 | /** |
183 | * d_drop - drop a dentry | 184 | * d_drop - drop a dentry |
diff --git a/include/linux/fs.h b/include/linux/fs.h index 1bcce660cf03..50078bb30a1c 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1470,6 +1470,8 @@ extern long do_mount(char *, char *, char *, unsigned long, void *); | |||
1470 | extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); | 1470 | extern struct vfsmount *copy_tree(struct vfsmount *, struct dentry *, int); |
1471 | extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, | 1471 | extern void mnt_set_mountpoint(struct vfsmount *, struct dentry *, |
1472 | struct vfsmount *); | 1472 | struct vfsmount *); |
1473 | extern struct vfsmount *collect_mounts(struct vfsmount *, struct dentry *); | ||
1474 | extern void drop_collected_mounts(struct vfsmount *); | ||
1473 | 1475 | ||
1474 | extern int vfs_statfs(struct dentry *, struct kstatfs *); | 1476 | extern int vfs_statfs(struct dentry *, struct kstatfs *); |
1475 | 1477 | ||
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index dfc4e4f68da4..2bd31fa623b6 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h | |||
@@ -41,8 +41,9 @@ static inline void fsnotify_d_move(struct dentry *entry) | |||
41 | */ | 41 | */ |
42 | static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, | 42 | static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, |
43 | const char *old_name, const char *new_name, | 43 | const char *old_name, const char *new_name, |
44 | int isdir, struct inode *target, struct inode *source) | 44 | int isdir, struct inode *target, struct dentry *moved) |
45 | { | 45 | { |
46 | struct inode *source = moved->d_inode; | ||
46 | u32 cookie = inotify_get_cookie(); | 47 | u32 cookie = inotify_get_cookie(); |
47 | 48 | ||
48 | if (old_dir == new_dir) | 49 | if (old_dir == new_dir) |
@@ -67,7 +68,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, | |||
67 | if (source) { | 68 | if (source) { |
68 | inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL); | 69 | inotify_inode_queue_event(source, IN_MOVE_SELF, 0, NULL, NULL); |
69 | } | 70 | } |
70 | audit_inode_child(new_name, source, new_dir); | 71 | audit_inode_child(new_name, moved, new_dir); |
71 | } | 72 | } |
72 | 73 | ||
73 | /* | 74 | /* |
@@ -98,7 +99,7 @@ static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) | |||
98 | inode_dir_notify(inode, DN_CREATE); | 99 | inode_dir_notify(inode, DN_CREATE); |
99 | inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name, | 100 | inotify_inode_queue_event(inode, IN_CREATE, 0, dentry->d_name.name, |
100 | dentry->d_inode); | 101 | dentry->d_inode); |
101 | audit_inode_child(dentry->d_name.name, dentry->d_inode, inode); | 102 | audit_inode_child(dentry->d_name.name, dentry, inode); |
102 | } | 103 | } |
103 | 104 | ||
104 | /* | 105 | /* |
@@ -109,7 +110,7 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) | |||
109 | inode_dir_notify(inode, DN_CREATE); | 110 | inode_dir_notify(inode, DN_CREATE); |
110 | inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, | 111 | inotify_inode_queue_event(inode, IN_CREATE | IN_ISDIR, 0, |
111 | dentry->d_name.name, dentry->d_inode); | 112 | dentry->d_name.name, dentry->d_inode); |
112 | audit_inode_child(dentry->d_name.name, dentry->d_inode, inode); | 113 | audit_inode_child(dentry->d_name.name, dentry, inode); |
113 | } | 114 | } |
114 | 115 | ||
115 | /* | 116 | /* |
diff --git a/include/linux/inotify.h b/include/linux/inotify.h index d4f48c6402e6..742b917e7d1b 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h | |||
@@ -120,6 +120,8 @@ extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *, | |||
120 | u32); | 120 | u32); |
121 | extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *, | 121 | extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *, |
122 | struct inode *, __u32); | 122 | struct inode *, __u32); |
123 | extern __s32 inotify_clone_watch(struct inotify_watch *, struct inotify_watch *); | ||
124 | extern void inotify_evict_watch(struct inotify_watch *); | ||
123 | extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *); | 125 | extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *); |
124 | extern int inotify_rm_wd(struct inotify_handle *, __u32); | 126 | extern int inotify_rm_wd(struct inotify_handle *, __u32); |
125 | extern void inotify_remove_watch_locked(struct inotify_handle *, | 127 | extern void inotify_remove_watch_locked(struct inotify_handle *, |
diff --git a/init/Kconfig b/init/Kconfig index 541382d539ad..b7dffa837926 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
@@ -234,6 +234,10 @@ config AUDITSYSCALL | |||
234 | such as SELinux. To use audit's filesystem watch feature, please | 234 | such as SELinux. To use audit's filesystem watch feature, please |
235 | ensure that INOTIFY is configured. | 235 | ensure that INOTIFY is configured. |
236 | 236 | ||
237 | config AUDIT_TREE | ||
238 | def_bool y | ||
239 | depends on AUDITSYSCALL && INOTIFY | ||
240 | |||
237 | config IKCONFIG | 241 | config IKCONFIG |
238 | tristate "Kernel .config support" | 242 | tristate "Kernel .config support" |
239 | ---help--- | 243 | ---help--- |
diff --git a/ipc/mqueue.c b/ipc/mqueue.c index c0b26dc4617b..bfa274ba9ed4 100644 --- a/ipc/mqueue.c +++ b/ipc/mqueue.c | |||
@@ -676,7 +676,7 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, | |||
676 | 676 | ||
677 | if (oflag & O_CREAT) { | 677 | if (oflag & O_CREAT) { |
678 | if (dentry->d_inode) { /* entry already exists */ | 678 | if (dentry->d_inode) { /* entry already exists */ |
679 | audit_inode(name, dentry->d_inode); | 679 | audit_inode(name, dentry); |
680 | error = -EEXIST; | 680 | error = -EEXIST; |
681 | if (oflag & O_EXCL) | 681 | if (oflag & O_EXCL) |
682 | goto out; | 682 | goto out; |
@@ -689,7 +689,7 @@ asmlinkage long sys_mq_open(const char __user *u_name, int oflag, mode_t mode, | |||
689 | error = -ENOENT; | 689 | error = -ENOENT; |
690 | if (!dentry->d_inode) | 690 | if (!dentry->d_inode) |
691 | goto out; | 691 | goto out; |
692 | audit_inode(name, dentry->d_inode); | 692 | audit_inode(name, dentry); |
693 | filp = do_open(dentry, oflag); | 693 | filp = do_open(dentry, oflag); |
694 | } | 694 | } |
695 | 695 | ||
@@ -837,7 +837,7 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr, | |||
837 | if (unlikely(filp->f_op != &mqueue_file_operations)) | 837 | if (unlikely(filp->f_op != &mqueue_file_operations)) |
838 | goto out_fput; | 838 | goto out_fput; |
839 | info = MQUEUE_I(inode); | 839 | info = MQUEUE_I(inode); |
840 | audit_inode(NULL, inode); | 840 | audit_inode(NULL, filp->f_path.dentry); |
841 | 841 | ||
842 | if (unlikely(!(filp->f_mode & FMODE_WRITE))) | 842 | if (unlikely(!(filp->f_mode & FMODE_WRITE))) |
843 | goto out_fput; | 843 | goto out_fput; |
@@ -921,7 +921,7 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr, | |||
921 | if (unlikely(filp->f_op != &mqueue_file_operations)) | 921 | if (unlikely(filp->f_op != &mqueue_file_operations)) |
922 | goto out_fput; | 922 | goto out_fput; |
923 | info = MQUEUE_I(inode); | 923 | info = MQUEUE_I(inode); |
924 | audit_inode(NULL, inode); | 924 | audit_inode(NULL, filp->f_path.dentry); |
925 | 925 | ||
926 | if (unlikely(!(filp->f_mode & FMODE_READ))) | 926 | if (unlikely(!(filp->f_mode & FMODE_READ))) |
927 | goto out_fput; | 927 | goto out_fput; |
diff --git a/kernel/Makefile b/kernel/Makefile index 79f017e09fbd..f60afe742599 100644 --- a/kernel/Makefile +++ b/kernel/Makefile | |||
@@ -46,6 +46,7 @@ obj-$(CONFIG_IKCONFIG) += configs.o | |||
46 | obj-$(CONFIG_STOP_MACHINE) += stop_machine.o | 46 | obj-$(CONFIG_STOP_MACHINE) += stop_machine.o |
47 | obj-$(CONFIG_AUDIT) += audit.o auditfilter.o | 47 | obj-$(CONFIG_AUDIT) += audit.o auditfilter.o |
48 | obj-$(CONFIG_AUDITSYSCALL) += auditsc.o | 48 | obj-$(CONFIG_AUDITSYSCALL) += auditsc.o |
49 | obj-$(CONFIG_AUDIT_TREE) += audit_tree.o | ||
49 | obj-$(CONFIG_KPROBES) += kprobes.o | 50 | obj-$(CONFIG_KPROBES) += kprobes.o |
50 | obj-$(CONFIG_SYSFS) += ksysfs.o | 51 | obj-$(CONFIG_SYSFS) += ksysfs.o |
51 | obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o | 52 | obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o |
diff --git a/kernel/audit.c b/kernel/audit.c index 6977ea57a7e2..f93c2713017d 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -468,6 +468,21 @@ int audit_send_list(void *_dest) | |||
468 | return 0; | 468 | return 0; |
469 | } | 469 | } |
470 | 470 | ||
471 | #ifdef CONFIG_AUDIT_TREE | ||
472 | static int prune_tree_thread(void *unused) | ||
473 | { | ||
474 | mutex_lock(&audit_cmd_mutex); | ||
475 | audit_prune_trees(); | ||
476 | mutex_unlock(&audit_cmd_mutex); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | void audit_schedule_prune(void) | ||
481 | { | ||
482 | kthread_run(prune_tree_thread, NULL, "audit_prune_tree"); | ||
483 | } | ||
484 | #endif | ||
485 | |||
471 | struct sk_buff *audit_make_reply(int pid, int seq, int type, int done, | 486 | struct sk_buff *audit_make_reply(int pid, int seq, int type, int done, |
472 | int multi, void *payload, int size) | 487 | int multi, void *payload, int size) |
473 | { | 488 | { |
@@ -540,6 +555,8 @@ static int audit_netlink_ok(struct sk_buff *skb, u16 msg_type) | |||
540 | case AUDIT_SIGNAL_INFO: | 555 | case AUDIT_SIGNAL_INFO: |
541 | case AUDIT_TTY_GET: | 556 | case AUDIT_TTY_GET: |
542 | case AUDIT_TTY_SET: | 557 | case AUDIT_TTY_SET: |
558 | case AUDIT_TRIM: | ||
559 | case AUDIT_MAKE_EQUIV: | ||
543 | if (security_netlink_recv(skb, CAP_AUDIT_CONTROL)) | 560 | if (security_netlink_recv(skb, CAP_AUDIT_CONTROL)) |
544 | err = -EPERM; | 561 | err = -EPERM; |
545 | break; | 562 | break; |
@@ -756,6 +773,76 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
756 | uid, seq, data, nlmsg_len(nlh), | 773 | uid, seq, data, nlmsg_len(nlh), |
757 | loginuid, sid); | 774 | loginuid, sid); |
758 | break; | 775 | break; |
776 | case AUDIT_TRIM: | ||
777 | audit_trim_trees(); | ||
778 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); | ||
779 | if (!ab) | ||
780 | break; | ||
781 | audit_log_format(ab, "auid=%u", loginuid); | ||
782 | if (sid) { | ||
783 | u32 len; | ||
784 | ctx = NULL; | ||
785 | if (selinux_sid_to_string(sid, &ctx, &len)) | ||
786 | audit_log_format(ab, " ssid=%u", sid); | ||
787 | else | ||
788 | audit_log_format(ab, " subj=%s", ctx); | ||
789 | kfree(ctx); | ||
790 | } | ||
791 | audit_log_format(ab, " op=trim res=1"); | ||
792 | audit_log_end(ab); | ||
793 | break; | ||
794 | case AUDIT_MAKE_EQUIV: { | ||
795 | void *bufp = data; | ||
796 | u32 sizes[2]; | ||
797 | size_t len = nlmsg_len(nlh); | ||
798 | char *old, *new; | ||
799 | |||
800 | err = -EINVAL; | ||
801 | if (len < 2 * sizeof(u32)) | ||
802 | break; | ||
803 | memcpy(sizes, bufp, 2 * sizeof(u32)); | ||
804 | bufp += 2 * sizeof(u32); | ||
805 | len -= 2 * sizeof(u32); | ||
806 | old = audit_unpack_string(&bufp, &len, sizes[0]); | ||
807 | if (IS_ERR(old)) { | ||
808 | err = PTR_ERR(old); | ||
809 | break; | ||
810 | } | ||
811 | new = audit_unpack_string(&bufp, &len, sizes[1]); | ||
812 | if (IS_ERR(new)) { | ||
813 | err = PTR_ERR(new); | ||
814 | kfree(old); | ||
815 | break; | ||
816 | } | ||
817 | /* OK, here comes... */ | ||
818 | err = audit_tag_tree(old, new); | ||
819 | |||
820 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); | ||
821 | if (!ab) { | ||
822 | kfree(old); | ||
823 | kfree(new); | ||
824 | break; | ||
825 | } | ||
826 | audit_log_format(ab, "auid=%u", loginuid); | ||
827 | if (sid) { | ||
828 | u32 len; | ||
829 | ctx = NULL; | ||
830 | if (selinux_sid_to_string(sid, &ctx, &len)) | ||
831 | audit_log_format(ab, " ssid=%u", sid); | ||
832 | else | ||
833 | audit_log_format(ab, " subj=%s", ctx); | ||
834 | kfree(ctx); | ||
835 | } | ||
836 | audit_log_format(ab, " op=make_equiv old="); | ||
837 | audit_log_untrustedstring(ab, old); | ||
838 | audit_log_format(ab, " new="); | ||
839 | audit_log_untrustedstring(ab, new); | ||
840 | audit_log_format(ab, " res=%d", !err); | ||
841 | audit_log_end(ab); | ||
842 | kfree(old); | ||
843 | kfree(new); | ||
844 | break; | ||
845 | } | ||
759 | case AUDIT_SIGNAL_INFO: | 846 | case AUDIT_SIGNAL_INFO: |
760 | err = selinux_sid_to_string(audit_sig_sid, &ctx, &len); | 847 | err = selinux_sid_to_string(audit_sig_sid, &ctx, &len); |
761 | if (err) | 848 | if (err) |
diff --git a/kernel/audit.h b/kernel/audit.h index 95877435c347..2554bd524fd1 100644 --- a/kernel/audit.h +++ b/kernel/audit.h | |||
@@ -73,6 +73,9 @@ struct audit_field { | |||
73 | struct selinux_audit_rule *se_rule; | 73 | struct selinux_audit_rule *se_rule; |
74 | }; | 74 | }; |
75 | 75 | ||
76 | struct audit_tree; | ||
77 | struct audit_chunk; | ||
78 | |||
76 | struct audit_krule { | 79 | struct audit_krule { |
77 | int vers_ops; | 80 | int vers_ops; |
78 | u32 flags; | 81 | u32 flags; |
@@ -86,7 +89,8 @@ struct audit_krule { | |||
86 | struct audit_field *arch_f; /* quick access to arch field */ | 89 | struct audit_field *arch_f; /* quick access to arch field */ |
87 | struct audit_field *inode_f; /* quick access to an inode field */ | 90 | struct audit_field *inode_f; /* quick access to an inode field */ |
88 | struct audit_watch *watch; /* associated watch */ | 91 | struct audit_watch *watch; /* associated watch */ |
89 | struct list_head rlist; /* entry in audit_watch.rules list */ | 92 | struct audit_tree *tree; /* associated watched tree */ |
93 | struct list_head rlist; /* entry in audit_{watch,tree}.rules list */ | ||
90 | }; | 94 | }; |
91 | 95 | ||
92 | struct audit_entry { | 96 | struct audit_entry { |
@@ -130,6 +134,34 @@ extern void audit_handle_ievent(struct inotify_watch *, u32, u32, u32, | |||
130 | const char *, struct inode *); | 134 | const char *, struct inode *); |
131 | extern int selinux_audit_rule_update(void); | 135 | extern int selinux_audit_rule_update(void); |
132 | 136 | ||
137 | extern struct mutex audit_filter_mutex; | ||
138 | extern void audit_free_rule_rcu(struct rcu_head *); | ||
139 | |||
140 | #ifdef CONFIG_AUDIT_TREE | ||
141 | extern struct audit_chunk *audit_tree_lookup(const struct inode *); | ||
142 | extern void audit_put_chunk(struct audit_chunk *); | ||
143 | extern int audit_tree_match(struct audit_chunk *, struct audit_tree *); | ||
144 | extern int audit_make_tree(struct audit_krule *, char *, u32); | ||
145 | extern int audit_add_tree_rule(struct audit_krule *); | ||
146 | extern int audit_remove_tree_rule(struct audit_krule *); | ||
147 | extern void audit_trim_trees(void); | ||
148 | extern int audit_tag_tree(char *old, char *new); | ||
149 | extern void audit_schedule_prune(void); | ||
150 | extern void audit_prune_trees(void); | ||
151 | extern const char *audit_tree_path(struct audit_tree *); | ||
152 | extern void audit_put_tree(struct audit_tree *); | ||
153 | #else | ||
154 | #define audit_remove_tree_rule(rule) BUG() | ||
155 | #define audit_add_tree_rule(rule) -EINVAL | ||
156 | #define audit_make_tree(rule, str, op) -EINVAL | ||
157 | #define audit_trim_trees() (void)0 | ||
158 | #define audit_put_tree(tree) (void)0 | ||
159 | #define audit_tag_tree(old, new) -EINVAL | ||
160 | #define audit_tree_path(rule) "" /* never called */ | ||
161 | #endif | ||
162 | |||
163 | extern char *audit_unpack_string(void **, size_t *, size_t); | ||
164 | |||
133 | #ifdef CONFIG_AUDITSYSCALL | 165 | #ifdef CONFIG_AUDITSYSCALL |
134 | extern int __audit_signal_info(int sig, struct task_struct *t); | 166 | extern int __audit_signal_info(int sig, struct task_struct *t); |
135 | static inline int audit_signal_info(int sig, struct task_struct *t) | 167 | static inline int audit_signal_info(int sig, struct task_struct *t) |
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c new file mode 100644 index 000000000000..f4fcf58f20f8 --- /dev/null +++ b/kernel/audit_tree.c | |||
@@ -0,0 +1,903 @@ | |||
1 | #include "audit.h" | ||
2 | #include <linux/inotify.h> | ||
3 | #include <linux/namei.h> | ||
4 | #include <linux/mount.h> | ||
5 | |||
6 | struct audit_tree; | ||
7 | struct audit_chunk; | ||
8 | |||
9 | struct audit_tree { | ||
10 | atomic_t count; | ||
11 | int goner; | ||
12 | struct audit_chunk *root; | ||
13 | struct list_head chunks; | ||
14 | struct list_head rules; | ||
15 | struct list_head list; | ||
16 | struct list_head same_root; | ||
17 | struct rcu_head head; | ||
18 | char pathname[]; | ||
19 | }; | ||
20 | |||
21 | struct audit_chunk { | ||
22 | struct list_head hash; | ||
23 | struct inotify_watch watch; | ||
24 | struct list_head trees; /* with root here */ | ||
25 | int dead; | ||
26 | int count; | ||
27 | struct rcu_head head; | ||
28 | struct node { | ||
29 | struct list_head list; | ||
30 | struct audit_tree *owner; | ||
31 | unsigned index; /* index; upper bit indicates 'will prune' */ | ||
32 | } owners[]; | ||
33 | }; | ||
34 | |||
35 | static LIST_HEAD(tree_list); | ||
36 | static LIST_HEAD(prune_list); | ||
37 | |||
38 | /* | ||
39 | * One struct chunk is attached to each inode of interest. | ||
40 | * We replace struct chunk on tagging/untagging. | ||
41 | * Rules have pointer to struct audit_tree. | ||
42 | * Rules have struct list_head rlist forming a list of rules over | ||
43 | * the same tree. | ||
44 | * References to struct chunk are collected at audit_inode{,_child}() | ||
45 | * time and used in AUDIT_TREE rule matching. | ||
46 | * These references are dropped at the same time we are calling | ||
47 | * audit_free_names(), etc. | ||
48 | * | ||
49 | * Cyclic lists galore: | ||
50 | * tree.chunks anchors chunk.owners[].list hash_lock | ||
51 | * tree.rules anchors rule.rlist audit_filter_mutex | ||
52 | * chunk.trees anchors tree.same_root hash_lock | ||
53 | * chunk.hash is a hash with middle bits of watch.inode as | ||
54 | * a hash function. RCU, hash_lock | ||
55 | * | ||
56 | * tree is refcounted; one reference for "some rules on rules_list refer to | ||
57 | * it", one for each chunk with pointer to it. | ||
58 | * | ||
59 | * chunk is refcounted by embedded inotify_watch. | ||
60 | * | ||
61 | * node.index allows to get from node.list to containing chunk. | ||
62 | * MSB of that sucker is stolen to mark taggings that we might have to | ||
63 | * revert - several operations have very unpleasant cleanup logics and | ||
64 | * that makes a difference. Some. | ||
65 | */ | ||
66 | |||
67 | static struct inotify_handle *rtree_ih; | ||
68 | |||
69 | static struct audit_tree *alloc_tree(const char *s) | ||
70 | { | ||
71 | struct audit_tree *tree; | ||
72 | |||
73 | tree = kmalloc(sizeof(struct audit_tree) + strlen(s) + 1, GFP_KERNEL); | ||
74 | if (tree) { | ||
75 | atomic_set(&tree->count, 1); | ||
76 | tree->goner = 0; | ||
77 | INIT_LIST_HEAD(&tree->chunks); | ||
78 | INIT_LIST_HEAD(&tree->rules); | ||
79 | INIT_LIST_HEAD(&tree->list); | ||
80 | INIT_LIST_HEAD(&tree->same_root); | ||
81 | tree->root = NULL; | ||
82 | strcpy(tree->pathname, s); | ||
83 | } | ||
84 | return tree; | ||
85 | } | ||
86 | |||
87 | static inline void get_tree(struct audit_tree *tree) | ||
88 | { | ||
89 | atomic_inc(&tree->count); | ||
90 | } | ||
91 | |||
92 | static void __put_tree(struct rcu_head *rcu) | ||
93 | { | ||
94 | struct audit_tree *tree = container_of(rcu, struct audit_tree, head); | ||
95 | kfree(tree); | ||
96 | } | ||
97 | |||
98 | static inline void put_tree(struct audit_tree *tree) | ||
99 | { | ||
100 | if (atomic_dec_and_test(&tree->count)) | ||
101 | call_rcu(&tree->head, __put_tree); | ||
102 | } | ||
103 | |||
104 | /* to avoid bringing the entire thing in audit.h */ | ||
105 | const char *audit_tree_path(struct audit_tree *tree) | ||
106 | { | ||
107 | return tree->pathname; | ||
108 | } | ||
109 | |||
110 | static struct audit_chunk *alloc_chunk(int count) | ||
111 | { | ||
112 | struct audit_chunk *chunk; | ||
113 | size_t size; | ||
114 | int i; | ||
115 | |||
116 | size = offsetof(struct audit_chunk, owners) + count * sizeof(struct node); | ||
117 | chunk = kzalloc(size, GFP_KERNEL); | ||
118 | if (!chunk) | ||
119 | return NULL; | ||
120 | |||
121 | INIT_LIST_HEAD(&chunk->hash); | ||
122 | INIT_LIST_HEAD(&chunk->trees); | ||
123 | chunk->count = count; | ||
124 | for (i = 0; i < count; i++) { | ||
125 | INIT_LIST_HEAD(&chunk->owners[i].list); | ||
126 | chunk->owners[i].index = i; | ||
127 | } | ||
128 | inotify_init_watch(&chunk->watch); | ||
129 | return chunk; | ||
130 | } | ||
131 | |||
132 | static void __free_chunk(struct rcu_head *rcu) | ||
133 | { | ||
134 | struct audit_chunk *chunk = container_of(rcu, struct audit_chunk, head); | ||
135 | int i; | ||
136 | |||
137 | for (i = 0; i < chunk->count; i++) { | ||
138 | if (chunk->owners[i].owner) | ||
139 | put_tree(chunk->owners[i].owner); | ||
140 | } | ||
141 | kfree(chunk); | ||
142 | } | ||
143 | |||
144 | static inline void free_chunk(struct audit_chunk *chunk) | ||
145 | { | ||
146 | call_rcu(&chunk->head, __free_chunk); | ||
147 | } | ||
148 | |||
149 | void audit_put_chunk(struct audit_chunk *chunk) | ||
150 | { | ||
151 | put_inotify_watch(&chunk->watch); | ||
152 | } | ||
153 | |||
154 | enum {HASH_SIZE = 128}; | ||
155 | static struct list_head chunk_hash_heads[HASH_SIZE]; | ||
156 | static __cacheline_aligned_in_smp DEFINE_SPINLOCK(hash_lock); | ||
157 | |||
158 | static inline struct list_head *chunk_hash(const struct inode *inode) | ||
159 | { | ||
160 | unsigned long n = (unsigned long)inode / L1_CACHE_BYTES; | ||
161 | return chunk_hash_heads + n % HASH_SIZE; | ||
162 | } | ||
163 | |||
164 | /* hash_lock is held by caller */ | ||
165 | static void insert_hash(struct audit_chunk *chunk) | ||
166 | { | ||
167 | struct list_head *list = chunk_hash(chunk->watch.inode); | ||
168 | list_add_rcu(&chunk->hash, list); | ||
169 | } | ||
170 | |||
171 | /* called under rcu_read_lock */ | ||
172 | struct audit_chunk *audit_tree_lookup(const struct inode *inode) | ||
173 | { | ||
174 | struct list_head *list = chunk_hash(inode); | ||
175 | struct list_head *pos; | ||
176 | |||
177 | list_for_each_rcu(pos, list) { | ||
178 | struct audit_chunk *p = container_of(pos, struct audit_chunk, hash); | ||
179 | if (p->watch.inode == inode) { | ||
180 | get_inotify_watch(&p->watch); | ||
181 | return p; | ||
182 | } | ||
183 | } | ||
184 | return NULL; | ||
185 | } | ||
186 | |||
187 | int audit_tree_match(struct audit_chunk *chunk, struct audit_tree *tree) | ||
188 | { | ||
189 | int n; | ||
190 | for (n = 0; n < chunk->count; n++) | ||
191 | if (chunk->owners[n].owner == tree) | ||
192 | return 1; | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | /* tagging and untagging inodes with trees */ | ||
197 | |||
198 | static void untag_chunk(struct audit_chunk *chunk, struct node *p) | ||
199 | { | ||
200 | struct audit_chunk *new; | ||
201 | struct audit_tree *owner; | ||
202 | int size = chunk->count - 1; | ||
203 | int i, j; | ||
204 | |||
205 | mutex_lock(&chunk->watch.inode->inotify_mutex); | ||
206 | if (chunk->dead) { | ||
207 | mutex_unlock(&chunk->watch.inode->inotify_mutex); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | owner = p->owner; | ||
212 | |||
213 | if (!size) { | ||
214 | chunk->dead = 1; | ||
215 | spin_lock(&hash_lock); | ||
216 | list_del_init(&chunk->trees); | ||
217 | if (owner->root == chunk) | ||
218 | owner->root = NULL; | ||
219 | list_del_init(&p->list); | ||
220 | list_del_rcu(&chunk->hash); | ||
221 | spin_unlock(&hash_lock); | ||
222 | inotify_evict_watch(&chunk->watch); | ||
223 | mutex_unlock(&chunk->watch.inode->inotify_mutex); | ||
224 | put_inotify_watch(&chunk->watch); | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | new = alloc_chunk(size); | ||
229 | if (!new) | ||
230 | goto Fallback; | ||
231 | if (inotify_clone_watch(&chunk->watch, &new->watch) < 0) { | ||
232 | free_chunk(new); | ||
233 | goto Fallback; | ||
234 | } | ||
235 | |||
236 | chunk->dead = 1; | ||
237 | spin_lock(&hash_lock); | ||
238 | list_replace_init(&chunk->trees, &new->trees); | ||
239 | if (owner->root == chunk) { | ||
240 | list_del_init(&owner->same_root); | ||
241 | owner->root = NULL; | ||
242 | } | ||
243 | |||
244 | for (i = j = 0; i < size; i++, j++) { | ||
245 | struct audit_tree *s; | ||
246 | if (&chunk->owners[j] == p) { | ||
247 | list_del_init(&p->list); | ||
248 | i--; | ||
249 | continue; | ||
250 | } | ||
251 | s = chunk->owners[j].owner; | ||
252 | new->owners[i].owner = s; | ||
253 | new->owners[i].index = chunk->owners[j].index - j + i; | ||
254 | if (!s) /* result of earlier fallback */ | ||
255 | continue; | ||
256 | get_tree(s); | ||
257 | list_replace_init(&chunk->owners[i].list, &new->owners[j].list); | ||
258 | } | ||
259 | |||
260 | list_replace_rcu(&chunk->hash, &new->hash); | ||
261 | list_for_each_entry(owner, &new->trees, same_root) | ||
262 | owner->root = new; | ||
263 | spin_unlock(&hash_lock); | ||
264 | inotify_evict_watch(&chunk->watch); | ||
265 | mutex_unlock(&chunk->watch.inode->inotify_mutex); | ||
266 | put_inotify_watch(&chunk->watch); | ||
267 | return; | ||
268 | |||
269 | Fallback: | ||
270 | // do the best we can | ||
271 | spin_lock(&hash_lock); | ||
272 | if (owner->root == chunk) { | ||
273 | list_del_init(&owner->same_root); | ||
274 | owner->root = NULL; | ||
275 | } | ||
276 | list_del_init(&p->list); | ||
277 | p->owner = NULL; | ||
278 | put_tree(owner); | ||
279 | spin_unlock(&hash_lock); | ||
280 | mutex_unlock(&chunk->watch.inode->inotify_mutex); | ||
281 | } | ||
282 | |||
283 | static int create_chunk(struct inode *inode, struct audit_tree *tree) | ||
284 | { | ||
285 | struct audit_chunk *chunk = alloc_chunk(1); | ||
286 | if (!chunk) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | if (inotify_add_watch(rtree_ih, &chunk->watch, inode, IN_IGNORED | IN_DELETE_SELF) < 0) { | ||
290 | free_chunk(chunk); | ||
291 | return -ENOSPC; | ||
292 | } | ||
293 | |||
294 | mutex_lock(&inode->inotify_mutex); | ||
295 | spin_lock(&hash_lock); | ||
296 | if (tree->goner) { | ||
297 | spin_unlock(&hash_lock); | ||
298 | chunk->dead = 1; | ||
299 | inotify_evict_watch(&chunk->watch); | ||
300 | mutex_unlock(&inode->inotify_mutex); | ||
301 | put_inotify_watch(&chunk->watch); | ||
302 | return 0; | ||
303 | } | ||
304 | chunk->owners[0].index = (1U << 31); | ||
305 | chunk->owners[0].owner = tree; | ||
306 | get_tree(tree); | ||
307 | list_add(&chunk->owners[0].list, &tree->chunks); | ||
308 | if (!tree->root) { | ||
309 | tree->root = chunk; | ||
310 | list_add(&tree->same_root, &chunk->trees); | ||
311 | } | ||
312 | insert_hash(chunk); | ||
313 | spin_unlock(&hash_lock); | ||
314 | mutex_unlock(&inode->inotify_mutex); | ||
315 | return 0; | ||
316 | } | ||
317 | |||
318 | /* the first tagged inode becomes root of tree */ | ||
319 | static int tag_chunk(struct inode *inode, struct audit_tree *tree) | ||
320 | { | ||
321 | struct inotify_watch *watch; | ||
322 | struct audit_tree *owner; | ||
323 | struct audit_chunk *chunk, *old; | ||
324 | struct node *p; | ||
325 | int n; | ||
326 | |||
327 | if (inotify_find_watch(rtree_ih, inode, &watch) < 0) | ||
328 | return create_chunk(inode, tree); | ||
329 | |||
330 | old = container_of(watch, struct audit_chunk, watch); | ||
331 | |||
332 | /* are we already there? */ | ||
333 | spin_lock(&hash_lock); | ||
334 | for (n = 0; n < old->count; n++) { | ||
335 | if (old->owners[n].owner == tree) { | ||
336 | spin_unlock(&hash_lock); | ||
337 | put_inotify_watch(watch); | ||
338 | return 0; | ||
339 | } | ||
340 | } | ||
341 | spin_unlock(&hash_lock); | ||
342 | |||
343 | chunk = alloc_chunk(old->count + 1); | ||
344 | if (!chunk) | ||
345 | return -ENOMEM; | ||
346 | |||
347 | mutex_lock(&inode->inotify_mutex); | ||
348 | if (inotify_clone_watch(&old->watch, &chunk->watch) < 0) { | ||
349 | mutex_unlock(&inode->inotify_mutex); | ||
350 | free_chunk(chunk); | ||
351 | return -ENOSPC; | ||
352 | } | ||
353 | spin_lock(&hash_lock); | ||
354 | if (tree->goner) { | ||
355 | spin_unlock(&hash_lock); | ||
356 | chunk->dead = 1; | ||
357 | inotify_evict_watch(&chunk->watch); | ||
358 | mutex_unlock(&inode->inotify_mutex); | ||
359 | put_inotify_watch(&chunk->watch); | ||
360 | return 0; | ||
361 | } | ||
362 | list_replace_init(&old->trees, &chunk->trees); | ||
363 | for (n = 0, p = chunk->owners; n < old->count; n++, p++) { | ||
364 | struct audit_tree *s = old->owners[n].owner; | ||
365 | p->owner = s; | ||
366 | p->index = old->owners[n].index; | ||
367 | if (!s) /* result of fallback in untag */ | ||
368 | continue; | ||
369 | get_tree(s); | ||
370 | list_replace_init(&old->owners[n].list, &p->list); | ||
371 | } | ||
372 | p->index = (chunk->count - 1) | (1U<<31); | ||
373 | p->owner = tree; | ||
374 | get_tree(tree); | ||
375 | list_add(&p->list, &tree->chunks); | ||
376 | list_replace_rcu(&old->hash, &chunk->hash); | ||
377 | list_for_each_entry(owner, &chunk->trees, same_root) | ||
378 | owner->root = chunk; | ||
379 | old->dead = 1; | ||
380 | if (!tree->root) { | ||
381 | tree->root = chunk; | ||
382 | list_add(&tree->same_root, &chunk->trees); | ||
383 | } | ||
384 | spin_unlock(&hash_lock); | ||
385 | inotify_evict_watch(&old->watch); | ||
386 | mutex_unlock(&inode->inotify_mutex); | ||
387 | put_inotify_watch(&old->watch); | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | static struct audit_chunk *find_chunk(struct node *p) | ||
392 | { | ||
393 | int index = p->index & ~(1U<<31); | ||
394 | p -= index; | ||
395 | return container_of(p, struct audit_chunk, owners[0]); | ||
396 | } | ||
397 | |||
398 | static void kill_rules(struct audit_tree *tree) | ||
399 | { | ||
400 | struct audit_krule *rule, *next; | ||
401 | struct audit_entry *entry; | ||
402 | struct audit_buffer *ab; | ||
403 | |||
404 | list_for_each_entry_safe(rule, next, &tree->rules, rlist) { | ||
405 | entry = container_of(rule, struct audit_entry, rule); | ||
406 | |||
407 | list_del_init(&rule->rlist); | ||
408 | if (rule->tree) { | ||
409 | /* not a half-baked one */ | ||
410 | ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_CONFIG_CHANGE); | ||
411 | audit_log_format(ab, "op=remove rule dir="); | ||
412 | audit_log_untrustedstring(ab, rule->tree->pathname); | ||
413 | if (rule->filterkey) { | ||
414 | audit_log_format(ab, " key="); | ||
415 | audit_log_untrustedstring(ab, rule->filterkey); | ||
416 | } else | ||
417 | audit_log_format(ab, " key=(null)"); | ||
418 | audit_log_format(ab, " list=%d res=1", rule->listnr); | ||
419 | audit_log_end(ab); | ||
420 | rule->tree = NULL; | ||
421 | list_del_rcu(&entry->list); | ||
422 | call_rcu(&entry->rcu, audit_free_rule_rcu); | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | |||
427 | /* | ||
428 | * finish killing struct audit_tree | ||
429 | */ | ||
430 | static void prune_one(struct audit_tree *victim) | ||
431 | { | ||
432 | spin_lock(&hash_lock); | ||
433 | while (!list_empty(&victim->chunks)) { | ||
434 | struct node *p; | ||
435 | struct audit_chunk *chunk; | ||
436 | |||
437 | p = list_entry(victim->chunks.next, struct node, list); | ||
438 | chunk = find_chunk(p); | ||
439 | get_inotify_watch(&chunk->watch); | ||
440 | spin_unlock(&hash_lock); | ||
441 | |||
442 | untag_chunk(chunk, p); | ||
443 | |||
444 | put_inotify_watch(&chunk->watch); | ||
445 | spin_lock(&hash_lock); | ||
446 | } | ||
447 | spin_unlock(&hash_lock); | ||
448 | put_tree(victim); | ||
449 | } | ||
450 | |||
451 | /* trim the uncommitted chunks from tree */ | ||
452 | |||
453 | static void trim_marked(struct audit_tree *tree) | ||
454 | { | ||
455 | struct list_head *p, *q; | ||
456 | spin_lock(&hash_lock); | ||
457 | if (tree->goner) { | ||
458 | spin_unlock(&hash_lock); | ||
459 | return; | ||
460 | } | ||
461 | /* reorder */ | ||
462 | for (p = tree->chunks.next; p != &tree->chunks; p = q) { | ||
463 | struct node *node = list_entry(p, struct node, list); | ||
464 | q = p->next; | ||
465 | if (node->index & (1U<<31)) { | ||
466 | list_del_init(p); | ||
467 | list_add(p, &tree->chunks); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | while (!list_empty(&tree->chunks)) { | ||
472 | struct node *node; | ||
473 | struct audit_chunk *chunk; | ||
474 | |||
475 | node = list_entry(tree->chunks.next, struct node, list); | ||
476 | |||
477 | /* have we run out of marked? */ | ||
478 | if (!(node->index & (1U<<31))) | ||
479 | break; | ||
480 | |||
481 | chunk = find_chunk(node); | ||
482 | get_inotify_watch(&chunk->watch); | ||
483 | spin_unlock(&hash_lock); | ||
484 | |||
485 | untag_chunk(chunk, node); | ||
486 | |||
487 | put_inotify_watch(&chunk->watch); | ||
488 | spin_lock(&hash_lock); | ||
489 | } | ||
490 | if (!tree->root && !tree->goner) { | ||
491 | tree->goner = 1; | ||
492 | spin_unlock(&hash_lock); | ||
493 | mutex_lock(&audit_filter_mutex); | ||
494 | kill_rules(tree); | ||
495 | list_del_init(&tree->list); | ||
496 | mutex_unlock(&audit_filter_mutex); | ||
497 | prune_one(tree); | ||
498 | } else { | ||
499 | spin_unlock(&hash_lock); | ||
500 | } | ||
501 | } | ||
502 | |||
503 | /* called with audit_filter_mutex */ | ||
504 | int audit_remove_tree_rule(struct audit_krule *rule) | ||
505 | { | ||
506 | struct audit_tree *tree; | ||
507 | tree = rule->tree; | ||
508 | if (tree) { | ||
509 | spin_lock(&hash_lock); | ||
510 | list_del_init(&rule->rlist); | ||
511 | if (list_empty(&tree->rules) && !tree->goner) { | ||
512 | tree->root = NULL; | ||
513 | list_del_init(&tree->same_root); | ||
514 | tree->goner = 1; | ||
515 | list_move(&tree->list, &prune_list); | ||
516 | rule->tree = NULL; | ||
517 | spin_unlock(&hash_lock); | ||
518 | audit_schedule_prune(); | ||
519 | return 1; | ||
520 | } | ||
521 | rule->tree = NULL; | ||
522 | spin_unlock(&hash_lock); | ||
523 | return 1; | ||
524 | } | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | void audit_trim_trees(void) | ||
529 | { | ||
530 | struct list_head cursor; | ||
531 | |||
532 | mutex_lock(&audit_filter_mutex); | ||
533 | list_add(&cursor, &tree_list); | ||
534 | while (cursor.next != &tree_list) { | ||
535 | struct audit_tree *tree; | ||
536 | struct nameidata nd; | ||
537 | struct vfsmount *root_mnt; | ||
538 | struct node *node; | ||
539 | struct list_head list; | ||
540 | int err; | ||
541 | |||
542 | tree = container_of(cursor.next, struct audit_tree, list); | ||
543 | get_tree(tree); | ||
544 | list_del(&cursor); | ||
545 | list_add(&cursor, &tree->list); | ||
546 | mutex_unlock(&audit_filter_mutex); | ||
547 | |||
548 | err = path_lookup(tree->pathname, 0, &nd); | ||
549 | if (err) | ||
550 | goto skip_it; | ||
551 | |||
552 | root_mnt = collect_mounts(nd.mnt, nd.dentry); | ||
553 | path_release(&nd); | ||
554 | if (!root_mnt) | ||
555 | goto skip_it; | ||
556 | |||
557 | list_add_tail(&list, &root_mnt->mnt_list); | ||
558 | spin_lock(&hash_lock); | ||
559 | list_for_each_entry(node, &tree->chunks, list) { | ||
560 | struct audit_chunk *chunk = find_chunk(node); | ||
561 | struct inode *inode = chunk->watch.inode; | ||
562 | struct vfsmount *mnt; | ||
563 | node->index |= 1U<<31; | ||
564 | list_for_each_entry(mnt, &list, mnt_list) { | ||
565 | if (mnt->mnt_root->d_inode == inode) { | ||
566 | node->index &= ~(1U<<31); | ||
567 | break; | ||
568 | } | ||
569 | } | ||
570 | } | ||
571 | spin_unlock(&hash_lock); | ||
572 | trim_marked(tree); | ||
573 | put_tree(tree); | ||
574 | list_del_init(&list); | ||
575 | drop_collected_mounts(root_mnt); | ||
576 | skip_it: | ||
577 | mutex_lock(&audit_filter_mutex); | ||
578 | } | ||
579 | list_del(&cursor); | ||
580 | mutex_unlock(&audit_filter_mutex); | ||
581 | } | ||
582 | |||
583 | static int is_under(struct vfsmount *mnt, struct dentry *dentry, | ||
584 | struct nameidata *nd) | ||
585 | { | ||
586 | if (mnt != nd->mnt) { | ||
587 | for (;;) { | ||
588 | if (mnt->mnt_parent == mnt) | ||
589 | return 0; | ||
590 | if (mnt->mnt_parent == nd->mnt) | ||
591 | break; | ||
592 | mnt = mnt->mnt_parent; | ||
593 | } | ||
594 | dentry = mnt->mnt_mountpoint; | ||
595 | } | ||
596 | return is_subdir(dentry, nd->dentry); | ||
597 | } | ||
598 | |||
599 | int audit_make_tree(struct audit_krule *rule, char *pathname, u32 op) | ||
600 | { | ||
601 | |||
602 | if (pathname[0] != '/' || | ||
603 | rule->listnr != AUDIT_FILTER_EXIT || | ||
604 | op & ~AUDIT_EQUAL || | ||
605 | rule->inode_f || rule->watch || rule->tree) | ||
606 | return -EINVAL; | ||
607 | rule->tree = alloc_tree(pathname); | ||
608 | if (!rule->tree) | ||
609 | return -ENOMEM; | ||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | void audit_put_tree(struct audit_tree *tree) | ||
614 | { | ||
615 | put_tree(tree); | ||
616 | } | ||
617 | |||
618 | /* called with audit_filter_mutex */ | ||
619 | int audit_add_tree_rule(struct audit_krule *rule) | ||
620 | { | ||
621 | struct audit_tree *seed = rule->tree, *tree; | ||
622 | struct nameidata nd; | ||
623 | struct vfsmount *mnt, *p; | ||
624 | struct list_head list; | ||
625 | int err; | ||
626 | |||
627 | list_for_each_entry(tree, &tree_list, list) { | ||
628 | if (!strcmp(seed->pathname, tree->pathname)) { | ||
629 | put_tree(seed); | ||
630 | rule->tree = tree; | ||
631 | list_add(&rule->rlist, &tree->rules); | ||
632 | return 0; | ||
633 | } | ||
634 | } | ||
635 | tree = seed; | ||
636 | list_add(&tree->list, &tree_list); | ||
637 | list_add(&rule->rlist, &tree->rules); | ||
638 | /* do not set rule->tree yet */ | ||
639 | mutex_unlock(&audit_filter_mutex); | ||
640 | |||
641 | err = path_lookup(tree->pathname, 0, &nd); | ||
642 | if (err) | ||
643 | goto Err; | ||
644 | mnt = collect_mounts(nd.mnt, nd.dentry); | ||
645 | path_release(&nd); | ||
646 | if (!mnt) { | ||
647 | err = -ENOMEM; | ||
648 | goto Err; | ||
649 | } | ||
650 | list_add_tail(&list, &mnt->mnt_list); | ||
651 | |||
652 | get_tree(tree); | ||
653 | list_for_each_entry(p, &list, mnt_list) { | ||
654 | err = tag_chunk(p->mnt_root->d_inode, tree); | ||
655 | if (err) | ||
656 | break; | ||
657 | } | ||
658 | |||
659 | list_del(&list); | ||
660 | drop_collected_mounts(mnt); | ||
661 | |||
662 | if (!err) { | ||
663 | struct node *node; | ||
664 | spin_lock(&hash_lock); | ||
665 | list_for_each_entry(node, &tree->chunks, list) | ||
666 | node->index &= ~(1U<<31); | ||
667 | spin_unlock(&hash_lock); | ||
668 | } else { | ||
669 | trim_marked(tree); | ||
670 | goto Err; | ||
671 | } | ||
672 | |||
673 | mutex_lock(&audit_filter_mutex); | ||
674 | if (list_empty(&rule->rlist)) { | ||
675 | put_tree(tree); | ||
676 | return -ENOENT; | ||
677 | } | ||
678 | rule->tree = tree; | ||
679 | put_tree(tree); | ||
680 | |||
681 | return 0; | ||
682 | Err: | ||
683 | mutex_lock(&audit_filter_mutex); | ||
684 | list_del_init(&tree->list); | ||
685 | list_del_init(&tree->rules); | ||
686 | put_tree(tree); | ||
687 | return err; | ||
688 | } | ||
689 | |||
690 | int audit_tag_tree(char *old, char *new) | ||
691 | { | ||
692 | struct list_head cursor, barrier; | ||
693 | int failed = 0; | ||
694 | struct nameidata nd; | ||
695 | struct vfsmount *tagged; | ||
696 | struct list_head list; | ||
697 | struct vfsmount *mnt; | ||
698 | struct dentry *dentry; | ||
699 | int err; | ||
700 | |||
701 | err = path_lookup(new, 0, &nd); | ||
702 | if (err) | ||
703 | return err; | ||
704 | tagged = collect_mounts(nd.mnt, nd.dentry); | ||
705 | path_release(&nd); | ||
706 | if (!tagged) | ||
707 | return -ENOMEM; | ||
708 | |||
709 | err = path_lookup(old, 0, &nd); | ||
710 | if (err) { | ||
711 | drop_collected_mounts(tagged); | ||
712 | return err; | ||
713 | } | ||
714 | mnt = mntget(nd.mnt); | ||
715 | dentry = dget(nd.dentry); | ||
716 | path_release(&nd); | ||
717 | |||
718 | if (dentry == tagged->mnt_root && dentry == mnt->mnt_root) | ||
719 | follow_up(&mnt, &dentry); | ||
720 | |||
721 | list_add_tail(&list, &tagged->mnt_list); | ||
722 | |||
723 | mutex_lock(&audit_filter_mutex); | ||
724 | list_add(&barrier, &tree_list); | ||
725 | list_add(&cursor, &barrier); | ||
726 | |||
727 | while (cursor.next != &tree_list) { | ||
728 | struct audit_tree *tree; | ||
729 | struct vfsmount *p; | ||
730 | |||
731 | tree = container_of(cursor.next, struct audit_tree, list); | ||
732 | get_tree(tree); | ||
733 | list_del(&cursor); | ||
734 | list_add(&cursor, &tree->list); | ||
735 | mutex_unlock(&audit_filter_mutex); | ||
736 | |||
737 | err = path_lookup(tree->pathname, 0, &nd); | ||
738 | if (err) { | ||
739 | put_tree(tree); | ||
740 | mutex_lock(&audit_filter_mutex); | ||
741 | continue; | ||
742 | } | ||
743 | |||
744 | spin_lock(&vfsmount_lock); | ||
745 | if (!is_under(mnt, dentry, &nd)) { | ||
746 | spin_unlock(&vfsmount_lock); | ||
747 | path_release(&nd); | ||
748 | put_tree(tree); | ||
749 | mutex_lock(&audit_filter_mutex); | ||
750 | continue; | ||
751 | } | ||
752 | spin_unlock(&vfsmount_lock); | ||
753 | path_release(&nd); | ||
754 | |||
755 | list_for_each_entry(p, &list, mnt_list) { | ||
756 | failed = tag_chunk(p->mnt_root->d_inode, tree); | ||
757 | if (failed) | ||
758 | break; | ||
759 | } | ||
760 | |||
761 | if (failed) { | ||
762 | put_tree(tree); | ||
763 | mutex_lock(&audit_filter_mutex); | ||
764 | break; | ||
765 | } | ||
766 | |||
767 | mutex_lock(&audit_filter_mutex); | ||
768 | spin_lock(&hash_lock); | ||
769 | if (!tree->goner) { | ||
770 | list_del(&tree->list); | ||
771 | list_add(&tree->list, &tree_list); | ||
772 | } | ||
773 | spin_unlock(&hash_lock); | ||
774 | put_tree(tree); | ||
775 | } | ||
776 | |||
777 | while (barrier.prev != &tree_list) { | ||
778 | struct audit_tree *tree; | ||
779 | |||
780 | tree = container_of(barrier.prev, struct audit_tree, list); | ||
781 | get_tree(tree); | ||
782 | list_del(&tree->list); | ||
783 | list_add(&tree->list, &barrier); | ||
784 | mutex_unlock(&audit_filter_mutex); | ||
785 | |||
786 | if (!failed) { | ||
787 | struct node *node; | ||
788 | spin_lock(&hash_lock); | ||
789 | list_for_each_entry(node, &tree->chunks, list) | ||
790 | node->index &= ~(1U<<31); | ||
791 | spin_unlock(&hash_lock); | ||
792 | } else { | ||
793 | trim_marked(tree); | ||
794 | } | ||
795 | |||
796 | put_tree(tree); | ||
797 | mutex_lock(&audit_filter_mutex); | ||
798 | } | ||
799 | list_del(&barrier); | ||
800 | list_del(&cursor); | ||
801 | list_del(&list); | ||
802 | mutex_unlock(&audit_filter_mutex); | ||
803 | dput(dentry); | ||
804 | mntput(mnt); | ||
805 | drop_collected_mounts(tagged); | ||
806 | return failed; | ||
807 | } | ||
808 | |||
809 | /* | ||
810 | * That gets run when evict_chunk() ends up needing to kill audit_tree. | ||
811 | * Runs from a separate thread, with audit_cmd_mutex held. | ||
812 | */ | ||
813 | void audit_prune_trees(void) | ||
814 | { | ||
815 | mutex_lock(&audit_filter_mutex); | ||
816 | |||
817 | while (!list_empty(&prune_list)) { | ||
818 | struct audit_tree *victim; | ||
819 | |||
820 | victim = list_entry(prune_list.next, struct audit_tree, list); | ||
821 | list_del_init(&victim->list); | ||
822 | |||
823 | mutex_unlock(&audit_filter_mutex); | ||
824 | |||
825 | prune_one(victim); | ||
826 | |||
827 | mutex_lock(&audit_filter_mutex); | ||
828 | } | ||
829 | |||
830 | mutex_unlock(&audit_filter_mutex); | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Here comes the stuff asynchronous to auditctl operations | ||
835 | */ | ||
836 | |||
837 | /* inode->inotify_mutex is locked */ | ||
838 | static void evict_chunk(struct audit_chunk *chunk) | ||
839 | { | ||
840 | struct audit_tree *owner; | ||
841 | int n; | ||
842 | |||
843 | if (chunk->dead) | ||
844 | return; | ||
845 | |||
846 | chunk->dead = 1; | ||
847 | mutex_lock(&audit_filter_mutex); | ||
848 | spin_lock(&hash_lock); | ||
849 | while (!list_empty(&chunk->trees)) { | ||
850 | owner = list_entry(chunk->trees.next, | ||
851 | struct audit_tree, same_root); | ||
852 | owner->goner = 1; | ||
853 | owner->root = NULL; | ||
854 | list_del_init(&owner->same_root); | ||
855 | spin_unlock(&hash_lock); | ||
856 | kill_rules(owner); | ||
857 | list_move(&owner->list, &prune_list); | ||
858 | audit_schedule_prune(); | ||
859 | spin_lock(&hash_lock); | ||
860 | } | ||
861 | list_del_rcu(&chunk->hash); | ||
862 | for (n = 0; n < chunk->count; n++) | ||
863 | list_del_init(&chunk->owners[n].list); | ||
864 | spin_unlock(&hash_lock); | ||
865 | mutex_unlock(&audit_filter_mutex); | ||
866 | } | ||
867 | |||
868 | static void handle_event(struct inotify_watch *watch, u32 wd, u32 mask, | ||
869 | u32 cookie, const char *dname, struct inode *inode) | ||
870 | { | ||
871 | struct audit_chunk *chunk = container_of(watch, struct audit_chunk, watch); | ||
872 | |||
873 | if (mask & IN_IGNORED) { | ||
874 | evict_chunk(chunk); | ||
875 | put_inotify_watch(watch); | ||
876 | } | ||
877 | } | ||
878 | |||
879 | static void destroy_watch(struct inotify_watch *watch) | ||
880 | { | ||
881 | struct audit_chunk *chunk = container_of(watch, struct audit_chunk, watch); | ||
882 | free_chunk(chunk); | ||
883 | } | ||
884 | |||
885 | static const struct inotify_operations rtree_inotify_ops = { | ||
886 | .handle_event = handle_event, | ||
887 | .destroy_watch = destroy_watch, | ||
888 | }; | ||
889 | |||
890 | static int __init audit_tree_init(void) | ||
891 | { | ||
892 | int i; | ||
893 | |||
894 | rtree_ih = inotify_init(&rtree_inotify_ops); | ||
895 | if (IS_ERR(rtree_ih)) | ||
896 | audit_panic("cannot initialize inotify handle for rectree watches"); | ||
897 | |||
898 | for (i = 0; i < HASH_SIZE; i++) | ||
899 | INIT_LIST_HEAD(&chunk_hash_heads[i]); | ||
900 | |||
901 | return 0; | ||
902 | } | ||
903 | __initcall(audit_tree_init); | ||
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index df66a21fb360..5d96f2cc7be8 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c | |||
@@ -87,7 +87,7 @@ struct list_head audit_filter_list[AUDIT_NR_FILTERS] = { | |||
87 | #endif | 87 | #endif |
88 | }; | 88 | }; |
89 | 89 | ||
90 | static DEFINE_MUTEX(audit_filter_mutex); | 90 | DEFINE_MUTEX(audit_filter_mutex); |
91 | 91 | ||
92 | /* Inotify handle */ | 92 | /* Inotify handle */ |
93 | extern struct inotify_handle *audit_ih; | 93 | extern struct inotify_handle *audit_ih; |
@@ -145,7 +145,7 @@ static inline void audit_free_rule(struct audit_entry *e) | |||
145 | kfree(e); | 145 | kfree(e); |
146 | } | 146 | } |
147 | 147 | ||
148 | static inline void audit_free_rule_rcu(struct rcu_head *head) | 148 | void audit_free_rule_rcu(struct rcu_head *head) |
149 | { | 149 | { |
150 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); | 150 | struct audit_entry *e = container_of(head, struct audit_entry, rcu); |
151 | audit_free_rule(e); | 151 | audit_free_rule(e); |
@@ -217,7 +217,7 @@ static inline struct audit_entry *audit_init_entry(u32 field_count) | |||
217 | 217 | ||
218 | /* Unpack a filter field's string representation from user-space | 218 | /* Unpack a filter field's string representation from user-space |
219 | * buffer. */ | 219 | * buffer. */ |
220 | static char *audit_unpack_string(void **bufp, size_t *remain, size_t len) | 220 | char *audit_unpack_string(void **bufp, size_t *remain, size_t len) |
221 | { | 221 | { |
222 | char *str; | 222 | char *str; |
223 | 223 | ||
@@ -247,7 +247,7 @@ static inline int audit_to_inode(struct audit_krule *krule, | |||
247 | struct audit_field *f) | 247 | struct audit_field *f) |
248 | { | 248 | { |
249 | if (krule->listnr != AUDIT_FILTER_EXIT || | 249 | if (krule->listnr != AUDIT_FILTER_EXIT || |
250 | krule->watch || krule->inode_f) | 250 | krule->watch || krule->inode_f || krule->tree) |
251 | return -EINVAL; | 251 | return -EINVAL; |
252 | 252 | ||
253 | krule->inode_f = f; | 253 | krule->inode_f = f; |
@@ -266,7 +266,7 @@ static int audit_to_watch(struct audit_krule *krule, char *path, int len, | |||
266 | if (path[0] != '/' || path[len-1] == '/' || | 266 | if (path[0] != '/' || path[len-1] == '/' || |
267 | krule->listnr != AUDIT_FILTER_EXIT || | 267 | krule->listnr != AUDIT_FILTER_EXIT || |
268 | op & ~AUDIT_EQUAL || | 268 | op & ~AUDIT_EQUAL || |
269 | krule->inode_f || krule->watch) /* 1 inode # per rule, for hash */ | 269 | krule->inode_f || krule->watch || krule->tree) |
270 | return -EINVAL; | 270 | return -EINVAL; |
271 | 271 | ||
272 | watch = audit_init_watch(path); | 272 | watch = audit_init_watch(path); |
@@ -622,6 +622,17 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data, | |||
622 | goto exit_free; | 622 | goto exit_free; |
623 | } | 623 | } |
624 | break; | 624 | break; |
625 | case AUDIT_DIR: | ||
626 | str = audit_unpack_string(&bufp, &remain, f->val); | ||
627 | if (IS_ERR(str)) | ||
628 | goto exit_free; | ||
629 | entry->rule.buflen += f->val; | ||
630 | |||
631 | err = audit_make_tree(&entry->rule, str, f->op); | ||
632 | kfree(str); | ||
633 | if (err) | ||
634 | goto exit_free; | ||
635 | break; | ||
625 | case AUDIT_INODE: | 636 | case AUDIT_INODE: |
626 | err = audit_to_inode(&entry->rule, f); | 637 | err = audit_to_inode(&entry->rule, f); |
627 | if (err) | 638 | if (err) |
@@ -668,7 +679,7 @@ exit_free: | |||
668 | } | 679 | } |
669 | 680 | ||
670 | /* Pack a filter field's string representation into data block. */ | 681 | /* Pack a filter field's string representation into data block. */ |
671 | static inline size_t audit_pack_string(void **bufp, char *str) | 682 | static inline size_t audit_pack_string(void **bufp, const char *str) |
672 | { | 683 | { |
673 | size_t len = strlen(str); | 684 | size_t len = strlen(str); |
674 | 685 | ||
@@ -747,6 +758,11 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule) | |||
747 | data->buflen += data->values[i] = | 758 | data->buflen += data->values[i] = |
748 | audit_pack_string(&bufp, krule->watch->path); | 759 | audit_pack_string(&bufp, krule->watch->path); |
749 | break; | 760 | break; |
761 | case AUDIT_DIR: | ||
762 | data->buflen += data->values[i] = | ||
763 | audit_pack_string(&bufp, | ||
764 | audit_tree_path(krule->tree)); | ||
765 | break; | ||
750 | case AUDIT_FILTERKEY: | 766 | case AUDIT_FILTERKEY: |
751 | data->buflen += data->values[i] = | 767 | data->buflen += data->values[i] = |
752 | audit_pack_string(&bufp, krule->filterkey); | 768 | audit_pack_string(&bufp, krule->filterkey); |
@@ -795,6 +811,11 @@ static int audit_compare_rule(struct audit_krule *a, struct audit_krule *b) | |||
795 | if (strcmp(a->watch->path, b->watch->path)) | 811 | if (strcmp(a->watch->path, b->watch->path)) |
796 | return 1; | 812 | return 1; |
797 | break; | 813 | break; |
814 | case AUDIT_DIR: | ||
815 | if (strcmp(audit_tree_path(a->tree), | ||
816 | audit_tree_path(b->tree))) | ||
817 | return 1; | ||
818 | break; | ||
798 | case AUDIT_FILTERKEY: | 819 | case AUDIT_FILTERKEY: |
799 | /* both filterkeys exist based on above type compare */ | 820 | /* both filterkeys exist based on above type compare */ |
800 | if (strcmp(a->filterkey, b->filterkey)) | 821 | if (strcmp(a->filterkey, b->filterkey)) |
@@ -897,6 +918,14 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old, | |||
897 | new->inode_f = old->inode_f; | 918 | new->inode_f = old->inode_f; |
898 | new->watch = NULL; | 919 | new->watch = NULL; |
899 | new->field_count = old->field_count; | 920 | new->field_count = old->field_count; |
921 | /* | ||
922 | * note that we are OK with not refcounting here; audit_match_tree() | ||
923 | * never dereferences tree and we can't get false positives there | ||
924 | * since we'd have to have rule gone from the list *and* removed | ||
925 | * before the chunks found by lookup had been allocated, i.e. before | ||
926 | * the beginning of list scan. | ||
927 | */ | ||
928 | new->tree = old->tree; | ||
900 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); | 929 | memcpy(new->fields, old->fields, sizeof(struct audit_field) * fcount); |
901 | 930 | ||
902 | /* deep copy this information, updating the se_rule fields, because | 931 | /* deep copy this information, updating the se_rule fields, because |
@@ -1217,6 +1246,7 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1217 | struct audit_entry *e; | 1246 | struct audit_entry *e; |
1218 | struct audit_field *inode_f = entry->rule.inode_f; | 1247 | struct audit_field *inode_f = entry->rule.inode_f; |
1219 | struct audit_watch *watch = entry->rule.watch; | 1248 | struct audit_watch *watch = entry->rule.watch; |
1249 | struct audit_tree *tree = entry->rule.tree; | ||
1220 | struct nameidata *ndp = NULL, *ndw = NULL; | 1250 | struct nameidata *ndp = NULL, *ndw = NULL; |
1221 | int h, err; | 1251 | int h, err; |
1222 | #ifdef CONFIG_AUDITSYSCALL | 1252 | #ifdef CONFIG_AUDITSYSCALL |
@@ -1238,6 +1268,9 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1238 | mutex_unlock(&audit_filter_mutex); | 1268 | mutex_unlock(&audit_filter_mutex); |
1239 | if (e) { | 1269 | if (e) { |
1240 | err = -EEXIST; | 1270 | err = -EEXIST; |
1271 | /* normally audit_add_tree_rule() will free it on failure */ | ||
1272 | if (tree) | ||
1273 | audit_put_tree(tree); | ||
1241 | goto error; | 1274 | goto error; |
1242 | } | 1275 | } |
1243 | 1276 | ||
@@ -1259,6 +1292,13 @@ static inline int audit_add_rule(struct audit_entry *entry, | |||
1259 | h = audit_hash_ino((u32)watch->ino); | 1292 | h = audit_hash_ino((u32)watch->ino); |
1260 | list = &audit_inode_hash[h]; | 1293 | list = &audit_inode_hash[h]; |
1261 | } | 1294 | } |
1295 | if (tree) { | ||
1296 | err = audit_add_tree_rule(&entry->rule); | ||
1297 | if (err) { | ||
1298 | mutex_unlock(&audit_filter_mutex); | ||
1299 | goto error; | ||
1300 | } | ||
1301 | } | ||
1262 | 1302 | ||
1263 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { | 1303 | if (entry->rule.flags & AUDIT_FILTER_PREPEND) { |
1264 | list_add_rcu(&entry->list, list); | 1304 | list_add_rcu(&entry->list, list); |
@@ -1292,6 +1332,7 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1292 | struct audit_entry *e; | 1332 | struct audit_entry *e; |
1293 | struct audit_field *inode_f = entry->rule.inode_f; | 1333 | struct audit_field *inode_f = entry->rule.inode_f; |
1294 | struct audit_watch *watch, *tmp_watch = entry->rule.watch; | 1334 | struct audit_watch *watch, *tmp_watch = entry->rule.watch; |
1335 | struct audit_tree *tree = entry->rule.tree; | ||
1295 | LIST_HEAD(inotify_list); | 1336 | LIST_HEAD(inotify_list); |
1296 | int h, ret = 0; | 1337 | int h, ret = 0; |
1297 | #ifdef CONFIG_AUDITSYSCALL | 1338 | #ifdef CONFIG_AUDITSYSCALL |
@@ -1336,6 +1377,9 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1336 | } | 1377 | } |
1337 | } | 1378 | } |
1338 | 1379 | ||
1380 | if (e->rule.tree) | ||
1381 | audit_remove_tree_rule(&e->rule); | ||
1382 | |||
1339 | list_del_rcu(&e->list); | 1383 | list_del_rcu(&e->list); |
1340 | call_rcu(&e->rcu, audit_free_rule_rcu); | 1384 | call_rcu(&e->rcu, audit_free_rule_rcu); |
1341 | 1385 | ||
@@ -1354,6 +1398,8 @@ static inline int audit_del_rule(struct audit_entry *entry, | |||
1354 | out: | 1398 | out: |
1355 | if (tmp_watch) | 1399 | if (tmp_watch) |
1356 | audit_put_watch(tmp_watch); /* match initial get */ | 1400 | audit_put_watch(tmp_watch); /* match initial get */ |
1401 | if (tree) | ||
1402 | audit_put_tree(tree); /* that's the temporary one */ | ||
1357 | 1403 | ||
1358 | return ret; | 1404 | return ret; |
1359 | } | 1405 | } |
@@ -1737,6 +1783,7 @@ int selinux_audit_rule_update(void) | |||
1737 | { | 1783 | { |
1738 | struct audit_entry *entry, *n, *nentry; | 1784 | struct audit_entry *entry, *n, *nentry; |
1739 | struct audit_watch *watch; | 1785 | struct audit_watch *watch; |
1786 | struct audit_tree *tree; | ||
1740 | int i, err = 0; | 1787 | int i, err = 0; |
1741 | 1788 | ||
1742 | /* audit_filter_mutex synchronizes the writers */ | 1789 | /* audit_filter_mutex synchronizes the writers */ |
@@ -1748,6 +1795,7 @@ int selinux_audit_rule_update(void) | |||
1748 | continue; | 1795 | continue; |
1749 | 1796 | ||
1750 | watch = entry->rule.watch; | 1797 | watch = entry->rule.watch; |
1798 | tree = entry->rule.tree; | ||
1751 | nentry = audit_dupe_rule(&entry->rule, watch); | 1799 | nentry = audit_dupe_rule(&entry->rule, watch); |
1752 | if (unlikely(IS_ERR(nentry))) { | 1800 | if (unlikely(IS_ERR(nentry))) { |
1753 | /* save the first error encountered for the | 1801 | /* save the first error encountered for the |
@@ -1763,7 +1811,9 @@ int selinux_audit_rule_update(void) | |||
1763 | list_add(&nentry->rule.rlist, | 1811 | list_add(&nentry->rule.rlist, |
1764 | &watch->rules); | 1812 | &watch->rules); |
1765 | list_del(&entry->rule.rlist); | 1813 | list_del(&entry->rule.rlist); |
1766 | } | 1814 | } else if (tree) |
1815 | list_replace_init(&entry->rule.rlist, | ||
1816 | &nentry->rule.rlist); | ||
1767 | list_replace_rcu(&entry->list, &nentry->list); | 1817 | list_replace_rcu(&entry->list, &nentry->list); |
1768 | } | 1818 | } |
1769 | call_rcu(&entry->rcu, audit_free_rule_rcu); | 1819 | call_rcu(&entry->rcu, audit_free_rule_rcu); |
diff --git a/kernel/auditsc.c b/kernel/auditsc.c index e19b5a33aede..80ecab0942ef 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c | |||
@@ -65,6 +65,7 @@ | |||
65 | #include <linux/binfmts.h> | 65 | #include <linux/binfmts.h> |
66 | #include <linux/highmem.h> | 66 | #include <linux/highmem.h> |
67 | #include <linux/syscalls.h> | 67 | #include <linux/syscalls.h> |
68 | #include <linux/inotify.h> | ||
68 | 69 | ||
69 | #include "audit.h" | 70 | #include "audit.h" |
70 | 71 | ||
@@ -179,6 +180,11 @@ struct audit_aux_data_pids { | |||
179 | int pid_count; | 180 | int pid_count; |
180 | }; | 181 | }; |
181 | 182 | ||
183 | struct audit_tree_refs { | ||
184 | struct audit_tree_refs *next; | ||
185 | struct audit_chunk *c[31]; | ||
186 | }; | ||
187 | |||
182 | /* The per-task audit context. */ | 188 | /* The per-task audit context. */ |
183 | struct audit_context { | 189 | struct audit_context { |
184 | int dummy; /* must be the first element */ | 190 | int dummy; /* must be the first element */ |
@@ -211,6 +217,9 @@ struct audit_context { | |||
211 | pid_t target_pid; | 217 | pid_t target_pid; |
212 | u32 target_sid; | 218 | u32 target_sid; |
213 | 219 | ||
220 | struct audit_tree_refs *trees, *first_trees; | ||
221 | int tree_count; | ||
222 | |||
214 | #if AUDIT_DEBUG | 223 | #if AUDIT_DEBUG |
215 | int put_count; | 224 | int put_count; |
216 | int ino_count; | 225 | int ino_count; |
@@ -265,6 +274,117 @@ static int audit_match_perm(struct audit_context *ctx, int mask) | |||
265 | } | 274 | } |
266 | } | 275 | } |
267 | 276 | ||
277 | /* | ||
278 | * We keep a linked list of fixed-sized (31 pointer) arrays of audit_chunk *; | ||
279 | * ->first_trees points to its beginning, ->trees - to the current end of data. | ||
280 | * ->tree_count is the number of free entries in array pointed to by ->trees. | ||
281 | * Original condition is (NULL, NULL, 0); as soon as it grows we never revert to NULL, | ||
282 | * "empty" becomes (p, p, 31) afterwards. We don't shrink the list (and seriously, | ||
283 | * it's going to remain 1-element for almost any setup) until we free context itself. | ||
284 | * References in it _are_ dropped - at the same time we free/drop aux stuff. | ||
285 | */ | ||
286 | |||
287 | #ifdef CONFIG_AUDIT_TREE | ||
288 | static int put_tree_ref(struct audit_context *ctx, struct audit_chunk *chunk) | ||
289 | { | ||
290 | struct audit_tree_refs *p = ctx->trees; | ||
291 | int left = ctx->tree_count; | ||
292 | if (likely(left)) { | ||
293 | p->c[--left] = chunk; | ||
294 | ctx->tree_count = left; | ||
295 | return 1; | ||
296 | } | ||
297 | if (!p) | ||
298 | return 0; | ||
299 | p = p->next; | ||
300 | if (p) { | ||
301 | p->c[30] = chunk; | ||
302 | ctx->trees = p; | ||
303 | ctx->tree_count = 30; | ||
304 | return 1; | ||
305 | } | ||
306 | return 0; | ||
307 | } | ||
308 | |||
309 | static int grow_tree_refs(struct audit_context *ctx) | ||
310 | { | ||
311 | struct audit_tree_refs *p = ctx->trees; | ||
312 | ctx->trees = kzalloc(sizeof(struct audit_tree_refs), GFP_KERNEL); | ||
313 | if (!ctx->trees) { | ||
314 | ctx->trees = p; | ||
315 | return 0; | ||
316 | } | ||
317 | if (p) | ||
318 | p->next = ctx->trees; | ||
319 | else | ||
320 | ctx->first_trees = ctx->trees; | ||
321 | ctx->tree_count = 31; | ||
322 | return 1; | ||
323 | } | ||
324 | #endif | ||
325 | |||
326 | static void unroll_tree_refs(struct audit_context *ctx, | ||
327 | struct audit_tree_refs *p, int count) | ||
328 | { | ||
329 | #ifdef CONFIG_AUDIT_TREE | ||
330 | struct audit_tree_refs *q; | ||
331 | int n; | ||
332 | if (!p) { | ||
333 | /* we started with empty chain */ | ||
334 | p = ctx->first_trees; | ||
335 | count = 31; | ||
336 | /* if the very first allocation has failed, nothing to do */ | ||
337 | if (!p) | ||
338 | return; | ||
339 | } | ||
340 | n = count; | ||
341 | for (q = p; q != ctx->trees; q = q->next, n = 31) { | ||
342 | while (n--) { | ||
343 | audit_put_chunk(q->c[n]); | ||
344 | q->c[n] = NULL; | ||
345 | } | ||
346 | } | ||
347 | while (n-- > ctx->tree_count) { | ||
348 | audit_put_chunk(q->c[n]); | ||
349 | q->c[n] = NULL; | ||
350 | } | ||
351 | ctx->trees = p; | ||
352 | ctx->tree_count = count; | ||
353 | #endif | ||
354 | } | ||
355 | |||
356 | static void free_tree_refs(struct audit_context *ctx) | ||
357 | { | ||
358 | struct audit_tree_refs *p, *q; | ||
359 | for (p = ctx->first_trees; p; p = q) { | ||
360 | q = p->next; | ||
361 | kfree(p); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree) | ||
366 | { | ||
367 | #ifdef CONFIG_AUDIT_TREE | ||
368 | struct audit_tree_refs *p; | ||
369 | int n; | ||
370 | if (!tree) | ||
371 | return 0; | ||
372 | /* full ones */ | ||
373 | for (p = ctx->first_trees; p != ctx->trees; p = p->next) { | ||
374 | for (n = 0; n < 31; n++) | ||
375 | if (audit_tree_match(p->c[n], tree)) | ||
376 | return 1; | ||
377 | } | ||
378 | /* partial */ | ||
379 | if (p) { | ||
380 | for (n = ctx->tree_count; n < 31; n++) | ||
381 | if (audit_tree_match(p->c[n], tree)) | ||
382 | return 1; | ||
383 | } | ||
384 | #endif | ||
385 | return 0; | ||
386 | } | ||
387 | |||
268 | /* Determine if any context name data matches a rule's watch data */ | 388 | /* Determine if any context name data matches a rule's watch data */ |
269 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 | 389 | /* Compare a task_struct with an audit_rule. Return 1 on match, 0 |
270 | * otherwise. */ | 390 | * otherwise. */ |
@@ -379,6 +499,10 @@ static int audit_filter_rules(struct task_struct *tsk, | |||
379 | result = (name->dev == rule->watch->dev && | 499 | result = (name->dev == rule->watch->dev && |
380 | name->ino == rule->watch->ino); | 500 | name->ino == rule->watch->ino); |
381 | break; | 501 | break; |
502 | case AUDIT_DIR: | ||
503 | if (ctx) | ||
504 | result = match_tree_refs(ctx, rule->tree); | ||
505 | break; | ||
382 | case AUDIT_LOGINUID: | 506 | case AUDIT_LOGINUID: |
383 | result = 0; | 507 | result = 0; |
384 | if (ctx) | 508 | if (ctx) |
@@ -727,6 +851,8 @@ static inline void audit_free_context(struct audit_context *context) | |||
727 | context->name_count, count); | 851 | context->name_count, count); |
728 | } | 852 | } |
729 | audit_free_names(context); | 853 | audit_free_names(context); |
854 | unroll_tree_refs(context, NULL, 0); | ||
855 | free_tree_refs(context); | ||
730 | audit_free_aux(context); | 856 | audit_free_aux(context); |
731 | kfree(context->filterkey); | 857 | kfree(context->filterkey); |
732 | kfree(context); | 858 | kfree(context); |
@@ -1270,6 +1396,7 @@ void audit_syscall_exit(int valid, long return_code) | |||
1270 | tsk->audit_context = new_context; | 1396 | tsk->audit_context = new_context; |
1271 | } else { | 1397 | } else { |
1272 | audit_free_names(context); | 1398 | audit_free_names(context); |
1399 | unroll_tree_refs(context, NULL, 0); | ||
1273 | audit_free_aux(context); | 1400 | audit_free_aux(context); |
1274 | context->aux = NULL; | 1401 | context->aux = NULL; |
1275 | context->aux_pids = NULL; | 1402 | context->aux_pids = NULL; |
@@ -1281,6 +1408,95 @@ void audit_syscall_exit(int valid, long return_code) | |||
1281 | } | 1408 | } |
1282 | } | 1409 | } |
1283 | 1410 | ||
1411 | static inline void handle_one(const struct inode *inode) | ||
1412 | { | ||
1413 | #ifdef CONFIG_AUDIT_TREE | ||
1414 | struct audit_context *context; | ||
1415 | struct audit_tree_refs *p; | ||
1416 | struct audit_chunk *chunk; | ||
1417 | int count; | ||
1418 | if (likely(list_empty(&inode->inotify_watches))) | ||
1419 | return; | ||
1420 | context = current->audit_context; | ||
1421 | p = context->trees; | ||
1422 | count = context->tree_count; | ||
1423 | rcu_read_lock(); | ||
1424 | chunk = audit_tree_lookup(inode); | ||
1425 | rcu_read_unlock(); | ||
1426 | if (!chunk) | ||
1427 | return; | ||
1428 | if (likely(put_tree_ref(context, chunk))) | ||
1429 | return; | ||
1430 | if (unlikely(!grow_tree_refs(context))) { | ||
1431 | printk(KERN_WARNING "out of memory, audit has lost a tree reference"); | ||
1432 | audit_set_auditable(context); | ||
1433 | audit_put_chunk(chunk); | ||
1434 | unroll_tree_refs(context, p, count); | ||
1435 | return; | ||
1436 | } | ||
1437 | put_tree_ref(context, chunk); | ||
1438 | #endif | ||
1439 | } | ||
1440 | |||
1441 | static void handle_path(const struct dentry *dentry) | ||
1442 | { | ||
1443 | #ifdef CONFIG_AUDIT_TREE | ||
1444 | struct audit_context *context; | ||
1445 | struct audit_tree_refs *p; | ||
1446 | const struct dentry *d, *parent; | ||
1447 | struct audit_chunk *drop; | ||
1448 | unsigned long seq; | ||
1449 | int count; | ||
1450 | |||
1451 | context = current->audit_context; | ||
1452 | p = context->trees; | ||
1453 | count = context->tree_count; | ||
1454 | retry: | ||
1455 | drop = NULL; | ||
1456 | d = dentry; | ||
1457 | rcu_read_lock(); | ||
1458 | seq = read_seqbegin(&rename_lock); | ||
1459 | for(;;) { | ||
1460 | struct inode *inode = d->d_inode; | ||
1461 | if (inode && unlikely(!list_empty(&inode->inotify_watches))) { | ||
1462 | struct audit_chunk *chunk; | ||
1463 | chunk = audit_tree_lookup(inode); | ||
1464 | if (chunk) { | ||
1465 | if (unlikely(!put_tree_ref(context, chunk))) { | ||
1466 | drop = chunk; | ||
1467 | break; | ||
1468 | } | ||
1469 | } | ||
1470 | } | ||
1471 | parent = d->d_parent; | ||
1472 | if (parent == d) | ||
1473 | break; | ||
1474 | d = parent; | ||
1475 | } | ||
1476 | if (unlikely(read_seqretry(&rename_lock, seq) || drop)) { /* in this order */ | ||
1477 | rcu_read_unlock(); | ||
1478 | if (!drop) { | ||
1479 | /* just a race with rename */ | ||
1480 | unroll_tree_refs(context, p, count); | ||
1481 | goto retry; | ||
1482 | } | ||
1483 | audit_put_chunk(drop); | ||
1484 | if (grow_tree_refs(context)) { | ||
1485 | /* OK, got more space */ | ||
1486 | unroll_tree_refs(context, p, count); | ||
1487 | goto retry; | ||
1488 | } | ||
1489 | /* too bad */ | ||
1490 | printk(KERN_WARNING | ||
1491 | "out of memory, audit has lost a tree reference"); | ||
1492 | unroll_tree_refs(context, p, count); | ||
1493 | audit_set_auditable(context); | ||
1494 | return; | ||
1495 | } | ||
1496 | rcu_read_unlock(); | ||
1497 | #endif | ||
1498 | } | ||
1499 | |||
1284 | /** | 1500 | /** |
1285 | * audit_getname - add a name to the list | 1501 | * audit_getname - add a name to the list |
1286 | * @name: name to add | 1502 | * @name: name to add |
@@ -1403,10 +1619,11 @@ static void audit_copy_inode(struct audit_names *name, const struct inode *inode | |||
1403 | * | 1619 | * |
1404 | * Called from fs/namei.c:path_lookup(). | 1620 | * Called from fs/namei.c:path_lookup(). |
1405 | */ | 1621 | */ |
1406 | void __audit_inode(const char *name, const struct inode *inode) | 1622 | void __audit_inode(const char *name, const struct dentry *dentry) |
1407 | { | 1623 | { |
1408 | int idx; | 1624 | int idx; |
1409 | struct audit_context *context = current->audit_context; | 1625 | struct audit_context *context = current->audit_context; |
1626 | const struct inode *inode = dentry->d_inode; | ||
1410 | 1627 | ||
1411 | if (!context->in_syscall) | 1628 | if (!context->in_syscall) |
1412 | return; | 1629 | return; |
@@ -1426,6 +1643,7 @@ void __audit_inode(const char *name, const struct inode *inode) | |||
1426 | idx = context->name_count - 1; | 1643 | idx = context->name_count - 1; |
1427 | context->names[idx].name = NULL; | 1644 | context->names[idx].name = NULL; |
1428 | } | 1645 | } |
1646 | handle_path(dentry); | ||
1429 | audit_copy_inode(&context->names[idx], inode); | 1647 | audit_copy_inode(&context->names[idx], inode); |
1430 | } | 1648 | } |
1431 | 1649 | ||
@@ -1443,17 +1661,20 @@ void __audit_inode(const char *name, const struct inode *inode) | |||
1443 | * must be hooked prior, in order to capture the target inode during | 1661 | * must be hooked prior, in order to capture the target inode during |
1444 | * unsuccessful attempts. | 1662 | * unsuccessful attempts. |
1445 | */ | 1663 | */ |
1446 | void __audit_inode_child(const char *dname, const struct inode *inode, | 1664 | void __audit_inode_child(const char *dname, const struct dentry *dentry, |
1447 | const struct inode *parent) | 1665 | const struct inode *parent) |
1448 | { | 1666 | { |
1449 | int idx; | 1667 | int idx; |
1450 | struct audit_context *context = current->audit_context; | 1668 | struct audit_context *context = current->audit_context; |
1451 | const char *found_parent = NULL, *found_child = NULL; | 1669 | const char *found_parent = NULL, *found_child = NULL; |
1670 | const struct inode *inode = dentry->d_inode; | ||
1452 | int dirlen = 0; | 1671 | int dirlen = 0; |
1453 | 1672 | ||
1454 | if (!context->in_syscall) | 1673 | if (!context->in_syscall) |
1455 | return; | 1674 | return; |
1456 | 1675 | ||
1676 | if (inode) | ||
1677 | handle_one(inode); | ||
1457 | /* determine matching parent */ | 1678 | /* determine matching parent */ |
1458 | if (!dname) | 1679 | if (!dname) |
1459 | goto add_names; | 1680 | goto add_names; |