Log corruption
by Steve Grubb
Hello,
Still testing the new kernel. I've noticed that there is now log corruption
since we added the netlink patches:
type=KERNEL msg=audit(1115556451.615:8458544): item=0
name="/usr/libexec/dovecot/imap.#prelink#.QmQDxR" inode=1102979 dev=03:02
mode=040755 uid=0 gid=0 rdev=00:00:00d=0 gid=0 rde
What's the gid in twice for?
type=KERNEL msg=audit(1115556451.620:0): auid 4325 removed an audit rule
ccess=yes exit=5
rule was supposed to be end of sentence
type=USER msg=audit(1115557262.120:0): user pid=9262 uid=0 length=100
loginuid=4294967295 msg='PAM accounting: user=root exe=/usr/sbin/crond
(hostname=?, addr=?, terminal=cron result=Success)' sgid=0 fsgid=0
type=LOGIN msg=audit(1115557262.249:0): login pid=9262 uid=0 old
loginuid=4294967295 new loginuid=00 a1=80c2 a2=180
Why does the above have a1 & a2?
type=USER msg=audit(1115557262.453:0): user pid=9262 uid=0 length=100
loginuid=0 msg='PAM session open: user=root exe=/usr/sbin/crond (hostname=?,
addr=?, terminal=cron result=Success)'ccess)' sgid=0 f
Ended with an f
type=USER msg=audit(1115557262.784:0): user pid=9262 uid=0 length=100
loginuid=0 msg='PAM session close: user=root exe=/usr/sbin/crond (hostname=?,
addr=?, terminal=cron result=Success)'gid=0 sgid=0 fsg
cron should have been the end of message.
Also, should audit_expand take a parameter to suggest how big to grow? For
example, the buffer is inited to 1024, but it need to put a PATH_MAX sized
filename into a message. In audit_vformat, if does 1 if staement and then one
increment. So now the buffer is 2048. That's still too small for a 4096 byte
filename. Either the call to expand should be in a while loop, or it should
take a hint.
-Steve
19 years, 7 months
audit.33 kernel
by David Woodhouse
An audit.33 kernel is uploading, again to the original yum repository on
ftp.uk.linux.org. It fixes the lack of audit_filesystem_init() call in
audit_init(), and adds the suggested parameter to audit_expand() to
specify how much to expand the skb by.
There's an audit.34 building, with a different fix for the avc deadlock
which was observed, and with module signing disabled on ppc64.
There'll probably also be an audit.35 by the end of the day too, with
errors from netlink_unicast() in audit_send_reply() handled, and with
the abuse of va_args in audit_log_vformat() fixed.
You're not allowed to use va_args twice, so this is wrong:
len = vsnprintf(skb->tail, avail, fmt, args);
if (len >= avail) {
<...>
len = vsnprintf(skb->tail, avail, fmt, args);
--
dwmw2
19 years, 7 months
audit 0.7.4 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:
- Make sure ausearch ts & te obey DST.
- Code cleanups to make file system watches work correctly
This update is required to get auditctl sync'ed with the file system watches.
Please let me know if there are any problems.
-Steve
19 years, 7 months
Fw: Audit record emission
by Kris Wilson
These are the files for the stress test, cleaned up to remove LTP
dependencies.
(See attached file: test.conf)(See attached file: test1.c)(See attached
file: test2.c)(See attached file: test3.c)(See attached file: test3.sh)
19 years, 7 months
ausearch tool
by Kris Wilson
Regarding this problem from Dan Jones:
>> I've installed the audit-0.7.3 but ausearch just returns the usage
statement.
Steve Grubb replied:
>> David Woodhouse changed the kernel message format to have
>> a success statement.
Dan is using the .28 kernel. Is there a version of the kernel with the
change
mentioned above?
Thanks!
Kris Wilson
Linux Security
(512) 838-0126 T/L:678-0126
krisw(a)us.ibm.com
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
Audit record emission
by Steve Grubb
Hi,
I was looking into a problem from the test team and ran across this comment in
the kernel code:
http://lxr.linux.no/source/kernel/auditsc.c#L652
It basically says that audit records may be emitted as event records are
generated as opposed to syscall exit. The problem shows up during stress
testing. The records that get sent from the kernel are no where close to each
other and are hard to correlate.
The comment says that if the current technique isn't suitable, maybe we can
keep formatted records off of the context and then send them all at syscall
exit.
Can anyone see any problems with changing this?
-Steve
19 years, 7 months
syscalls
by Javier Godinez
Quick question,
Do the supported system calls depend on what the kernel supports or do
they depend on what auditd supports? It seems to me that it would have
to depend on whatever the kernel wants to send to user space right? So
every syscall that we want to be audited would have to be fist
implemented in the kernel, am I getting this right? I was looking
through the auditd sources and I was not able to find a list of
supported syscalls.
Any help would be greatly appreciated, thanks, Javier Godinez
19 years, 8 months
audit 0.7.3 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:
- Add code to get watch list to auditctl
- Get -f & -hn working in ausearch
- Added search by terminal, exe, and syscall to ausearch program
- Added -w parameter to match whole word in ausearch
This is the first untested merge of Tim's audit watch list code. It may be
buggy and I will look at it Thursday.
-Steve
19 years, 8 months