syscall arguments in audit records
by Timothy R. Chavez
System call arguments are pretty useless unless you're in a process where the
memory addresses are still valid (like a testcase). Would it be useful to
put an option in at a later date that allows you to dump arguments as human
readable?
-tim
19 years, 8 months
[RFC][PATCH] (#6 U1) the latest incarnation
by Timothy R. Chavez
Hello,
This is the latest patch. Quite a bit has changed and a lot more of the complexity has been stripped away. I've added back the hooks for d_instantiate() and d_splice_alias() for good form and rethought the positioning of the d_move() hooks. Also, I've done quite a bit with the locking. Now, [admittedly] I'm a novice, so the reader-writer locking stradegy I've used is probably not the best for performance -- especially since I've hooked __d_lookup() and will hit a write_lock() when I enter audit_attach_watch() (formly called audit_watch()).
Perhaps the locking can eventually change to using RCU and what not, but I'm pressed for time and have limited knowledge.
To save space in the inode struct, I've kept the i_audit as a pointer, rather then statically allocating memory for it. Now, all inode's get i_audit data allocated to them upon creation, and freed upon deletion. Is it reasonable to keep it this way? I am using kmalloc for this allocation, so I do believe I'm still wasting space, but I suppose I can change this back to audit_data slab allocations -- I just didn't know of a good way of creating the cache outside of the audit_inode_alloc() function itself.
I'll get cracking on the "list all watches" feature.
This is a patch against 2.6.11 for immediate gratification (and testing). David, I'll try to get you a patch to the the bitkeeper tree asap.
-tim
19 years, 8 months
report on testing patch #6 U1
by Mounir Bsaibes
My main goal for this test was to see how the code handles the watch
list management specially on boundaries.
I did not try to be too creative in this test. I ran the test on
intel 2.6.11 kernel. Non SMP.
The short conclusion is that the Watch list management holds pretty
good. Again, this is was not an SMP box.
Suggested improvements:
- A little slow on delete. Did not time, just from observation.
- Change the return error whenever deleting a nonexistent watch from:
operation not permitted to something like watch does not exists.
- Need (not necessarily a CAPP requirement) an audit record whenever a
Watch is added or deleted.
What I did:
- Just one Watch node in the Watch list -> OK
- 10000 Watch nodes for 10000 files, touch all the files -> OK, got
all the records.
- Added 100 to the 10000 Watch nodes with auditcl -w xxx -p w -k keyxxx ->
OK,
got all records.
- Looping and accessing the last watch point for 1000 times -> OK, got
all records, no hang.
- Looping on a middle Watch node for 1000 times -> OK, got all records, no
hang.
- Looping on the first Watch node for 1000 times -> Ok, got all records no
hang.
- 4 simultaneous loops with 10000 iteration each all accessing the
same file. -> OK, correct number of records, no hang.
- Delete the last Watch point & access the file -> OK, no record
generated.
- Delete 500 Watch nodes from the middle -> OK, BUT took somewhat
longer than expected
- Delete 3000 Watch nodes from the end. & access the remaining files
-> OK, correct number of records.
- Delete 500 from the beginning of the list & access the remaining
files -> OK, correct number of records.
- Add Watch nodes for 1000 directories: auditctl -w xxx -p w -k kxxx
-> OK. correct number of records.
- Create one file in each of these directories -> OK, correct number of
records.
- Loop creating 10 files in each directory -> OK, correct number of
records.
Mounir Bsaibes
Linux Security
Tel: (512) 838-1301
Cell: (512) 762-9957
Fax: (512) 838-8858
e-mail: bsaibes(a)us.ibm.com
19 years, 8 months
get_stamp serial is unsigned
by Steve Grubb
Hi,
Attached is a patch that corrects a signed/unsigned warning. I also noticed
that we needlessly init serial to 0. That only needs to occur if the kernel
was compiled without the audit system.
-Steve Grubb
19 years, 8 months
Re: Fw: [PATCH] namei: add audit_inode to all branches in path_lookup
by David Woodhouse
On Tue, 2005-03-22 at 19:10 -0800, Andrew Morton wrote:
> There's a patch for you over on linux-mm.
Looks sane; added to the BK tree. Thanks.
> Is there an audit mailing list?
There is. linux-audit(a)redhat.com
> Begin forwarded message:
>
> Date: Tue, 22 Mar 2005 19:15:02 -0800
> From: pmeda(a)akamai.com
> To: akpm(a)osdl.org
> Cc: linux-mm(a)kvack.org
> Subject: [PATCH] namei: add audit_inode to all branches in path_lookup
>
>
>
> Main change is in path_lookup: added a goto to do audit_inode
> instead of return statement, when emul_lookup_dentry for root
> is successful. The existing code does audit_inode only when
> lookup is done in normal root or cwd.
>
> Other changes: Some lookup routines are returning zero on success,
> and some are returning zero on failure. I documented the related
> function signatures in this code path, so that one can glance over
> abstract functions without understanding the entire code.
>
> Signed-off-by: Prasanna Meda <pmeda(a)akamai.com>
>
>
> --- linux/fs/namei.c Tue Mar 22 01:29:37 2005
> +++ Linux/fs/namei.c Tue Mar 22 01:48:24 2005
> @@ -675,11 +675,11 @@
>
> /*
> * Name resolution.
> + * This is the basic name resolution function, turning a pathname into
> + * the final dentry. We expect 'base' to be positive and a directory.
> *
> - * This is the basic name resolution function, turning a pathname
> - * into the final dentry.
> - *
> - * We expect 'base' to be positive and a directory.
> + * Returns 0 and nd will have valid dentry and mnt on success.
> + * Returns error and drops reference to input namei data on failure.
> */
> int fastcall link_path_walk(const char * name, struct nameidata *nd)
> {
> @@ -887,8 +887,10 @@
> return link_path_walk(name, nd);
> }
>
> -/* SMP-safe */
> -/* returns 1 if everything is done */
> +/*
> + * SMP-safe: Returns 1 and nd will have valid dentry and mnt, if
> + * everything is done. Returns 0 and drops input nd, if lookup failed;
> + */
> static int __emul_lookup_dentry(const char *name, struct nameidata *nd)
> {
> if (path_walk(name, nd))
> @@ -952,9 +954,10 @@
> }
> }
>
> +/* Returns 0 and nd will be valid on success; Retuns error, otherwise. */
> int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
> {
> - int retval;
> + int retval = 0;
>
> nd->last_type = LAST_ROOT; /* if there are only slashes... */
> nd->flags = flags;
> @@ -967,7 +970,7 @@
> nd->dentry = dget(current->fs->altroot);
> read_unlock(¤t->fs->lock);
> if (__emul_lookup_dentry(name,nd))
> - return 0;
> + goto out; /* found in altroot */
> read_lock(¤t->fs->lock);
> }
> nd->mnt = mntget(current->fs->rootmnt);
> @@ -979,6 +982,7 @@
> read_unlock(¤t->fs->lock);
> current->total_link_count = 0;
> retval = link_path_walk(name, nd);
> +out:
> if (unlikely(current->audit_context
> && nd && nd->dentry && nd->dentry->d_inode))
> audit_inode(name,
--
dwmw2
19 years, 8 months
[PATCH] per arch syscall info
by Chris Wright
Here's a real rough stab at per arch syscall info. I tested it very
briefly on x86_64 and ia64 with both native and ia32 binaries. It at
least spit out the arch values I expected, and I was able to filter by
arch as expected (with audit userspace patch attached).
thanks,
-chris
--
===== arch/ia64/kernel/ptrace.c 1.47 vs edited =====
--- 1.47/arch/ia64/kernel/ptrace.c 2005-03-13 15:29:47 -08:00
+++ edited/arch/ia64/kernel/ptrace.c 2005-03-16 17:25:17 -08:00
@@ -1598,12 +1598,16 @@ syscall_trace_enter (long arg0, long arg
long syscall;
if (unlikely(current->audit_context)) {
- if (IS_IA32_PROCESS(®s))
+ int arch;
+ if (IS_IA32_PROCESS(®s)) {
syscall = regs.r1;
- else
+ arch = AUDIT_ARCH_IA32;
+ } else {
syscall = regs.r15;
+ arch = AUDIT_ARCH_IA64;
+ }
- audit_syscall_entry(current, syscall, arg0, arg1, arg2, arg3);
+ audit_syscall_entry(current, arch, syscall, arg0, arg1, arg2, arg3);
}
if (test_thread_flag(TIF_SYSCALL_TRACE)
@@ -1618,8 +1622,10 @@ syscall_trace_leave (long arg0, long arg
long arg4, long arg5, long arg6, long arg7,
struct pt_regs regs)
{
- if (unlikely(current->audit_context))
- audit_syscall_exit(current, regs.r8);
+ if (unlikely(current->audit_context)) {
+ int arch = IS_IA32_PROCESS(®s) ? AUDIT_ARCH_IA32 : AUDIT_ARCH_IA64;
+ audit_syscall_exit(current, arch, regs.r8);
+ }
if (test_thread_flag(TIF_SYSCALL_TRACE)
&& (current->ptrace & PT_PTRACED))
===== arch/x86_64/kernel/ptrace.c 1.21 vs edited =====
--- 1.21/arch/x86_64/kernel/ptrace.c 2005-03-07 20:41:40 -08:00
+++ edited/arch/x86_64/kernel/ptrace.c 2005-03-15 13:55:54 -08:00
@@ -520,13 +520,16 @@ static void syscall_trace(struct pt_regs
}
}
+#define audit_arch() \
+test_thread_flag(TIF_IA32) ? AUDIT_ARCH_IA32 : AUDIT_ARCH_X86_64
+
asmlinkage void syscall_trace_enter(struct pt_regs *regs)
{
/* do the secure computing check first */
secure_computing(regs->orig_rax);
if (unlikely(current->audit_context))
- audit_syscall_entry(current, regs->orig_rax,
+ audit_syscall_entry(current, audit_arch(), regs->orig_rax,
regs->rdi, regs->rsi,
regs->rdx, regs->r10);
@@ -537,8 +540,9 @@ asmlinkage void syscall_trace_enter(stru
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
{
- if (unlikely(current->audit_context))
- audit_syscall_exit(current, regs->rax);
+ if (unlikely(current->audit_context)) {
+ audit_syscall_exit(current, audit_arch(), regs->rax);
+ }
if ((test_thread_flag(TIF_SYSCALL_TRACE)
|| test_thread_flag(TIF_SINGLESTEP))
===== include/linux/audit.h 1.3 vs edited =====
--- 1.3/include/linux/audit.h 2005-03-11 12:32:31 -08:00
+++ edited/include/linux/audit.h 2005-03-15 13:50:26 -08:00
@@ -67,6 +67,7 @@
#define AUDIT_FSGID 8
#define AUDIT_LOGINUID 9
#define AUDIT_PERS 10
+#define AUDIT_ARCH 11
/* These are ONLY useful when checking
* at syscall exit time (AUDIT_AT_EXIT). */
@@ -96,6 +97,14 @@
#define AUDIT_FAIL_PRINTK 1
#define AUDIT_FAIL_PANIC 2
+/* distinguish syscall tables */
+#define AUDIT_ARCH_IA32 0
+#define AUDIT_ARCH_X86_64 1
+#define AUDIT_ARCH_IA64 2
+#define AUDIT_ARCH_PPC 3
+#define AUDIT_ARCH_PPC64 4
+#define AUDIT_ARCH_MIPS 5
+
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
@@ -135,10 +144,10 @@ struct audit_context;
/* Public API */
extern int audit_alloc(struct task_struct *task);
extern void audit_free(struct task_struct *task);
-extern void audit_syscall_entry(struct task_struct *task,
+extern void audit_syscall_entry(struct task_struct *task, int table,
int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
-extern void audit_syscall_exit(struct task_struct *task, int return_code);
+extern void audit_syscall_exit(struct task_struct *task, int table, int return_code);
extern void audit_getname(const char *name);
extern void audit_putname(const char *name);
extern void audit_inode(const char *name, unsigned long ino, dev_t rdev);
@@ -153,8 +162,8 @@ extern uid_t audit_get_loginuid(struct a
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
-#define audit_syscall_entry(t,a,b,c,d,e) do { ; } while (0)
-#define audit_syscall_exit(t,r) do { ; } while (0)
+#define audit_syscall_entry(t,ta,a,b,c,d,e) do { ; } while (0)
+#define audit_syscall_exit(t,ta,r) do { ; } while (0)
#define audit_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i,d) do { ; } while (0)
===== kernel/auditsc.c 1.7 vs edited =====
--- 1.7/kernel/auditsc.c 2005-03-01 00:15:00 -08:00
+++ edited/kernel/auditsc.c 2005-03-15 17:41:51 -08:00
@@ -113,6 +113,7 @@ struct audit_context {
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
+ int arch;
#if AUDIT_DEBUG
int put_count;
@@ -326,6 +327,10 @@ static int audit_filter_rules(struct tas
case AUDIT_PERS:
result = (tsk->personality == value);
break;
+ case AUDIT_ARCH:
+ if (ctx)
+ result = (ctx->arch == value);
+ break;
case AUDIT_EXIT:
if (ctx && ctx->return_valid)
@@ -588,6 +593,7 @@ static void audit_log_exit(struct audit_
audit_log_format(ab, "syscall=%d", context->major);
if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality);
+ audit_log_format(ab, " arch=%d", context->arch);
if (context->return_valid)
audit_log_format(ab, " exit=%d", context->return_code);
audit_log_format(ab,
@@ -686,7 +692,7 @@ static inline unsigned int audit_serial(
* then the record will be written at syscall exit time (otherwise, it
* will only be written if another part of the kernel requests that it
* be written). */
-void audit_syscall_entry(struct task_struct *tsk, int major,
+void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4)
{
@@ -740,6 +746,7 @@ void audit_syscall_entry(struct task_str
if (!audit_enabled)
return;
+ context->arch = arch;
context->major = major;
context->argv[0] = a1;
context->argv[1] = a2;
@@ -763,7 +770,7 @@ void audit_syscall_entry(struct task_str
* filtering, or because some other part of the kernel write an audit
* message), then write out the syscall information. In call cases,
* free the names stored from getname(). */
-void audit_syscall_exit(struct task_struct *tsk, int return_code)
+void audit_syscall_exit(struct task_struct *tsk, int arch, int return_code)
{
struct audit_context *context;
@@ -782,6 +789,7 @@ void audit_syscall_exit(struct task_stru
context->in_syscall = 0;
context->auditable = 0;
+ context->arch = arch;
if (context->previous) {
struct audit_context *new_context = context->previous;
context->previous = NULL;
19 years, 8 months
[RFC][PATCH] (#6) filesystem auditing
by Timothy R. Chavez
Hello,
It's been a little while. This patch has enough changes in it to be called
patch #6. The major difference between this patch and the last patch #5,
other then the updates based on feedback, falls back on the core logic.
Basically, this implementation has a more concise definition of what is and
isn't allowed. The advantage is that it is less complex and easier to
describe and think about. I feel a little saner, personally ;-)
The only thing I'm a little concerned about in this patch has to do with
audit_watch_insert(). I think I need to lock around where I check to see if
the watchlist is empty and then possibly update the inode. Other then that,
I think I have it pretty much all covered. I feel more comfortable with the
locking in patch #6 then I did in patch #5. I'll make some updates on
audit_watch_insert() and wait for others comments and feedback. Hopefully
with one update on patch #6, we can submit the and idea to linux-fsdevel for
critiqueing.
The hooks appear in:
fs/inode.c: destroy_inode(), alloc_inode()
fs/dcache.c: d_move(), d_delete(), __d_lookup()
fs/namei.c: permission(), exec_permission_lite(), may_delete()
I think I've corrected the d_move() leakage by placing two hooks to
audit_watch() in d_move() that works on the source dentry and the target
dentry.
I'd really appreciate feedback and comments. I made a lot of changes to other
portions of the code based on feedback and suggestions I received from patch
#5.
-tim
diff -Nurp linux-2.6.11/fs/dcache.c linux-2.6.11-audit/fs/dcache.c
--- linux-2.6.11/fs/dcache.c 2005-03-02 01:37:48.000000000 -0600
+++ linux-2.6.11-audit/fs/dcache.c 2005-03-14 16:00:31.000000000 -0600
@@ -32,6 +32,7 @@
#include <linux/seqlock.h>
#include <linux/swap.h>
#include <linux/bootmem.h>
+#include <linux/audit.h>
/* #define DCACHE_DEBUG 1 */
@@ -1086,6 +1087,7 @@ struct dentry * __d_lookup(struct dentry
if (!d_unhashed(dentry)) {
atomic_inc(&dentry->d_count);
found = dentry;
+ audit_watch(found, 0);
}
spin_unlock(&dentry->d_lock);
break;
@@ -1094,6 +1096,7 @@ next:
}
rcu_read_unlock();
+
return found;
}
@@ -1166,6 +1169,7 @@ void d_delete(struct dentry * dentry)
*/
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
+ audit_watch(dentry, 1);
if (atomic_read(&dentry->d_count) == 1) {
dentry_iput(dentry);
return;
@@ -1295,6 +1299,8 @@ void d_move(struct dentry * dentry, stru
spin_lock(&target->d_lock);
}
+ audit_watch(dentry, 1);
+
/* Move the dentry to the target hash queue, if on different bucket */
if (dentry->d_flags & DCACHE_UNHASHED)
goto already_unhashed;
@@ -1308,6 +1314,8 @@ already_unhashed:
/* Unhash the target: dput() will then get rid of it */
__d_drop(target);
+ audit_watch(target, 1);
+
list_del(&dentry->d_child);
list_del(&target->d_child);
diff -Nurp linux-2.6.11/fs/inode.c linux-2.6.11-audit/fs/inode.c
--- linux-2.6.11/fs/inode.c 2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/fs/inode.c 2005-03-08 11:56:29.000000000 -0600
@@ -21,6 +21,7 @@
#include <linux/pagemap.h>
#include <linux/cdev.h>
#include <linux/bootmem.h>
+#include <linux/audit.h>
/*
* This is needed for the following functions:
@@ -136,6 +137,7 @@ static struct inode *alloc_inode(struct
inode->i_rdev = 0;
inode->i_security = NULL;
inode->dirtied_when = 0;
+ audit_inode_alloc(inode);
if (security_inode_alloc(inode)) {
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
@@ -174,6 +176,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.11/fs/namei.c linux-2.6.11-audit/fs/namei.c
--- linux-2.6.11/fs/namei.c 2005-03-02 01:37:55.000000000 -0600
+++ linux-2.6.11-audit/fs/namei.c 2005-03-14 16:39:15.000000000 -0600
@@ -214,6 +214,10 @@ int permission(struct inode *inode, int
{
int retval, submask;
+ /* Can only return either 0 or -ENOMEM */
+ if (audit_notify_watch(inode, mask) < 0)
+ return -ENOMEM;
+
if (mask & MAY_WRITE) {
umode_t mode = inode->i_mode;
@@ -344,6 +348,10 @@ static inline int exec_permission_lite(s
{
umode_t mode = inode->i_mode;
+ /* Can only return either 0 or -ENOMEM */
+ if (audit_notify_watch(inode, MAY_EXEC) < 0)
+ return -ENOMEM;
+
if (inode->i_op && inode->i_op->permission)
return -EAGAIN;
@@ -1128,6 +1136,10 @@ static inline int may_delete(struct inod
BUG_ON(victim->d_parent->d_inode != dir);
+ /* Can only return either 0 or -ENOMEM */
+ if (audit_notify_watch(victim->d_inode, 0) < 0)
+ return -ENOMEM;
+
error = permission(dir,MAY_WRITE | MAY_EXEC, NULL);
if (error)
return error;
diff -Nurp linux-2.6.11/include/linux/audit.h
linux-2.6.11-audit/include/linux/audit.h
--- linux-2.6.11/include/linux/audit.h 2005-03-02 01:38:09.000000000 -0600
+++ linux-2.6.11-audit/include/linux/audit.h 2005-03-14 15:30:51.000000000
-0600
@@ -24,15 +24,23 @@
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
+#ifdef __KERNEL__
+#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 information */
+#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
+#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
+#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
@@ -91,11 +99,15 @@
#define AUDIT_STATUS_PID 0x0004
#define AUDIT_STATUS_RATE_LIMIT 0x0008
#define AUDIT_STATUS_BACKLOG_LIMIT 0x0010
+
/* Failure-to-log actions */
#define AUDIT_FAIL_SILENT 0
#define AUDIT_FAIL_PRINTK 1
#define AUDIT_FAIL_PANIC 2
+/* 32 byte max key size */
+#define AUDIT_FILTERKEY_MAX 32
+
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
@@ -123,8 +135,32 @@ struct audit_rule { /* for AUDIT_LIST,
__u32 values[AUDIT_MAX_FIELDS];
};
+struct audit_watch {
+ int namelen;
+ int fklen;
+ char *name;
+ char *filterkey;
+ __u32 perms;
+};
+
#ifdef __KERNEL__
+struct audit_data {
+ struct audit_wentry *wentry;
+ struct list_head watchlist;
+ rwlock_t watchlist_lock;
+ struct list_head link;
+};
+
+struct audit_wentry {
+ struct list_head w_list;
+ atomic_t w_count;
+ struct audit_watch *w_watch;
+ unsigned int w_valid;
+ unsigned int w_cached;
+
+};
+
#ifdef CONFIG_AUDIT
struct audit_buffer;
struct audit_context;
@@ -150,6 +186,7 @@ extern void audit_get_stamp(struct audit
struct timespec *t, int *serial);
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_notify_watch(struct inode *inode, int mask);
#else
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
@@ -161,6 +198,26 @@ extern uid_t audit_get_loginuid(struct a
#define audit_get_loginuid(c) ({ -1; })
#endif
+#ifdef CONFIG_AUDITFILESYSTEM
+extern int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct audit_watch *req);
+extern int audit_filesystem_init(void);
+extern void audit_inode_alloc(struct inode *inode);
+extern void audit_inode_free(struct inode *inode);
+extern void audit_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_receive_watch(t,p,u,s,r) ({ -EOPNOTSUPP; })
+#define audit_filesystem_init() ({ 0; })
+#define audit_inode_alloc(i) do { ; } while(0)
+#define audit_inode_free(i) do { ; } while(0)
+#define audit_watch(d,r) do { ; } while (0)
+#define audit_wentry_put(w) do { ; } while(0)
+#define audit_wentry_get(w) ({ 0; })
+#define audit_notify_watch(i,m) ({ 0; })
+#endif
+
#ifdef CONFIG_AUDIT
/* These are defined in audit.c */
/* Public API */
diff -Nurp linux-2.6.11/include/linux/fs.h
linux-2.6.11-audit/include/linux/fs.h
--- linux-2.6.11/include/linux/fs.h 2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/include/linux/fs.h 2005-03-04 16:20:29.000000000 -0600
@@ -457,6 +457,9 @@ struct inode {
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
+#ifdef CONFIG_AUDITFILESYSTEM
+ struct audit_data *i_audit;
+#endif
/* These three should probably be a union */
struct list_head i_devices;
struct pipe_inode_info *i_pipe;
diff -Nurp linux-2.6.11/init/Kconfig linux-2.6.11-audit/init/Kconfig
--- linux-2.6.11/init/Kconfig 2005-03-02 01:38:19.000000000 -0600
+++ linux-2.6.11-audit/init/Kconfig 2005-03-04 16:20:29.000000000 -0600
@@ -174,6 +174,17 @@ config AUDITSYSCALL
can be used independently or with another kernel subsystem,
such as SELinux.
+config AUDITFILESYSTEM
+ bool "Enable filesystem auditing support"
+ depends on AUDITSYSCALL
+ default n
+ help
+ Generate audit records for regular files or directories that
+ are being watched for access by audited syscalls. To insert
+ and remove watch points into the filesystem you may use the
+ auditctl program provided with auditd. For more information,
+ 'man auditctl'
+
config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
range 12 21
diff -Nurp linux-2.6.11/kernel/Makefile linux-2.6.11-audit/kernel/Makefile
--- linux-2.6.11/kernel/Makefile 2005-03-02 01:37:50.000000000 -0600
+++ linux-2.6.11-audit/kernel/Makefile 2005-03-04 16:20:29.000000000 -0600
@@ -23,6 +23,7 @@ obj-$(CONFIG_IKCONFIG_PROC) += configs.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+obj-$(CONFIG_AUDITFILESYSTEM) += auditfs.o
obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_SYSFS) += ksysfs.o
obj-$(CONFIG_GENERIC_HARDIRQS) += irq/
diff -Nurp linux-2.6.11/kernel/audit.c linux-2.6.11-audit/kernel/audit.c
--- linux-2.6.11/kernel/audit.c 2005-03-02 01:38:33.000000000 -0600
+++ linux-2.6.11-audit/kernel/audit.c 2005-03-14 14:21:59.000000000 -0600
@@ -20,6 +20,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ * Modified by Timothy R. Chavez <chavezt(a)us.ibm.com>
*
* Goals: 1) Integrate fully with SELinux.
* 2) Minimal run-time overhead:
@@ -319,6 +320,8 @@ static int audit_netlink_ok(kernel_cap_t
case AUDIT_SET:
case AUDIT_ADD:
case AUDIT_DEL:
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM;
break;
@@ -413,6 +416,12 @@ static int audit_receive_msg(struct sk_b
err = -EOPNOTSUPP;
#endif
break;
+ case AUDIT_WATCH_INS:
+ case AUDIT_WATCH_REM:
+ err = audit_receive_watch(nlh->nlmsg_type,
+ NETLINK_CB(skb).pid,
+ uid, seq, data);
+ break;
default:
err = -EINVAL;
break;
@@ -557,6 +566,7 @@ 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.11/kernel/auditfs.c linux-2.6.11-audit/kernel/auditfs.c
--- linux-2.6.11/kernel/auditfs.c 1969-12-31 17:00:00.000000000 -0700
+++ linux-2.6.11-audit/kernel/auditfs.c 2005-03-14 14:23:46.000000000 -0600
@@ -0,0 +1,601 @@
+/* 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;
+kmem_cache_t *audit_data_cache;
+
+LIST_HEAD(audit_data_stack);
+
+/* Private Interface */
+
+void audit_wentry_put(struct audit_wentry *wentry);
+static void audit_data_free(struct audit_data *data);
+static void audit_data_stack_push(struct audit_data *data);
+static struct audit_data *audit_data_stack_pop(void);
+
+/*
+ * Ultimately, any detatchment of a watchlist entry (wentry) occurs at this
+ * point. Once the wentry has been detatched from its watchlist, it becomes
+ * invalid. This means that any remaining references to it are invalidated
+ * and will be ignored by the audit subsystem and removed when determined to
+ * be possible. If the file/directory associated with the wentry does not
+ * exist, w_cached should be set to 1 and we'll pop it off the stack cache
+ * and free it. This prevents any leakage of memory in the stack cache.
+ *
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_destroy_wentry(struct audit_wentry *wentry)
+{
+ if (wentry) {
+ list_del_init(&wentry->w_list);
+ wentry->w_valid = 0;
+ if (wentry->w_cached)
+ audit_data_free(audit_data_stack_pop());
+ audit_wentry_put(wentry);
+ }
+
+}
+/*
+ * Caller must hold i_audit->watchlist_lock
+ */
+static inline void audit_drain_watchlist(struct audit_data *data)
+{
+ struct audit_wentry *wentry, *tmp;
+
+ list_for_each_entry_safe(wentry, tmp, &data->watchlist, w_list)
+ audit_destroy_wentry(wentry);
+}
+
+static inline void audit_drain_watchlist_lock(struct audit_data *data)
+{
+ unsigned long flags;
+
+ if (data) {
+ write_lock_irqsave(&data->watchlist_lock, flags);
+ audit_drain_watchlist(data);
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+ }
+}
+
+/*
+ * The stack cache is a simple structure for keeping memory available for
+ * watched files and directories. Because we cannot correctly handled memory
+ * allocations in our dcache hook (audit_watch), we preallocate memory upon
+ * watch creation and push it onto the stack cache. Then, instead of trying
+ * to allocate memory in audit_watch, we simply pop it off the stack cache.
+ * If the file or directory then leaves the watch and the watchlist entry
+ * remains, we push the memory back onto the stack cache.
+ *
+ */
+static void audit_data_stack_push(struct audit_data *data)
+{
+ if (data) {
+ audit_wentry_put(data->wentry);
+ audit_drain_watchlist_lock(data);
+ list_add(&data->link, &audit_data_stack);
+ printk("Pushed data on cache stack\n");
+ }
+}
+
+static struct audit_data *audit_data_stack_pop(void)
+{
+ struct audit_data *data;
+
+ BUG_ON(list_empty(&audit_data_stack));
+
+ data = list_entry(audit_data_stack.next, struct audit_data, link);
+ list_del_init(audit_data_stack.next);
+ printk("Popped data off cache stack\n");
+
+ return data;
+}
+
+/*
+ * Caller must hold i_audit->watchlist_lock and is responsible for
+ * putting back the returned reference
+ */
+static inline struct audit_wentry *audit_wentry_fetch(const char *name,
+ struct audit_data *data)
+{
+ struct audit_wentry *wentry, *ret = NULL;
+
+ list_for_each_entry(wentry, &data->watchlist, w_list)
+ 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)
+{
+ unsigned long flags;
+ struct audit_wentry *ret = NULL;
+
+ if (name && data) {
+ read_lock_irqsave(&data->watchlist_lock, flags);
+ ret = audit_wentry_fetch(name, data);
+ read_unlock_irqrestore(&data->watchlist_lock, flags);
+ }
+
+ return ret;
+}
+
+static struct audit_data *audit_data_alloc(void)
+{
+ struct audit_data *data;
+
+ data = kmem_cache_alloc(audit_data_cache, GFP_KERNEL);
+ if (data) {
+ data->wentry = NULL;
+ INIT_LIST_HEAD(&data->watchlist);
+ data->watchlist_lock = RW_LOCK_UNLOCKED;
+ }
+
+ return data;
+}
+
+static void audit_data_free(struct audit_data *data)
+{
+ if (data) {
+ audit_drain_watchlist_lock(data);
+
+ if (data->wentry)
+ audit_wentry_put(data->wentry);
+
+ kmem_cache_free(audit_data_cache, data);
+ }
+}
+
+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->namelen = 0;
+ watch->fklen = 0;
+ 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->filterkey);
+ kmem_cache_free(audit_watch_cache, watch);
+ }
+}
+
+static struct audit_watch *audit_create_watch(const char *name,
+ const char *filterkey,
+ __u32 perms)
+{
+ struct audit_watch *err = NULL;
+ struct audit_watch *watch = NULL;
+
+ err = ERR_PTR(-EINVAL);
+ if (!name || strlen(name) + 1 > PATH_MAX)
+ goto audit_create_watch_fail;
+
+ if (filterkey && strlen(filterkey) + 1 > AUDIT_FILTERKEY_MAX)
+ goto audit_create_watch_fail;
+
+ if (perms > 15)
+ goto audit_create_watch_fail;
+
+ err = ERR_PTR(-ENOMEM);
+ watch = audit_watch_alloc();
+ if (watch) {
+ watch->namelen = strlen(name) + 1;
+ watch->name = kmalloc(watch->namelen, GFP_KERNEL);
+ if (!watch->name)
+ goto audit_create_watch_fail;
+ strcpy(watch->name, name);
+
+ if (filterkey) {
+ watch->fklen = strlen(filterkey) + 1;
+ watch->filterkey = kmalloc(watch->fklen, GFP_KERNEL);
+ if (!watch->filterkey)
+ goto audit_create_watch_fail;
+ strcpy(watch->filterkey, filterkey);
+ }
+
+ watch->perms = perms;
+
+ goto audit_create_watch_exit;
+ }
+
+
+audit_create_watch_fail:
+ audit_watch_free(watch);
+ watch = err;
+audit_create_watch_exit:
+ return watch;
+}
+
+/*
+ * First reference of audit_wentry is returned on allocation
+ */
+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_valid = 0;
+ 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 wentry is inaccessible until added to the watchlist */
+static int audit_create_wentry(const char *name,
+ const char *filterkey,
+ unsigned int perms, struct audit_data *data)
+{
+ int ret;
+ unsigned long flags;
+ struct audit_wentry *wentry = NULL;
+ struct audit_wentry *new_wentry = NULL;
+ struct audit_data *cached_data;
+
+ ret = -ENOMEM;
+ cached_data = audit_data_alloc();
+ if (!cached_data)
+ goto audit_create_wentry_exit;
+ audit_data_stack_push(cached_data);
+
+ new_wentry = audit_wentry_alloc();
+ if (!new_wentry)
+ goto audit_create_wentry_fail;
+
+ new_wentry->w_watch = audit_create_watch(name, filterkey, perms);
+ if (IS_ERR(new_wentry->w_watch)) {
+ ret = PTR_ERR(new_wentry->w_watch);
+ new_wentry->w_watch = NULL;
+ goto audit_create_wentry_fail;
+ }
+
+ new_wentry->w_valid = 1;
+ new_wentry->w_cached = 1;
+
+ ret = 0;
+ write_lock_irqsave(&data->watchlist_lock, flags);
+ wentry = audit_wentry_fetch(name, data);
+ if (!wentry) {
+ list_add(&new_wentry->w_list, &data->watchlist);
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+ goto audit_create_wentry_exit;
+ }
+ audit_wentry_put(wentry);
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+ ret = -EEXIST;
+
+audit_create_wentry_fail:
+ audit_data_free(audit_data_stack_pop());
+ audit_wentry_put(new_wentry);
+audit_create_wentry_exit:
+ return ret;
+}
+
+static int audit_insert_watch(struct audit_watch *req)
+{
+ int ret;
+ char *path = NULL;
+ char *filterkey = NULL;
+ struct nameidata nd;
+ struct audit_data *data;
+
+ path = getname(req->name);
+ if (IS_ERR(req->name)) {
+ ret = PTR_ERR(req->name);
+ goto audit_insert_watch_exit;
+ }
+
+ if (req->fklen) {
+ ret = -ENOMEM;
+ filterkey = kmalloc(req->fklen, GFP_KERNEL);
+ if (!filterkey)
+ goto audit_insert_watch_exit;
+
+ ret = strncpy_from_user(filterkey, req->filterkey, req->fklen);
+ if (ret < 0)
+ goto audit_insert_watch_exit;
+ }
+
+ ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+ if (ret < 0)
+ goto audit_insert_watch_release;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_insert_watch_release;
+
+ ret = -ENOMEM;
+ if (!nd.dentry->d_inode->i_audit) {
+ nd.dentry->d_inode->i_audit = audit_data_alloc();
+ if (!nd.dentry->d_inode->i_audit)
+ goto audit_insert_watch_release;
+ }
+
+ data = nd.dentry->d_inode->i_audit;
+ ret = audit_create_wentry(nd.last.name, filterkey, req->perms, data);
+ if (ret < 0) {
+ if (!data->wentry && list_empty(&data->watchlist)) {
+ audit_data_free(nd.dentry->d_inode->i_audit);
+ nd.dentry->d_inode->i_audit = NULL;
+ }
+ goto audit_insert_watch_release;
+ }
+
+audit_insert_watch_release:
+ path_release(&nd);
+audit_insert_watch_exit:
+ putname(path);
+ kfree(filterkey);
+ return ret;
+}
+
+static int audit_remove_watch(struct audit_watch *req)
+{
+ int ret;
+ unsigned long flags;
+ char *path = NULL;
+ struct nameidata nd;
+ struct audit_wentry *wentry;
+ struct audit_data *data;
+
+ path = getname(req->name);
+ if (IS_ERR(req->name)) {
+ ret = PTR_ERR(req->name);
+ goto audit_remove_watch_exit;
+ }
+
+ ret = -EINVAL;
+ if (!path || (path && strlen(path) > PATH_MAX))
+ goto audit_remove_watch_exit;
+
+ ret = path_lookup(path, LOOKUP_PARENT | LOOKUP_FOLLOW, &nd);
+ if (ret < 0)
+ goto audit_remove_watch_release;
+
+ ret = -EPERM;
+ if (nd.last_type != LAST_NORM || !nd.last.name)
+ goto audit_remove_watch_release;
+
+ data = nd.dentry->d_inode->i_audit;
+ if (!data)
+ goto audit_remove_watch_release;
+
+ wentry = audit_wentry_fetch_lock(nd.last.name, data);
+ if (!wentry)
+ goto audit_remove_watch_release;
+
+ spin_lock(&nd.dentry->d_lock);
+ write_lock_irqsave(&data->watchlist_lock, flags);
+ audit_destroy_wentry(wentry);
+ write_unlock_irqrestore(&data->watchlist_lock, flags);
+
+ if (!data->wentry && list_empty(&data->watchlist)) {
+ audit_data_free(nd.dentry->d_inode->i_audit);
+ nd.dentry->d_inode->i_audit = NULL;
+ }
+ spin_unlock(&nd.dentry->d_lock);
+
+
+ audit_wentry_put(wentry);
+
+ ret = 0;
+
+audit_remove_watch_release:
+ path_release(&nd);
+audit_remove_watch_exit:
+ putname(path);
+ return ret;
+}
+
+/* Public interface */
+
+/* There are four ways we can get a reference to a wentry:
+ * 1. When we insert a watch, a wentry is created, and a reference to it
is
+ * held by the watchlist. When we remove a watch, we put back this
+ * reference.
+ * 2. When a file/directory is at a watch point, it gets a reference to
its
+ * wentry. When it leaves that watch point, it puts back this
reference.
+ * 2. We perform a search on the watchlist for a particular watch. If
found
+ * we get a reference to the wentry and return it. After we are done
w/
+ * this reference, we put it back.
+ * 3. An audited syscall accesses the watched file or directory. As soon
+ * as the audit subsystem generates a record, it puts this reference
+ * back.
+ */
+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);
+}
+
+/*
+ * We are protected by dcache locking and audit_remove_watch() locking.
+ *
+ * Hook appears in fs/dcache.c:
+ * d_move()
+ * d_delete(),
+ * __d_lookup(),
+ */
+void audit_watch(struct dentry *dentry, int remove)
+{
+ struct audit_wentry *wentry;
+ struct audit_data *data;
+
+ if (!dentry || !dentry->d_inode)
+ return;
+
+ if (!dentry->d_parent || !dentry->d_parent->d_inode)
+ return;
+
+ data = dentry->d_inode->i_audit;
+ wentry = audit_wentry_fetch_lock(dentry->d_name.name,
+ dentry->d_parent->d_inode->i_audit);
+ if (remove && data) {
+ if (wentry && data->wentry) {
+ dentry->d_inode->i_audit->wentry->w_cached = 1;
+ audit_data_stack_push(dentry->d_inode->i_audit);
+ } else
+ audit_data_free(dentry->d_inode->i_audit);
+ dentry->d_inode->i_audit = NULL;
+ }
+
+ if (wentry) {
+ if (data) {
+ if (!data->wentry) {
+ dentry->d_inode->i_audit->wentry =
+ audit_wentry_get(wentry);
+ } else if (!data->wentry->w_valid) {
+ audit_data_free(dentry->d_inode->i_audit);
+ dentry->d_inode->i_audit = NULL;
+ }
+ } else {
+ dentry->d_inode->i_audit = audit_data_stack_pop();
+ dentry->d_inode->i_audit->wentry =
+ audit_wentry_get(wentry);
+ dentry->d_inode->i_audit->wentry->w_cached = 0;
+ }
+ }
+
+ audit_wentry_put(wentry);
+}
+
+/* If CONFIG_AUDITFILESYSTEM 'N', audit_receive_watch() resolves to
+ * -EOPNOTSUPP via a macro.
+ */
+int audit_receive_watch(int type, int pid, int uid, int seq,
+ struct audit_watch *req)
+{
+ int err;
+
+ switch (type) {
+ case AUDIT_WATCH_INS:
+ err = audit_insert_watch(req);
+ break;
+ case AUDIT_WATCH_REM:
+ err = audit_remove_watch(req);
+ break;
+ default:
+ err = -EINVAL;
+ }
+
+ audit_send_reply(pid, seq, type, 0, 1, &err, sizeof(int));
+
+ return 0;
+}
+
+void audit_inode_alloc(struct inode *inode)
+{
+ if (inode)
+ inode->i_audit = NULL;
+}
+
+void audit_inode_free(struct inode *inode)
+{
+ if (inode && inode->i_audit)
+ 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;
+
+ audit_data_cache =
+ kmem_cache_create("audit_data_cache",
+ sizeof(struct audit_data), 0, 0, NULL, NULL);
+ if (!audit_data_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);
+ kmem_cache_destroy(audit_data_cache);
+audit_filesystem_init_exit:
+ return ret;
+
+}
diff -Nurp linux-2.6.11/kernel/auditsc.c linux-2.6.11-audit/kernel/auditsc.c
--- linux-2.6.11/kernel/auditsc.c 2005-03-02 01:38:17.000000000 -0600
+++ linux-2.6.11-audit/kernel/auditsc.c 2005-03-14 15:31:19.000000000 -0600
@@ -19,6 +19,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Written by Rickard E. (Rik) Faith <faith(a)redhat.com>
+ * Modified by Timothy R. Chavez <chavezt(a)us.ibm.com>
*
* Many of the ideas implemented here are from Stephen C. Tweedie,
* especially the idea of avoiding a copy by using getname.
@@ -113,6 +114,10 @@ struct audit_context {
uid_t uid, euid, suid, fsuid;
gid_t gid, egid, sgid, fsgid;
unsigned long personality;
+ struct list_head wtrail; /* The list of watched files/dirs that were
+ * accessed and determined to be valid and
+ * unfiltered in this audit_context
+ */
#if AUDIT_DEBUG
int put_count;
@@ -134,6 +139,22 @@ struct audit_entry {
struct audit_rule rule;
};
+/* The structure that stores information about files/directories being
+ * watched in the filesystem, that the syscall accessed.
+ */
+
+struct audit_file {
+ struct audit_wentry *wentry;
+ struct list_head list;
+ unsigned long ino;
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ dev_t dev;
+ dev_t rdev;
+ int mask;
+};
+
/* Check to see if two rules are identical. It is called from
* audit_del_rule during AUDIT_DEL. */
static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
@@ -504,6 +525,37 @@ static inline void audit_free_names(stru
context->name_count = 0;
}
+static inline struct audit_file *audit_file_alloc(void)
+{
+ struct audit_file *file;
+
+ file = kmalloc(sizeof(struct audit_file), GFP_KERNEL);
+
+ if (file)
+ file->wentry = NULL;
+
+ return file;
+}
+
+static inline void audit_file_free(struct audit_file *file)
+{
+ if (file) {
+ audit_wentry_put(file->wentry);
+ file->wentry = NULL;
+ kfree(file);
+ }
+}
+
+static inline void audit_free_files(struct audit_context *context)
+{
+ struct audit_file *file, *tmp;
+
+ list_for_each_entry_safe(file, tmp, &context->wtrail, list) {
+ list_del(&file->list);
+ audit_file_free(file);
+ }
+}
+
static inline void audit_zero_context(struct audit_context *context,
enum audit_state state)
{
@@ -512,6 +564,7 @@ static inline void audit_zero_context(st
memset(context, 0, sizeof(*context));
context->state = state;
context->loginuid = loginuid;
+ INIT_LIST_HEAD(&context->wtrail);
}
static inline struct audit_context *audit_alloc_context(enum audit_state
state)
@@ -570,6 +623,7 @@ static inline void audit_free_context(st
context->name_count, count);
}
audit_free_names(context);
+ audit_free_files(context);
kfree(context);
context = previous;
} while (context);
@@ -580,6 +634,7 @@ static inline void audit_free_context(st
static void audit_log_exit(struct audit_context *context)
{
int i;
+ struct audit_file *file;
struct audit_buffer *ab;
ab = audit_log_start(context);
@@ -626,6 +681,24 @@ static void audit_log_exit(struct audit_
MINOR(context->names[i].rdev));
audit_log_end(ab);
}
+
+ list_for_each_entry(file, &context->wtrail, list) {
+ ab = audit_log_start(context);
+ if (!ab)
+ continue;
+
+ audit_log_format(ab,
+ "name=%s filter_key=%s perm=%u perm_mask=%d "
+ "inode=%lu inode_mode=%d inode_uid=%d inode_gid=%d "
+ "inode_dev=%02x:%02x inode_rdev=%02x:%02x",
+ file->wentry->w_watch->name,
+ file->wentry->w_watch->filterkey,
+ file->wentry->w_watch->perms,
+ file->mask, file->ino, file->mode, file->uid,
+ file->gid, MAJOR(file->dev), MINOR(file->dev),
+ MAJOR(file->rdev), MINOR(file->rdev));
+ audit_log_end(ab);
+ }
}
/* Free a per-task audit context. Called from copy_process and
@@ -789,6 +862,7 @@ void audit_syscall_exit(struct task_stru
tsk->audit_context = new_context;
} else {
audit_free_names(context);
+ audit_free_files(context);
audit_zero_context(context, context->state);
tsk->audit_context = context;
}
@@ -927,3 +1001,57 @@ uid_t audit_get_loginuid(struct audit_co
{
return ctx ? ctx->loginuid : -1;
}
+
+/* If file/dir has an audit_context and has filesystem auditing
+ * turned on, then if this inode is being watched, collect info
+ * about it.
+ *
+ * Hook appears in:
+ * fs/namie.c:permission(), exec_permission_lite(), vfs_unlink/rmdir
+ *
+ */
+#ifdef CONFIG_AUDITFILESYSTEM
+int audit_notify_watch(struct inode *inode, int mask)
+{
+ int ret = 0;
+ struct audit_context *context;
+ struct audit_file *file;
+
+ context = current->audit_context;
+
+ if (!context || !context->in_syscall)
+ goto audit_notify_watch_exit;
+
+ if (!inode || !inode->i_audit)
+ goto audit_notify_watch_exit;
+
+ if (!inode->i_audit->wentry || !inode->i_audit->wentry->w_valid)
+ goto audit_notify_watch_exit;
+
+ if (!mask || (inode->i_audit->wentry->w_watch->perms &&
+ !(inode->i_audit->wentry->w_watch->perms & mask))) {
+ audit_free_files(context);
+ goto audit_notify_watch_exit;
+ }
+
+ file = audit_file_alloc();
+ if (!file) {
+ ret = -ENOMEM;
+ goto audit_notify_watch_exit;
+ }
+
+ file->wentry = audit_wentry_get(inode->i_audit->wentry);
+ file->ino = inode->i_ino;
+ file->uid = inode->i_uid;
+ file->gid = inode->i_gid;
+ file->dev = inode->i_sb->s_dev;
+ file->rdev = inode->i_rdev;
+ file->mask = mask;
+
+ list_add(&file->list, &context->wtrail);
+
+ audit_notify_watch_exit:
+ return ret;
+}
+#endif
+
19 years, 9 months
Re: [patch] Syscall auditing - move "name=" field to the end
by Casey Schaufler
--- David Woodhouse <dwmw2(a)infradead.org> wrote:
> A string in quotes isn't sufficient -- a file name
> can contain quotes as
> well as carriage returns. The only safe option is
> hex -- it's just a
> question of whether it's "string or fall back to hex
> if dangerous" or
> whether it's "just hex".
Another alternative is the old "\ddd" notation.
It's not nice, but it is much better than dumping
path names in hex. I know that an entire generation
would have to learn octal, but if the Unix crowd
could deal with hex and octal I expect the Linux
community could too.
Casey Schaufler
casey(a)schaufler-ca.com
__________________________________
Do you Yahoo!?
Yahoo! Small Business - Try our new resources site!
http://smallbusiness.yahoo.com/resources/
19 years, 9 months
audit-0.6.9 released
by Steve Grubb
Hello,
I've put the next version of audit at http://people.redhat.com/sgrubb/audit.
It will also be available in rawhide tomorrow.
The changes are:
- Added filesystem watch patch from Tim Chavez
- Added version information to audit start message
- Change netlink code to use ack in order to get error notification
Let me know if you see anything strange with the netlink code. I've been
testing here for about an hour and haven't seen any problems. The netlink
changes should allow me to integrate the audit messaging into the main pam
package.
Thanks,
-Steve
19 years, 9 months
Re: [patch] Syscall auditing - move "name=" field to the end
by Chris Wright
* Ondrej Zary (linux(a)rainbow-software.org) wrote:
> This patch moves the "name=" field to the end of audit records. The
> original placement is bad because it cannot be properly parsed. It is
> impossible to tell if the name is "/bin/true" or "/bin/true inode=469634
> dev=00:00" because the "inode=" and "dev=" fields can be omitted.
>
> Before:
> audit(1111008486.824:89346): item=0 name=/bin/true inode=469634 dev=00:00
>
> After:
> audit(1111008486.824:89346): item=0 inode=469634 dev=00:00 name=/bin/true
>
> Signed-off-by: Ondrej Zary <linux(a)rainbow-software.org>
Looks reasonable. Thanks,
-chris
--
Linux Security Modules http://lsm.immunix.org http://lsm.bkbits.net
19 years, 9 months