remove struct audit_message?
by Timothy R. Chavez
Hello,
I'm just browsing around and noticed that struct audit_message is defined in
audit.h but is no longer used anywhere? Probably obsoleted at some point?
Should we get rid of this structure? Am I missing something?
-tim
19 years, 7 months
ausearch errors (audit 0.8)
by Daniel H. Jones
I'm running kernel.35 with the audit 0.8 package and I see these problems.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ausearch -p 0 returns records that do not have a pid of 0.
----
time->Fri May 13 15:06:59 2005
type=CONFIG_CHANGE msg=audit(1116014819.245:0): audit_enabled=1 old=1 by
auid 4294967295
----
time->Fri May 13 15:06:59 2005
type=CONFIG_CHANGE msg=audit(1116014819.457:0): audit_backlog_limit=256
old=256 by auid 4294967295
----
time->Fri May 13 15:07:11 2005
type=CONFIG_CHANGE msg=audit(1116014831.958:0): auid 4294967295 added an
audit rule
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ausearch -ul 0 returns records that do not have a login uid of 0.
type=DAEMON_START msg=audit(1116014676.856:314) auditd start, ver=0.8,
format=raw, uid=0 auditd pid=7489
type=CONFIG_CHANGE msg=audit(1116014677.059:0): audit_enabled=1 old=1 by
auid 0
type=CONFIG_CHANGE msg=audit(1116014677.271:0): audit_backlog_limit=256
old=256 by auid 0
type=CONFIG_CHANGE msg=audit(1116014679.581:0): auid 0 added an audit rule
type=DAEMON_END msg=audit(1116014685.651:315) auditd normal halt,
sending pid=7503 uid=0 auditd pid=7489
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ausearch -ua xxx does not find records with a uid or effective uid of xxx.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ausearch -x /bin/chmod does not find records containing the executable name.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
ausearch -ul 4294967295 returns records that do not match the login uid.
type=DAEMON_START msg=audit(1116014693.044:454) auditd start, ver=0.8,
format=raw, uid=0 auditd pid=7640
type=CONFIG_CHANGE msg=audit(1116014693.256:0): auid 0 removed an audit rule
type=CONFIG_CHANGE msg=audit(1116014693.249:0): audit_enabled=1 old=1 by
auid 0
type=CONFIG_CHANGE msg=audit(1116014693.664:0): audit_backlog_limit=256
old=256 by auid 0
type=LOGIN msg=audit(1116014701.630:0): login pid=7653 uid=0 old
loginuid=4294967295 new loginuid=503
type=USER msg=audit(1116014701.834:0): user pid=7653 uid=0 length=132
loginuid=503 msg='PAM session open: user=ausrch_u exe=/usr/sbin/sshd
(hostname=localhost.localdomain, addr=127.0.0.1, terminal=ssh
result=Success)'
type=DAEMON_END msg=audit(1116014715.222:455) auditd normal halt,
sending pid=7684 uid=0 auditd pid=7640
--
Thanks,
Dan Jones
IBM Linux Technology Center, Security
512-838-1794 (T/L 678-1794)
hotrats(a)us.ibm.com
19 years, 7 months
[RFC][PATCH] (#7U5) [2-6.12-rc3-mm3] file system auditing
by Timothy R. Chavez
Hello,
I've pulled the latest git-audit tree but I need to take some time to make a
clean patch against it. So, in the meantime, I've patched the 2.6.12-rc3-mm3
tree for comments. Patch appears at bottom of message.
Let's discuss locking first. I've tried to incorporate the BANGing feedback I
received *slaps knee* as well as making some adjustments of my own.
* I've removed the path_lookup() that was nestled between the global
audit_master_watchlist_lock spinlock in audit_to_transport. This was
admittedly stupid and is now an exercise for user space.
* I've completely removed the audit_master_watchlist_lock spinlock to protect
the master watchlist, in favor of RCU locking. To protect against concurrent
watch removals from the master watchlist, we conveniently use the local
data->lock rw_lock in place of another spinlock. Thus, only one removal can
occur at a time. To prevent the "rug being pulled out form under us" while
we're reading the master watchlist, we defer putting our reference back to
the wentry until the current reader is finished.
I'm thinking about removing the local locks on inode audit data in favor of a
global spinlock in auditfs to dispell contention between multiple writers and
switching its list primitives to RCU as well. Any opinions on this matter?
* Since we can only ever have one insertion into the watchlist at a time, we
never have to worry about racing with another insertion.. so the locking here
was removed. Objections?
And now, the pinning.
* struct audit_wentry was augmented with struct dentry *w_dentry which holds a
reference to the pinned dentry.
* Three new functions were added: audit_pin, audit_unpin, and
audit_dentry_unpin.
audit_pin:
Grab a reference to a dentry and store it at its wentry->w_dentry. The only
time we can truly know when to pin a dentry is in the audit_update_watch()
hook.
audit_unpin:
Put back the wentry->w_dentry reference. For most cases we're able to unpin
memory from within the auditfs mechanism itself. For instance, when the
watch is removed, but the file still it exists, or when the watch is removed
and while the file does not exist.
audit_dentry_unpin:
This hook is called from d_delete so that we may release the wentry->w_dentry
should it have a reference... otherwise very bad things will happen :-):-) This
is to cover the case when the watch exists, but the file is "removed" (when
the dentry is deleted), which cannot be handled internally by audit_unpin.
Finally, there were some other minor fixes and cleanups introduced by Steve
which I've absorbed (and cleaned up where necessary).
-tim
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/dcache.c linux-2.6.12-rc3-mm3~audit/fs/dcache.c
--- linux-2.6.12-rc3-mm3~orig/fs/dcache.c 2005-05-06 13:05:08.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/dcache.c 2005-05-10 23:37:50.000000000 -0500
@@ -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, 1);
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, 0);
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, 0);
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, 0);
spin_unlock(&dcache_lock);
security_d_instantiate(dentry, inode);
d_rehash(dentry);
@@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentry
if (!d_unhashed(dentry)) {
atomic_inc(&dentry->d_count);
found = dentry;
+ audit_update_watch(found, 0);
}
spin_unlock(&dentry->d_lock);
break;
@@ -1170,6 +1176,9 @@ void d_delete(struct dentry * dentry)
*/
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
+
+ audit_dentry_unpin(dentry);
+
if (atomic_read(&dentry->d_count) == 1) {
dentry_iput(dentry);
return;
@@ -1299,6 +1308,8 @@ void d_move(struct dentry * dentry, stru
spin_lock(&target->d_lock);
}
+ audit_update_watch(dentry, 1);
+
/* Move the dentry to the target hash queue, if on different bucket */
if (dentry->d_flags & DCACHE_UNHASHED)
goto already_unhashed;
@@ -1332,6 +1343,7 @@ already_unhashed:
list_add(&target->d_child, &target->d_parent->d_subdirs);
}
+ audit_update_watch(dentry, 0);
list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
spin_unlock(&target->d_lock);
spin_unlock(&dentry->d_lock);
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/inode.c linux-2.6.12-rc3-mm3~audit/fs/inode.c
--- linux-2.6.12-rc3-mm3~orig/fs/inode.c 2005-05-06 13:05:08.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/inode.c 2005-05-06 14:29:04.000000000 -0500
@@ -22,6 +22,7 @@
#include <linux/cdev.h>
#include <linux/bootmem.h>
#include <linux/inotify.h>
+#include <linux/audit.h>
/*
* This is needed for the following functions:
@@ -139,9 +140,11 @@ static struct inode *alloc_inode(struct
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
+ inode->i_audit = NULL;
inode->i_security = NULL;
inode->dirtied_when = 0;
- if (security_inode_alloc(inode)) {
+ if (audit_inode_alloc(inode) || security_inode_alloc(inode)) {
+ audit_inode_free(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
@@ -179,6 +182,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);
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/namei.c linux-2.6.12-rc3-mm3~audit/fs/namei.c
--- linux-2.6.12-rc3-mm3~orig/fs/namei.c 2005-05-06 13:05:09.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/namei.c 2005-05-06 14:29:04.000000000 -0500
@@ -225,6 +225,8 @@ int permission(struct inode *inode, int
{
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(s
if (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 inod
BUG_ON(victim->d_parent->d_inode != dir);
+ audit_notify_watch(victim->d_inode, MAY_WRITE);
+
error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
if (error)
return error;
@@ -1296,6 +1302,7 @@ int vfs_create(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode, nd);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_create(dir, dentry, mode);
}
@@ -1601,6 +1608,7 @@ int vfs_mknod(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mknod(dir, dentry, mode, dev);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_mknod(dir, dentry, mode, dev);
}
@@ -1674,6 +1682,7 @@ int vfs_mkdir(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_mkdir(dir, dentry->d_name.name);
security_inode_post_mkdir(dir,dentry, mode);
}
@@ -1915,6 +1924,7 @@ int vfs_symlink(struct inode *dir, struc
DQUOT_INIT(dir);
error = dir->i_op->symlink(dir, dentry, oldname);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
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);
fsnotify_create(dir, new_dentry->d_name.name);
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)
diff -Nurp linux-2.6.12-rc3-mm3~orig/include/linux/audit.h linux-2.6.12-rc3-mm3~audit/include/linux/audit.h
--- linux-2.6.12-rc3-mm3~orig/include/linux/audit.h 2005-05-06 13:05:14.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/include/linux/audit.h 2005-05-10 23:46:30.000000000 -0500
@@ -24,18 +24,29 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
#include <linux/sched.h>
#include <linux/elf.h>
+
+struct hlist_head;
+struct hlist_node;
+struct dentry;
+struct atomic_t;
+#endif
+
/* Request and reply types */
-#define AUDIT_GET 1000 /* Get status */
-#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
-#define AUDIT_LIST 1002 /* List filtering rules */
-#define AUDIT_ADD 1003 /* Add filtering rule */
-#define AUDIT_DEL 1004 /* Delete filtering rule */
-#define AUDIT_USER 1005 /* Send a message from user-space */
-#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
-#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
+#define AUDIT_GET 1000 /* Get status */
+#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
+#define AUDIT_LIST 1002 /* List filtering rules */
+#define AUDIT_ADD 1003 /* Add filtering rule */
+#define AUDIT_DEL 1004 /* Delete filtering rule */
+#define AUDIT_USER 1005 /* Send a message from user-space */
+#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
+#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
+#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
+#define AUDIT_WATCH_LIST 1009 /* List all watches */
+#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
@@ -132,6 +143,9 @@
#define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX 32
+
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
@@ -159,8 +173,46 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+/* Structure to transport watch data to and from the kernel */
+
+struct watch_transport {
+ __u32 dev_major;
+ __u32 dev_minor;
+ __u32 perms;
+ __u32 valid;
+ __u32 pathlen;
+ __u32 fklen;
+ char buf[0];
+};
+
#ifdef __KERNEL__
+/* Structure associated with inode->i_audit */
+
+struct audit_inode_data {
+ struct audit_wentry *wentry;
+ struct hlist_head watchlist;
+ rwlock_t lock;
+};
+
+struct audit_watch {
+ dev_t dev; /* Superblock device */
+ __u32 perms; /* Permissions filtering */
+ char *name; /* Watch point beneath parent */
+ char *path; /* Insertion path */
+ char *filterkey; /* An arbitrary filtering key */
+};
+
+struct audit_wentry {
+ struct rcu_head w_rcu;
+ struct hlist_node w_node;
+ struct hlist_node w_master;
+ struct audit_watch *w_watch;
+ struct dentry *w_dentry;
+ atomic_t w_count;
+
+};
+
struct audit_buffer;
struct audit_context;
struct inode;
@@ -190,6 +242,7 @@ extern void audit_get_stamp(struct audit
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_notify_watch(struct inode *inode, int mask);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -198,8 +251,32 @@ extern int audit_ipc_perms(unsigned long
#define audit_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i) do { ; } while (0)
+#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
#define audit_get_loginuid(c) ({ -1; })
#define audit_ipc_perms(q,u,g,m) ({ 0; })
+#define audit_notify_watch(i,m) ({ 0; })
+#endif
+
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_filesystem_init(void);
+extern int audit_list_watches(int pid, int seq);
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req, uid_t loginuid);
+extern int audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_update_watch(struct dentry *dentry, int remove);
+extern void audit_wentry_put(struct audit_wentry *wentry);
+extern void audit_dentry_unpin(struct dentry *dentry);
+extern struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry);
+#else
+#define audit_filesystem_init() ({ 0; })
+#define audit_list_watches(p,s) ({ -EOPNOTSUPP; })
+#define audit_receive_watch(t,p,u,s,r,l) ({ -EOPNOTSUPP; })
+#define audit_inode_alloc(i) ({ 0; })
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_update_watch(d,r) do { ; } while (0)
+#define audit_wentry_put(w) do { ; } while(0)
+#define audit_wentry_get(w) ({ 0; })
#endif
#ifdef CONFIG_AUDIT
diff -Nurp linux-2.6.12-rc3-mm3~orig/include/linux/fs.h linux-2.6.12-rc3-mm3~audit/include/linux/fs.h
--- linux-2.6.12-rc3-mm3~orig/include/linux/fs.h 2005-05-06 13:05:14.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/include/linux/fs.h 2005-05-06 14:29:04.000000000 -0500
@@ -226,6 +226,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 *);
@@ -477,6 +478,7 @@ struct inode {
struct semaphore inotify_sem; /* protects the watches list */
#endif
+ struct audit_inode_data *i_audit;
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
diff -Nurp linux-2.6.12-rc3-mm3~orig/init/Kconfig linux-2.6.12-rc3-mm3~audit/init/Kconfig
--- linux-2.6.12-rc3-mm3~orig/init/Kconfig 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/init/Kconfig 2005-05-06 14:29:04.000000000 -0500
@@ -180,6 +180,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
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/Makefile linux-2.6.12-rc3-mm3~audit/kernel/Makefile
--- linux-2.6.12-rc3-mm3~orig/kernel/Makefile 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/Makefile 2005-05-06 14:29:04.000000000 -0500
@@ -25,6 +25,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_DETECT_SOFTLOCKUP) += softlockup.o
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/audit.c linux-2.6.12-rc3-mm3~audit/kernel/audit.c
--- linux-2.6.12-rc3-mm3~orig/kernel/audit.c 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/audit.c 2005-05-10 11:41:42.000000000 -0500
@@ -112,8 +112,13 @@ static LIST_HEAD(audit_entlist);
static LIST_HEAD(audit_extlist);
/* The netlink socket is only to be read by 1 CPU, which lets us assume
- * that list additions and deletions never happen simultaneiously in
- * auditsc.c */
+ * that list additions and deletions, and watch insertions never happen
+ * simultaneiously in auditsc.c and auditfs.c respectively.
+ *
+ * Even though there can only be one watch removal via netlink at a time,
+ * there is still a chance of watch removal via audit_update_watch(). In
+ * this case, the semaphore is not enough.
+ */
static DECLARE_MUTEX(audit_netlink_sem);
/* AUDIT_BUFSIZ is the size of the temporary buffer used for formatting
@@ -321,6 +326,9 @@ static int audit_netlink_ok(kernel_cap_t
case AUDIT_SET:
case AUDIT_ADD:
case AUDIT_DEL:
+ case AUDIT_WATCH_LIST:
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
@@ -412,12 +420,19 @@ static int audit_receive_msg(struct sk_b
return -EINVAL;
/* fallthrough */
case AUDIT_LIST:
-#ifdef CONFIG_AUDITSYSCALL
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
uid, seq, data, loginuid);
-#else
- err = -EOPNOTSUPP;
-#endif
+ 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;
default:
err = -EINVAL;
@@ -559,6 +574,7 @@ static int __init audit_init(void)
audit_panic("cannot initialize netlink socket");
audit_initialized = 1;
+ audit_filesystem_init();
audit_enabled = audit_default;
audit_log(NULL, "initialized");
return 0;
@@ -586,6 +602,7 @@ int __init audit_init(void)
audit_pid = 0;
audit_initialized = 1;
+ audit_filesystem_init();
audit_enabled = audit_default;
audit_log(NULL, "initialized");
return 0;
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/auditfs.c linux-2.6.12-rc3-mm3~audit/kernel/auditfs.c
--- linux-2.6.12-rc3-mm3~orig/kernel/auditfs.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.12-rc3-mm3~audit/kernel/auditfs.c 2005-05-11 11:35:27.000000000 -0500
@@ -0,0 +1,674 @@
+/* auditfs.c -- Filesystem auditing support -*- linux-c -*-
+ * Implements filesystem auditing support, depends on kernel/auditsc.c
+ *
+ * Copyright 2005 International Business Machines Corp. (IBM)
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ * Written by Timothy R. Chavez <chavezt(a)us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <asm/uaccess.h>
+
+
+extern int audit_enabled;
+
+static kmem_cache_t *audit_watch_cache;
+static kmem_cache_t *audit_wentry_cache;
+
+/* Read-heavy list */
+HLIST_HEAD(audit_master_watchlist);
+
+
+/* Private Interface */
+
+/* Unpin the dentry stored at wentry->w_dentry. */
+static inline void audit_unpin(struct audit_wentry *wentry)
+{
+ if (wentry && wentry->w_dentry) {
+ dput(wentry->w_dentry);
+ wentry->w_dentry = NULL;
+ }
+}
+
+/* Pin the dentry and store it at wentry->w_dentry. */
+static inline void audit_pin(struct audit_wentry *wentry,
+ struct dentry *dentry)
+{
+ if (wentry && !wentry->w_dentry)
+ wentry->w_dentry = dget(dentry);
+}
+
+static inline struct audit_wentry *audit_wentry_fetch(const char *name,
+ struct audit_inode_data *data)
+{
+ struct audit_wentry *wentry, *ret = NULL;
+ struct hlist_node *pos;
+
+ hlist_for_each_entry(wentry, pos, &data->watchlist, w_node)
+ if(!strcmp(wentry->w_watch->name, name)) {
+ ret = audit_wentry_get(wentry);
+ break;
+ }
+
+ return ret;
+}
+
+static inline struct audit_wentry *audit_wentry_fetch_lock(const char *name,
+ struct audit_inode_data *data)
+{
+ struct audit_wentry *ret = NULL;
+
+ if (name && data) {
+ read_lock(&data->lock);
+ ret = audit_wentry_fetch(name, data);
+ read_unlock(&data->lock);
+ }
+
+ return ret;
+}
+
+static inline struct audit_watch *audit_watch_alloc(void)
+{
+ struct audit_watch *watch;
+
+ watch = kmem_cache_alloc(audit_watch_cache, GFP_KERNEL);
+ if (watch) {
+ watch->name = NULL;
+ watch->path = NULL;
+ watch->filterkey = NULL;
+ watch->perms = 0;
+ }
+
+ return watch;
+}
+
+static inline void audit_watch_free(struct audit_watch *watch)
+{
+ if (watch) {
+ kfree(watch->name);
+ kfree(watch->path);
+ kfree(watch->filterkey);
+ kmem_cache_free(audit_watch_cache, watch);
+ }
+}
+
+/* Convert a watch_transport structure into a kernel audit_watch structure. */
+static inline struct audit_watch *audit_to_watch(void *memblk)
+{
+ unsigned int offset;
+ struct watch_transport *t;
+ struct audit_watch *watch;
+
+ watch = audit_watch_alloc();
+ if (!watch)
+ goto audit_to_watch_exit;
+
+ t = (struct watch_transport *)memblk;
+
+ watch->perms = t->perms;
+
+ offset = sizeof(struct watch_transport);
+ watch->filterkey = kmalloc(t->fklen, GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_to_watch_fail;
+ watch->filterkey[0] = 0;
+ strncat(watch->filterkey, memblk + offset, t->fklen);
+
+ offset += t->fklen;
+ watch->path = kmalloc(t->pathlen, GFP_KERNEL);
+ if (!watch->path)
+ goto audit_to_watch_fail;
+ watch->path[0] = 0;
+ strncat(watch->path, memblk + offset, t->pathlen);
+
+ goto audit_to_watch_exit;
+
+audit_to_watch_fail:
+ audit_watch_free(watch);
+ watch = NULL;
+audit_to_watch_exit:
+ return watch;
+}
+
+/*
+ * Convert a kernel audit_watch structure into a watch_transport structure.
+ * We do this to send watch information back to user space.
+ */
+static inline void *audit_to_transport(struct audit_watch *watch, size_t size)
+{
+ unsigned int offset;
+ struct watch_transport t;
+ void *memblk;
+
+ memblk = kmalloc(size, GFP_KERNEL);
+ if (!memblk)
+ goto audit_to_transport_exit;
+
+ memset(&t, 0, sizeof(t));
+
+ t.dev_major = MAJOR(watch->dev);
+ t.dev_minor = MINOR(watch->dev);
+ t.perms = watch->perms;
+ t.pathlen = strlen(watch->path) + 1;
+ if (watch->filterkey)
+ t.fklen = strlen(watch->filterkey) + 1;
+
+ memcpy(memblk, &t, sizeof(t));
+ offset = sizeof(t);
+ memcpy(memblk + offset, watch->filterkey, t.fklen);
+ offset += t.fklen;
+ memcpy(memblk + offset, watch->path, t.pathlen);
+
+audit_to_transport_exit:
+ return memblk;
+}
+
+static inline struct audit_watch *audit_create_watch(const char *path,
+ const char *name,
+ const char *filterkey,
+ __u32 perms, dev_t dev)
+{
+ struct audit_watch *err = NULL;
+ struct audit_watch *watch = NULL;
+
+ err = ERR_PTR(-ENOMEM);
+ watch = audit_watch_alloc();
+ if (watch) {
+ watch->path = kmalloc(strlen(path)+1, GFP_KERNEL);
+ if (!watch->path)
+ goto audit_create_watch_fail;
+ strcpy(watch->path, path);
+
+ watch->name = kmalloc(strlen(name)+1, GFP_KERNEL);
+ if (!watch->name)
+ goto audit_create_watch_fail;
+ strcpy(watch->name, name);
+
+ if (filterkey) {
+ watch->filterkey = kmalloc(strlen(filterkey)+1,
+ GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_create_watch_fail;
+ strcpy(watch->filterkey, filterkey);
+ }
+
+ watch->dev = dev;
+ watch->perms = perms;
+
+ goto audit_create_watch_exit;
+ }
+
+audit_create_watch_fail:
+ audit_watch_free(watch);
+ watch = err;
+audit_create_watch_exit:
+ return watch;
+}
+
+static inline struct audit_wentry *audit_wentry_alloc(void)
+{
+ struct audit_wentry *wentry;
+
+ wentry = kmem_cache_alloc(audit_wentry_cache, GFP_KERNEL);
+ if (wentry) {
+ atomic_set(&wentry->w_count, 1);
+ wentry->w_watch = NULL;
+ wentry->w_dentry = NULL;
+ }
+
+ return wentry;
+}
+
+/*
+ * If a watch has been removed from a watchlist, we promptly unpin the
+ * dentry that was being watched, as it is no longer important to us.
+ */
+static inline void audit_wentry_free(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ audit_unpin(wentry);
+ audit_watch_free(wentry->w_watch);
+ kmem_cache_free(audit_wentry_cache, wentry);
+ }
+}
+
+/*
+ * Protected by audit_netlink_sem, thus there will never be a race on
+ * insertions as all insertion requests must come through netlink.
+ */
+static inline int audit_create_wentry(const char *path,
+ const char *name,
+ const char *filterkey,
+ __u32 perms, dev_t dev,
+ struct audit_inode_data *data)
+{
+ int ret;
+ struct audit_wentry *wentry = NULL;
+ struct audit_wentry *new = NULL;
+ struct audit_watch *watch;
+
+ ret = -EEXIST;
+ wentry = audit_wentry_fetch_lock(name, data);
+ if (wentry) {
+ audit_wentry_put(wentry);
+ goto audit_create_wentry_exit;
+ }
+
+ ret = -ENOMEM;
+ new = audit_wentry_alloc();
+ if (!new)
+ goto audit_create_wentry_exit;
+
+ watch = audit_create_watch(path, name, filterkey, perms, dev);
+ if (IS_ERR(watch)) {
+ ret = PTR_ERR(watch);
+ audit_wentry_put(new);
+ goto audit_create_wentry_exit;
+ }
+
+ ret = 0;
+
+ new->w_watch = watch;
+ audit_wentry_get(new);
+
+ hlist_add_head_rcu(&new->w_master, &audit_master_watchlist);
+ hlist_add_head(&new->w_node, &data->watchlist);
+
+audit_create_wentry_exit:
+ return ret;
+}
+
+static inline void audit_wentry_rcu_put(struct rcu_head *head)
+{
+ struct audit_wentry *wentry;
+
+ wentry = container_of(head, struct audit_wentry, w_rcu);
+ audit_wentry_put(wentry);
+}
+
+/*
+ * There's three ways we can arrive at audit_destroy_wentry. Two of the ways
+ * may cause contention with one another, while the third may not. They are:
+ *
+ * 1) An administrator has explicitly requested a watch removal
+ * 2) A directory with an "active" watchlist is rename()'ed
+ * 3) The inode is in the process of being destroyed
+ *
+ * In ways 1) and 2) the data->lock must be held to safely enter. Because
+ * of this, we're able to remove the watchlist entry from the master list
+ * as well, without needing an additional spinlock.
+ *
+ * If way 3) occurs there will no longer be any reachable references to the
+ * inode, thus there is only one way to remove this wentry, and no need to
+ * hold the data->lock.
+ */
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ hlist_del_init(&wentry->w_node);
+ audit_wentry_put(wentry);
+ hlist_del_rcu(&wentry->w_master);
+ call_rcu(&wentry->w_rcu, audit_wentry_rcu_put);
+ }
+}
+
+
+/*
+ * We only drain the watchlist in two ways. They are:
+ *
+ * 1) Upon deletion of the inode (no contention)
+ * 2) Upon rename()'ing a directory with an "active" watchlist (contention)
+ *
+ * Caller should hold data->lock where contention is possible.
+ */
+static inline void audit_drain_watchlist(struct audit_inode_data *data)
+{
+ struct audit_wentry *wentry;
+ struct hlist_node *pos, *tmp;
+
+ hlist_for_each_entry_safe(wentry, pos, tmp, &data->watchlist, w_node)
+ audit_destroy_wentry(wentry);
+}
+
+static inline struct audit_inode_data *audit_data_alloc(void)
+{
+ struct audit_inode_data *data;
+
+ data = kmalloc(sizeof(struct audit_inode_data), GFP_KERNEL);
+ if (data) {
+ data->wentry = NULL;
+ INIT_HLIST_HEAD(&data->watchlist);
+ data->lock = RW_LOCK_UNLOCKED;
+ }
+
+ return data;
+}
+
+static inline void audit_data_free(struct audit_inode_data *data)
+{
+ if (data) {
+ audit_drain_watchlist(data);
+ audit_wentry_put(data->wentry);
+ kfree(data);
+ }
+}
+
+static inline int audit_insert_watch(struct audit_watch *watch, uid_t loginuid)
+{
+ int ret;
+ struct nameidata nd;
+
+ ret = path_lookup(watch->path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto audit_insert_watch_exit;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_insert_watch_release;
+
+ ret = audit_create_wentry(watch->path,
+ nd.last.name,
+ watch->filterkey,
+ watch->perms,
+ nd.dentry->d_inode->i_sb->s_dev,
+ nd.dentry->d_inode->i_audit);
+ if (ret < 0)
+ goto audit_insert_watch_release;
+
+ audit_log(NULL, "auid %u inserted watch\n", loginuid);
+ /* __d_lookup will attach the audit data, if nd.last exists. */
+ dput(d_lookup(nd.dentry, &nd.last));
+
+audit_insert_watch_release:
+ path_release(&nd);
+audit_insert_watch_exit:
+ return ret;
+}
+
+/*
+ * We hold the data->lock here to eliminate contention between this user space
+ * request and an action taken by audit_update_watch() in which watch removal
+ * is also possible when a directory with an "active" watchlist is rename()'ed.
+ *
+ */
+static inline int audit_remove_watch(struct audit_watch *watch, uid_t loginuid)
+{
+ int ret;
+ struct nameidata nd;
+ struct audit_inode_data *data;
+ struct audit_wentry *wentry;
+
+ ret = path_lookup(watch->path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto audit_remove_watch_exit;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_remove_watch_release;
+
+ data = nd.dentry->d_inode->i_audit;
+
+ write_lock(&data->lock);
+ wentry = audit_wentry_fetch(nd.last.name, data);
+ if (!wentry) {
+ write_unlock(&data->lock);
+ goto audit_remove_watch_release;
+ }
+ audit_destroy_wentry(wentry);
+ audit_wentry_put(wentry);
+ audit_log(NULL, "auid %u removed watch\n", loginuid);
+ write_unlock(&data->lock);
+
+ ret = 0;
+
+audit_remove_watch_release:
+ path_release(&nd);
+audit_remove_watch_exit:
+ return ret;
+}
+
+struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ BUG_ON(!atomic_read(&wentry->w_count));
+ atomic_inc(&wentry->w_count);
+ }
+
+ return wentry;
+}
+
+void audit_wentry_put(struct audit_wentry *wentry)
+{
+ if (wentry && atomic_dec_and_test(&wentry->w_count))
+ audit_wentry_free(wentry);
+}
+
+/*
+ * The update hook is responsible for watching and unwatching d_inodes during
+ * their lifetimes in dcache. Each d_inode being watched is pinned in memory.
+ * As soon as a d_inode becomes unwatched (ie: dentry is destroyed, watch is
+ * unhashed / removed from watchlist, dentry is moved out of watch path).
+ *
+ * Hook appears in fs/dcache.c:
+ * d_move(),
+ * dentry_iput(),
+ * d_instantiate(),
+ * d_splice_alias()
+ * __d_lookup()
+ */
+void audit_update_watch(struct dentry *dentry, int remove)
+{
+ struct audit_wentry *wentry;
+ struct audit_inode_data *data, *parent;
+
+ if (likely(!audit_enabled))
+ return;
+
+ if (!dentry || !dentry->d_inode)
+ return;
+
+ if (!dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ data = dentry->d_inode->i_audit;
+ parent = dentry->d_parent->d_inode->i_audit;
+
+ wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
+
+ write_lock(&data->lock);
+ /* FIXME: long watchlist == too much spinning? */
+ if (remove) {
+ audit_drain_watchlist(data);
+ if ((wentry && data->wentry) && (wentry == data->wentry)) {
+ audit_wentry_put(data->wentry);
+ data->wentry = NULL;
+ }
+ } else if (!data->wentry) {
+ data->wentry = audit_wentry_get(wentry);
+ audit_pin(data->wentry, dentry);
+ } else if (hlist_unhashed(&data->wentry->w_node)) {
+ audit_wentry_put(data->wentry);
+ data->wentry = audit_wentry_get(wentry);
+ audit_pin(data->wentry, dentry);
+ }
+ write_unlock(&data->lock);
+
+ audit_wentry_put(wentry);
+}
+
+int audit_send_watch(int pid, int seq, struct audit_watch *watch)
+{
+ int ret;
+ size_t size;
+ void *memblk;
+
+ /* We must include space for both "\0" */
+ size = sizeof(struct watch_transport) + strlen(watch->path) +
+ strlen(watch->filterkey) + 2;
+
+ ret = -ENOMEM;
+ memblk = audit_to_transport(watch, size);
+ if (!memblk)
+ goto audit_send_watch_exit;
+
+ ret = 0;
+ audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 0, 1, memblk, size);
+ kfree(memblk);
+
+audit_send_watch_exit:
+ return ret;
+}
+
+/*
+ * Read the "master watchlist" which is a watchlist of all watches in the
+ * file system and send it to user space. There will never be concurrent
+ * readers of this list.
+ *
+ * The reference to wentry will not be put back during a read upon a
+ * watch removal, until after we're done reading. So, the potential
+ * for the rug being pulled out from under us is NIL.
+ *
+ * This list is only a "snapshot in time". It is not gospel.
+ */
+int audit_list_watches(int pid, int seq)
+{
+ int ret = 0;
+ struct audit_wentry *wentry;
+ struct hlist_node *pos;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(wentry, pos, &audit_master_watchlist, w_master) {
+ ret = audit_send_watch(pid, seq, wentry->w_watch);
+ if (ret < 0)
+ break;
+ }
+ rcu_read_unlock();
+
+ audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1, NULL, 0);
+
+ return ret;
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req, uid_t loginuid)
+{
+ int ret = 0;
+ struct audit_watch *watch = NULL;
+
+ ret = -EINVAL;
+ if (req->pathlen > PATH_MAX || req->pathlen == 0)
+ goto audit_receive_watch_exit;
+
+ if (req->fklen > AUDIT_FILTERKEY_MAX)
+ goto audit_receive_watch_exit;
+
+ if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
+ goto audit_receive_watch_exit;
+
+ ret = -ENOMEM;
+ watch = audit_to_watch(req);
+ if (!watch)
+ goto audit_receive_watch_exit;
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ ret = audit_insert_watch(watch, loginuid);
+ break;
+ case AUDIT_WATCH_REM:
+ ret = audit_remove_watch(watch, loginuid);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+audit_receive_watch_exit:
+ audit_watch_free(watch);
+ return ret;
+}
+
+int audit_inode_alloc(struct inode *inode)
+{
+ if (inode) {
+ inode->i_audit = audit_data_alloc();
+ if (!inode->i_audit)
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+ if (inode)
+ audit_data_free(inode->i_audit);
+}
+/*
+ * When we delete a dentry we check to see whether or not we're being
+ * watched. If we are watched, we have to put back our reference to
+ * the dentry to unpin it from memory or bad things happen.
+ *
+ * Hook appears in fs/dcache.c:d_delete()
+ */
+void audit_dentry_unpin(struct dentry *dentry)
+{
+ struct audit_inode_data *parent;
+ struct audit_wentry *wentry;
+
+ parent = dentry->d_parent->d_inode->i_audit;
+ wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
+ if (wentry) {
+ audit_unpin(dentry->d_inode->i_audit->wentry);
+ audit_wentry_put(wentry);
+ }
+}
+
+int audit_filesystem_init(void)
+{
+ int ret;
+
+ ret = -ENOMEM;
+ audit_watch_cache =
+ kmem_cache_create("audit_watch_cache",
+ sizeof(struct audit_watch), 0, 0, NULL, NULL);
+ if (!audit_watch_cache)
+ goto audit_filesystem_init_fail;
+
+ audit_wentry_cache =
+ kmem_cache_create("audit_wentry_cache",
+ sizeof(struct audit_wentry), 0, 0, NULL, NULL);
+ if (!audit_wentry_cache)
+ goto audit_filesystem_init_fail;
+
+ ret = 0;
+ goto audit_filesystem_init_exit;
+
+audit_filesystem_init_fail:
+ kmem_cache_destroy(audit_watch_cache);
+ kmem_cache_destroy(audit_wentry_cache);
+audit_filesystem_init_exit:
+ return ret;
+}
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/auditsc.c linux-2.6.12-rc3-mm3~audit/kernel/auditsc.c
--- linux-2.6.12-rc3-mm3~orig/kernel/auditsc.c 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/auditsc.c 2005-05-06 14:29:04.000000000 -0500
@@ -102,6 +102,7 @@ struct audit_aux_data {
};
#define AUDIT_AUX_IPCPERM 0
+#define AUDIT_AUX_WATCH 1
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
@@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
mode_t mode;
};
+struct audit_aux_data_watched {
+ struct audit_aux_data link;
+ struct audit_wentry *wentry;
+ 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 {
@@ -696,6 +707,24 @@ static void audit_log_exit(struct audit_
audit_log_format(ab,
" qbytes=%lx uid=%d gid=%d mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
+ break;
+ }
+
+ case AUDIT_AUX_WATCH: {
+ struct audit_aux_data_watched *axi = (void *)aux;
+ audit_log_format(ab, " watch=");
+ audit_log_untrustedstring(ab, axi->wentry->w_watch->name);
+ audit_log_format(ab,
+ " filterkey=%s perm=%u perm_mask=%d"
+ " inode=%lu inode_uid=%d inode_gid=%d"
+ " inode_dev=%02x:%02x inode_rdev=%02x:%02x",
+ axi->wentry->w_watch->filterkey,
+ axi->wentry->w_watch->perms,
+ axi->mask, axi->ino, axi->uid, axi->gid,
+ MAJOR(axi->dev), MINOR(axi->dev),
+ MAJOR(axi->rdev), MINOR(axi->rdev));
+ audit_wentry_put(axi->wentry);
+ break;
}
}
audit_log_end(ab);
@@ -1056,3 +1085,52 @@ int audit_ipc_perms(unsigned long qbytes
context->aux = (void *)ax;
return 0;
}
+
+int audit_notify_watch(struct inode *inode, int mask)
+{
+ int ret = 0;
+ struct audit_context *context = current->audit_context;
+ struct audit_aux_data_watched *ax;
+ struct audit_wentry *wentry = NULL;
+
+ if (likely(!audit_enabled))
+ goto audit_notify_watch_exit;
+
+ if (!inode)
+ goto audit_notify_watch_exit;
+
+ wentry = audit_wentry_get(inode->i_audit->wentry);
+ if (!wentry)
+ goto audit_notify_watch_exit;
+
+ if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
+ goto audit_notify_watch_fail;
+
+ ret = -ENOMEM;
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ goto audit_notify_watch_fail;
+
+ ret = 0;
+ if (context->in_syscall && !context->auditable)
+ context->auditable = 1;
+
+ ax->wentry = wentry;
+ 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_AUX_WATCH;
+ ax->link.next = context->aux;
+ context->aux = (void *)ax;
+
+ goto audit_notify_watch_exit;
+
+audit_notify_watch_fail:
+ audit_wentry_put(wentry);
+audit_notify_watch_exit:
+ return ret;
+}
diff -Nurp linux-2.6.12-rc3-mm3~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc3-mm3~audit/security/selinux/nlmsgtab.c
--- linux-2.6.12-rc3-mm3~orig/security/selinux/nlmsgtab.c 2005-05-06 13:05:16.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/security/selinux/nlmsgtab.c 2005-05-06 14:29:04.000000000 -0500
@@ -97,6 +97,9 @@ static struct nlmsg_perm nlmsg_audit_per
{ AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
+ { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
};
19 years, 7 months
[PATCH] add message types
by Steve Grubb
Hello,
This patch adds more messages types to the audit subsystem so that audit
analysis is quicker, intuitive, and more useful.
Signed-off-by: Steve Grubb <sgrubb(a)redhat.com>
19 years, 7 months
[Patch] One more type
by Steve Grubb
Hi,
I forgot one type in the big patch. I need to add one for user space
originating SE Linux avc messages. This is used by dbus and nscd. Patch is
attached.
-Steve
19 years, 7 months
audit 0.8 released
by Steve Grubb
Hello,
I've just released a new version of the audit daemon. It can be downloaded
from http://people.redhat.com/sgrubb/audit The Changelog is:
- ausearch fix bugs related to -f & -x
- Parse messages using new types
- Properly unescape filenames
- Update interface for sending userspace messages to use new types
This release takes advantage of the new audit types recently introduced. I
would recommend using this with the most recent kernel. This release also now
properly escapes filenames when doing searches for filenames or executables.
The last major piece of this update was to change the library interfaces for
sending userspace messages to allow the message type to be specified. For the
moment, this breaks passwd if you are using the "patched" version. I will get
a new version of passwd out shortly. I preserved the interface for pam & dbus
since they are already in wide distribution. I will migrate both of those
soon as well.
Let me know if there are any problems.
-Steve
19 years, 7 months
[RFC][PATCH] (#7U4) file system auditing by location and name
by Timothy R. Chavez
Hello,
This patch is against 12-rc3-mm3 and incorporates feedback I received on
#7U3 and does not include fixes based on Viro feedback (I'll be working
on this over the weekend).
Thanks for all your patience. Patch appears below change log.
-tim
Notable changes:
audit.h:
Changed struct audit_transport to struct watch_transport
Changed struct audit_data to struct audit_inode_data
auditfs.c:
Added Steve Grubb's watch_to_transport and transport_to_watch helper
functions to auditfs.c as audit_to_transport and audit_to_watch
respectively to ensure consistency
Put length checking prior to audit_to_watch transformation in
audit_receive_watch (as we have that information available to us from
watch_transport)
Changed audit_insert/remove_watch parameters to struct audit_watch's
Altered audit_send_watch to use audit_to_transport transformation
Added likely(!audit_enabled) check to the audit_update_watch hook
Removed string comparison check in audit_update_watch in favor of
pointer comparison, but I've not tested this yet. The string comparison
seemed a bit wasteful, though
Made sure selinux nlmsgtab entries for AUDIT_WATCH_INS/REM/LIST were
correct (thanks David for cleaning up after me in the past ;-))
auditsc.c
Added likely(!audit_enabled) check to the audit_notify_watch hook
Removed context check from the audit_notify_watch hook
---
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/dcache.c linux-2.6.12-rc3-mm3~audit/fs/dcache.c
--- linux-2.6.12-rc3-mm3~orig/fs/dcache.c 2005-05-06 13:05:08.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/dcache.c 2005-05-06 14:29:04.000000000 -0500
@@ -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, 1);
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, 0);
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, 0);
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, 0);
spin_unlock(&dcache_lock);
security_d_instantiate(dentry, inode);
d_rehash(dentry);
@@ -1090,6 +1095,7 @@ struct dentry * __d_lookup(struct dentry
if (!d_unhashed(dentry)) {
atomic_inc(&dentry->d_count);
found = dentry;
+ audit_update_watch(found, 0);
}
spin_unlock(&dentry->d_lock);
break;
@@ -1299,6 +1305,8 @@ void d_move(struct dentry * dentry, stru
spin_lock(&target->d_lock);
}
+ audit_update_watch(dentry, 1);
+
/* 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, 0);
list_add(&dentry->d_child, &dentry->d_parent->d_subdirs);
spin_unlock(&target->d_lock);
spin_unlock(&dentry->d_lock);
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/inode.c linux-2.6.12-rc3-mm3~audit/fs/inode.c
--- linux-2.6.12-rc3-mm3~orig/fs/inode.c 2005-05-06 13:05:08.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/inode.c 2005-05-06 14:29:04.000000000 -0500
@@ -22,6 +22,7 @@
#include <linux/cdev.h>
#include <linux/bootmem.h>
#include <linux/inotify.h>
+#include <linux/audit.h>
/*
* This is needed for the following functions:
@@ -139,9 +140,11 @@ static struct inode *alloc_inode(struct
inode->i_bdev = NULL;
inode->i_cdev = NULL;
inode->i_rdev = 0;
+ inode->i_audit = NULL;
inode->i_security = NULL;
inode->dirtied_when = 0;
- if (security_inode_alloc(inode)) {
+ if (audit_inode_alloc(inode) || security_inode_alloc(inode)) {
+ audit_inode_free(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
@@ -179,6 +182,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);
diff -Nurp linux-2.6.12-rc3-mm3~orig/fs/namei.c linux-2.6.12-rc3-mm3~audit/fs/namei.c
--- linux-2.6.12-rc3-mm3~orig/fs/namei.c 2005-05-06 13:05:09.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/fs/namei.c 2005-05-06 14:29:04.000000000 -0500
@@ -225,6 +225,8 @@ int permission(struct inode *inode, int
{
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(s
if (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 inod
BUG_ON(victim->d_parent->d_inode != dir);
+ audit_notify_watch(victim->d_inode, MAY_WRITE);
+
error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
if (error)
return error;
@@ -1296,6 +1302,7 @@ int vfs_create(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->create(dir, dentry, mode, nd);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_create(dir, dentry, mode);
}
@@ -1601,6 +1608,7 @@ int vfs_mknod(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mknod(dir, dentry, mode, dev);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
security_inode_post_mknod(dir, dentry, mode, dev);
}
@@ -1674,6 +1682,7 @@ int vfs_mkdir(struct inode *dir, struct
DQUOT_INIT(dir);
error = dir->i_op->mkdir(dir, dentry, mode);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_mkdir(dir, dentry->d_name.name);
security_inode_post_mkdir(dir,dentry, mode);
}
@@ -1915,6 +1924,7 @@ int vfs_symlink(struct inode *dir, struc
DQUOT_INIT(dir);
error = dir->i_op->symlink(dir, dentry, oldname);
if (!error) {
+ audit_notify_watch(dentry->d_inode, MAY_WRITE);
fsnotify_create(dir, dentry->d_name.name);
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);
fsnotify_create(dir, new_dentry->d_name.name);
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)
diff -Nurp linux-2.6.12-rc3-mm3~orig/include/linux/audit.h linux-2.6.12-rc3-mm3~audit/include/linux/audit.h
--- linux-2.6.12-rc3-mm3~orig/include/linux/audit.h 2005-05-06 13:05:14.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/include/linux/audit.h 2005-05-06 14:29:04.000000000 -0500
@@ -24,18 +24,29 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
#include <linux/sched.h>
#include <linux/elf.h>
+
+struct hlist_head;
+struct hlist_node;
+struct spinlock_t;
+struct atomic_t;
+#endif
+
/* Request and reply types */
-#define AUDIT_GET 1000 /* Get status */
-#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
-#define AUDIT_LIST 1002 /* List filtering rules */
-#define AUDIT_ADD 1003 /* Add filtering rule */
-#define AUDIT_DEL 1004 /* Delete filtering rule */
-#define AUDIT_USER 1005 /* Send a message from user-space */
-#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
-#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
+#define AUDIT_GET 1000 /* Get status */
+#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
+#define AUDIT_LIST 1002 /* List filtering rules */
+#define AUDIT_ADD 1003 /* Add filtering rule */
+#define AUDIT_DEL 1004 /* Delete filtering rule */
+#define AUDIT_USER 1005 /* Send a message from user-space */
+#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
+#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
+#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
+#define AUDIT_WATCH_LIST 1009 /* List all watches */
+#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
@@ -132,6 +143,9 @@
#define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX 32
+
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
@@ -159,8 +173,44 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+/* Structure to transport watch data to and from the kernel */
+
+struct watch_transport {
+ __u32 dev_major;
+ __u32 dev_minor;
+ __u32 perms;
+ __u32 valid;
+ __u32 pathlen;
+ __u32 fklen;
+ char buf[0];
+};
+
#ifdef __KERNEL__
+/* Structure associated with inode->i_audit */
+
+struct audit_inode_data {
+ struct audit_wentry *wentry;
+ struct hlist_head watchlist;
+ rwlock_t lock;
+};
+
+struct audit_watch {
+ dev_t dev; /* Superblock device */
+ __u32 perms; /* Permissions filtering */
+ char *name; /* Watch point beneath parent */
+ char *path; /* Insertion path */
+ char *filterkey; /* An arbitrary filtering key */
+};
+
+struct audit_wentry {
+ struct hlist_node w_node;
+ struct hlist_node w_master;
+ struct audit_watch *w_watch;
+ atomic_t w_count;
+
+};
+
struct audit_buffer;
struct audit_context;
struct inode;
@@ -190,6 +240,7 @@ extern void audit_get_stamp(struct audit
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx);
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
+extern int audit_notify_watch(struct inode *inode, int mask);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -200,6 +251,28 @@ extern int audit_ipc_perms(unsigned long
#define audit_inode(n,i) do { ; } while (0)
#define audit_get_loginuid(c) ({ -1; })
#define audit_ipc_perms(q,u,g,m) ({ 0; })
+#define audit_notify_watch(i,m) ({ 0; })
+#endif
+
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_list_watches(int pid, int seq);
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req);
+extern int audit_filesystem_init(void);
+extern int audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_update_watch(struct dentry *dentry, int remove);
+extern void audit_wentry_put(struct audit_wentry *wentry);
+extern struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry);
+#else
+#define audit_list_watches(p,s) ({ 0; })
+#define audit_receive_watch(t,p,u,s,r) ({ -EOPNOTSUPP; })
+#define audit_filesystem_init() ({ 0; })
+#define audit_inode_alloc(i) ({ 0; })
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_update_watch(d,r) do { ; } while (0)
+#define audit_wentry_put(w) do { ; } while(0)
+#define audit_wentry_get(w) ({ 0; })
#endif
#ifdef CONFIG_AUDIT
diff -Nurp linux-2.6.12-rc3-mm3~orig/include/linux/fs.h linux-2.6.12-rc3-mm3~audit/include/linux/fs.h
--- linux-2.6.12-rc3-mm3~orig/include/linux/fs.h 2005-05-06 13:05:14.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/include/linux/fs.h 2005-05-06 14:29:04.000000000 -0500
@@ -226,6 +226,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 *);
@@ -477,6 +478,7 @@ struct inode {
struct semaphore inotify_sem; /* protects the watches list */
#endif
+ struct audit_inode_data *i_audit;
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
diff -Nurp linux-2.6.12-rc3-mm3~orig/init/Kconfig linux-2.6.12-rc3-mm3~audit/init/Kconfig
--- linux-2.6.12-rc3-mm3~orig/init/Kconfig 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/init/Kconfig 2005-05-06 14:29:04.000000000 -0500
@@ -180,6 +180,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
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/Makefile linux-2.6.12-rc3-mm3~audit/kernel/Makefile
--- linux-2.6.12-rc3-mm3~orig/kernel/Makefile 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/Makefile 2005-05-06 14:29:04.000000000 -0500
@@ -25,6 +25,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_DETECT_SOFTLOCKUP) += softlockup.o
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/audit.c linux-2.6.12-rc3-mm3~audit/kernel/audit.c
--- linux-2.6.12-rc3-mm3~orig/kernel/audit.c 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/audit.c 2005-05-06 14:29:04.000000000 -0500
@@ -321,6 +321,9 @@ static int audit_netlink_ok(kernel_cap_t
case AUDIT_SET:
case AUDIT_ADD:
case AUDIT_DEL:
+ case AUDIT_WATCH_LIST:
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
@@ -419,6 +422,17 @@ static int audit_receive_msg(struct sk_b
err = -EOPNOTSUPP;
#endif
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);
+ break;
default:
err = -EINVAL;
break;
@@ -560,6 +574,7 @@ static int __init audit_init(void)
audit_initialized = 1;
audit_enabled = audit_default;
+ audit_filesystem_init();
audit_log(NULL, "initialized");
return 0;
}
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/auditfs.c linux-2.6.12-rc3-mm3~audit/kernel/auditfs.c
--- linux-2.6.12-rc3-mm3~orig/kernel/auditfs.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.12-rc3-mm3~audit/kernel/auditfs.c 2005-05-06 14:29:04.000000000 -0500
@@ -0,0 +1,608 @@
+/* auditfs.c -- Filesystem auditing support -*- linux-c -*-
+ * Implements filesystem auditing support, depends on kernel/auditsc.c
+ *
+ * Copyright 2005 International Business Machines Corp. (IBM)
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307 USA
+ *
+ * Written by Timothy R. Chavez <chavezt(a)us.ibm.com>
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/namei.h>
+#include <linux/mount.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/audit.h>
+#include <asm/uaccess.h>
+
+extern int audit_enabled;
+
+kmem_cache_t *audit_watch_cache;
+kmem_cache_t *audit_wentry_cache;
+
+DEFINE_SPINLOCK(audit_master_watchlist_lock);
+HLIST_HEAD(audit_master_watchlist);
+
+
+/* Private Interface */
+
+static inline struct audit_wentry *audit_wentry_fetch(const char *name,
+ struct audit_inode_data *data)
+{
+ struct audit_wentry *wentry, *ret = NULL;
+ struct hlist_node *pos;
+
+ hlist_for_each_entry(wentry, pos, &data->watchlist, w_node)
+ if(!strcmp(wentry->w_watch->name, name)) {
+ ret = audit_wentry_get(wentry);
+ break;
+ }
+
+ return ret;
+}
+
+static inline struct audit_wentry *audit_wentry_fetch_lock(const char *name,
+ struct audit_inode_data *data)
+{
+ struct audit_wentry *ret = NULL;
+
+ if (name && data) {
+ read_lock(&data->lock);
+ ret = audit_wentry_fetch(name, data);
+ read_unlock(&data->lock);
+ }
+
+ return ret;
+}
+
+static inline struct audit_watch *audit_watch_alloc(void)
+{
+ struct audit_watch *watch;
+
+ watch = kmem_cache_alloc(audit_watch_cache, GFP_KERNEL);
+ if (watch) {
+ watch->name = NULL;
+ watch->filterkey = NULL;
+ watch->perms = 0;
+ }
+
+ return watch;
+}
+
+static inline void audit_watch_free(struct audit_watch *watch)
+{
+ if (watch) {
+ kfree(watch->name);
+ kfree(watch->path);
+ kfree(watch->filterkey);
+ kmem_cache_free(audit_watch_cache, watch);
+ }
+}
+
+struct audit_watch *audit_to_watch(void *memblk)
+{
+ unsigned int offset;
+ struct watch_transport *t;
+ struct audit_watch *watch = NULL;
+
+ watch = audit_watch_alloc();
+ if (!watch)
+ goto audit_to_watch_exit;
+
+ t = (struct watch_transport *)memblk;
+
+ watch->perms = t->perms;
+
+ offset = sizeof(struct watch_transport);
+ watch->filterkey = kmalloc(t->fklen, GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_to_watch_fail;
+ strncpy(watch->filterkey, memblk + offset, t->fklen);
+
+ offset += t->fklen;
+ watch->path = kmalloc(t->pathlen, GFP_KERNEL);
+ if (!watch->path)
+ goto audit_to_watch_fail;
+ strncpy(watch->path, memblk + offset, t->pathlen);
+
+ goto audit_to_watch_exit;
+
+audit_to_watch_fail:
+ audit_watch_free(watch);
+audit_to_watch_exit:
+ return watch;
+}
+
+
+static void *audit_to_transport(struct audit_watch *watch,
+ unsigned int size)
+{
+ int ret;
+ unsigned int offset;
+ struct watch_transport t;
+ struct audit_wentry *wentry;
+ struct audit_inode_data *data;
+ struct nameidata nd;
+ void *memblk;
+
+ memblk = kmalloc(size, GFP_KERNEL);
+ if (!memblk)
+ goto audit_to_transport_exit;
+
+ memset(&t, 0, sizeof(t));
+
+ t.dev_major = MAJOR(watch->dev);
+ t.dev_minor = MINOR(watch->dev);
+ t.perms = watch->perms;
+ t.pathlen = strlen(watch->path) + 1;
+ if (watch->filterkey)
+ t.fklen = strlen(watch->filterkey) + 1;
+
+ ret = path_lookup(watch->path, LOOKUP_PARENT, &nd);
+ if (!ret) {
+ data = nd.dentry->d_inode->i_audit;
+ wentry = audit_wentry_fetch_lock(watch->name, data);
+ if (wentry && nd.dentry->d_inode->i_sb->s_dev == watch->dev)
+ t.valid = 1;
+ audit_wentry_put(wentry);
+ path_release(&nd);
+ }
+
+ printk("filterkey=%s\n", watch->filterkey);
+
+ memcpy(memblk, &t, sizeof(t));
+ offset = sizeof(t);
+ memcpy(memblk + offset, watch->filterkey, t.fklen);
+ offset += t.fklen;
+ memcpy(memblk + offset, watch->path, t.pathlen);
+
+audit_to_transport_exit:
+ return memblk;
+}
+
+
+static inline struct audit_watch *audit_create_watch(const char *path,
+ const char *name,
+ const char *filterkey,
+ __u32 perms, dev_t dev)
+{
+ struct audit_watch *err = NULL;
+ struct audit_watch *watch = NULL;
+
+ err = ERR_PTR(-ENOMEM);
+ watch = audit_watch_alloc();
+ if (watch) {
+ watch->path = kmalloc(strlen(path)+1, GFP_KERNEL);
+ if (!watch->path)
+ goto audit_create_watch_fail;
+ strcpy(watch->path, path);
+ watch->name = kmalloc(strlen(name)+1, GFP_KERNEL);
+ if (!watch->name)
+ goto audit_create_watch_fail;
+ strcpy(watch->name, name);
+
+ if (filterkey) {
+ watch->filterkey = kmalloc(strlen(filterkey)+1,
+ GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_create_watch_fail;
+ strcpy(watch->filterkey, filterkey);
+ }
+
+ watch->dev = dev;
+ watch->perms = perms;
+
+ goto audit_create_watch_exit;
+ }
+
+
+audit_create_watch_fail:
+ audit_watch_free(watch);
+ watch = err;
+audit_create_watch_exit:
+ return watch;
+}
+
+static inline struct audit_wentry *audit_wentry_alloc(void)
+{
+ struct audit_wentry *wentry;
+
+ wentry = kmem_cache_alloc(audit_wentry_cache, GFP_KERNEL);
+ if (wentry) {
+ atomic_set(&wentry->w_count, 1);
+ wentry->w_watch = NULL;
+ }
+
+ return wentry;
+}
+
+static inline void audit_wentry_free(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ audit_watch_free(wentry->w_watch);
+ kmem_cache_free(audit_wentry_cache, wentry);
+ }
+}
+
+/* The only time the new wentry gets updated is when it is inaccessible
+ * (out of the list).
+ */
+static inline int audit_create_wentry(const char *path,
+ const char *name,
+ const char *filterkey,
+ __u32 perms, dev_t dev,
+ struct audit_inode_data *data)
+{
+ int ret;
+ struct audit_wentry *wentry = NULL;
+ struct audit_wentry *new = NULL;
+
+ ret = -ENOMEM;
+ new = audit_wentry_alloc();
+ if (!new)
+ goto audit_create_wentry_fail;
+
+ new->w_watch = audit_create_watch(path, name, filterkey, perms, dev);
+ if (IS_ERR(new->w_watch)) {
+ ret = PTR_ERR(new->w_watch);
+ new->w_watch = NULL;
+ goto audit_create_wentry_fail;
+ }
+
+ ret = 0;
+ write_lock(&data->lock);
+ wentry = audit_wentry_fetch(name, data);
+ if (!wentry) {
+ spin_lock(&audit_master_watchlist_lock);
+ new = audit_wentry_get(new);
+ hlist_add_head(&new->w_master, &audit_master_watchlist);
+ spin_unlock(&audit_master_watchlist_lock);
+ hlist_add_head(&new->w_node, &data->watchlist);
+ write_unlock(&data->lock);
+ goto audit_create_wentry_exit;
+ }
+ audit_wentry_put(wentry);
+ write_unlock(&data->lock);
+
+ ret = -EEXIST;
+
+audit_create_wentry_fail:
+ audit_wentry_put(new);
+audit_create_wentry_exit:
+ return ret;
+}
+
+/* Caller must hold watchlist_lock */
+
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ spin_lock(&audit_master_watchlist_lock);
+ hlist_del_init(&wentry->w_master);
+ audit_wentry_put(wentry);
+ spin_unlock(&audit_master_watchlist_lock);
+ hlist_del_init(&wentry->w_node);
+ audit_wentry_put(wentry);
+ }
+}
+
+
+/* Caller must hold data->lock */
+
+static inline void audit_drain_watchlist(struct audit_inode_data *data)
+{
+ struct audit_wentry *wentry;
+ struct hlist_node *pos, *buf;
+
+ hlist_for_each_entry_safe(wentry, pos, buf, &data->watchlist, w_node)
+ audit_destroy_wentry(wentry);
+}
+
+static inline struct audit_inode_data *audit_data_alloc(void)
+{
+ struct audit_inode_data *data;
+
+ data = kmalloc(sizeof(struct audit_inode_data), GFP_KERNEL);
+ if (data) {
+ data->wentry = NULL;
+ INIT_HLIST_HEAD(&data->watchlist);
+ data->lock = RW_LOCK_UNLOCKED;
+ }
+
+ return data;
+}
+
+static inline void audit_data_free(struct audit_inode_data *data)
+{
+ if (data) {
+ write_lock(&data->lock);
+ audit_drain_watchlist(data);
+ audit_wentry_put(data->wentry);
+ write_unlock(&data->lock);
+ kfree(data);
+ }
+}
+
+static inline int audit_insert_watch(struct audit_watch *watch)
+{
+ int ret;
+ struct nameidata nd;
+
+ ret = path_lookup(watch->path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto audit_insert_watch_exit;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_insert_watch_release;
+
+ ret = audit_create_wentry(watch->path,
+ nd.last.name,
+ watch->filterkey,
+ watch->perms,
+ nd.dentry->d_inode->i_sb->s_dev,
+ nd.dentry->d_inode->i_audit);
+
+ /* __d_lookup will attach the audit data, if nd.last exists. */
+ dput(d_lookup(nd.dentry, &nd.last));
+
+audit_insert_watch_release:
+ path_release(&nd);
+audit_insert_watch_exit:
+ return ret;
+}
+
+static inline int audit_remove_watch(struct audit_watch *watch)
+{
+ int ret;
+ struct nameidata nd;
+ struct audit_inode_data *data;
+ struct audit_wentry *wentry;
+
+ ret = path_lookup(watch->path, LOOKUP_PARENT, &nd);
+ if (ret < 0)
+ goto audit_remove_watch_exit;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_remove_watch_release;
+
+ data = nd.dentry->d_inode->i_audit;
+
+ write_lock(&data->lock);
+ wentry = audit_wentry_fetch(nd.last.name, data);
+ if (!wentry) {
+ write_unlock(&data->lock);
+ goto audit_remove_watch_release;
+ }
+ audit_destroy_wentry(wentry);
+ audit_wentry_put(wentry);
+ write_unlock(&data->lock);
+
+ ret = 0;
+
+audit_remove_watch_release:
+ path_release(&nd);
+audit_remove_watch_exit:
+ return ret;
+}
+
+struct audit_wentry *audit_wentry_get(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ BUG_ON(!atomic_read(&wentry->w_count));
+ atomic_inc(&wentry->w_count);
+ }
+
+ return wentry;
+}
+
+void audit_wentry_put(struct audit_wentry *wentry)
+{
+ if (wentry && atomic_dec_and_test(&wentry->w_count))
+ audit_wentry_free(wentry);
+}
+
+/*
+ * The update hook is responsible for watching and unwatching d_inodes during
+ * their lifetimes in dcache. Each d_inode being watched is pinned in memory.
+ * As soon as a d_inode becomes unwatched (when !d_inode->i_audit->wentry), it
+ * is unpinned.
+ *
+ * Hook appears in fs/dcache.c:
+ * d_move(),
+ * d_delete(),
+ * d_instantiate(),
+ * d_splice_alias()
+ * __d_lookup()
+ */
+void audit_update_watch(struct dentry *dentry, int remove)
+{
+ struct audit_wentry *wentry;
+ struct audit_inode_data *data, *parent;
+
+ if (likely(!audit_enabled))
+ return;
+
+ if (!dentry || !dentry->d_inode)
+ return;
+
+ if (!dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ data = dentry->d_inode->i_audit;
+ parent = dentry->d_parent->d_inode->i_audit;
+
+ wentry = audit_wentry_fetch_lock(dentry->d_name.name, parent);
+
+ write_lock(&data->lock);
+ /* FIXME: long watchlist == too much spinning? */
+ if (remove) {
+ audit_drain_watchlist(data);
+ if (wentry && data->wentry) {
+ /* UNTESTED */
+ if (wentry == data->wentry) {
+ /*
+ if (!strcmp(wentry->w_watch->name,
+ data->wentry->w_watch->name)) {
+ */
+ dput(dentry);
+ audit_wentry_put(data->wentry);
+ data->wentry = NULL;
+ }
+ }
+ } else if (!data->wentry) {
+ data->wentry = audit_wentry_get(wentry);
+ if (data->wentry)
+ dget(dentry);
+ } else if (hlist_unhashed(&data->wentry->w_node)) {
+ dput(dentry);
+ audit_wentry_put(data->wentry);
+ data->wentry = audit_wentry_get(wentry);
+ if (data->wentry)
+ dget(dentry);
+ }
+
+ audit_wentry_put(wentry);
+ write_unlock(&data->lock);
+}
+
+int audit_send_watch(int pid, int seq, struct audit_watch *watch)
+{
+ int ret = 0;
+ size_t size;
+ void *memblk;
+
+ size = sizeof(struct watch_transport) +
+ strlen(watch->path) + strlen(watch->filterkey) + 2;
+
+ memblk = audit_to_transport(watch, size);
+ if (!memblk)
+ return -ENOMEM;
+
+ audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 0, 1,
+ memblk, size);
+ kfree(memblk);
+
+ return ret;
+}
+
+int audit_list_watches(int pid, int seq)
+{
+ int ret = 0;
+ struct audit_wentry *wentry;
+ struct hlist_node *pos;
+
+ spin_lock(&audit_master_watchlist_lock);
+ hlist_for_each_entry(wentry, pos, &audit_master_watchlist, w_master) {
+ ret = audit_send_watch(pid, seq, wentry->w_watch);
+ if (ret < 0)
+ break;
+ }
+ spin_unlock(&audit_master_watchlist_lock);
+
+ audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 1, 1,
+ NULL, 0);
+
+ return ret;
+}
+
+int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct watch_transport *req)
+{
+ int ret = 0;
+ struct audit_watch *watch = NULL;
+
+ ret = -EINVAL;
+ if (req->pathlen > PATH_MAX)
+ goto audit_receive_watch_exit;
+
+ if (req->fklen > AUDIT_FILTERKEY_MAX)
+ goto audit_receive_watch_exit;
+
+ if (req->perms > (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND))
+ goto audit_receive_watch_exit;
+
+ ret = -ENOMEM;
+ watch = audit_to_watch(req);
+ if (!watch)
+ goto audit_receive_watch_exit;
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ ret = audit_insert_watch(watch);
+ break;
+ case AUDIT_WATCH_REM:
+ ret = audit_remove_watch(watch);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+audit_receive_watch_exit:
+ audit_watch_free(watch);
+ return ret;
+}
+
+int audit_inode_alloc(struct inode *inode)
+{
+ if (inode) {
+ inode->i_audit = audit_data_alloc();
+ if (!inode->i_audit)
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+ if (inode)
+ audit_data_free(inode->i_audit);
+}
+
+int audit_filesystem_init()
+{
+ int ret = 0;
+
+ audit_watch_cache =
+ kmem_cache_create("audit_watch_cache",
+ sizeof(struct audit_watch), 0, 0, NULL, NULL);
+ if (!audit_watch_cache)
+ goto audit_filesystem_init_fail;
+
+ audit_wentry_cache =
+ kmem_cache_create("audit_wentry_cache",
+ sizeof(struct audit_wentry), 0, 0, NULL, NULL);
+ if (!audit_wentry_cache)
+ goto audit_filesystem_init_fail;
+
+ goto audit_filesystem_init_exit;
+
+audit_filesystem_init_fail:
+ ret = -ENOMEM;
+ kmem_cache_destroy(audit_watch_cache);
+ kmem_cache_destroy(audit_wentry_cache);
+audit_filesystem_init_exit:
+ return ret;
+
+}
diff -Nurp linux-2.6.12-rc3-mm3~orig/kernel/auditsc.c linux-2.6.12-rc3-mm3~audit/kernel/auditsc.c
--- linux-2.6.12-rc3-mm3~orig/kernel/auditsc.c 2005-05-06 13:05:15.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/kernel/auditsc.c 2005-05-06 14:29:04.000000000 -0500
@@ -102,6 +102,7 @@ struct audit_aux_data {
};
#define AUDIT_AUX_IPCPERM 0
+#define AUDIT_AUX_WATCH 1
struct audit_aux_data_ipcctl {
struct audit_aux_data d;
@@ -112,6 +113,16 @@ struct audit_aux_data_ipcctl {
mode_t mode;
};
+struct audit_aux_data_watched {
+ struct audit_aux_data link;
+ struct audit_wentry *wentry;
+ 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 {
@@ -696,6 +707,24 @@ static void audit_log_exit(struct audit_
audit_log_format(ab,
" qbytes=%lx uid=%d gid=%d mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode);
+ break;
+ }
+
+ case AUDIT_AUX_WATCH: {
+ struct audit_aux_data_watched *axi = (void *)aux;
+ audit_log_format(ab, " watch=");
+ audit_log_untrustedstring(ab, axi->wentry->w_watch->name);
+ audit_log_format(ab,
+ " filterkey=%s perm=%u perm_mask=%d"
+ " inode=%lu inode_uid=%d inode_gid=%d"
+ " inode_dev=%02x:%02x inode_rdev=%02x:%02x",
+ axi->wentry->w_watch->filterkey,
+ axi->wentry->w_watch->perms,
+ axi->mask, axi->ino, axi->uid, axi->gid,
+ MAJOR(axi->dev), MINOR(axi->dev),
+ MAJOR(axi->rdev), MINOR(axi->rdev));
+ audit_wentry_put(axi->wentry);
+ break;
}
}
audit_log_end(ab);
@@ -1056,3 +1085,52 @@ int audit_ipc_perms(unsigned long qbytes
context->aux = (void *)ax;
return 0;
}
+
+int audit_notify_watch(struct inode *inode, int mask)
+{
+ int ret = 0;
+ struct audit_context *context = current->audit_context;
+ struct audit_aux_data_watched *ax;
+ struct audit_wentry *wentry = NULL;
+
+ if (likely(!audit_enabled))
+ goto audit_notify_watch_exit;
+
+ if (!inode)
+ goto audit_notify_watch_exit;
+
+ wentry = audit_wentry_get(inode->i_audit->wentry);
+ if (!wentry)
+ goto audit_notify_watch_exit;
+
+ if (mask && (wentry->w_watch->perms && !(wentry->w_watch->perms&mask)))
+ goto audit_notify_watch_fail;
+
+ ret = -ENOMEM;
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ goto audit_notify_watch_fail;
+
+ ret = 0;
+ if (context->in_syscall && !context->auditable)
+ context->auditable = 1;
+
+ ax->wentry = wentry;
+ 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_AUX_WATCH;
+ ax->link.next = context->aux;
+ context->aux = (void *)ax;
+
+ goto audit_notify_watch_exit;
+
+audit_notify_watch_fail:
+ audit_wentry_put(wentry);
+audit_notify_watch_exit:
+ return ret;
+}
diff -Nurp linux-2.6.12-rc3-mm3~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc3-mm3~audit/security/selinux/nlmsgtab.c
--- linux-2.6.12-rc3-mm3~orig/security/selinux/nlmsgtab.c 2005-05-06 13:05:16.000000000 -0500
+++ linux-2.6.12-rc3-mm3~audit/security/selinux/nlmsgtab.c 2005-05-06 14:29:04.000000000 -0500
@@ -97,6 +97,9 @@ static struct nlmsg_perm nlmsg_audit_per
{ AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
+ { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_READPRIV },
};
19 years, 7 months
[PATCH 0/4] misc. cleanups and audit_log_type
by Chris Wright
Here's a small series of cleanups, plus addition of an audit_log_type
function to help facilitate better audit message types.
1. audit-nlmsg_len-fix.patch - use NLMSG_SPACE(0)
2. audit-requires-net.patch - using skbs, we depend on CONFIG_NET
3. audit-bury-config-auditsyscall-in-header.patch - does just that
4. audit-log-type.patch - add audit_log_type
Against latest git tree.
thanks,
-chris
19 years, 7 months
audit message types
by Steve Grubb
Hello,
I wanted to start a discussion about an old topic that we last discussed back
in December. The problem basically centers around the audit message type
being too coarse to be of any real use. Right now, we have 3 message types
that the kernel sends to the audit daemon: AUDIT_KERNEL, AUDIT_USER, and
AUDIT_LOGIN.
AUDIT_LOGIN is fine. Its a very specific message. AUDIT_USER can be used for
anything from userspace. Anything means pam going through its various phases
- authentication, account management, session start, credentials set, session
closed, credentials destroyed. It also means any error message that may come
out of pam. Various trusted applications use it to note any kind of change to
user account attributes and any error therein.
We are trying to create an audit system that meets CAPP requirements (Section
FAU_SAR.3). We need to allow the administrator to create searches that, for
example, let them find any failed attempt to change a user's password. What
this means is that we have to search for any record that is of type
AUDIT_USER, then examine each one of those looking for the one that comes
from passwd. (Or maybe they were logging in and login made them change their
passwd since it expired. Two potential message sources for the same action.)
That's way too many messages to parse to find one specific kind of event.
I also feel that the kernel needs to break its packets down a little better
for searching. For example, both of these are KERNEL types:
type=KERNEL msg=audit(1113856292.392:10258978): item=0 name="/usr/bin/kompare"
inode=589078 dev=03:02 mode=0100755 uid=0 gid=0 rdev=00:00
type=KERNEL msg=audit(1113856292.392:10258978): syscall=11 arch=40000003
success=yes exit=0 a0=86629c0 a1=865f280 a2=85a6870 a3=805264c items=2
pid=4078 loginuid=525 uid=525 gid=525 euid=525 suid=525 fsuid=525
egid=525 sgid=525 fsgid=525 comm="kompare" exe=/usr/bin/kompare
But the first is a helper (or supplementary info) for the second. It would be
so much easier if the first one was a different message type. For example,
suppose we have to do a search for a specific uid. which one of the above
should we "hit" on? The seocnd one obviously, but it would be better if it
was a different type so the ausearch program has a spot to key on for data
about the event.
Then to top it off, we have SE Linux avc messages in the mix.
I think it would be a tremendous help to add some new message types that can
be filtered on. For example, if we had more kernel message types so SE Linux
avc denials were a differnt message type from syscall auditing, I could
easily add a filter to auditd so that a user can specify what kind of message
types they want logged. This will let them save disk space and improve
performance.
For example, someone writing SE Linux policy may want AUDIT_SELINUX_AVC_DENIAL
messages only. Everything else could be thrown away as far as they are
concerned. Or maybe people want to log only AUDIT_LOGIN messages. Some people
may want to log only failures to log in. Without finer grained message types,
this is not even a possibility.
In addition, if a user has 10 gigabytes of audit messages and they are all
AUDIT_KERNEL and they want to do a search on failed login attempts,
performance is going to suck. The reason, is because we will have to parse
many of the wrong kind of messages just to find the ones we are looking for.
To solve the problem, I would like to propose that AUDIT_KERNEL be used only
for things like adding & deleting rules, changing backlog, failure flag,
enabled/disabled, and message rate limit. We should introduce new message
types that partition the messages into: event, supplementary information
(for example - look at the log snippet above, the first chunk is this type),
SE Linux, authentication, account attribute change, account management,
credentials, session start, session close.
Of these, supplementary information should be subdivided into various types,
AUDIT_FILE_INFO for example. The SE Linux messages should be subdivided into
various message types, too. The rest would have a success and failure
subtype.
The benefits to doing this is: ausearch can better search for the right
information, users can filter what goes to disk, audit records are easier for
people to read, test cases are easier to construct.
Thoughts?
-Steve Grubb
19 years, 7 months
kernel.34 and audit-0.7.4
by Loulwa Salem
I just tried the latest kernel and audit. I am seeing some problems
regarding missing watch records. the kernel also seems to hang!
I tried twice, and got the same results. also after doing a mv on the
file, the system hangs (all windows hang, and I have to force reboot it).
Here is what I tried:
[root@comp1 objident]# auditctl -w /tmp/file1 -k file1-key
No rules
AUDIT_WATCH_LIST: dev=253:0, path=/tmp/file1, filterkey=file1-key,
perms=0, valid=1
[root@comp1 objident]# touch /tmp/file1
[root@comp1 objident]# auditctl -w /tmp/file2 -k file2-key
No rules
AUDIT_WATCH_LIST: dev=253:0, path=/tmp/file2, filterkey=file2-key,
perms=0, valid=1
AUDIT_WATCH_LIST: dev=253:0, path=/tmp/file1, filterkey=file1-key,
perms=0, valid=1
[root@comp1 objident]# touch /tmp/file2
[root@comp1 objident]# echo "test" >> /tmp/file1
[root@comp1 objident]# cat /tmp/file1
test
[root@comp1 objident]# echo "test file2" >> /tmp/file2
[root@comp1 objident]# cat /tmp/file2
test file2
[root@comp1 objident]# mv /tmp/file1 /tmp/foo
mv: overwrite `/tmp/foo'? y
I only see two records corresponding with the touch on both watched
files. The records also seem to be in different order than before
(backwards):
type=DAEMON msg=audit(1115727149.996:751) auditd start, ver=0.7.4,
format=raw, uid=514, auditd pid=2795
Init complete, audit pid set to: 2795
type=KERNEL msg=audit(1115727150.199:0): audit_enabled=1 old=1 by auid 514
type=KERNEL msg=audit(1115727176.862:367655): syscall=5 arch=40000003
success=yes exit=3 a0=bff21c18 a1=8941 a2=1b6 a3=8941 items=1 pid=2798
loginuid=514 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
comm="touch" exe=/bin/touch
type=KERNEL msg=audit(1115727176.862:367655): auxitem=1 watch="file1"
filterkey=file1-key perm=0 perm_mask=2 inode=2224526 inode_uid=0
inode_gid=0 inode_dev=fd:00 inode_rdev=00:00
type=KERNEL msg=audit(1115727176.862:367655): item=0 name="/tmp/file1"
inode=2223873 dev=fd:00 mode=041777 uid=0 gid=0 rdev=00:00
type=KERNEL msg=audit(1115727221.850:371646): syscall=5 arch=40000003
success=yes exit=3 a0=bffd1c18 a1=8941 a2=1b6 a3=8941 items=1 pid=2800
loginuid=514 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0
comm="touch" exe=/bin/touch
type=KERNEL msg=audit(1115727221.850:371646): auxitem=1 watch="file2"
filterkey=file2-key perm=0 perm_mask=2 inode=2224528 inode_uid=0
inode_gid=0 inode_dev=fd:00 inode_rdev=00:00
type=KERNEL msg=audit(1115727221.850:371646): item=0 name="/tmp/file2"
inode=2223873 dev=fd:00 mode=041777 uid=0 gid=0 rdev=00:00
- loulwa
19 years, 7 months