filesystem auditing by location+name
Subsystems (3)
RFC PATCH 1/1 filesystem auditing by location+name
Hello,
This patch augments the audit subsystem and VFS to support file system auditing in which an object is audited based on its location and name. Depending on the administrator's requirement, this feature can be used standalone or in conjunction with the (device,inode)-based filters. The audit subsystem is currently incapable of auditing a file system object based on its location and name. This is critical for auditing well-defined and security-relevant locations such as /etc/shadow, where the file is re-created on each transaction and cannot rely solely on the (device, inode)-based filters to ensure persistence of auditing across transactions.
This work is being done to make the Linux audit subsystem compliant with Common Criteria's Controlled Access Protection Profile (CAPP) specification. CAPP requires that the administrator is able to audit a file's content in two fundamental ways:
1) Where content confidentiality is important.
This includes security-relevant databases like /etc/passwd and the audit logs of which the contents are security sensitive. The (device,inode)-based system-call filters are sufficient to cover this requirement and are already available to the administrator.
2) Where content integrity is important.
This includes configuration files that affect the operation of trusted programs like /etc/shadow. Content integrity ensures audit persists across transactions where the underlying inode may change, but the content remains relevant. This is not doable without this patch.
We've submitted this patch to LKML with the ultimate goal of upstream acceptance. All questions and comments are welcomed.
This patch is against audit-2.6.git and appears below.
-tim
--- fs/dcache.c | 9 fs/inode.c | 7 fs/namei.c | 32 + include/linux/audit.h | 74 +++ include/linux/fs.h | 2 init/Kconfig | 12 kernel/Makefile | 1 kernel/audit.c | 14 kernel/auditfs.c | 852 ++++++++++++++++++++++++++++++++++++++++++++ kernel/auditsc.c | 109 +++++ security/selinux/nlmsgtab.c | 3 11 files changed, 1107 insertions(+), 8 deletions(-)
fs/dcache.c +9 -0
@@ -32,6 +32,7 @@#include <linux/seqlock.h> #include <linux/swap.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* #define DCACHE_DEBUG 1 */
@@ -97,6 +98,7 @@ static inline void dentry_iput(struct de {struct inode *inode = dentry->d_inode;if (inode) { +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); dentry->d_inode =NULL ;list_del_init (&dentry->d_alias);spin_unlock (&dentry->d_lock);
@@ -802,6 +804,7 @@ void d_instantiate(struct dentry *entry,if (inode)list_add (&entry->d_alias, &inode->i_dentry); entry->d_inode = inode; +audit_update_watch (entry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (entry, inode); }
@@ -978,6 +981,7 @@ struct dentry *d_splice_alias(struct ino new =__d_find_alias (inode,1 );if (new) {BUG_ON (!(new->d_flags &DCACHE_DISCONNECTED )); +audit_update_watch (new,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (new, inode);d_rehash (dentry);
@@ -987,6 +991,7 @@ struct dentry *d_splice_alias(struct ino/* d_instantiate takes dcache_lock, so we do it by hand */ list_add (&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (dentry, inode);d_rehash (dentry);
@@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentryif (!d_unhashed (dentry)) {atomic_inc (&dentry->d_count); found = dentry; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL ); }spin_unlock (&dentry->d_lock);break ;
@@ -1299,6 +1305,8 @@ void d_move(struct dentry * dentry, struspin_lock (&target->d_lock); } +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); +/* Move the dentry to the target hash queue, if on different bucket */ if (dentry->d_flags &DCACHE_UNHASHED ) goto already_unhashed;
@@ -1332,6 +1340,7 @@ already_unhashed:list_add (&target->d_child, &target->d_parent->d_subdirs); } +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );list_add (&dentry->d_child, &dentry->d_parent->d_subdirs);spin_unlock (&target->d_lock);spin_unlock (&dentry->d_lock);
fs/inode.c +5 -2
@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* * This is needed for the following functions:
@@ -172,6 +173,7 @@ void destroy_inode(struct inode *inode) {if (inode_has_buffers (inode))BUG (); +audit_inode_free (inode);security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode) inode->i_sb->s_op->destroy_inode (inode);
@@ -257,7 +259,7 @@ void clear_inode(struct inode *inode)bd_forget (inode);if (inode->i_cdev)cd_forget (inode); - inode->i_state =I_CLEAR ; + inode->i_state =I_CLEAR | (inode->i_state &I_AUDIT ); }EXPORT_SYMBOL (clear_inode);
@@ -1009,7 +1011,7 @@ void generic_delete_inode(struct inode *hlist_del_init (&inode->i_hash);spin_unlock (&inode_lock);wake_up_inode (inode); -if (inode->i_state !=I_CLEAR ) +if ((inode->i_state & ~I_AUDIT ) !=I_CLEAR )BUG ();destroy_inode (inode); }
@@ -1323,6 +1325,7 @@ void __init inode_init(unsigned long mem inode_cachep =kmem_cache_create ("inode_cache" ,sizeof (struct inode ),0 ,SLAB_RECLAIM_ACCOUNT |SLAB_PANIC , init_once,NULL );set_shrinker (DEFAULT_SEEKS , shrink_icache_memory); +audit_filesystem_init ();/* Hash may have been set up in inode_init_early */ if (!hashdist)
fs/namei.c +29 -3
@@ -179,6 +179,9 @@ int generic_permission(struct inode *ino int (*check_acl)(struct inode *inode, int mask)) {umode_t mode = inode->i_mode; + +if (audit_notify_watch (inode, mask)) +return -ENOMEM ;if (current->fsuid == inode->i_uid) mode >>=6 ;
@@ -358,6 +361,9 @@ static inline int exec_permission_lite(sif (inode->i_op && inode->i_op->permission)return -EAGAIN ; +if (audit_notify_watch (inode,MAY_EXEC )) +return -ENOMEM ; +if (current->fsuid == inode->i_uid) mode >>=6 ;else if (in_group_p (inode->i_gid))
@@ -1172,6 +1178,10 @@ static inline int may_delete(struct inodBUG_ON (victim->d_parent->d_inode != dir); + error =audit_notify_watch (victim->d_inode,MAY_WRITE ); +if (error) +return error; + error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;
@@ -1295,6 +1305,8 @@ int vfs_create(struct inode *dir, structreturn error;DQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_create (dir, dentry, mode);
@@ -1601,6 +1613,8 @@ int vfs_mknod(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_mknod (dir, dentry, mode, dev);
@@ -1674,6 +1688,8 @@ int vfs_mkdir(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_mkdir (dir,dentry, mode);
@@ -1914,6 +1930,8 @@ int vfs_symlink(struct inode *dir, strucDQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_symlink (dir, dentry, oldname);
@@ -1987,6 +2005,8 @@ int vfs_link(struct dentry *old_dentry,DQUOT_INIT (dir); error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem); +if (!error) + error =audit_notify_watch (new_dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_link (old_dentry, dir, new_dentry);
@@ -2109,11 +2129,13 @@ static int vfs_rename_dir(struct inode *d_rehash (new_dentry);dput (new_dentry); } -if (!error) { +if (!error)d_move (old_dentry,new_dentry); + + error =audit_notify_watch (old_dentry->d_inode,MAY_WRITE ); +if (!error)security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); - }return error; }
@@ -2139,8 +2161,12 @@ static int vfs_rename_other(struct inode/* The following d_move() should become unconditional */ if (!(old_dir->i_sb->s_type->fs_flags &FS_ODD_RENAME ))d_move (old_dentry, new_dentry); -security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); } + + error =audit_notify_watch (old_dentry->d_inode,MAY_WRITE ); +if (!error) +security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); +if (target)up (&target->i_sem);dput (new_dentry);
include/linux/audit.h +74 -0
114 lines not on this page · raw message
include/linux/fs.h +2 -0
@@ -225,6 +225,7 @@ struct poll_table_struct;struct kstatfs ;struct vm_area_struct ;struct vfsmount ; +struct audit_inode_data ;/* Used to be a macro which just called the function, now just a function */ extern void update_atime (struct inode *);
@@ -1024,6 +1025,7 @@ struct super_operations {#define I_FREEING 16#define I_CLEAR 32#define I_NEW 64 +#define I_AUDIT 128#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
init/Kconfig +12 -0
@@ -166,6 +166,7 @@ config AUDIT bool "Auditing support" depends on NET default y if SECURITY_SELINUX + default n help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for
@@ -176,11 +177,22 @@ config AUDITSYSCALL bool "Enable system-call auditing support" depends on AUDIT && (X86 || PPC || PPC64 || ARCH_S390 || IA64 || UML) default y if SECURITY_SELINUX + default n help Enable low-overhead system-call auditing infrastructure that can be used independently or with another kernel subsystem, such as SELinux. +config AUDITFILESYSTEM + bool "Enable file system auditing support" + depends on AUDITSYSCALL + default n + help + Enable file system auditing for regular files and directories. + When a targeted file or directory is accessed, an audit record + is generated describing the inode accessed, how it was accessed, + and by whom (ie: pid and system call). + config HOTPLUG bool "Support for hot-pluggable devices" if !ARCH_S390 default ARCH_S390
kernel/Makefile +1 -0
@@ -24,6 +24,7 @@ obj-$(CONFIG_IKCONFIG_PROC) += configs.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o +obj-$(CONFIG_AUDITFILESYSTEM) += auditfs.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_SYSFS) += ksysfs.o obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
kernel/audit.c +14 -0
@@ -350,6 +350,9 @@ static int audit_netlink_ok(kernel_cap_tcase AUDIT_SET :case AUDIT_ADD :case AUDIT_DEL : +case AUDIT_WATCH_LIST : +case AUDIT_WATCH_INS : +case AUDIT_WATCH_REM :case AUDIT_SIGNAL_INFO :if (!cap_raised (eff_cap,CAP_AUDIT_CONTROL )) err = -EPERM ;
@@ -454,6 +457,17 @@ static int audit_receive_msg(struct sk_b err =audit_receive_filter (nlh->nlmsg_type,NETLINK_CB (skb).pid, uid, seq, data, loginuid);break ; +case AUDIT_WATCH_LIST : + err =audit_list_watches (pid, seq); +break ; +case AUDIT_WATCH_INS : +case AUDIT_WATCH_REM : +if (nlh->nlmsg_len <sizeof (struct watch_transport )) +return -EINVAL ; + err =audit_receive_watch (nlh->nlmsg_type, +NETLINK_CB (skb).pid, + uid, seq, data, loginuid); +break ;case AUDIT_SIGNAL_INFO : sig_data.uid = audit_sig_uid; sig_data.pid = audit_sig_pid;
kernel/auditfs.c +852 -0
852 lines not on this page · raw message
kernel/auditsc.c +106 -3
160 lines not on this page · raw message
security/selinux/nlmsgtab.c +3 -0
@@ -98,6 +98,9 @@ static struct nlmsg_perm nlmsg_audit_per { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, };
most of this looks like a copy & paste of the inotify code. Could you please arrange with the inotify folks to merge the patches?
Perhaps you could describe any specific reasons inotify isn't sufficient, as it's similar in many ways.
Also, could drop some of that extra debugging code (heh, like that MKDEV in audit_inode_free ;-)
Anyway, some quick comments below...
Quoted 117 lines
diff -Nurp
audit-2.6.git/fs/dcache.caudit-2.6.git~tc/fs/dcache.cfs/dcache.c +9
-0@@ -32,6 +32,7 @@#include <linux/seqlock.h> #include <linux/swap.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* #define DCACHE_DEBUG 1 */ @@ -97,6 +98,7 @@ static inline void dentry_iput(struct de {struct inode *inode = dentry->d_inode;if (inode) { +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); dentry->d_inode =NULL ;list_del_init (&dentry->d_alias);spin_unlock (&dentry->d_lock);@@ -802,6 +804,7 @@ void d_instantiate(struct dentry *entry,if (inode)list_add (&entry->d_alias, &inode->i_dentry); entry->d_inode = inode; +audit_update_watch (entry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (entry, inode); }@@ -978,6 +981,7 @@ struct dentry *d_splice_alias(struct ino new =__d_find_alias (inode,1 );if (new) {BUG_ON (!(new->d_flags &DCACHE_DISCONNECTED )); +audit_update_watch (new,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (new, inode);d_rehash (dentry);@@ -987,6 +991,7 @@ struct dentry *d_splice_alias(struct ino/* d_instantiate takes dcache_lock, so we do it by hand */ list_add (&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (dentry, inode);d_rehash (dentry);@@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentryif (!d_unhashed (dentry)) {atomic_inc (&dentry->d_count); found = dentry; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL ); }spin_unlock (&dentry->d_lock);break ;@@ -1299,6 +1305,8 @@ void d_move(struct dentry * dentry, struspin_lock (&target->d_lock); } +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); +/* Move the dentry to the target hash queue, if on different bucket */ if (dentry->d_flags &DCACHE_UNHASHED ) goto already_unhashed;@@ -1332,6 +1340,7 @@ already_unhashed:list_add (&target->d_child, &target->d_parent->d_subdirs); } +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );list_add (&dentry->d_child, &dentry->d_parent->d_subdirs);spin_unlock (&target->d_lock);spin_unlock (&dentry->d_lock);diff -Nurp
audit-2.6.git/fs/inode.caudit-2.6.git~tc/fs/inode.cfs/inode.c +5
-2@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* * This is needed for the following functions: @@ -172,6 +173,7 @@ void destroy_inode(struct inode *inode) {if (inode_has_buffers (inode))BUG (); +audit_inode_free (inode);security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode) inode->i_sb->s_op->destroy_inode (inode);@@ -257,7 +259,7 @@ void clear_inode(struct inode *inode)bd_forget (inode);if (inode->i_cdev)cd_forget (inode); - inode->i_state =I_CLEAR ; + inode->i_state =I_CLEAR | (inode->i_state &I_AUDIT ); }EXPORT_SYMBOL (clear_inode);@@ -1009,7 +1011,7 @@ void generic_delete_inode(struct inode *hlist_del_init (&inode->i_hash);spin_unlock (&inode_lock);wake_up_inode (inode); -if (inode->i_state !=I_CLEAR ) +if ((inode->i_state & ~I_AUDIT ) !=I_CLEAR )BUG ();destroy_inode (inode); }@@ -1323,6 +1325,7 @@ void __init inode_init(unsigned long mem inode_cachep =kmem_cache_create ("inode_cache" ,sizeof (struct inode ),0 ,SLAB_RECLAIM_ACCOUNT |SLAB_PANIC , init_once,NULL );set_shrinker (DEFAULT_SEEKS , shrink_icache_memory); +audit_filesystem_init ();/* Hash may have been set up in inode_init_early */ if (!hashdist)diff -Nurp
audit-2.6.git/fs/namei.caudit-2.6.git~tc/fs/namei.c
fs/namei.c int generic_permission(struct inode *ino · line 184
Quoted 6 lines
fs/namei.c +3
-0@@ -179,6 +179,9 @@ int generic_permission(struct inode *ino int (*check_acl)(struct inode *inode, int mask)) {umode_t mode = inode->i_mode; + +if (audit_notify_watch (inode, mask)) +return -ENOMEM ;
Why here? Filesystems that don't use generic_permission() won't get watched.
Quoted 13 lines
fs/namei.c +3
-0if (current->fsuid == inode->i_uid) mode >>=6 ;@@ -358,6 +361,9 @@ static inline int exec_permission_lite(sif (inode->i_op && inode->i_op->permission)return -EAGAIN ; +if (audit_notify_watch (inode,MAY_EXEC )) +return -ENOMEM ; +if (current->fsuid == inode->i_uid) mode >>=6 ;else if (in_group_p (inode->i_gid))
fs/namei.c static inline int may_delete(struct inod · line 1183
Quoted 6 lines
fs/namei.c +3
-0@@ -1172,6 +1178,10 @@ static inline int may_delete(struct inodBUG_ON (victim->d_parent->d_inode != dir); + error =audit_notify_watch (victim->d_inode,MAY_WRITE ); +if (error) +return error;
This looks like you'd potentially get two audit records, one for file and one for parent dir (the second coming from normal permission() call below). Intentional?
fs/namei.c int vfs_create(struct inode *dir, struct · line 1309
Quoted 10 lines
fs/namei.c +3
-0+ error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;@@ -1295,6 +1305,8 @@ int vfs_create(struct inode *dir, structreturn error;DQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );
This doesn't look right. The thing was created, but just because audit_notify_watch() failed, dnotify and security modules are never informed.
fs/namei.c int vfs_mknod(struct inode *dir, struct · line 1617
Quoted 9 lines
fs/namei.c +2
-0if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_create (dir, dentry, mode);@@ -1601,6 +1613,8 @@ int vfs_mknod(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );
same here.
fs/namei.c int vfs_mkdir(struct inode *dir, struct · line 1692
Quoted 9 lines
fs/namei.c +2
-0if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_mknod (dir, dentry, mode, dev);@@ -1674,6 +1688,8 @@ int vfs_mkdir(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );
and here
fs/namei.c int vfs_symlink(struct inode *dir, struc · line 1934
Quoted 9 lines
fs/namei.c +2
-0if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_mkdir (dir,dentry, mode);@@ -1914,6 +1930,8 @@ int vfs_symlink(struct inode *dir, strucDQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname); +if (!error) + error =audit_notify_watch (dentry->d_inode,MAY_WRITE );
and the rest of these...
Quoted 55 lines
fs/namei.c +11
-3if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_symlink (dir, dentry, oldname);@@ -1987,6 +2005,8 @@ int vfs_link(struct dentry *old_dentry,DQUOT_INIT (dir); error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem); +if (!error) + error =audit_notify_watch (new_dentry->d_inode,MAY_WRITE );if (!error) {inode_dir_notify (dir,DN_CREATE );security_inode_post_link (old_dentry, dir, new_dentry);@@ -2109,11 +2129,13 @@ static int vfs_rename_dir(struct inode *d_rehash (new_dentry);dput (new_dentry); } -if (!error) { +if (!error)d_move (old_dentry,new_dentry); + + error =audit_notify_watch (old_dentry->d_inode,MAY_WRITE ); +if (!error)security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); - }return error; }@@ -2139,8 +2161,12 @@ static int vfs_rename_other(struct inode/* The following d_move() should become unconditional */ if (!(old_dir->i_sb->s_type->fs_flags &FS_ODD_RENAME ))d_move (old_dentry, new_dentry); -security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); } + + error =audit_notify_watch (old_dentry->d_inode,MAY_WRITE ); +if (!error) +security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); +if (target)up (&target->i_sem);dput (new_dentry);diff -Nurp audit-2.6.git/init/Kconfig audit-2.6.git~tc/init/Kconfig
init/Kconfig +1
-0@@ -166,6 +166,7 @@ config AUDIT bool "Auditing support" depends on NET default y if SECURITY_SELINUX + default n help Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for
init/Kconfig config AUDITSYSCALL · line 180
Quoted 4 lines
init/Kconfig +1
-0@@ -176,11 +177,22 @@ config AUDITSYSCALL bool "Enable system-call auditing support" depends on AUDIT && (X86 || PPC || PPC64 || ARCH_S390 || IA64 || UML) default y if SECURITY_SELINUX + default n
Why these default changes?
Quoted 21 lines
init/Kconfig +10
-0help Enable low-overhead system-call auditing infrastructure that can be used independently or with another kernel subsystem, such as SELinux. +config AUDITFILESYSTEM + bool "Enable file system auditing support" + depends on AUDITSYSCALL + default n + help + Enable file system auditing for regular files and directories. + When a targeted file or directory is accessed, an audit record + is generated describing the inode accessed, how it was accessed, + and by whom (ie: pid and system call). + config HOTPLUG bool "Support for hot-pluggable devices" if !ARCH_S390 default ARCH_S390diff -Nurp
audit-2.6.git/kernel/auditfs.caudit-2.6.git~tc/kernel/auditfs.c
kernel/auditfs.c line 224
Quoted 2 lines
kernel/auditfs.c +2
-0+/* Convert a watch_transport structure into a kernel audit_watch structure. */ +static inline struct audit_watch *audit_to_watch (void *memblk)
This should not be untyped.
Quoted 18 lines
kernel/auditfs.c +18
-0+{ +unsigned int offset; +struct watch_transport *t; +struct audit_watch *watch; + + watch =audit_watch_alloc (); +if (!watch) + goto audit_to_watch_exit; + + t = memblk; + + watch->w_perms = t->perms; + + offset =sizeof (struct watch_transport ); + watch->w_filterkey =kmalloc (t->fklen+1 ,GFP_KERNEL ); +if (!watch->w_filterkey) + goto audit_to_watch_fail; + watch->w_filterkey[t->fklen] =0 ;
kernel/auditfs.c line 252
Quoted 10 lines
kernel/auditfs.c +10
-0+memcpy (watch->w_filterkey, memblk + offset, t->fklen); + + offset += t->fklen; + watch->w_path =kmalloc (t->pathlen+1 ,GFP_KERNEL ); +if (!watch->w_path) + goto audit_to_watch_fail; + watch->w_path[t->pathlen] =0 ; +memcpy (watch->w_path, memblk + offset, t->pathlen); + + goto audit_to_watch_exit;
typically label and return would just go here.
audit_to_watch_exit:
return watch;
Quoted 12 lines
kernel/auditfs.c +8
-0+ +audit_to_watch_fail: +audit_watch_free (watch); + watch =NULL ; +audit_to_watch_exit: +return watch; +} +diff -Nurp
audit-2.6.git/kernel/auditsc.caudit-2.6.git~tc/kernel/auditsc.c
kernel/auditsc.c struct audit_aux_data { · line 106
Quoted 5 lines
kernel/auditsc.c +0
-2@@ -102,8 +102,6 @@ struct audit_aux_data {int type; }; -#define AUDIT_AUX_IPCPERM 0 -
This looks unrelated (albeit unused)
Quoted 22 lines
kernel/auditsc.c +11
-0struct audit_aux_data_ipcctl {struct audit_aux_data d;struct ipc_perm p;@@ -131,6 +129,17 @@ struct audit_aux_data_path {struct vfsmount *mnt; }; +struct audit_aux_data_watched { +struct audit_aux_data link; +struct hlist_head watches; +unsigned long ino; +int mask; +uid_t uid; +gid_t gid; +dev_t dev; +dev_t rdev; +}; +/* The per-task audit context. */ struct audit_context {int in_syscall;/* 1 if task is in a syscall */
kernel/auditsc.c static inline int audit_del_rule(struct · line 260
Quoted 4 lines
kernel/auditsc.c +1
-0@@ -248,6 +257,7 @@ static inline int audit_del_rule(structreturn -EFAULT ;/* No matching rule */ } +#ifdef CONFIG_NET
We've dropped these #ifdefs, we rely on netlink and skb's.
Quoted 50 lines
kernel/auditsc.c +41
-0/* Copy rule from user-space to kernel-space. Called during * AUDIT_ADD. */ static int audit_copy_rule (struct audit_rule *d,struct audit_rule *s)@@ -328,6 +338,7 @@ int audit_receive_filter(int type, int preturn err; } +#endif +#ifdef CONFIG_AUDITFILESYSTEM +/* This has to be here instead of in auditfs.c, because it needs to +see the audit context */ +int auditfs_attach_wdata (struct inode *inode,struct hlist_head *watches, +int mask) +{ +struct audit_context *context = current->audit_context; +struct audit_aux_data_watched *ax; +struct audit_watch *watch; +struct audit_watch_info *this, *winfo; +struct hlist_node *pos, *tmp; + + ax =kmalloc (sizeof (*ax),GFP_KERNEL ); +if (!ax) +return -ENOMEM ; + +if (context->in_syscall && !context->auditable) + context->auditable =1 ; + +INIT_HLIST_HEAD (&ax->watches); + +hlist_for_each_entry (watch, pos, watches, w_watched) { + winfo =kmalloc (sizeof (struct audit_watch_info ),GFP_KERNEL ); +if (!winfo) + goto auditfs_attach_wdata_fail; +if (mask && (watch->w_perms && !(watch->w_perms&mask))) +continue ; + winfo->watch =audit_watch_get (watch); +hlist_add_head (&winfo->node, &ax->watches); + } + + ax->mask = mask; + ax->ino = inode->i_ino; + ax->uid = inode->i_uid; + ax->gid = inode->i_gid; + ax->dev = inode->i_sb->s_dev; + ax->rdev = inode->i_rdev; + + ax->link.type =AUDIT_FS_INODE ; + ax->link.next = context->aux;
kernel/auditsc.c int audit_receive_filter(int type, int p · line 1265
Quoted 10 lines
kernel/auditsc.c +10
-0+ context->aux = (void *)ax; + +return 0 ; + +auditfs_attach_wdata_fail: +hlist_for_each_entry_safe (this, pos, tmp, &ax->watches, node) { +hlist_del (&this->node); +audit_watch_put (this->watch); +kfree (this); + }
looks like it leaks 'ax' in error path
Quoted 2 lines
kernel/auditsc.c +2
-0+return -ENOMEM ; +}
Quoted 3 lines · Christoph Hellwig
most of this looks like a copy & paste of the inotify code. Could you please arrange with the inotify folks to merge the patches?
Christoph,
Thank you for the response.
When we first started this project we had assessed whether or not inotify would be a feasible starting point, but ultimately rejected it. What it came down to was that the objectives of inotify were distinctly different from the objectives of auditfs. These distinctions were rooted in the very design of inotify. Why? Inotify is not a generic fs events notification subsystem.
Some of the cursory problems with using inotify which would need changing were:
1) Coverage
Inotify is only interested in a portion of the fs ops that we are interested in. Most notably, we need the ability to generate records every time the [exec_]permission[_lite]() function is called.
2) Processing
The processing of events has to occur in the kernel, not in user space. Inotify collects events and tosses them back over the fence for processing. We cannot introduce any window of opportunity for audit subversion.
You see, auditfs is interested in the inode at location+name, whatever it may be when the auditable event occurs. Thus, we've had to hook dcache to help us with this processing. This is something Inotify does not care about nor should it.
Inotify will resolve a path to its inode and watch _that_ inode, we're saying "watch any inode at this path"
To build auditfs on top of inotify would require watching directories for events on its children and processing the events in the kernel (and not simply tossing them over the fence to user space). This gets very... hackish ;)
3) Queue
Depending on the work, there may be a requirement that no audit record can be lost. The message queue structure of inotify has the potential to overflow and drop events. This is a no-go for us, but perfectly reasonable for inotify.
To make this work for us we'd have to hook the queueing functions to forward events on to the audit subsystem. This would basically mean fitting a system that has been designed for interaction with userspace to also have interaction with other parts of the kernel... again, hackish.
4) Context
We require information regarding the process and system call associated with the fs event. This is not necessarily evident from where the event occurred (ie: permission()). Inotify would have to hook into the audit subsystem to get at this information.
The only place that the two projects converge is in fs/namei.c where they both collect information about events. One project notifies the audit subsystem and the other notifies user space. I think it is here where a collaboration could take place without skewing the objectives of either project.
-tim
Quoted 5 lines · Timothy R. Chavez
1) Coverage
Inotify is only interested in a portion of the fs ops that we are interested in. Most notably, we need the ability to generate records every time the [exec_]permission[_lite]() function is called.
That still means you could use the same infrastructure and now forward these events to inotify-ish clients, right?
Quoted 5 lines
2) Processing
The processing of events has to occur in the kernel, not in user space. Inotify collects events and tosses them back over the fence for processing. We cannot introduce any window of opportunity for audit subversion.
still means it could be a client to the same core event processing code and filesystem hooks, right?
Quoted 5 lines
You see, auditfs is interested in the inode at location+name, whatever it may be when the auditable event occurs. Thus, we've had to hook dcache to help us with this processing. This is something Inotify does not care about nor should it.
Nor should the auditing code. There's no way to find a good name for an inode _at all_, and only the last pathname component for a dentry. If you try anything else your code is broken and will not get anywhere the mainline kernel.
Quoted 11 lines
3) Queue
Depending on the work, there may be a requirement that no audit record can be lost. The message queue structure of inotify has the potential to overflow and drop events. This is a no-go for us, but perfectly reasonable for inotify.
To make this work for us we'd have to hook the queueing functions to forward events on to the audit subsystem. This would basically mean fitting a system that has been designed for interaction with userspace to also have interaction with other parts of the kernel... again, hackish.
Or build inotify ontop of your system. It's not like inotify is perfect as-is (it's not, the userland interface is extremly horrible)
Quoted 9 lines · Christoph Hellwig
Quoted 5 lines · Timothy R. Chavez
1) Coverage
Inotify is only interested in a portion of the fs ops that we are interested in. Most notably, we need the ability to generate records every time the [exec_]permission[_lite]() function is called.
That still means you could use the same infrastructure and now forward these events to inotify-ish clients, right?
I believe a common infrastructure is appropriate, yes. I think it should forward on events to whomever is interested, yes ;)
Quoted 9 lines
Quoted 6 lines
2) Processing
The processing of events has to occur in the kernel, not in user space. Inotify collects events and tosses them back over the fence for processing. We cannot introduce any window of opportunity for audit subversion.
still means it could be a client to the same core event processing code and filesystem hooks, right?
Yes, this too is workable. I do not believe, however, that this should be merged into inotify's logic though. I believe that auditfs and inotify would each be their own discreet client. I think a common set of filesystem hooks is appropriate, but I think processing of the events being channeled through those hooks should be left up to the recipient/client (like it already is).
Quoted 11 lines
Quoted 6 lines
You see, auditfs is interested in the inode at location+name, whatever it may be when the auditable event occurs. Thus, we've had to hook dcache to help us with this processing. This is something Inotify does not care about nor should it.
Nor should the auditing code. There's no way to find a good name for an inode
_atall_, and only the last pathname component for a dentry. If you try anything else your code is broken and will not get anywhere the mainline kernel.
Sure it should :) The point is, you have a security relevant file like /etc/shadow at a well defined path and you want to audit it. If you rely solely on (inode,dev)- based filtering, you'll lose audit before your "passwd" completes its execution because the underlying inode associated with "shadow" would has changed. This is no-good.
There are other locations+names you'd also like to audit as well, such as the audit logs themselves, configuration files for the various trusted programs, etc.
Quoted 1 line
From an auditing stand point it makes perfect sense to audit a database as
such. Or at the very least, have the option to do so. Where auditing strictly on (inode,device) makes sense or meets the administrators goals, it is encouraged.
However, being able to have a complete and comprehensive record of a security relevant database is crucial, I think ;)
Quoted 8 lines
Quoted 8 lines
3) Queue
Depending on the work, there may be a requirement that no audit record can be lost. The message queue structure of inotify has the potential to overflow and drop events. This is a no-go for us, but perfectly reasonable for inotify.
Quoted 7 lines
Quoted 4 lines
To make this work for us we'd have to hook the queueing functions to forward events on to the audit subsystem. This would basically mean fitting a system that has been designed for interaction with userspace to also have interaction with other parts of the kernel... again, hackish.
Or build inotify ontop of your system. It's not like inotify is perfect as-is (it's not, the userland interface is extremly horrible)
I think inotify's has sufficiently distinct goals that both projects would benefit more from a common framework rather then a merger. I think Robert would agree.
I really do appreciate your comments. The main goal here is to give the mainline kernel a robust and useful audit subsystem.
-tim
Quoted 2 lines · Chris Wright
Perhaps you could describe any specific reasons inotify isn't sufficient, as it's similar in many ways.
Hi Chris,
I replied to Cristoph with my attempt at explaining this to some degree. I believe that the objectives of both auditfs and inotify are actually quite different and should not be implemented in terms of one another or merged. However, they do share some commonality in that they are both interested in receiving event notification from the file system. In this light, I do believe that having some generic hooking framework that inotify, auditfs, and whatever else comes in the future can use to mitigate duplicating code and function while not compromising objective.
Quoted 3 lines
Also, could drop some of that extra debugging code (heh, like that MKDEV in
audit_inode_free;-)
Done.
Quoted 3 lines
Anyway, some quick comments below...
Thanks, this patch incorporates most of those comments.
-tim
fs/dcache.c +9 -0
@@ -32,6 +32,7 @@#include <linux/seqlock.h> #include <linux/swap.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* #define DCACHE_DEBUG 1 */
@@ -97,6 +98,7 @@ static inline void dentry_iput(struct de {struct inode *inode = dentry->d_inode;if (inode) { +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); dentry->d_inode =NULL ;list_del_init (&dentry->d_alias);spin_unlock (&dentry->d_lock);
@@ -802,6 +804,7 @@ void d_instantiate(struct dentry *entry,if (inode)list_add (&entry->d_alias, &inode->i_dentry); entry->d_inode = inode; +audit_update_watch (entry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (entry, inode); }
@@ -978,6 +981,7 @@ struct dentry *d_splice_alias(struct ino new =__d_find_alias (inode,1 );if (new) {BUG_ON (!(new->d_flags &DCACHE_DISCONNECTED )); +audit_update_watch (new,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (new, inode);d_rehash (dentry);
@@ -987,6 +991,7 @@ struct dentry *d_splice_alias(struct ino/* d_instantiate takes dcache_lock, so we do it by hand */ list_add (&dentry->d_alias, &inode->i_dentry); dentry->d_inode = inode; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );spin_unlock (&dcache_lock);security_d_instantiate (dentry, inode);d_rehash (dentry);
@@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentryif (!d_unhashed (dentry)) {atomic_inc (&dentry->d_count); found = dentry; +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL ); }spin_unlock (&dentry->d_lock);break ;
@@ -1299,6 +1305,8 @@ void d_move(struct dentry * dentry, struspin_lock (&target->d_lock); } +audit_update_watch (dentry,AUDIT_UPDATE_REMOVE ); +/* Move the dentry to the target hash queue, if on different bucket */ if (dentry->d_flags &DCACHE_UNHASHED ) goto already_unhashed;
@@ -1332,6 +1340,7 @@ already_unhashed:list_add (&target->d_child, &target->d_parent->d_subdirs); } +audit_update_watch (dentry,AUDIT_UPDATE_NORMAL );list_add (&dentry->d_child, &dentry->d_parent->d_subdirs);spin_unlock (&target->d_lock);spin_unlock (&dentry->d_lock);
fs/inode.c +5 -2
@@ -21,6 +21,7 @@#include <linux/pagemap.h> #include <linux/cdev.h> #include <linux/bootmem.h> +#include <linux/audit.h> /* * This is needed for the following functions:
@@ -172,6 +173,7 @@ void destroy_inode(struct inode *inode) {if (inode_has_buffers (inode))BUG (); +audit_inode_free (inode);security_inode_free (inode);if (inode->i_sb->s_op->destroy_inode) inode->i_sb->s_op->destroy_inode (inode);
@@ -257,7 +259,7 @@ void clear_inode(struct inode *inode)bd_forget (inode);if (inode->i_cdev)cd_forget (inode); - inode->i_state =I_CLEAR ; + inode->i_state =I_CLEAR | (inode->i_state &I_AUDIT ); }EXPORT_SYMBOL (clear_inode);
@@ -1009,7 +1011,7 @@ void generic_delete_inode(struct inode *hlist_del_init (&inode->i_hash);spin_unlock (&inode_lock);wake_up_inode (inode); -if (inode->i_state !=I_CLEAR ) +if ((inode->i_state & ~I_AUDIT ) !=I_CLEAR )BUG ();destroy_inode (inode); }
@@ -1323,6 +1325,7 @@ void __init inode_init(unsigned long mem inode_cachep =kmem_cache_create ("inode_cache" ,sizeof (struct inode ),0 ,SLAB_RECLAIM_ACCOUNT |SLAB_PANIC , init_once,NULL );set_shrinker (DEFAULT_SEEKS , shrink_icache_memory); +audit_filesystem_init ();/* Hash may have been set up in inode_init_early */ if (!hashdist)
fs/namei.c +13 -0
@@ -224,6 +224,8 @@ int generic_permission(struct inode *inoint permission (struct inode *inode,int mask,struct nameidata *nd) {int retval, submask; + +audit_notify_watch (inode, mask);if (mask &MAY_WRITE ) {umode_t mode = inode->i_mode;
@@ -358,6 +360,8 @@ static inline int exec_permission_lite(sif (inode->i_op && inode->i_op->permission)return -EAGAIN ; +audit_notify_watch (inode,MAY_EXEC ); +if (current->fsuid == inode->i_uid) mode >>=6 ;else if (in_group_p (inode->i_gid))
@@ -1172,6 +1176,8 @@ static inline int may_delete(struct inodBUG_ON (victim->d_parent->d_inode != dir); +audit_notify_watch (victim->d_inode,0 ); + error =permission (dir,MAY_WRITE |MAY_EXEC ,NULL );if (error)return error;
@@ -1296,6 +1302,7 @@ int vfs_create(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->create (dir, dentry, mode, nd);if (!error) { +audit_notify_watch (dentry->d_inode,MAY_WRITE );inode_dir_notify (dir,DN_CREATE );security_inode_post_create (dir, dentry, mode); }
@@ -1602,6 +1609,7 @@ int vfs_mknod(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mknod (dir, dentry, mode, dev);if (!error) { +audit_notify_watch (dentry->d_inode,MAY_WRITE );inode_dir_notify (dir,DN_CREATE );security_inode_post_mknod (dir, dentry, mode, dev); }
@@ -1675,6 +1683,7 @@ int vfs_mkdir(struct inode *dir, structDQUOT_INIT (dir); error = dir->i_op->mkdir (dir, dentry, mode);if (!error) { +audit_notify_watch (dentry->d_inode,MAY_WRITE );inode_dir_notify (dir,DN_CREATE );security_inode_post_mkdir (dir,dentry, mode); }
@@ -1915,6 +1924,7 @@ int vfs_symlink(struct inode *dir, strucDQUOT_INIT (dir); error = dir->i_op->symlink (dir, dentry, oldname);if (!error) { +audit_notify_watch (dentry->d_inode,MAY_WRITE );inode_dir_notify (dir,DN_CREATE );security_inode_post_symlink (dir, dentry, oldname); }
@@ -1988,6 +1998,7 @@ int vfs_link(struct dentry *old_dentry, error = dir->i_op->link (old_dentry, dir, new_dentry);up (&old_dentry->d_inode->i_sem);if (!error) { +audit_notify_watch (new_dentry->d_inode,MAY_WRITE );inode_dir_notify (dir,DN_CREATE );security_inode_post_link (old_dentry, dir, new_dentry); }
@@ -2111,6 +2122,7 @@ static int vfs_rename_dir(struct inode * }if (!error) {d_move (old_dentry,new_dentry); +audit_notify_watch (old_dentry->d_inode,MAY_WRITE );security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); }
@@ -2139,6 +2151,7 @@ static int vfs_rename_other(struct inode/* The following d_move() should become unconditional */ if (!(old_dir->i_sb->s_type->fs_flags &FS_ODD_RENAME ))d_move (old_dentry, new_dentry); +audit_notify_watch (old_dentry->d_inode,MAY_WRITE );security_inode_post_rename (old_dir, old_dentry, new_dir, new_dentry); }if (target)
include/linux/audit.h +74 -0
114 lines not on this page · raw message
include/linux/fs.h +2 -0
@@ -225,6 +225,7 @@ struct poll_table_struct;struct kstatfs ;struct vm_area_struct ;struct vfsmount ; +struct audit_inode_data ;/* Used to be a macro which just called the function, now just a function */ extern void update_atime (struct inode *);
@@ -1024,6 +1025,7 @@ struct super_operations {#define I_FREEING 16#define I_CLEAR 32#define I_NEW 64 +#define I_AUDIT 128#define I_DIRTY (I_DIRTY_SYNC | I_DIRTY_DATASYNC | I_DIRTY_PAGES)
init/Kconfig +10 -0
@@ -181,6 +181,16 @@ config AUDITSYSCALL can be used independently or with another kernel subsystem, such as SELinux. +config AUDITFILESYSTEM + bool "Enable file system auditing support" + depends on AUDITSYSCALL + default n + help + Enable file system auditing for regular files and directories. + When a targeted file or directory is accessed, an audit record + is generated describing the inode accessed, how it was accessed, + and by whom (ie: pid and system call). + config HOTPLUG bool "Support for hot-pluggable devices" if !ARCH_S390 default ARCH_S390
kernel/Makefile +1 -0
@@ -24,6 +24,7 @@ obj-$(CONFIG_IKCONFIG_PROC) += configs.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_AUDIT) += audit.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o +obj-$(CONFIG_AUDITFILESYSTEM) += auditfs.o obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_SYSFS) += ksysfs.o obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
kernel/audit.c +14 -0
@@ -350,6 +350,9 @@ static int audit_netlink_ok(kernel_cap_tcase AUDIT_SET :case AUDIT_ADD :case AUDIT_DEL : +case AUDIT_WATCH_LIST : +case AUDIT_WATCH_INS : +case AUDIT_WATCH_REM :case AUDIT_SIGNAL_INFO :if (!cap_raised (eff_cap,CAP_AUDIT_CONTROL )) err = -EPERM ;
@@ -454,6 +457,17 @@ static int audit_receive_msg(struct sk_b err =audit_receive_filter (nlh->nlmsg_type,NETLINK_CB (skb).pid, uid, seq, data, loginuid);break ; +case AUDIT_WATCH_LIST : + err =audit_list_watches (pid, seq); +break ; +case AUDIT_WATCH_INS : +case AUDIT_WATCH_REM : +if (nlh->nlmsg_len <sizeof (struct watch_transport )) +return -EINVAL ; + err =audit_receive_watch (nlh->nlmsg_type, +NETLINK_CB (skb).pid, + uid, seq, data, loginuid); +break ;case AUDIT_SIGNAL_INFO : sig_data.uid = audit_sig_uid; sig_data.pid = audit_sig_pid;
kernel/auditfs.c +844 -0
844 lines not on this page · raw message
kernel/auditsc.c +105 -1
139 lines not on this page · raw message
security/selinux/nlmsgtab.c +3 -0
@@ -98,6 +98,9 @@ static struct nlmsg_perm nlmsg_audit_per { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ }, + { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, + { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV }, };