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
[RFC][PATCH] (#7U3) [linux-2.6.12-rc2-mm1] file system auditing
by Timothy R. Chavez
Hello,
This is update #3 to patch #7. I'm going to assume that the people
looking at this e-mail are familiar with what the patch is all about.
Two updates were made and a bug was fixed (Thank you Amy).
CHANGELOG (from Patch #7U2):
* Changed audit_attach_watch to audit_update_watch
* Used the 'data' pointer in audit_attach(update)_watch to shorten
assignments
* Added support in audit.c for AUDIT_WATCH_LIST. So, in the kernel, the
facility for listing watches and listing rules are now kept seperate.
When the kernel receives an AUDIT_LIST, it lists rules and when it
receives AUDIT_WATCH_LIST it lists watches. I had a pretty stupid
mistake in Patch #7U2 which was brought to light by Amy. Both the user
space piece and the kernel space piece were buggy. Once I saw the bug
in user space, I noticed the bug in the kernel. I was basically trying
to couple both types of lists in audit.c:audit_receive_msg() under the
AUDIT_LIST: directive. The only problem is when adding a rule, there's
a fall-through to AUDIT_LIST, and if what's being sent to the kernel is
AUDIT_ADD, then we'd list the watches twice (once for the fall through
and once after a request from user space).
-tim
diff -Nurp linux-2.6.12-rc2-mm1~orig/fs/dcache.c linux-2.6.12-rc2-mm1~audit/fs/dcache.c
--- linux-2.6.12-rc2-mm1~orig/fs/dcache.c 2005-04-11 09:14:36.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/dcache.c 2005-04-27 16:39:10.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-rc2-mm1~orig/fs/inode.c linux-2.6.12-rc2-mm1~audit/fs/inode.c
--- linux-2.6.12-rc2-mm1~orig/fs/inode.c 2005-04-11 09:14:37.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/inode.c 2005-04-05 13:16: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:
@@ -140,9 +141,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
@@ -180,6 +183,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-rc2-mm1~orig/fs/namei.c linux-2.6.12-rc2-mm1~audit/fs/namei.c
--- linux-2.6.12-rc2-mm1~orig/fs/namei.c 2005-04-11 09:14:36.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/fs/namei.c 2005-04-05 13:16: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 @@ int vfs_rename_dir(struct inode *old_dir
}
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 @@ int vfs_rename_other(struct inode *old_d
/* 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-rc2-mm1~orig/include/linux/audit.h linux-2.6.12-rc2-mm1~audit/include/linux/audit.h
--- linux-2.6.12-rc2-mm1~orig/include/linux/audit.h 2005-04-11 09:15:03.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/include/linux/audit.h 2005-04-27 16:37:54.000000000 -0500
@@ -24,18 +24,27 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
#include <linux/sched.h>
#include <linux/elf.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <asm/atomic.h>
+#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 +141,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 +171,42 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+/* Structure to transport watch data to and from the kernel */
+
+struct audit_transport {
+ __u32 dev_major;
+ __u32 dev_minor;
+ __u32 perms;
+ __u32 valid;
+ __u32 pathlen;
+ __u32 fklen;
+ char buf[0];
+};
+
#ifdef __KERNEL__
+struct audit_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 +236,7 @@ extern void audit_get_stamp(struct audit
extern int audit_set_loginuid(struct audit_context *ctx, 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 +247,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 audit_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-rc2-mm1~orig/include/linux/fs.h linux-2.6.12-rc2-mm1~audit/include/linux/fs.h
--- linux-2.6.12-rc2-mm1~orig/include/linux/fs.h 2005-04-11 09:15:02.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/include/linux/fs.h 2005-04-20 17:20:32.000000000 -0500
@@ -226,6 +226,7 @@ struct poll_table_struct;
struct kstatfs;
struct vm_area_struct;
struct vfsmount;
+struct audit_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_data *i_audit;
unsigned long i_state;
unsigned long dirtied_when; /* jiffies of first dirtying */
diff -Nurp linux-2.6.12-rc2-mm1~orig/init/Kconfig linux-2.6.12-rc2-mm1~audit/init/Kconfig
--- linux-2.6.12-rc2-mm1~orig/init/Kconfig 2005-04-11 09:15:38.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/init/Kconfig 2005-04-05 13:16:26.000000000 -0500
@@ -198,6 +198,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-rc2-mm1~orig/kernel/Makefile linux-2.6.12-rc2-mm1~audit/kernel/Makefile
--- linux-2.6.12-rc2-mm1~orig/kernel/Makefile 2005-04-11 09:15:35.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/kernel/Makefile 2005-04-05 13:21:20.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-rc2-mm1~orig/kernel/audit.c linux-2.6.12-rc2-mm1~audit/kernel/audit.c
--- linux-2.6.12-rc2-mm1~orig/kernel/audit.c 2005-04-11 09:15:36.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/kernel/audit.c 2005-04-27 17:42:06.000000000 -0500
@@ -322,6 +322,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;
@@ -416,6 +419,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 audit_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-rc2-mm1~orig/kernel/auditfs.c linux-2.6.12-rc2-mm1~audit/kernel/auditfs.c
--- linux-2.6.12-rc2-mm1~orig/kernel/auditfs.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.12-rc2-mm1~audit/kernel/auditfs.c 2005-04-27 17:00:31.000000000 -0500
@@ -0,0 +1,546 @@
+/* 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>
+
+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_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_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);
+ }
+}
+
+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_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_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_data *audit_data_alloc(void)
+{
+ struct audit_data *data;
+
+ data = kmalloc(sizeof(struct audit_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_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(char *path, char *filterkey, __u32 perms)
+{
+ int ret;
+ struct nameidata nd;
+
+ ret = path_lookup(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(path, nd.last.name, filterkey, 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(char *path)
+{
+ int ret;
+ struct nameidata nd;
+ struct audit_data *data;
+ struct audit_wentry *wentry;
+
+ ret = path_lookup(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);
+}
+
+/*
+ * 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_data *data, *parent;
+
+ 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) {
+ if (!strcmp(wentry->w_watch->name,
+ data->wentry->w_watch->name)) {
+ audit_wentry_put(data->wentry);
+ data->wentry = NULL;
+ }
+ }
+ } else if (!data->wentry || hlist_unhashed(&data->wentry->w_node)) {
+ audit_wentry_put(data->wentry);
+ data->wentry = audit_wentry_get(wentry);
+ }
+ audit_wentry_put(wentry);
+ write_unlock(&data->lock);
+}
+
+int audit_send_watch(int pid, int seq, struct audit_watch *watch)
+{
+ int ret = 0;
+ void *memblk;
+ unsigned int offset;
+ unsigned int total;
+ struct audit_data *data;
+ struct audit_wentry *wentry;
+ struct audit_transport req;
+ struct nameidata nd;
+
+ req.valid = 0;
+ req.dev_major = MAJOR(watch->dev);
+ req.dev_minor = MINOR(watch->dev);
+ req.perms = watch->perms;
+ req.pathlen = strlen(watch->path) + 1;
+ if (watch->filterkey)
+ req.fklen = strlen(watch->filterkey) + 1;
+ else
+ req.fklen = 0;
+
+ ret = -ENOMEM;
+ total = sizeof(req) + req.pathlen + req.fklen;
+ memblk = kmalloc(total, GFP_KERNEL);
+ if (!memblk)
+ goto audit_send_watch_exit;
+
+ /* See if path to watch is valid */
+ 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)
+ req.valid = 1;
+ audit_wentry_put(wentry);
+ path_release(&nd);
+ }
+
+ /* Payload */
+ memcpy(memblk, &req, sizeof(req));
+ offset = total - req.fklen;
+ memcpy(memblk + offset, watch->filterkey, req.fklen);
+ offset = offset - req.pathlen;
+ memcpy(memblk + offset, watch->path, req.pathlen);
+
+ audit_send_reply(pid, seq, AUDIT_WATCH_LIST, 0, 1,
+ memblk, total);
+
+ kfree(memblk);
+
+ ret = 0;
+
+audit_send_watch_exit:
+ 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 audit_transport *req)
+{
+ int ret = 0;
+ char *path = NULL;
+ char *filterkey = NULL;
+
+ ret = -ENOMEM;
+ path = kmalloc(req->pathlen, GFP_KERNEL);
+ if (!path)
+ goto audit_receive_watch_exit;
+ strncpy(path, req->buf, req->pathlen);
+
+ if (req->fklen) {
+ filterkey = kmalloc(req->fklen, GFP_KERNEL);
+ if (!filterkey)
+ goto audit_receive_watch_exit;
+ }
+ strncpy(filterkey, req->buf+req->pathlen, req->fklen);
+
+ ret = -EINVAL;
+ if (!path || strlen(path) + 1 > PATH_MAX)
+ goto audit_receive_watch_exit;
+
+ /* Includes terminating '\0' */
+ 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;
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ ret = audit_insert_watch(path, filterkey, req->perms);
+ break;
+ case AUDIT_WATCH_REM:
+ ret = audit_remove_watch(path);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+audit_receive_watch_exit:
+ kfree(path);
+ kfree(filterkey);
+ 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-rc2-mm1~orig/kernel/auditsc.c linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c
--- linux-2.6.12-rc2-mm1~orig/kernel/auditsc.c 2005-04-11 09:15:36.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/kernel/auditsc.c 2005-04-27 16:59:34.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 {
@@ -665,6 +676,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);
@@ -1024,3 +1053,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(!context))
+ goto audit_notify_watch_fail;
+
+ if (!inode)
+ goto audit_notify_watch_fail;
+
+ wentry = audit_wentry_get(inode->i_audit->wentry);
+ if (!wentry)
+ goto audit_notify_watch_fail;
+
+ 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-rc2-mm1~orig/security/selinux/nlmsgtab.c linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c
--- linux-2.6.12-rc2-mm1~orig/security/selinux/nlmsgtab.c 2005-04-11 09:15:36.000000000 -0500
+++ linux-2.6.12-rc2-mm1~audit/security/selinux/nlmsgtab.c 2005-04-05 17:58:51.000000000 -0500
@@ -98,6 +98,9 @@ static struct nlmsg_perm nlmsg_audit_per
{ AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_LOGIN, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_INS, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_REM, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
+ { AUDIT_WATCH_LIST, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
};
.
19 years, 7 months
[RFC] Testcase Scenarios for Auditfs Code
by Loulwa F Salem
These are the testcases I am writing to test the code Tim is providing for
the audit filesystem. The testcases will be included in LTP.
These cases are meant to cover the CAPP requirements. there is some FVT
testing covered as well, but that still needs to be expanded to test the
permissions and boundary cases (watch fields lengths, invalid fields ...
etc).
Please give feedback if you can think of any scenarios that I have not
considered, or modifications that I need to implement.
Thanks
- Loulwa
Test 1 : Access watched file by opening it (file already exists)
Expected: 1 watch records for file access/open
Test 2 : Create a watched file (file created after watch is inserted)
Expected: 1 watch records for open() with regards to file
creation
Test 3 : Create hard link to a watched file and then access it by opening
it for read
Expected: 1 watch record for open() on original file
Test 4 : Create hard link to an unwatched file, watch file, then access
hard link by opening it for read
Expected: 1 watch record for open() on original file
Test 5 : Create hard link to watched file, delete watched file, then
access hard link by opening it for read
Expected: No watch record
Test 6 : Recreate a watched file that has been deleted previously
Expected: 1 watch record for unlink() with regards to file
deletion
Expected: 1 watch record for open() with regards to file
recreation
Test 7 : Recreate a watched file that has been deleted previously
(creating hardlink to it first, so on recreation we are
certain we get new inode number)
Expected: 1 watch record for unlink() with regards to file
deletion
Expected: 1 watch record for open() with regards to file
recreation
Test 8 : Delete a watched file
Expected: 1 watch record for file deletion
Test 9 : Remove a watched directory
Expected: 1 watch record for rmdir() with respect to
directory removal
Test 10 : copy a watched file to unwatched name (copy out) - access
through new name
Expected: 1 watch record for original file access/open
Expected: No record for accessing through new name
Test 11 : copy a file to a watched name (copy in) - access through new
name
Expected: 1 record for file creation in new name
Expected: 1 record for new file access/open
Test 12 : Move a watched file out of a watched location and access by
opening it for read
Expected: 1 watch record for rename() with regards to
moving out of a watched location.
Expected: No watch record for new file open()
Test 13 : Move a watched file into a watched location and access by
opening it for read
Expected: 1 record for rename() with regards to moving out
of a watched location.
Expected: 1 watch record for open() in new location
Test 14 : Move a directory having a watched file - access file in new
location
Expected: No watch record
19 years, 7 months
[PATCH] add new audit data to last skb
by Chris Wright
When adding more formatted audit data to an skb for delivery to userspace,
the kernel will attempt to reuse an skb that has spare room. However, if
the audit message has already been fragmented to multiple skb's, the search
for spare room in the skb uses the head of the list. This will corrupt the
audit message with trailing bytes being placed midway through the stream.
Fix is to look at the end of the list.
Signed-off-by: Chris Wright <chrisw(a)osdl.org>
---
audit.c | 2 +-
1 files changed, 1 insertion(+), 1 deletion(-)
kernel/audit.c: 4a697c73faec0b952d4b240e7c3fadef49313148
--- k/kernel/audit.c
+++ l/kernel/audit.c
@@ -486,7 +486,7 @@ static void audit_log_move(struct audit_
if (ab->len == 0)
return;
- skb = skb_peek(&ab->sklist);
+ skb = skb_peek_tail(&ab->sklist);
if (!skb || skb_tailroom(skb) <= ab->len + extra) {
skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC);
if (!skb) {
19 years, 7 months
[PATCH] audit-0.6.10 (update for kernel patch #7U3)
by Timothy R. Chavez
Hello,
This patch is to keep in sync with kernel Patch #7U3. It fixes a buggy
rules and watches listing feature and has been modified to work with the
new kernel rules and watches listing interface. The user space rules
and watches listing feature would try to list both rules and watches all
in one get_reply() (with one NLMSG_DONE), but didn't get this right. As
a consequence, rules were not being listed, because it was exiting the
get_reply() before it had a chance. Once I fixed this bug, I discovered
another bug in the kernel related to listing rules and watches which was
addressed in the kernel Patch #7U3. This bug is described in the e-mail
containing Patch #7U3.
I'm also sorry that this patch is not available for the latest audit
user space patch. I know it makes it really hard to sync user space
with kernel space and having to keep rebuilding a user space package and
kernel is annoying to boot. When Steve is ready, he'll adapt/merge this
code up to the latest version of the audit user package.
-tim
diff -Nurp audit-0.6.10/lib/libaudit.c auditfs~audit-0.6.10/lib/libaudit.c
--- audit-0.6.10/lib/libaudit.c 2005-03-31 16:04:57.000000000 -0600
+++ auditfs~audit-0.6.10/lib/libaudit.c 2005-04-27 17:35:49.000000000 -0500
@@ -194,7 +194,16 @@ int audit_set_backlog_limit(int fd, int
return rc;
}
-int audit_request_list(int fd)
+int audit_request_list_watches(int fd)
+{
+ int rc = audit_send(fd, AUDIT_WATCH_LIST, NULL, 0);
+ if (rc < 0)
+ msg(LOG_WARNING, "Error sending list request (%s)",
+ strerror(-rc));
+ return rc;
+}
+
+int audit_request_list_rules(int fd)
{
int rc = audit_send(fd, AUDIT_LIST, NULL, 0);
if (rc < 0)
@@ -203,18 +212,44 @@ int audit_request_list(int fd)
return rc;
}
-int audit_insert_watch(int fd, struct audit_watch *req)
+int audit_insert_watch(int fd, struct audit_transport *req, char **buf)
{
- int rc = audit_send(fd, AUDIT_WATCH_INS, req, sizeof(*req));
+ void *memblk = NULL;
+ unsigned int offset, total;
+
+ total = sizeof(*req) + req->pathlen + req->fklen;
+ memblk = (void*)malloc(total);
+ if (!memblk)
+ return -1;
+
+ memcpy(memblk, req, sizeof(*req));
+ offset = total - req->fklen;
+ memcpy(memblk + offset, buf[1], req->fklen);
+ offset = offset - req->pathlen;
+ memcpy(memblk + offset, buf[0], req->pathlen);
+
+ int rc = audit_send(fd, AUDIT_WATCH_INS, memblk, total);
if (rc < 0)
msg(LOG_WARNING, "Error sending watch insert request (%s)",
strerror(-rc));
return rc;
}
-int audit_remove_watch(int fd, struct audit_watch *req)
+int audit_remove_watch(int fd, struct audit_transport *req, char **buf)
{
- int rc = audit_send(fd, AUDIT_WATCH_REM, req, sizeof(*req));
+ void *memblk = NULL;
+ unsigned int offset, total;
+
+ total = sizeof(*req) + req->pathlen;
+ memblk = (void*)malloc(total);
+ if (!memblk)
+ return -1;
+
+ memcpy(memblk, req, sizeof(*req));
+ offset = total - req->pathlen;
+ memcpy(memblk + offset, buf[0], req->pathlen);
+
+ int rc = audit_send(fd, AUDIT_WATCH_REM, memblk, total);
if (rc < 0)
msg(LOG_WARNING, "Error sending watch remove request (%s)",
strerror(-rc));
diff -Nurp audit-0.6.10/lib/libaudit.h auditfs~audit-0.6.10/lib/libaudit.h
--- audit-0.6.10/lib/libaudit.h 2005-04-01 12:29:48.000000000 -0600
+++ auditfs~audit-0.6.10/lib/libaudit.h 2005-04-27 17:35:43.000000000 -0500
@@ -42,11 +42,22 @@
#define AUDIT_WATCH_LIST 1009
struct audit_watch {
- uint32_t namelen;
- uint32_t fklen;
- char *name;
- char *filterkey;
- uint32_t perms;
+ uint32_t dev_major;
+ uint32_t dev_minor;
+ char *path;
+ char *filterkey;
+ uint32_t perms;
+ uint32_t valid;
+};
+
+struct audit_transport {
+ uint32_t dev_major;
+ uint32_t dev_minor;
+ uint32_t perms;
+ uint32_t valid;
+ uint32_t pathlen;
+ uint32_t fklen;
+ char buf[0];
};
/* 32 byte max key size */
#define AUDIT_FILTERKEY_MAX 32
@@ -57,15 +68,15 @@ struct audit_watch {
struct audit_reply {
struct audit_message msg;
- int type;
- int len;
- struct nlmsghdr *nlh;
- struct audit_status *status;
- struct audit_rule *rule;
- struct audit_login *login;
- const char *message;
- struct nlmsgerr *error;
- int watch;
+ int type;
+ int len;
+ struct nlmsghdr *nlh;
+ struct audit_status *status;
+ struct audit_rule *rule;
+ struct audit_login *login;
+ struct audit_transport *watch;
+ const char *message;
+ struct nlmsgerr *error;
};
struct auditd_reply_list {
@@ -117,11 +128,12 @@ extern int audit_set_rate_limit(int fd,
extern int audit_set_backlog_limit(int fd, int limit);
/* AUDIT_LIST */
-extern int audit_request_list(int fd);
+extern int audit_request_list_rules(int fd);
+extern int audit_request_list_watches(int fd);
/* AUDIT_WATCH */
-extern int audit_insert_watch(int fd, struct audit_watch *req);
-extern int audit_remove_watch(int fd, struct audit_watch *req);
+extern int audit_insert_watch(int fd, struct audit_transport *req, char **buf);
+extern int audit_remove_watch(int fd, struct audit_transport *req, char **buf);
/* AUDIT_ADD */
extern int audit_add_rule(int fd, struct audit_rule *rule,
diff -Nurp audit-0.6.10/lib/netlink.c auditfs~audit-0.6.10/lib/netlink.c
--- audit-0.6.10/lib/netlink.c 2005-03-31 16:04:57.000000000 -0600
+++ auditfs~audit-0.6.10/lib/netlink.c 2005-04-27 17:37:12.000000000 -0500
@@ -133,7 +133,7 @@ static int adjust_reply(struct audit_rep
rep->rule = NULL;
rep->message = NULL;
rep->error = NULL;
- rep->watch = 0;
+ rep->watch = NULL;
if (!NLMSG_OK(rep->nlh, (unsigned int)len))
return 0;
switch (rep->type) {
@@ -153,7 +153,8 @@ static int adjust_reply(struct audit_rep
break;
case AUDIT_WATCH_INS:
case AUDIT_WATCH_REM:
- memcpy(&rep->watch, NLMSG_DATA(rep->nlh), sizeof(int));
+ case AUDIT_WATCH_LIST:
+ rep->watch = NLMSG_DATA(rep->nlh);
break;
}
return len;
diff -Nurp audit-0.6.10/src/auditctl.c auditfs~audit-0.6.10/src/auditctl.c
--- audit-0.6.10/src/auditctl.c 2005-04-01 13:06:42.000000000 -0600
+++ auditfs~audit-0.6.10/src/auditctl.c 2005-04-27 17:40:01.000000000 -0500
@@ -67,6 +67,7 @@ static int delete_all_rules(void);
/* Global vars */
static int fd = -1;
+static int list_request = 0;
static int list_requested = 0;
static int syscalladded = 0;
static int add = 0, del = 0, action = 0;
@@ -74,6 +75,8 @@ static int ins = 0, rem = 0;
static struct audit_rule rule;
static struct audit_watch watch;
+static struct audit_transport wreq;
+static char *wreq_buf[2] = {NULL, NULL};
/*
* This function will reset everything used for each loop when loading
@@ -81,6 +84,7 @@ static struct audit_watch watch;
*/
static int reset_vars(void)
{
+ list_request = 0;
list_requested = 0;
syscalladded = 0;
add = 0;
@@ -90,7 +94,7 @@ static int reset_vars(void)
rem = 0;
memset(&rule, 0, sizeof(rule));
- memset(&watch, 0, sizeof(watch));
+ memset(&wreq, 0, sizeof(wreq));
if ((fd = audit_open()) < 0) {
fprintf(stderr, "Cannot open netlink audit socket\n");
return 1;
@@ -116,7 +120,8 @@ static void usage(void)
" -k <key> Set filterkey on watch\n"
" -l List rules\n"
" -m text Send a user-space message\n"
- " -p [r|w|e|a] Set permissions filter on watch:\n"
+ " -p [r|w|e|a] Set permissions filter on watch\n"
+ " r=read, w=write, e=execute, a=append\n"
" -r <rate> Set limit in messages/sec (0=none)\n"
" -R <file> read rules from file\n"
" -s Report status\n"
@@ -125,7 +130,6 @@ static void usage(void)
" -v Version\n"
" -w <path> Insert watch at <path>\n"
" -W <path> Remove watch at <path>\n"
- " r=read, w=write, e=execute, a=append\n"
);
}
@@ -183,24 +187,47 @@ static int check_path(const char *path)
return 0;
}
+static int make_watch(struct audit_transport *wreq, struct audit_watch *watch)
+{
+ watch->path = (char*)malloc(wreq->pathlen);
+ if (!watch->path)
+ return 0;
+
+ memcpy(watch->path, wreq->buf, wreq->pathlen);
+
+ if (wreq->fklen) {
+ watch->filterkey = (char*)malloc(wreq->fklen);
+ if (!watch->filterkey) {
+ free(watch->path);
+ return 0;
+ }
+ }
+
+ memcpy(watch->filterkey, wreq->buf+wreq->pathlen, wreq->fklen);
+
+ watch->dev_major = wreq->dev_major;
+ watch->dev_minor = wreq->dev_minor;
+ watch->perms = wreq->perms;
+ watch->valid = wreq->valid;
+
+ return 1;
+}
+
/*
- * Setup a watch. The "name" of the watch in userspace will be the <path> to
- * the watch. When this potential watch reaches the kernel, it will resolve
- * down to <name> (of terminating file or directory).
* Returns a 1 on success & -1 on failure.
*/
-static int audit_setup_watch_name(struct audit_watch *req, const char *opt,
+static int audit_setup_watch_path(struct audit_transport *req, const char *opt,
int *act)
{
- if (!req->name) {
+ if (!wreq_buf[0]) {
if (check_path(opt))
return -1;
- req->name = strdup(opt);
- if (!req->name) {
+ wreq_buf[0] = strdup(opt);
+ if (!wreq_buf[0]) {
fprintf(stderr, "Out of memory\n");
return -1;
}
- req->namelen = strlen(req->name) + 1;
+ req->pathlen = strlen(wreq_buf[0]) + 1;
*act = 1;
return 1;
}
@@ -212,18 +239,18 @@ static int audit_setup_watch_name(struct
* Setup a filterkey for the watch.
* Returns a 1 on success & -1 on failure.
*/
-static int audit_setup_filterkey(struct audit_watch *req, const char *opt)
+static int audit_setup_filterkey(struct audit_transport *req, const char *opt)
{
- if (!req->filterkey) {
- req->filterkey = strdup(opt);
- if (!req->filterkey) {
+ if (!wreq_buf[1]) {
+ wreq_buf[1] = strdup(opt);
+ if (!wreq_buf[1]) {
fprintf(stderr, "Out of memory\n");
return -1;
}
req->fklen = strlen(opt) + 1;
if (req->fklen > AUDIT_FILTERKEY_MAX) {
fprintf(stderr, "The filterkey is too big\n");
- free(req->filterkey);
+ free(wreq_buf[1]);
return -1;
}
return 1;
@@ -236,7 +263,7 @@ static int audit_setup_filterkey(struct
* Setup a watch permissions.
* Returns a 1 on success & -1 on failure.
*/
-static int audit_setup_perms(struct audit_watch *req, const char *opt)
+static int audit_setup_perms(struct audit_transport *req, const char *opt)
{
int i;
@@ -283,6 +310,23 @@ audit_watch_setup_exit:
return -1;
}
+int audit_request_list(int fd) {
+ if (audit_request_list_rules(fd)) {
+ list_requested = 1;
+ get_reply();
+ } else {
+ fprintf(stderr, "Error requesting rules list\n");
+ return -1;
+ }
+ if (audit_request_list_watches(fd)) {
+ list_requested = 1;
+ get_reply();
+ } else {
+ fprintf(stderr, "Error requesting watches list\n");
+ return -1;
+ }
+}
+
/*
* returns: < 0 error - noreply, 0 success - reply, > 0 success - rule
@@ -352,10 +396,7 @@ static int setopt(int count, char *vars[
}
break;
case 'l':
- if (audit_request_list(fd))
- list_requested = 1;
- else
- retval = -1;
+ list_request = 1;
break;
case 'a':
if (strstr(optarg, "task") && syscalladded) {
@@ -453,7 +494,7 @@ static int setopt(int count, char *vars[
break;
case 'w':
if (optarg)
- retval = audit_setup_watch_name(&watch, optarg, &ins);
+ retval = audit_setup_watch_path(&wreq, optarg, &ins);
else {
fprintf(stderr, "watch option needs a path\n");
retval = -1;
@@ -461,7 +502,7 @@ static int setopt(int count, char *vars[
break;
case 'W':
if (optarg)
- retval = audit_setup_watch_name(&watch, optarg, &rem);
+ retval = audit_setup_watch_path(&wreq, optarg, &rem);
else {
fprintf(stderr, "watch option needs a path\n");
retval = -1;
@@ -478,7 +519,7 @@ static int setopt(int count, char *vars[
retval = -1;
}
else
- retval = audit_setup_filterkey(&watch, optarg);
+ retval = audit_setup_filterkey(&wreq, optarg);
break;
case 'p':
if (!ins) {
@@ -491,7 +532,7 @@ static int setopt(int count, char *vars[
retval = -1;
}
else
- retval = audit_setup_perms(&watch, optarg);
+ retval = audit_setup_perms(&wreq, optarg);
break;
case 'v':
printf("auditctl version %s\n", VERSION);
@@ -655,32 +696,29 @@ int main(int argc, char *argv[])
*/
static int handle_request(int status)
{
- if (status == 0)
- get_reply();
- else if (status > 0) {
+ if (status == 0) {
+ if (list_request)
+ status = audit_request_list(fd);
+ else
+ get_reply();
+ } else if (status > 0) {
int rc;
if (add & 0x07)
rc = audit_add_rule(fd, &rule, add, action);
else if (del & 0x07)
rc = audit_delete_rule(fd, &rule, del, action);
else if (ins && !rem)
- rc = audit_insert_watch(fd, &watch);
+ rc = audit_insert_watch(fd, &wreq, wreq_buf);
else if (rem && !ins)
- rc = audit_remove_watch(fd, &watch);
+ rc = audit_remove_watch(fd, &wreq, wreq_buf);
else {
usage();
audit_close(fd);
exit(1);
}
- if (rc > 0) {
- if (audit_request_list(fd) > 0) {
- list_requested = 1;
- get_reply();
- } else {
- fprintf(stderr, "Error requesting list\n");
- status = -1;
- }
- } else {
+ if (rc > 0)
+ status = audit_request_list(fd);
+ else {
fprintf(stderr, "Error sending rule to kernel\n");
status = -1;
}
@@ -729,8 +767,6 @@ static int audit_print_reply(struct audi
case NLMSG_NOOP:
return 1;
case NLMSG_DONE:
- if (list_requested)
- printf("No rules\n");
return 0;
case NLMSG_ERROR:
printf("NLMSG_ERROR %d (%s) type=%d seq=%d\n",
@@ -797,6 +833,21 @@ static int audit_print_reply(struct audi
}
printf("\n");
return 1; /* get more messages until NLMSG_DONE */
+ case AUDIT_WATCH_LIST: {
+ int ret = make_watch(rep->watch, &watch);
+ if (!ret)
+ return ret;
+ list_requested = 0;
+ printf("AUDIT_WATCH_LIST: dev=%u:%u, path=%s, "
+ "filterkey=%s, perms=%u, valid=%d\n",
+ watch.dev_major, watch.dev_minor, watch.path,
+ watch.filterkey, watch.perms, watch.valid);
+
+ free(watch.path);
+ free(watch.filterkey);
+ memset(&watch, 0, sizeof(watch));
+ }
+ return 1;
default:
printf("Unknown: type=%d, len=%d\n", rep->type,
rep->nlh->nlmsg_len);
@@ -811,7 +862,7 @@ static int delete_all_rules(void)
struct audit_reply rep;
/* list the rules */
- rc = audit_request_list(fd);
+ rc = audit_request_list_rules(fd);
if (rc <= 0)
return -1;
@@ -842,7 +893,7 @@ static int delete_all_rules(void)
/* Finish by proving we are empty */
list_requested = 1;
- if (audit_request_list(fd) < 0)
+ if (audit_request_list_rules(fd) < 0)
return -1;
else
return 0;
19 years, 7 months
audit 0.7.2 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 It will also be in rawhide
tomorrow. The Changelog is:
- Allow ausearch uid & gid to be non-numeric (root, wheel, etc)
- Fix problems with changing run level
- Added new code for logging shutdown reason credentials
- Update DAEMON messages to use better timestamp
- Fixed netlink error handling issues
This release has the user space portion of the shutdown credentials kernel
patch. It is sync'ed with the patch that was issued this afternoon.
There were some minor netlink clean ups that fix an issue where spurious "No
rules" would be emitted.
Let me know if there are any problems...
-Steve
19 years, 7 months
audit.27 kernel
by David Woodhouse
Currently uploading to ftp.uk.linux.org slowly, and also already on
zeniv.uk.linux.org as before.
* Fri Apr 29 2005 David Woodhouse <dwmw2(a)redhat.com> audit.27
- (Attempt to) fix x86_64 auditing of i386 tasks. The syscall
calling convention is different.
- Exclude S390/S390X since those machines seem to be temporarily
absent from the build farm.
* Fri Apr 29 2005 David Woodhouse <dwmw2(a)redhat.com> audit.26
- Fix the audit-kill printk so it's actually conditional
- Reorder patches again to reflect what's in -mm
* Fri Apr 29 2005 David Woodhouse <dwmw2(a)redhat.com> audit.25
- auditfs #7U3
- Log correct uid/pid when setting loginuid.
- printk when auditd is killed as well as sending audit message
--
dwmw2
19 years, 7 months
auditd netlink headers
by Chris Wright
I'm missing what in auditd allows for continuation of a netlink packet
from the kernel. If the payload is larger than one packet, AFAICT the
whole thing is dropped. This could be used to hide activity I think.
Problem is this:
lib/netlink.c::adjust_reply()
if (!NLMSG_OK(rep->nlh, (unsigned int)len))
return 0;
If the payload spans audit_buffers, the first packet has the netlink
header, and subsequent packets don't. Also, the netlink header on the
first packet says the length is the full audit buffer, which could be
larger than the 1200byte + header size that audit_get_reply() looks for.
thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
19 years, 7 months