diff -Naur linux-2.4.21/Makefile audit/Makefile
--- linux-2.4.21/Makefile	2003-09-30 11:44:35.000000000 +0200
+++ audit/Makefile	2003-09-30 11:46:38.000000000 +0200
@@ -189,6 +189,9 @@
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
 DRIVERS-y += drivers/cdrom/driver.o
 endif
+ifneq ($(CONFIG_AUDIT),)
+DRIVERS-y += drivers/audit/audit-stubs.o
+endif
 
 DRIVERS-$(CONFIG_SOUND) += drivers/sound/sounddrivers.o
 DRIVERS-$(CONFIG_PCI) += drivers/pci/driver.o
diff -Naur linux-2.4.21/arch/i386/config.in audit/arch/i386/config.in
--- linux-2.4.21/arch/i386/config.in	2003-09-30 11:44:30.000000000 +0200
+++ audit/arch/i386/config.in	2003-09-30 11:46:38.000000000 +0200
@@ -391,6 +391,8 @@
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+tristate 'System call auditing' CONFIG_AUDIT
+
 bool 'Power Management support' CONFIG_PM
 
 if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
diff -Naur linux-2.4.21/arch/i386/kernel/entry.S audit/arch/i386/kernel/entry.S
--- linux-2.4.21/arch/i386/kernel/entry.S	2003-09-30 11:44:03.000000000 +0200
+++ audit/arch/i386/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -188,7 +188,7 @@
 	call SYMBOL_NAME(schedule_tail)
 	addl $4, %esp
 	GET_CURRENT(%ebx)
-	testb $0x02,tsk_ptrace(%ebx)	# PT_TRACESYS
+	testb $0x22,tsk_ptrace(%ebx)	# PT_TRACESYS|PT_AUDITED
 	jne tracesys_exit
 	jmp	ret_from_sys_call
 
@@ -215,7 +215,7 @@
 	pushl %eax			# save orig_eax
 	SAVE_ALL
 	GET_CURRENT(%ebx)
-	testb $0x02,tsk_ptrace(%ebx)	# PT_TRACESYS
+	testb $0x22,tsk_ptrace(%ebx)	# PT_TRACESYS|PT_AUDITED
 	jne tracesys
 	cmpl $(NR_syscalls),%eax
 	jae badsys
@@ -251,14 +251,20 @@
 	ALIGN
 tracesys:
 	movl $-ENOSYS,EAX(%esp)
-	call SYMBOL_NAME(syscall_trace)
+	movl %esp,%eax
+	pushl %eax
+	call SYMBOL_NAME(syscall_trace_enter)
+	addl $4,%esp
 	movl ORIG_EAX(%esp),%eax
 	cmpl $(NR_syscalls),%eax
 	jae tracesys_exit
 	call *SYMBOL_NAME(sys_call_table)(,%eax,4)
 	movl %eax,EAX(%esp)		# save the return value
 tracesys_exit:
-	call SYMBOL_NAME(syscall_trace)
+	movl %esp,%eax
+	pushl %eax
+	call SYMBOL_NAME(syscall_trace_leave)
+	addl $4,%esp
 	jmp ret_from_sys_call
 badsys:
 	movl $-ENOSYS,EAX(%esp)
diff -Naur linux-2.4.21/arch/i386/kernel/ptrace.c audit/arch/i386/kernel/ptrace.c
--- linux-2.4.21/arch/i386/kernel/ptrace.c	2002-08-03 02:39:42.000000000 +0200
+++ audit/arch/i386/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -13,6 +13,9 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -430,11 +433,8 @@
 	return ret;
 }
 
-asmlinkage void syscall_trace(void)
+static void syscall_ptrace(void)
 {
-	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
-			(PT_PTRACED|PT_TRACESYS))
-		return;
 	/* the 0x80 provides a way for the tracing parent to distinguish
 	   between a syscall stop and SIGTRAP delivery */
 	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
@@ -452,3 +452,25 @@
 		current->exit_code = 0;
 	}
 }
+
+asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+{
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_intercept(regs);
+#endif
+}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_result(regs);
+#endif
+
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+}
diff -Naur linux-2.4.21/arch/ppc/config.in audit/arch/ppc/config.in
--- linux-2.4.21/arch/ppc/config.in	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/ppc/config.in	2003-09-30 11:46:39.000000000 +0200
@@ -192,6 +192,8 @@
 define_bool CONFIG_KERNEL_ELF y
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+tristate 'System call auditing' CONFIG_AUDIT
+
 source drivers/pci/Config.in
 
 bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
diff -Naur linux-2.4.21/arch/ppc/kernel/entry.S audit/arch/ppc/kernel/entry.S
--- linux-2.4.21/arch/ppc/kernel/entry.S	2003-06-13 16:51:31.000000000 +0200
+++ audit/arch/ppc/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -85,7 +85,7 @@
 	cmpi    0,r0,0x6666     /* Special case for 'sys_rt_sigreturn' */
 	beq-    16f
 	lwz	r10,TASK_PTRACE(r2)
-	andi.	r10,r10,PT_TRACESYS
+	andi.	r10,r10,PT_TRACESYS|PT_AUDITED
 	bne-	50f
 	cmpli	0,r0,NR_syscalls
 	bge-	66f
@@ -140,7 +140,8 @@
 	bge	ret_from_except
 	b	20b
 /* Traced system call support */
-50:	bl	syscall_trace
+50:	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	syscall_trace_enter
 	lwz	r0,GPR0(r1)	/* Restore original registers */
 	lwz	r3,GPR3(r1)
 	lwz	r4,GPR4(r1)
@@ -174,7 +175,8 @@
 	oris	r10,r10,0x1000
 	stw	r10,_CCR(r1)
 60:	stw	r3,GPR3(r1)	/* Update return value */
-	bl	syscall_trace
+	addi    r3,r1,STACK_FRAME_OVERHEAD
+	bl	syscall_trace_leave
 	b	ret_from_except
 66:	li	r3,ENOSYS
 	b	52b
@@ -262,8 +264,9 @@
 ret_from_fork:
 	bl	schedule_tail
 	lwz	r0,TASK_PTRACE(r2)
-	andi.	r0,r0,PT_TRACESYS
-	bnel-	syscall_trace
+	andi.	r0,r0,PT_TRACESYS|PT_AUDITED
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bnel-	syscall_trace_leave
 	b	ret_from_except
 
 	.globl	ret_from_intercept
diff -Naur linux-2.4.21/arch/ppc/kernel/mk_defs.c audit/arch/ppc/kernel/mk_defs.c
--- linux-2.4.21/arch/ppc/kernel/mk_defs.c	2003-09-30 11:44:24.000000000 +0200
+++ audit/arch/ppc/kernel/mk_defs.c	2003-09-30 11:46:39.000000000 +0200
@@ -45,6 +45,7 @@
 	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 	DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
 	DEFINE(PT_TRACESYS, PT_TRACESYS);
+	DEFINE(PT_AUDITED, PT_AUDITED);
 	DEFINE(TASK_FLAGS, offsetof(struct task_struct, flags));
 	DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace));
 	DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched));
diff -Naur linux-2.4.21/arch/ppc/kernel/ppc_ksyms.c audit/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.21/arch/ppc/kernel/ppc_ksyms.c	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/ppc/kernel/ppc_ksyms.c	2003-09-30 11:46:39.000000000 +0200
@@ -57,7 +57,6 @@
 #define EXPORT_SYMTAB_STROPS
 
 extern void transfer_to_handler(void);
-extern void syscall_trace(void);
 extern void do_IRQ(struct pt_regs *regs);
 extern void MachineCheckException(struct pt_regs *regs);
 extern void AlignmentException(struct pt_regs *regs);
@@ -79,7 +78,9 @@
 
 EXPORT_SYMBOL(clear_page);
 EXPORT_SYMBOL(do_signal);
+#if !defined(CONFIG_AUDIT) && !defined(CONFIG_AUDIT_MODULE)
 EXPORT_SYMBOL(syscall_trace);
+#endif
 EXPORT_SYMBOL(transfer_to_handler);
 EXPORT_SYMBOL(do_IRQ);
 EXPORT_SYMBOL(MachineCheckException);
diff -Naur linux-2.4.21/arch/ppc/kernel/ptrace.c audit/arch/ppc/kernel/ptrace.c
--- linux-2.4.21/arch/ppc/kernel/ptrace.c	2003-06-13 16:51:31.000000000 +0200
+++ audit/arch/ppc/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -24,6 +24,9 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -342,11 +345,8 @@
 	return ret;
 }
 
-void syscall_trace(void)
+static void syscall_ptrace(void)
 {
-	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
-			!= (PT_PTRACED|PT_TRACESYS))
-		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
 	notify_parent(current, SIGCHLD);
@@ -361,3 +361,25 @@
 		current->exit_code = 0;
 	}
 }
+
+void syscall_trace_enter(struct pt_regs *regs)
+{
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_intercept(regs);
+#endif
+}
+
+void syscall_trace_leave(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_result(regs);
+#endif
+
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+}
diff -Naur linux-2.4.21/arch/ppc64/config.in audit/arch/ppc64/config.in
--- linux-2.4.21/arch/ppc64/config.in	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/ppc64/config.in	2003-09-30 11:46:39.000000000 +0200
@@ -95,6 +95,8 @@
 
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+tristate 'System call auditing' CONFIG_AUDIT
+
 source drivers/pci/Config.in
 
 bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
diff -Naur linux-2.4.21/arch/ppc64/kernel/entry.S audit/arch/ppc64/kernel/entry.S
--- linux-2.4.21/arch/ppc64/kernel/entry.S	2003-09-30 11:43:34.000000000 +0200
+++ audit/arch/ppc64/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -83,7 +83,7 @@
 1:
 #endif /* SHOW_SYSCALLS */
 	ld	r11,TASK_PTRACE(r10)
-	andi.	r11,r11,PT_TRACESYS
+	andi.	r11,r11,PT_TRACESYS|PT_AUDITED
 	bne-	50f
 	cmpli	0,r0,NR_syscalls
 	bge-	66f
@@ -143,7 +143,8 @@
 	b	22b
         
 /* Traced system call support */
-50:	bl	.syscall_trace
+50:	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.syscall_trace_enter
 	ld	r0,GPR0(r1)	/* Restore original registers */
 	ld	r3,GPR3(r1)
 	ld	r4,GPR4(r1)
@@ -191,7 +192,8 @@
 	oris	r10,r10,0x1000
 	std	r10,_CCR(r1)
 60:	std	r3,GPR3(r1)	/* Update return value */
-	bl	.syscall_trace
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.syscall_trace_leave
 	b	.ret_from_except
 66:	li	r3,ENOSYS
 	b	57b
@@ -219,14 +221,15 @@
 
 80:	ld	r10,PACACURRENT(r13)
 	ld	r10,TASK_PTRACE(r10)
-	andi.	r10,r10,PT_TRACESYS
+	andi.	r10,r10,PT_TRACESYS|PT_AUDITED
 	bne-	81f
 	cmpi	0,r3,0
 	bge	.ret_from_except
 	b	20b
 81:	cmpi	0,r3,0
 	blt	58b
-	bl	.syscall_trace
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.syscall_trace_leave
 	b	.ret_from_except
 
 /*
@@ -302,9 +305,10 @@
 	bl	.schedule_tail
 	ld	r4,PACACURRENT(r13)
 	ld	r0,TASK_PTRACE(r4)
-	andi.	r0,r0,PT_TRACESYS
+	andi.	r0,r0,PT_TRACESYS|PT_AUDITED
 	beq+	.ret_from_except
-	bl	.syscall_trace
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	bl	.syscall_trace_leave
 	b	.ret_from_except
 
 _GLOBAL(ret_from_except)
diff -Naur linux-2.4.21/arch/ppc64/kernel/mk_defs.c audit/arch/ppc64/kernel/mk_defs.c
--- linux-2.4.21/arch/ppc64/kernel/mk_defs.c	2003-09-30 11:43:34.000000000 +0200
+++ audit/arch/ppc64/kernel/mk_defs.c	2003-09-30 11:46:39.000000000 +0200
@@ -128,6 +128,7 @@
 	DEFINE(LAST_SYSCALL, offsetof(struct thread_struct, last_syscall));
 	DEFINE(PT_REGS, offsetof(struct thread_struct, regs));
 	DEFINE(PT_TRACESYS, PT_TRACESYS);
+	DEFINE(PT_AUDITED, PT_AUDITED);
 	DEFINE(TASK_PTRACE, offsetof(struct task_struct, ptrace));
 	DEFINE(NEED_RESCHED, offsetof(struct task_struct, need_resched));
 	DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0]));
diff -Naur linux-2.4.21/arch/ppc64/kernel/ppc_ksyms.c audit/arch/ppc64/kernel/ppc_ksyms.c
--- linux-2.4.21/arch/ppc64/kernel/ppc_ksyms.c	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/ppc64/kernel/ppc_ksyms.c	2003-09-30 11:46:39.000000000 +0200
@@ -58,7 +58,8 @@
 /* Tell string.h we don't want memcpy etc. as cpp defines */
 #define EXPORT_SYMTAB_STROPS
 
-extern void syscall_trace(void);
+extern void syscall_trace_enter(void);
+extern void syscall_trace_leave(void);
 extern void do_IRQ(struct pt_regs *regs, int isfake);
 extern void SystemResetException(struct pt_regs *regs);
 extern void MachineCheckException(struct pt_regs *regs);
@@ -91,7 +92,10 @@
 #endif
 
 EXPORT_SYMBOL(do_signal);
-EXPORT_SYMBOL(syscall_trace);
+#if !defined(CONFIG_AUDIT) && !defined(CONFIG_AUDIT_MODULE)
+EXPORT_SYMBOL(syscall_trace_enter);
+EXPORT_SYMBOL(syscall_trace_leave);
+#endif
 EXPORT_SYMBOL(do_IRQ);
 EXPORT_SYMBOL(SystemResetException);
 EXPORT_SYMBOL(MachineCheckException);
diff -Naur linux-2.4.21/arch/ppc64/kernel/ptrace.c audit/arch/ppc64/kernel/ptrace.c
--- linux-2.4.21/arch/ppc64/kernel/ptrace.c	2003-09-30 11:43:34.000000000 +0200
+++ audit/arch/ppc64/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -25,6 +25,9 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/page.h>
@@ -368,11 +371,8 @@
 	return ret;
 }
 
-void syscall_trace(void)
+static void syscall_ptrace(void)
 {
-  if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
-			!= (PT_PTRACED|PT_TRACESYS))
-		return;
 	current->exit_code = SIGTRAP;
 	current->state = TASK_STOPPED;
 	notify_parent(current, SIGCHLD);
@@ -387,3 +387,25 @@
 		current->exit_code = 0;
 	}
 }
+
+void syscall_trace_enter(struct pt_regs *regs)
+{
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_intercept(regs);
+#endif
+}
+
+void syscall_trace_leave(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_result(regs);
+#endif
+
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+}
diff -Naur linux-2.4.21/arch/s390/config.in audit/arch/s390/config.in
--- linux-2.4.21/arch/s390/config.in	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/s390/config.in	2003-09-30 11:46:39.000000000 +0200
@@ -63,6 +63,7 @@
 bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
 bool 'Pseudo page fault support' CONFIG_PFAULT
 bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
+tristate 'System call auditing' CONFIG_AUDIT
 endmenu
 
 source drivers/s390/Config.in
diff -Naur linux-2.4.21/arch/s390/kernel/entry.S audit/arch/s390/kernel/entry.S
--- linux-2.4.21/arch/s390/kernel/entry.S	2003-09-30 11:43:40.000000000 +0200
+++ audit/arch/s390/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -175,12 +175,12 @@
 system_call:
 	SAVE_ALL_BASE
         SAVE_ALL __LC_SVC_OLD_PSW,1
-	lh	%r8,0x8a	  # get svc number from lowcore
-        sll     %r8,2
+	lh	%r7,0x8a	  # get svc number from lowcore
+        sll     %r7,2
         GET_CURRENT               # load pointer to task_struct to R9
         stosm   24(%r15),0x03     # reenable interrupts
-        l       %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr.
-        tm      __TASK_ptrace+3(%r9),0x02 # PT_TRACESYS
+        l       %r8,sys_call_table-entry_base(%r7,%r13) # get system call addr.
+        tm      __TASK_ptrace+3(%r9),0x22 # PT_TRACESYS|PT_AUDITED
         bnz     BASED(sysc_tracesys)
         basr    %r14,%r8          # call sys_xxxx
         st      %r2,SP_R2(%r15)   # store return value (change R2 on stack)
@@ -228,24 +228,26 @@
 # special linkage: %r11 contains the return address for trace_svc
 #
 trace_svc:
-        l       %r1,BASED(.Ltrace)
-	l       %r7,BASED(.Lc_ENOSYS)
-	st      %r7,SP_R2(%r15)   # give sysc_trace an -ENOSYS retval
-        basr    %r14,%r1
-	l       %r2,SP_R2(%r15)
-	cr      %r2,%r7		  # compare with saved -ENOSYS
-	be      BASED(trace_svc_go) # strace changed the syscall ?
-	sll     %r2,24
-	srl     %r2,22
-        l       %r8,sys_call_table-entry_base(%r2,%r13) # get system call addr.
+        l       %r1,BASED(.Ltrace_enter)
+	srl	%r7,2
+	st	%r7,SP_R2(%r15)
+	la	%r2,SP_PTREGS(%r15)
+	basr    %r14,%r1
+	clc	SP_R2(4,%r15),BASED(.Lnr_syscalls)
+	bnl     BASED(trace_svc_nogo)
+	l	%r7,SP_R2(%r15)   # strace might have changed the
+	sll	%r7,2             #  system call
+	l       %r8,sys_call_table-entry_base(%r7,%r13) # get system call addr.
 trace_svc_go:
 	lm      %r3,%r6,SP_R3(%r15)
 	l       %r2,SP_ORIG_R2(%r15)
-        basr    %r14,%r8          # call sys_xxx
-        st      %r2,SP_R2(%r15)   # store return value
-        l       %r1,BASED(.Ltrace)
+	basr    %r14,%r8          # call sys_xxx
+	st      %r2,SP_R2(%r15)   # store return value
+trace_svc_nogo:
+	l       %r1,BASED(.Ltrace_exit)
+	la	%r2,SP_PTREGS(%r15)
 	lr	%r14,%r11	  # return point is in %r11
-        br      %r1
+	br      %r1
 
 #
 # a new process exits the kernel with ret_from_fork
@@ -650,11 +652,11 @@
 pgm_svcper:
 	SAVE_ALL __LC_SVC_OLD_PSW,1
         GET_CURRENT               # load pointer to task_struct to R9
-	lh	%r8,0x8a	  # get svc number from lowcore
-        sll     %r8,2
+	lh	%r7,0x8a	  # get svc number from lowcore
+        sll     %r7,2
         stosm   24(%r15),0x03     # reenable interrupts
-        l       %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr.
-        tm      __TASK_ptrace+3(%r9),0x02 # PT_TRACESYS
+        l       %r8,sys_call_table-entry_base(%r7,%r13) # get system call addr.
+        tm      __TASK_ptrace+3(%r9),0x22 # PT_TRACESYS|PT_AUDITED
         bnz     BASED(pgm_tracesys)
         basr    %r14,%r8          # call sys_xxxx
         st      %r2,SP_R2(%r15)   # store return value (change R2 on stack)
@@ -865,7 +867,9 @@
 .Lsigreturn:   .long  sys_sigreturn
 .Lsigsuspend:  .long  sys_sigsuspend
 .Lsigaltstack: .long  sys_sigaltstack
-.Ltrace:       .long  syscall_trace
+.Lnr_syscalls: .long  NR_syscalls
+.Ltrace_enter: .long  syscall_trace_enter
+.Ltrace_exit:  .long  syscall_trace_exit
 .Lvfork:       .long  sys_vfork
 
 .Lschedtail:   .long  schedule_tail
diff -Naur linux-2.4.21/arch/s390/kernel/ptrace.c audit/arch/s390/kernel/ptrace.c
--- linux-2.4.21/arch/s390/kernel/ptrace.c	2002-11-29 00:53:11.000000000 +0100
+++ audit/arch/s390/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -30,6 +30,9 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/segment.h>
 #include <asm/page.h>
@@ -359,12 +362,9 @@
 	return ret;
 }
 
-asmlinkage void syscall_trace(void)
+static void syscall_ptrace(void)
 {
 	lock_kernel();
-	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
-	    != (PT_PTRACED|PT_TRACESYS))
-		goto out;
 	current->exit_code = SIGTRAP;
 	set_current_state(TASK_STOPPED);
 	notify_parent(current, SIGCHLD);
@@ -378,6 +378,28 @@
 		send_sig(current->exit_code, current, 1);
 		current->exit_code = 0;
 	}
- out:
 	unlock_kernel();
 }
+
+asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+{
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) ==
+		(PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_intercept(regs);
+#endif
+}
+
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_result(regs);
+#endif
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) ==
+		(PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+}
+
diff -Naur linux-2.4.21/arch/s390x/config.in audit/arch/s390x/config.in
--- linux-2.4.21/arch/s390x/config.in	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/s390x/config.in	2003-09-30 11:46:39.000000000 +0200
@@ -66,6 +66,7 @@
 bool 'Show crashed user process info' CONFIG_PROCESS_DEBUG
 bool 'Pseudo page fault support' CONFIG_PFAULT
 bool 'VM shared kernel support' CONFIG_SHARED_KERNEL
+tristate 'System call auditing' CONFIG_AUDIT
 endmenu
 
 
diff -Naur linux-2.4.21/arch/s390x/kernel/entry.S audit/arch/s390x/kernel/entry.S
--- linux-2.4.21/arch/s390x/kernel/entry.S	2003-09-30 11:43:40.000000000 +0200
+++ audit/arch/s390x/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -156,17 +156,17 @@
 	.globl  system_call
 system_call:
         SAVE_ALL __LC_SVC_OLD_PSW,1
-	larl    %r7,sys_call_table
-	llgh    %r8,__LC_SVC_INT_CODE # get svc number from lowcore
-        sll     %r8,3
+	larl    %r10,sys_call_table
+	llgh    %r7,__LC_SVC_INT_CODE # get svc number from lowcore
+        sll     %r7,3
         GET_CURRENT               # load pointer to task_struct to R9
 	stosm   48(%r15),0x03     # reenable interrupts
         tm      SP_PSW+3(%r15),0x01  # are we running in 31 bit mode ?
         jo      sysc_noemu
-	la      %r8,4(%r8)        # use 31 bit emulation system calls
+	la      %r10,4(%r10)      # use 31 bit emulation system calls
 sysc_noemu:
-        lgf     %r8,0(%r8,%r7)    # load address of system call routine
-        tm      __TASK_ptrace+7(%r9),0x02 # PT_TRACESYS
+        lgf     %r8,0(%r7,%r10)   # load address of system call routine
+        tm      __TASK_ptrace+7(%r9),0x22 # PT_TRACESYS|PT_AUDITED
         jnz     sysc_tracesys
         basr    %r14,%r8          # call sys_xxxx
         stg     %r2,SP_R2(%r15)   # store return value (change R2 on stack)
@@ -214,26 +214,25 @@
 # special linkage: %r11 contains the return address for trace_svc
 #
 trace_svc:
-	lghi    %r0,-ENOSYS
-	stg     %r0,SP_R2(%r15)     # give sysc_trace an -ENOSYS retval
-        brasl   %r14,syscall_trace
-	lg      %r2,SP_R2(%r15)
-	cghi    %r2,-ENOSYS
-	je      trace_svc_go
-	sllg    %r2,%r2,56          # strace wants to change the syscall
-	srlg    %r2,%r2,53          # zap unused bits & multiply by 8
-	tm      SP_PSW+3(%r15),0x01 # are we running in 31 bit mode ?
-        jo      trace_svc_noemu
-	la      %r2,4(%r2)          # use 31 bit emulation system calls
-trace_svc_noemu:
-	lgf	%r8,0(%r2,%r7)      # load address of system call routine
+	srl	%r7,3
+	stg	%r7,SP_R2(%r15)
+	la	%r2,SP_PTREGS(%r15)
+        brasl   %r14,syscall_trace_enter
+	larl	%r1,.Lnr_syscalls
+	clc	SP_R2(8,%r15),0(%r1)
+	jnl     trace_svc_nogo
+	lg	%r7,SP_R2(%r15)     # strace might have changed the
+	sll	%r7,3               #  system call
+	lgf	%r8,0(%r7,%r10)     # load address of system call routine
 trace_svc_go:	
 	lmg     %r3,%r6,SP_R3(%r15)
 	lg      %r2,SP_ORIG_R2(%r15)
         basr    %r14,%r8            # call sys_xxx
         stg     %r2,SP_R2(%r15)     # store return value
+trace_svc_nogo:
+	la	%r2,SP_PTREGS(%r15)
 	lgr	%r14,%r11	    # return point is in %r11
-        jg      syscall_trace       # return point is sysc_return
+        jg      syscall_trace_exit  # return point is sysc_return
 
 #
 # a new process exits the kernel with ret_from_fork
@@ -679,17 +678,17 @@
 #
 pgm_svcper:
 	SAVE_ALL __LC_SVC_OLD_PSW,1
-	larl    %r7,sys_call_table
-	llgh    %r8,__LC_SVC_INT_CODE # get svc number from lowcore
-        sll     %r8,3
+	larl    %r10,sys_call_table
+	llgh    %r7,__LC_SVC_INT_CODE # get svc number from lowcore
+        sll     %r7,3
         GET_CURRENT               # load pointer to task_struct to R9
 	stosm   48(%r15),0x03     # reenable interrupts
         tm      SP_PSW+3(%r15),0x01  # are we running in 31 bit mode ?
         jo      pgm_svcper_noemu
-	la      %r8,4(%r8)        # use 31 bit emulation system calls
+	la      %r10,4(%r10)      # use 31 bit emulation system calls
 pgm_svcper_noemu:
-        lgf     %r8,0(%r8,%r7)    # load address of system call routine
-        tm      __TASK_ptrace+7(%r9),0x02 # PT_TRACESYS
+        lgf     %r8,0(%r7,%r10)   # load address of system call routine
+        tm      __TASK_ptrace+7(%r9),0x22 # PT_TRACESYS|PT_AUDITED
         jnz     pgm_tracesys
         basr    %r14,%r8          # call sys_xxxx
         stg     %r2,SP_R2(%r15)   # store return value (change R2 on stack)
@@ -852,3 +851,4 @@
  */
                .align 4
 .Lc_ac:        .long  0,0,1
+.Lnr_syscalls: .long  NR_syscalls
diff -Naur linux-2.4.21/arch/s390x/kernel/ptrace.c audit/arch/s390x/kernel/ptrace.c
--- linux-2.4.21/arch/s390x/kernel/ptrace.c	2002-11-29 00:53:11.000000000 +0100
+++ audit/arch/s390x/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -592,12 +592,9 @@
 
 
 
-asmlinkage void syscall_trace(void)
+static void syscall_ptrace(void)
 {
 	lock_kernel();
-	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS))
-	    != (PT_PTRACED|PT_TRACESYS))
-		goto out;
 	current->exit_code = SIGTRAP;
 	set_current_state(TASK_STOPPED);
 	notify_parent(current, SIGCHLD);
@@ -611,6 +608,28 @@
 		send_sig(current->exit_code, current, 1);
 		current->exit_code = 0;
 	}
- out:
 	unlock_kernel();
 }
+
+asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+{
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) ==
+		(PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_intercept(regs);
+#endif
+}
+
+asmlinkage void syscall_trace_exit(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (current->ptrace & PT_AUDITED)
+		audit_result(regs);
+#endif
+	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) ==
+		(PT_PTRACED|PT_TRACESYS))
+		syscall_ptrace();
+}
+
diff -Naur linux-2.4.21/arch/x86_64/config.in audit/arch/x86_64/config.in
--- linux-2.4.21/arch/x86_64/config.in	2003-09-30 11:44:17.000000000 +0200
+++ audit/arch/x86_64/config.in	2003-09-30 11:46:39.000000000 +0200
@@ -134,6 +134,9 @@
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
 
+tristate 'System call auditing' CONFIG_AUDIT
+
+
 bool 'Power Management support' CONFIG_PM
 
 bool 'IA32 Emulation' CONFIG_IA32_EMULATION
diff -Naur linux-2.4.21/arch/x86_64/defconfig audit/arch/x86_64/defconfig
--- linux-2.4.21/arch/x86_64/defconfig	2003-09-30 11:43:44.000000000 +0200
+++ audit/arch/x86_64/defconfig	2003-09-30 11:46:39.000000000 +0200
@@ -63,6 +63,7 @@
 CONFIG_KCORE_ELF=y
 CONFIG_BINFMT_ELF=y
 # CONFIG_BINFMT_MISC is not set
+CONFIG_AUDIT=m
 CONFIG_PM=y
 CONFIG_IA32_EMULATION=y
 CONFIG_ACPI=y
diff -Naur linux-2.4.21/arch/x86_64/ia32/ia32entry.S audit/arch/x86_64/ia32/ia32entry.S
--- linux-2.4.21/arch/x86_64/ia32/ia32entry.S	2003-09-30 11:43:33.000000000 +0200
+++ audit/arch/x86_64/ia32/ia32entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -56,7 +56,7 @@
 	cld
 	SAVE_ARGS
 	GET_CURRENT(%r10)
-	testl $PT_TRACESYS,tsk_ptrace(%r10) 
+	testl $PT_TRACESYS|PT_AUDITED,tsk_ptrace(%r10) 
 	jne  ia32_tracesys
 	cmpl $(IA32_NR_syscalls),%eax
 	jae  ia32_badsys
@@ -69,7 +69,7 @@
 	SAVE_REST
 	movq $-ENOSYS,RAX(%rsp)
 	movq %rsp,%rdi        /* &pt_regs -> arg1 */
-	call syscall_trace
+	call syscall_trace_enter
 	LOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */
 	addq $ARGOFFSET,%rsp	
 	cmpl $(IA32_NR_syscalls),%eax
@@ -80,7 +80,7 @@
 	movq %rax,RAX-ARGOFFSET(%rsp)
 1:	SAVE_REST
 	movq %rsp,%rdi		/* &pt_regs -> arg1 */ 
-	call syscall_trace
+	call syscall_trace_leave
 	addq $ARGOFFSET,%rsp
 	jmp int_ret_from_sys_call
 		
diff -Naur linux-2.4.21/arch/x86_64/kernel/entry.S audit/arch/x86_64/kernel/entry.S
--- linux-2.4.21/arch/x86_64/kernel/entry.S	2003-09-30 11:43:33.000000000 +0200
+++ audit/arch/x86_64/kernel/entry.S	2003-09-30 11:46:39.000000000 +0200
@@ -73,7 +73,7 @@
 	movq %rax,%rdi		/* return value of __switch_to -> prev task */
 	call schedule_tail
 	GET_CURRENT(%rcx)
-	testb $PT_TRACESYS,tsk_ptrace(%rcx)
+	testb $PT_TRACESYS|PT_AUDITED,tsk_ptrace(%rcx)
 	jnz 2f
 1:
 	RESTORE_REST
@@ -85,7 +85,7 @@
 	jmp ret_from_sys_call
 2:
 	movq %rsp,%rdi	
-	call syscall_trace
+	call syscall_trace_leave
 	GET_CURRENT(%rcx)
 	jmp 1b
 
@@ -123,7 +123,7 @@
 	movq  %rax,ORIG_RAX-ARGOFFSET(%rsp) 
 	movq  %rcx,RIP-ARGOFFSET(%rsp)	
 	GET_CURRENT(%rcx)
-	testl $PT_TRACESYS,tsk_ptrace(%rcx)
+	testl $PT_TRACESYS|PT_AUDITED,tsk_ptrace(%rcx)
 	jne tracesys
 	cmpq $__NR_syscall_max,%rax
 	ja badsys
@@ -171,7 +171,7 @@
 	movq $-ENOSYS,RAX(%rsp)
 	FIXUP_TOP_OF_STACK %rdi
 	movq %rsp,%rdi
-	call syscall_trace
+	call syscall_trace_enter
 	LOAD_ARGS ARGOFFSET  /* reload args from stack in case ptrace changed it */
 	RESTORE_REST
 	cmpq $__NR_syscall_max,%rax
@@ -183,7 +183,7 @@
 tracesys_done:		/* backtrace marker */	
 	SAVE_REST
 	movq %rsp,%rdi
-	call syscall_trace
+	call syscall_trace_leave
 	RESTORE_TOP_OF_STACK %rbx
 	RESTORE_REST
 	jmp ret_from_sys_call
diff -Naur linux-2.4.21/arch/x86_64/kernel/ptrace.c audit/arch/x86_64/kernel/ptrace.c
--- linux-2.4.21/arch/x86_64/kernel/ptrace.c	2003-06-13 16:51:32.000000000 +0200
+++ audit/arch/x86_64/kernel/ptrace.c	2003-09-30 11:46:39.000000000 +0200
@@ -15,6 +15,9 @@
 #include <linux/errno.h>
 #include <linux/ptrace.h>
 #include <linux/user.h>
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -434,12 +437,8 @@
 	return ret;
 }
 
-asmlinkage void syscall_trace(struct pt_regs *regs)
+static void syscall_ptrace(struct pt_regs *regs)
 {
-	if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) !=
-			(PT_PTRACED|PT_TRACESYS))
-		return;
-	
 	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
 					? 0x80 : 0);
 	current->state = TASK_STOPPED;
@@ -455,3 +454,26 @@
 		current->exit_code = 0;
 	}
 }
+
+
+asmlinkage void syscall_trace_enter(struct pt_regs *regs)
+{
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+       if (current->ptrace & PT_AUDITED)
+               audit_intercept(regs);
+#endif
+
+       if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+               syscall_ptrace(regs);
+}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+       if ((current->ptrace & (PT_PTRACED|PT_TRACESYS)) == (PT_PTRACED|PT_TRACESYS))
+               syscall_ptrace(regs);
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+       if (current->ptrace & PT_AUDITED)
+               audit_result(regs);
+#endif
+}
diff -Naur linux-2.4.21/arch/x86_64/tools/offset.c audit/arch/x86_64/tools/offset.c
--- linux-2.4.21/arch/x86_64/tools/offset.c	2003-09-30 11:43:33.000000000 +0200
+++ audit/arch/x86_64/tools/offset.c	2003-09-30 11:46:39.000000000 +0200
@@ -48,6 +48,7 @@
 #undef ENTRY
 	output("#ifdef __ASSEMBLY__"); 
 	outconst("#define PT_TRACESYS %0", PT_TRACESYS);
+	outconst("#define PT_AUDITED %0", PT_AUDITED);
 	outconst("#define TASK_SIZE %0", TASK_SIZE); 
 	outconst("#define SIGCHLD %0", SIGCHLD);
 	outconst("#define CLONE_VFORK %0", CLONE_VFORK); 
diff -Naur linux-2.4.21/drivers/Makefile audit/drivers/Makefile
--- linux-2.4.21/drivers/Makefile	2003-09-30 11:43:46.000000000 +0200
+++ audit/drivers/Makefile	2003-09-30 11:46:39.000000000 +0200
@@ -8,7 +8,7 @@
 
 mod-subdirs :=	dio hil mtd sbus video macintosh iseries usb input telephony sgi ide \
 		message/i2o message/fusion scsi md ieee1394 pnp isdn atm \
-		fc4 net/hamradio i2c acpi bluetooth cpufreq evms sensors
+		fc4 audit net/hamradio i2c acpi bluetooth cpufreq evms sensors
 
 subdir-y :=	parport char block net sound misc media cdrom hotplug
 subdir-m :=	$(subdir-y)
@@ -43,6 +43,10 @@
 subdir-$(CONFIG_ISDN_BOOL)	+= isdn
 subdir-$(CONFIG_ATM)		+= atm
 subdir-$(CONFIG_FC4)		+= fc4
+subdir-$(CONFIG_AUDIT)		+= audit
+ifeq ($(CONFIG_AUDIT),m)
+subdir-y			+= audit
+endif
 
 # CONFIG_HAMRADIO can be set without CONFIG_NETDEVICE being set  -- ch
 subdir-$(CONFIG_HAMRADIO)	+= net/hamradio
diff -Naur linux-2.4.21/drivers/audit/Makefile audit/drivers/audit/Makefile
--- linux-2.4.21/drivers/audit/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/Makefile	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,23 @@
+export-objs := stubs.o
+
+AUDIT_OBJS := control.o message.o args.o fileset.o \
+		syscall.o syscall-$(ARCH).o filter.o sysctl.o \
+		ioctl.o
+O_TARGET := audit-stubs.o
+
+ifeq ($(CONFIG_AUDIT),y)
+obj-y := $(AUDIT_OBJS)
+endif
+
+ifeq ($(CONFIG_AUDIT),m)
+obj-y := stubs.o
+obj-m := audit.o
+endif
+
+list-multi	:= audit.o
+audit-objs	:= $(AUDIT_OBJS)
+
+include $(TOPDIR)/Rules.make
+
+audit.o: $(audit-objs)
+	$(LD) -r -o $@ $(audit-objs)
diff -Naur linux-2.4.21/drivers/audit/args.c audit/drivers/audit/args.c
--- linux-2.4.21/drivers/audit/args.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/args.c	2003-09-30 11:47:01.000000000 +0200
@@ -0,0 +1,1130 @@
+/*
+ * args.c
+ *
+ * Linux Audit Subsystem, argument handling
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/mman.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include <linux/audit.h>
+
+#include "audit-private.h"
+
+#undef DEBUG_MEMORY
+
+#define AUDIT_MAX_ARGV	32	/* Max # of argv entries copied for execve() */
+
+size_t			audit_scratch_vm_size = (AUDIT_MAX_SCRATCH_PAGES * PAGE_SIZE);
+
+
+static int		__audit_copy_arg(struct aud_syscall_data *, struct sysarg *,
+	       				struct sysarg_data *, u_int64_t);
+static void		__audit_release_arg(struct sysarg_data *);
+#ifdef DEBUG_MEMORY
+static void *		mem_alloc(size_t, int);
+static void		mem_free(void *);
+#else
+#define mem_alloc	kmalloc
+#define mem_free	kfree
+#endif
+
+/*
+ * Lock/unlock VM semaphore
+ */
+static inline void
+audit_lock_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm = current->mm;
+
+	if (!pinfo->locked_mm) {
+		atomic_inc(&mm->mm_users);
+		down_read(&mm->mmap_sem);
+		pinfo->locked_mm = mm;
+	}
+}
+
+static inline void
+audit_unlock_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm;
+
+	if ((mm = pinfo->locked_mm) != NULL) {
+		up_read(&mm->mmap_sem);
+		pinfo->locked_mm = NULL;
+		mmput(mm);
+	}
+}
+
+/*
+ * Create process scratch memory
+ *
+ * We map some memory in the VM of the calling process, and use it to
+ * copy system call arguments to.
+ */
+static int
+audit_alloc_scratch_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm = current->mm;
+	unsigned long	addr;
+	size_t		size;
+
+	DPRINTF("Allocating scratch info for process %d\n", current->pid);
+	BUG_ON(mm == NULL);
+	BUG_ON(pinfo->mm != NULL);
+
+	/* Make this constant for now */
+	size = audit_scratch_vm_size;
+
+	/* Create a read-only anonymous mapping in the calling process' VM */
+	down_write(&mm->mmap_sem);
+	addr = do_mmap(NULL, 0, size, PROT_READ, MAP_ANON|MAP_PRIVATE, 0);
+	up_write(&mm->mmap_sem);
+
+	DPRINTF("Mapped scratch VM for process %d; addr=%lx\n",
+		       	current->pid, addr);
+	if (IS_ERR((void *) addr))
+		return addr;
+
+	pinfo->mm      = mm;
+	pinfo->vm_area = find_vma(mm, addr);
+	pinfo->vm_addr = addr;
+	pinfo->vm_size = size;
+	memset(pinfo->vm_page, 0, sizeof(pinfo->vm_page));
+	memset(pinfo->vm_phys, 0, sizeof(pinfo->vm_phys));
+
+	atomic_inc(&mm->mm_users);
+
+	return 0;
+}
+
+/*
+ * Verify that the scratch VM is still as it was when we created it
+ */
+static int
+audit_validate_scratch_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct	*vma;
+
+	if (pinfo->vm_addr == 0)
+		return 0;
+
+	/* We may have called execve(), so the mm struct has changed.
+	 * We should drop it now. */
+	if (pinfo->mm != mm)
+		return 0;
+
+	vma = find_vma(mm, pinfo->vm_addr);
+	if (vma == NULL
+   	 || pinfo->vm_addr < vma->vm_start
+	 || pinfo->vm_addr + pinfo->vm_size > vma->vm_end
+	 || vma->vm_file
+	 || (vma->vm_flags & VM_WRITE)) {
+		printk(KERN_NOTICE "Process %u (login uid=%u) changed its scratch VM\n",
+				current->pid, pinfo->audit_uid);
+		return 0;
+	}
+
+	return 1;
+}
+
+/*
+ * Map a page of scratch memory
+ */
+static inline int
+audit_map_scratch_page(struct aud_process *pinfo, unsigned int nr)
+{
+	struct vm_area_struct *vma;
+	unsigned long	addr;
+	struct page	*page;
+	int		err;
+
+	if (nr >= AUDIT_MAX_SCRATCH_PAGES)
+		return -EINVAL;
+
+	if ((page = pinfo->vm_page[nr]) == NULL) {
+		addr = pinfo->vm_addr + nr * PAGE_SIZE;
+		err = get_user_pages(current, pinfo->mm, addr, 1, 1, 1, &page, &vma);
+		if (err < 0) {
+			printk(KERN_NOTICE "%s: get_user_pages failed, err=%d\n",
+					__FUNCTION__, err);
+			return err;
+		}
+
+		pinfo->vm_page[nr] = page;
+		pinfo->vm_phys[nr] = (unsigned long) kmap(page);
+	}
+
+	return 0;
+}
+
+/*
+ * Release scratch memory
+ */
+void
+audit_release_scratch_vm(struct aud_process *pinfo)
+{
+	struct mm_struct *mm;
+	unsigned int	i;
+
+	if ((mm = pinfo->mm) == NULL)
+		return;
+
+	/* Release lock on scratch memory if we hold it */
+	audit_unlock_vm(pinfo);
+
+	down_write(&mm->mmap_sem);
+
+	if (pinfo->vm_addr) {
+		DPRINTF("Unmapping scratch vmi info\n");
+		do_munmap(mm, pinfo->vm_addr, pinfo->vm_size);
+
+		pinfo->vm_addr = 0;
+		pinfo->vm_size = 0;
+	}
+
+	for (i = 0; i < AUDIT_MAX_SCRATCH_PAGES; i++) {
+		struct page	*page;
+
+		if (!(page = pinfo->vm_page[i]))
+			continue;
+
+		kunmap(page);
+		put_page(page);
+		pinfo->vm_page[i] = NULL;
+		pinfo->vm_phys[i] = 0;
+	}
+
+	up_write(&mm->mmap_sem);
+	pinfo->mm = NULL;
+	mmput(mm);
+}
+
+/*
+ * Check if we need to lock the argument into scratch memory
+ */
+static int
+need_to_lock(unsigned long addr, size_t len)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	struct file	*f;
+	unsigned long	end = addr + len;
+	int 		res = 0;
+
+	DPRINTF("pid %d, mm = %p, addr=%lx, len=%u\n", current->pid, mm, addr, len);
+	for (vma = find_vma(mm, addr); addr < end; vma = vma->vm_next) {
+		if (vma == NULL || addr < vma->vm_start) {
+			res = -EFAULT;
+			break;
+		}
+
+		addr = vma->vm_end;
+
+		DPRINTF("vma=%p, start=%lx, end=%lx%s%s\n",
+			 vma, vma->vm_start, vma->vm_end,
+			 (vma->vm_flags & VM_SHARED)? ", shared" : "",
+			 (vma->vm_flags & VM_WRITE)? ", write" : "");
+
+		/* We always need to lock arguments residing in a
+		 * shared VM area, because there may always be a way
+		 * for an attacker to modify that shared area.
+		 *
+		 * The fact that _we_ mapped something read-only doesn't
+		 * mean it's read-only for everyone else (think of mmapped
+		 * files).
+		 *
+		 * We do want to optimize for the case where the
+		 * underlying file is owned by root and not writable
+		 * by anyone else (happens if the argument is a
+		 * filename from a .rodata section of an ELF binary,
+		 * for instance)
+		 */
+		if ((f = vma->vm_file) && f->f_dentry && f->f_dentry->d_inode) {
+			struct inode	*inode = f->f_dentry->d_inode;
+
+			if (S_ISREG(inode->i_mode)
+			 && !inode->i_uid
+			 && !(inode->i_mode & 022))
+		       		continue;
+
+			res |= AUD_ARG_NEED_LOCK | AUD_ARG_NEED_COPY;
+		} else
+		if (atomic_read(&mm->mm_users) != 2) {
+			/* Two references are normal - one by the process,
+			 * and one by the audit subsystem */
+			res |= AUD_ARG_NEED_LOCK;
+			if (vma->vm_flags & (VM_WRITE|VM_MAYWRITE))
+				res |= AUD_ARG_NEED_COPY;
+		}
+	}
+
+	return res;
+}
+
+/*
+ * Copy argument from user space
+ */
+static int
+do_copy_from_user(struct sysarg_data *target, void *arg, size_t len)
+{
+	int	res;
+
+	res = need_to_lock((unsigned long) arg, len);
+	DPRINTF("need_to_lock returns 0x%x\n", res);
+	if (res < 0)
+       		return res;
+	target->at_flags |= res;
+	if (target->at_flags & AUD_ARG_INOUT)
+		target->at_flags &= ~AUD_ARG_NEED_COPY;
+
+	if (copy_from_user(target->at_data.ptr, arg, len))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * Copy argument to scratch memory, and overwrite system call
+ * argument. This will cause the kernel to look at the read-only
+ * copy of the argument.
+ */
+static int
+lock_argument(struct aud_syscall_data *sc, unsigned int n, struct sysarg_data *target)
+{
+	struct aud_process	*pinfo = (struct aud_process *) current->audit;
+	size_t			copied, len, pad_len;
+	int			err;
+
+	len = target->at_data.len + 1;
+	pad_len = (len + 7) & ~7;
+
+	if (pinfo->vm_used + pad_len > pinfo->vm_size) {
+		printk(KERN_NOTICE
+			"audit: scratch memory too small to hold %lu bytes\n",
+			(unsigned long) pad_len);
+		return -EFAULT;
+	}
+
+	for (copied = 0; copied < len; ) {
+		unsigned int	offset, bytes, nr;
+
+		nr = (pinfo->vm_used + copied) >> PAGE_SHIFT;
+		if ((err = audit_map_scratch_page(pinfo, nr)) < 0)
+			return err;
+
+		/* memcpy mustn't cross page boundary */
+		offset = (pinfo->vm_used + copied) & ~PAGE_MASK;
+		bytes = len - copied;
+		if (bytes > PAGE_SIZE - offset)
+			bytes = PAGE_SIZE - offset;
+
+		memcpy((void *) (pinfo->vm_phys[nr] + offset),
+				target->at_data.ptr + copied,
+				bytes);
+
+		flush_dcache_page(pinfo->vm_page[nr]);
+		copied += bytes;
+	}
+
+	audit_update_arg(sc, n, pinfo->vm_addr + pinfo->vm_used);
+	pinfo->vm_used += pad_len;
+
+	return 0;
+}
+
+/*
+ * Perform a realpath() sort of pathname resolution.
+ * The buffer pointed to by target->path.name must be
+ * allocated with __getname().
+ *
+ * XXX: need to lock path down to target
+ */
+static int
+do_realpath(struct sysarg_data *target, void *arg)
+{
+	struct nameidata nd;
+	unsigned int	name_len, pathsize;
+	int		error, flags, len;
+	char		*pathbuf, *slash, *str;
+
+	/* strnlen_user includes the NUL charatcer */
+	len = strnlen_user((char *) arg, PATH_MAX);
+	if (len > PATH_MAX)
+		len = PATH_MAX;
+	else if (len <= 1)
+		len = 1;
+
+	if ((error = do_copy_from_user(target, arg, len)) < 0)
+		return error;
+
+	pathbuf  = target->at_path.name;
+	pathsize = target->at_path.len;
+	pathbuf[--len] = '\0';
+
+	if (len == 0)
+		return 0;
+
+	DPRINTF("resolving [0x%p] \"%*.*s\"\n", arg, len, len, pathbuf);
+
+	if (target->at_flags & AUD_ARG_DIRNAME)
+		flags = LOOKUP_PARENT;
+	else
+		flags = LOOKUP_FOLLOW|LOOKUP_POSITIVE;
+
+	slash = NULL;
+	while (1) {
+		memset(&nd, 0, sizeof(nd));
+		error = -ENOBUFS;
+		if (!path_init(pathbuf, flags, &nd))
+			break;
+
+		error = path_walk(pathbuf, &nd);
+		if (error != -ENOENT)
+			break;
+
+		/* Shorten the path by one component */
+		if (!(str = strrchr(pathbuf, '/')))
+			break;
+		while (str > pathbuf && str[-1] == '/')
+			--str;
+		if (str == pathbuf)
+			break;
+
+		if (slash)
+			*slash = '/';
+		slash = str;
+		*slash = '\0';
+
+		/* No need to do a path_release; path_walk does that
+		 * for us in case of an error */
+		flags = LOOKUP_FOLLOW|LOOKUP_POSITIVE;
+	}
+
+	if (error < 0)
+		return error;
+
+	/* Keep the dentry for matching purposes */
+	target->at_path.dentry = dget(nd.dentry);
+	target->at_path.exists = (slash == NULL);
+
+	name_len = 0;
+
+	if (nd.last.len) {
+		name_len = nd.last.len;
+		slash = (char *) nd.last.name - 1;
+	}
+
+	/* If the file doesn't exist, we had to look up
+	 * a parent directory instead. Move the trailing
+	 * components out of the way so they don't get
+	 * clobbered by the d_path call below. */
+	if (slash) {
+		*slash = '/';
+		name_len = strlen(slash);
+
+		pathsize -= name_len;
+		memmove(pathbuf + pathsize, slash, name_len);
+		slash = pathbuf + pathsize;
+	}
+
+	str = d_path(nd.dentry, nd.mnt, pathbuf, pathsize);
+	if (IS_ERR(str)) {
+		DPRINTF("d_path returns error %ld\n", PTR_ERR(str));
+		return PTR_ERR(str);
+	}
+
+	len = strlen(str);
+	if (str != pathbuf)
+		memmove(pathbuf, str, len+1);
+	DPRINTF("dir=%s\n", pathbuf);
+
+	/* Attach the last path component (we've already made
+	 * sure above that the buffer space is sufficient */
+	if (name_len) {
+		DPRINTF("last=%.*s\n", name_len, slash);
+		memcpy(pathbuf + len, slash, name_len);
+		len += name_len;
+		pathbuf[len] = '\0';
+	}
+	target->at_path.len = len;
+
+	path_release(&nd);
+	return len;
+}
+
+/*
+ * Copying this argument failed... try to deal with it.
+ */
+static int
+__audit_fail_argument(struct sysarg_data *target, int error)
+{
+	/* Release any memory we may already have allocated to
+	 * this argument. */
+	__audit_release_arg(target);
+
+	memset(target, 0, sizeof(*target));
+	target->at_type = AUDIT_ARG_ERROR;
+	target->at_intval = -error;
+	return 0;
+}
+
+/*
+ * Copy path name argument from user space and perform realpath()
+ * on it
+ */
+static int
+__audit_copy_pathname(struct sysarg_data *target, long value)
+{
+	char	*pathname;
+
+	/* For pathnames, we want to perform a realpath()
+	 * call here
+	 */
+	if (!(pathname = __getname()))
+		return -1;
+	target->at_path.name = pathname;
+	target->at_type = AUDIT_ARG_PATH;
+	target->at_path.len = PATH_MAX;
+	target->at_path.dentry = NULL;
+
+	if (do_realpath(target, (void *) value) >= 0)
+		return 0;
+
+	memset(&target->at_path, 0, sizeof(target->at_path));
+	putname(pathname);
+	return -1;
+}
+
+/*
+ * Copy file descriptor argument and try to get the path name
+ * associated with it
+ */
+static int
+__audit_copy_filedesc(struct sysarg_data *target, long value)
+{
+	char		*pathname = NULL, *str;
+	struct file	*filp = NULL;
+	struct inode	*inode;
+	int		len, err = 0;
+
+	filp = fget(value);
+	if (!filp || !filp->f_dentry)
+		goto bad_filedesc;
+
+	if (!(pathname = __getname())) {
+		err = -ENOBUFS;
+		goto out;
+	}
+
+	target->at_path.name = pathname;
+	target->at_type = AUDIT_ARG_PATH;
+	target->at_path.len = PATH_MAX;
+	target->at_path.dentry = NULL;
+
+	inode = filp->f_dentry->d_inode;
+	if (inode->i_sock) {
+		struct socket   *sock = &inode->u.socket_i;
+
+		snprintf(pathname, PATH_MAX, "[sock:af=%d,type=%d]",
+				sock->ops->family, sock->type);
+		len = strlen(pathname);
+	} else {
+		if (!filp->f_vfsmnt)
+			goto bad_filedesc;
+		str = d_path(filp->f_dentry, filp->f_vfsmnt, pathname, PATH_MAX);
+		if (IS_ERR(str)) {
+			err = PTR_ERR(str);
+			goto out;
+		}
+		len = strlen(str);
+		if (str != pathname)
+			memmove(pathname, str, len+1);
+	}
+
+	DPRINTF("dir=%s\n", pathname);
+	target->at_path.dentry = dget(filp->f_dentry);
+	target->at_path.len = len;
+
+out:	if (err < 0 && pathname)
+{
+		putname(pathname);
+}
+	if (filp)
+		fput(filp);
+	return err;
+
+bad_filedesc:
+	/* Bad filedesc - this is nothing to worry about,
+	 * just flag it */
+	target->at_type = AUDIT_ARG_ERROR;
+	target->at_intval = EBADF;
+	goto out;
+}
+
+/*
+ * Copy arguments from user space
+ */
+static int
+__audit_copy_from_user(struct aud_syscall_data *sc, struct sysarg *sysarg,
+			struct sysarg_data *target, u_int64_t value)
+{
+	unsigned int	type, nitems;
+	caddr_t		pvalue;
+	size_t		len = 0;
+
+	type = sysarg->sa_type;
+
+	/* Interpret value as a pointer */
+	pvalue = (caddr_t) (long) value;
+
+	memset(target, 0, sizeof(*target));
+	target->at_flags = sysarg->sa_flags;
+	if (type == AUDIT_ARG_IMMEDIATE) {
+		/* Sign extend argument to 64bit if necessary */
+		if ((sysarg->sa_flags & AUD_ARG_SIGNED)
+		 && (audit_syscall_word_size(sc) == 32
+		  || (audit_syscall_word_size(sc) == 64 && sysarg->sa_size == 4)))
+			value = (__s32)(value & 0xFFFFFFFFUL);
+		target->at_type = AUDIT_ARG_IMMEDIATE;
+		target->at_intval = value;
+		return 0;
+	}
+
+	/* Pointer valued argument. First, check for NULL pointer */
+	if (type != AUDIT_ARG_FILEDESC && value == 0) {
+		target->at_type = AUDIT_ARG_NULL;
+		target->at_data.ptr = NULL;
+		target->at_data.len = 0;
+		return 0;
+	}
+
+	/* Path names are special; we copy the string from user
+	 * space _and_ perform a realpath() on it */
+	if (type == AUDIT_ARG_PATH) {
+		if (__audit_copy_pathname(target, value) >= 0)
+			return 0;
+
+		/* Failed; treat it as string */
+		memset(target, 0, sizeof(*target));
+		type = AUDIT_ARG_STRING;
+	} else if (type == AUDIT_ARG_FILEDESC) {
+		return __audit_copy_filedesc(target, value);
+	}
+
+	switch (type) {
+	case AUDIT_ARG_STRING:
+		/* strnlen_user includes the NUL charatcer.
+		 * We want to keep it, because we need to copy it
+		 * to our scratch VM in case we decide the
+		 * argument needs to be locked.
+		 * We'll discard it later in encode_arguments
+		 * when copying it to auditd. */
+		len = strnlen_user(pvalue, PATH_MAX);
+		if (len > PATH_MAX)
+			len = PATH_MAX;
+		break;
+
+	case AUDIT_ARG_POINTER:
+		len = sysarg->sa_size;
+		break;
+
+	case AUDIT_ARG_ARRAY:
+		/* Arrays are pointers, with another
+		 * syscall argument specifying the number
+		 * of elements */
+		nitems = sc->raw_args[sysarg->sa_ref];
+		if (nitems > sysarg->sa_max)
+			nitems = sysarg->sa_max;
+		type = AUDIT_ARG_POINTER;
+		len  = nitems * sysarg->sa_size;
+		break;
+
+	default:
+		DPRINTF("unknown arg type %u\n", type);
+		return -EINVAL;
+	}
+
+	target->at_type = type;
+	if (len != 0) {
+		int	err;
+
+		target->at_data.ptr = mem_alloc(len, GFP_KERNEL);
+		target->at_data.len = len;
+		if (!target->at_data.ptr)
+			return -ENOBUFS;
+
+		if ((err = do_copy_from_user(target, pvalue, len)) < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+/*
+ * Special case - copy argv[] type vector from user space
+ */
+static int
+__audit_copy_vector(struct aud_syscall_data *sc, struct sysarg *sysarg,
+			struct sysarg_data *target, u_int64_t value)
+{
+	struct sysarg_data *element;
+	struct sysarg	elem_def;
+	unsigned int	word_size, count;
+	caddr_t		pvalue;
+	size_t		total_size = 0;
+
+	/* This must be set at run-time because the process
+	 * could be either 32 or 64 bit */
+	word_size = audit_syscall_word_size(sc) >> 3;
+
+	/* Interpret value as a pointer */
+	pvalue = (caddr_t) (long) value;
+
+	/* Allocate memory for vector */
+	count = AUDIT_MAX_ARGV * sizeof(element[0]);
+	element = (struct sysarg_data *) kmalloc(count, GFP_KERNEL);
+	memset(element, 0, count);
+
+	/* Set up type info for the elements */
+	memset(&elem_def, 0, sizeof(elem_def));
+	elem_def.sa_type = sysarg->sa_ref;
+	elem_def.sa_size = word_size;
+
+	for (count = 0; count < AUDIT_MAX_ARGV; count++) {
+		struct sysarg_data *elem_target = &element[count];
+		u_int64_t	elem_value;
+		int		r;
+
+		/* For architectures that don't do 32/64 emulation,
+		 * one of the branches should be optimized away */
+		if (word_size == 4) {
+			u_int32_t	raw32;
+
+			r = copy_from_user(&raw32, pvalue + 4 * count, 4);
+			elem_value = raw32;
+		} else  {
+			r = copy_from_user(&elem_value, pvalue + 8 * count, 8);
+		}
+
+		if (r != 0) {
+			__audit_fail_argument(elem_target, -EFAULT);
+			break;
+		}
+		if (elem_value == 0)
+			break;
+
+		__audit_copy_arg(sc, &elem_def, elem_target, elem_value);
+		if (elem_target->at_type == AUDIT_ARG_STRING) {
+			total_size += elem_target->at_data.len;
+			if (total_size >= 2048)
+				break;
+		}
+	}
+
+	target->at_type = AUDIT_ARG_VECTOR;
+	target->at_vector.elements = element;
+	target->at_vector.count  = count;
+	return 0;
+}
+
+static int
+__audit_copy_arg(struct aud_syscall_data *sc, struct sysarg *sysarg,
+			struct sysarg_data *target, u_int64_t value)
+{
+	int	r;
+
+	/* See if we already have copied that argument */
+	if (target->at_type != 0)
+		return 0;
+
+	if (sysarg->sa_type == AUDIT_ARG_VECTOR)
+		r = __audit_copy_vector(sc, sysarg, target, value);
+	else
+		r = __audit_copy_from_user(sc, sysarg, target, value);
+	if (r < 0)
+		r = __audit_fail_argument(target, -r);
+	return r;
+}
+
+static int
+audit_copy_arg(struct aud_syscall_data *sc, unsigned int n)
+{
+	struct sysent	*entry;
+
+	if (!(entry = sc->entry) || n >= entry->sy_narg)
+		return -EINVAL;
+
+	return __audit_copy_arg(sc, &entry->sy_args[n],
+			&sc->args[n], sc->raw_args[n]);
+}
+
+struct sysarg_data *
+audit_get_argument(struct aud_syscall_data *sc,
+	       	   unsigned int n)
+{
+	int	err;
+
+	err = audit_copy_arg(sc, n);
+	if (err < 0)
+		return ERR_PTR(err);
+	return &sc->args[n];
+}
+
+int
+audit_copy_arguments(struct aud_syscall_data *sc)
+{
+	unsigned int	n;
+	int		err = 0;
+
+	if (!sc || !sc->entry)
+		return 0;
+
+	for (n = 0; n < sc->entry->sy_narg && err >= 0; n++)
+		err = audit_copy_arg(sc, n);
+	return err;
+}
+
+int
+audit_lock_arguments(struct aud_syscall_data *sc)
+{
+	struct aud_process	*pinfo;
+	struct sysarg_data	*target;
+	unsigned int		n;
+	int			err = 0, lock = 0;
+
+	if (!sc || !sc->entry)
+		return 0;
+
+	/* If we run in relaxed paranoia mode, don't bother
+	 * with locking the arguments in memory */
+	if (audit_paranoia == 0 || current->mm == NULL)
+		return 0;
+
+	pinfo = (struct aud_process *) current->audit;
+
+	audit_lock_vm(pinfo);
+
+	if (!audit_validate_scratch_vm(pinfo)) {
+		/* VM has changed. Release scratch and re-map it. */
+		audit_unlock_vm(pinfo);
+		audit_release_scratch_vm(pinfo);
+		if ((err = audit_alloc_scratch_vm(pinfo)) < 0)
+			return err;
+		audit_lock_vm(pinfo);
+	}
+
+	/* We can use all the scratch pages we have */
+	pinfo->vm_used = 0;
+
+	for (n = 0; n < sc->entry->sy_narg; n++) {
+		target = &sc->args[n];
+
+		err = audit_copy_arg(sc, n);
+		if (err < 0)
+			break;
+
+		/* Check if we need to copy this argument to
+		 * non-shared memory */
+		if (target->at_flags & AUD_ARG_NEED_COPY) {
+			err = lock_argument(sc, n, target);
+			if (err < 0)
+				break;
+		}
+		lock |= target->at_flags;
+	}
+
+	if (!(lock & AUD_ARG_NEED_LOCK) || err < 0)
+		audit_unlock_vm(pinfo);
+
+	return err;
+}
+
+static void
+__audit_release_arg(struct sysarg_data *target)
+{
+	switch (target->at_type) {
+	case AUDIT_ARG_PATH:
+		if (target->at_path.name)
+			putname(target->at_path.name);
+		if (target->at_path.dentry)
+			dput(target->at_path.dentry);
+		break;
+	case AUDIT_ARG_STRING:
+	case AUDIT_ARG_POINTER:
+		if (target->at_data.ptr)
+			mem_free(target->at_data.ptr);
+		break;
+	case AUDIT_ARG_VECTOR:
+		if (target->at_vector.elements) {
+			struct sysarg_data *element = target->at_vector.elements;
+			unsigned int	count = target->at_vector.count;
+
+			while (count--)
+				__audit_release_arg(&element[count]);
+			kfree(element);
+		}
+		break;
+	}
+
+	memset(target, 0, sizeof(*target));
+}
+
+void
+audit_release_arguments(struct aud_process *pinfo)
+{
+	struct aud_syscall_data *sc = &pinfo->syscall;
+	unsigned int	n;
+
+	/* Unlock the VM, if we locked it */
+	audit_unlock_vm(pinfo);
+
+	/* Release memory allocated to hold arguments */
+	if (sc && sc->entry) {
+		for (n = sc->entry->sy_narg; n--; )
+			__audit_release_arg(&sc->args[n]);;
+	}
+}
+
+/* Forward decl */
+static int __audit_encode_one(caddr_t, size_t,
+	       		struct sysarg_data *, struct aud_syscall_data *);
+
+/*
+ * Encode elements of a vector
+ */
+static int
+__audit_encode_vector(caddr_t dst, size_t dst_room,
+			struct sysarg_data *target,
+			struct aud_syscall_data *sc)
+{
+	unsigned int	len = 0, num;
+	int		r;
+
+	for (num = 0; num < target->at_vector.count; num++) {
+		r = __audit_encode_one(dst,
+				dst_room - 8 - len,
+				&target->at_vector.elements[num], sc);
+		if (r < 0)
+			return r;
+		if (dst)
+			dst += r;
+		len += r;
+	}
+
+	return len;
+}
+
+/*
+ * Encode a single argument
+ */
+static int
+__audit_encode_one(caddr_t dst, size_t dst_room, 
+			struct sysarg_data *target,
+			struct aud_syscall_data *sc)
+{
+	u_int32_t	type, len;
+	void		*src;
+	int		r;
+
+	type = target->at_type;
+	switch (type) {
+	case AUDIT_ARG_IMMEDIATE:
+	case AUDIT_ARG_ERROR:
+		src = &target->at_intval;
+		len = sizeof(target->at_intval);
+		break;
+
+	case AUDIT_ARG_PATH:
+		src = target->at_path.name;
+		len = target->at_path.len;
+		break;
+
+	case AUDIT_ARG_STRING:
+		src = target->at_data.ptr;
+		len = target->at_data.len;
+		/* Do not copy the NUL byte to user space */
+		if (len && ((char *) src)[len-1] == '\0')
+			len--;
+		break;
+
+	case AUDIT_ARG_POINTER:
+		src = target->at_data.ptr;
+		len = target->at_data.len;
+		break;
+
+	case AUDIT_ARG_VECTOR:
+		r = __audit_encode_vector(dst? dst + 8 : NULL, dst_room - 8, target, sc);
+		if (r < 0)
+			return r;
+		src = NULL; /* elements already copied */
+		len = r;
+		break;
+
+	default:
+		src = NULL;
+		len = 0;
+	}
+
+	if (dst != NULL) {
+		if (len + 8 > dst_room)
+			return -ENOBUFS;
+
+		memcpy(dst, &type, 4); dst += 4;
+		memcpy(dst, &len,  4); dst += 4;
+DPRINTF("    copy %p len %u\n", src, len);
+		if (src && len)
+			memcpy(dst, src, len);
+	}
+
+	return len + 8;
+}
+
+/*
+ * Encode all arguments
+ */
+int
+audit_encode_args(void *data, size_t length,
+			struct aud_syscall_data *sc)
+{
+	struct sysent	*entry = sc->entry;
+	caddr_t	 	dst = (caddr_t) data;
+	unsigned int	n, count = 0;
+	int		len, error = 0;
+
+	for (n = 0; n < entry->sy_narg; n++) {
+		struct sysarg_data *target = &sc->args[n];
+
+		if ((error = audit_copy_arg(sc, n)) < 0) {
+			return error;
+		}
+
+		/* 8 is the room we need room for the end marker */
+		len = __audit_encode_one(dst, length - 8 - count, target, sc);
+		if (len < 0)
+			return len;
+
+		if (audit_debug > 1)
+			DPRINTF("arg[%d]: type %d len %d\n",
+				       	n, target->at_type, len);
+
+		count += len;
+		if (dst)
+			dst += len;
+	}
+
+	/* Add the AUDIT_ARG_END marker */
+	if (dst)
+		memset(dst, 0, 8);
+	count += 8;
+
+	return count;
+}
+
+#ifdef DEBUG_MEMORY
+
+#define MI_MAGIC	0xfeeb1e
+
+struct mem_info {
+	int		magic;
+	list_t		entry;
+	int		syscall;
+	int		pid;
+	unsigned long	when;
+};
+
+static spinlock_t mem_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(mem_list);
+static unsigned long	mem_time;
+
+static void *
+mem_alloc(size_t len, int gfp)
+{
+	struct aud_process	*pinfo = (struct aud_process *) current->audit;
+	struct mem_info		*mi;
+
+	len += sizeof(*mi);
+	if (!(mi = (struct mem_info *) kmalloc(len, gfp)))
+		return NULL;
+
+	mi->magic = MI_MAGIC;
+	mi->syscall =  pinfo? pinfo->syscall.major : 0;
+	mi->pid = current->pid;
+	mi->when = jiffies + HZ / 10;
+
+	spin_lock(&mem_lock);
+	list_add(&mi->entry, &mem_list);
+	spin_unlock(&mem_lock);
+
+	return mi + 1;
+}
+
+void
+mem_free(void *p)
+{
+	struct mem_info	*mi = ((struct mem_info *) p) - 1;
+
+	BUG_ON(mi->magic != MI_MAGIC);
+	spin_lock(&mem_lock);
+	list_del_init(&mi->entry);
+	p = mi;
+
+	if (mem_time < jiffies) {
+		list_t		*pos;
+		unsigned long	cutoff = jiffies - HZ;
+		int		count = 0;
+
+		mem_time = jiffies + 30 * HZ;
+
+		list_for_each(pos, &mem_list) {
+			mi = list_entry(pos, struct mem_info, entry);
+
+			if (mi->when > cutoff)
+				continue;
+			if (!count++)
+				printk(KERN_NOTICE "--- Memory not freed ---\n");
+			printk(KERN_NOTICE "  %p pid %5d, syscall %5d, age %ldsec\n",
+					mi + 1, mi->pid, mi->syscall,
+					(jiffies - mi->when) / HZ);
+			if (count > 32)
+				break;
+		}
+	}
+
+	spin_unlock(&mem_lock);
+	kfree(p);
+}
+#endif
diff -Naur linux-2.4.21/drivers/audit/audit-private.h audit/drivers/audit/audit-private.h
--- linux-2.4.21/drivers/audit/audit-private.h	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/audit-private.h	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,352 @@
+/*
+ * audit-private.h
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef AUDITPRIVATE_H
+#define AUDITPRIVATE_H
+
+#include <linux/list.h>
+#include <linux/sys.h>
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+
+/* Lock debugging will go away if all the SMP bugs are gone */
+#ifndef DONT_DEBUG_LOCKS
+# undef AUDIT_DEBUG_LOCKS
+#endif
+#include "debug-locks.h"
+
+#undef DEBUG_FILTER
+
+#define DPRINTF(fmt, args...) \
+	do { if (audit_debug) \
+		printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ##args); \
+	} while (0)
+
+/*
+ * Audit policy for system calls and other events.
+ * You can either set an action unconditionally, or
+ * attach an additional filter
+ */
+struct aud_policy {
+	unsigned int		action;
+	struct aud_filter *	filter;
+};
+
+/*
+ * State attached to an open audit file handle
+ */
+struct aud_context {
+	unsigned int		reader;
+};
+
+struct aud_msg_head {
+	struct list_head	list;
+	struct aud_message	body;
+};
+
+/*
+ * System call table, and information on system
+ * call arguments.
+ */
+
+#define AUDIT_MAXARGS           8
+#define AUD_ARG_DIRNAME		0x0001
+#define AUD_ARG_SIGNED		0x0002
+#define AUD_ARG_INOUT		0x0004
+#define AUD_ARG_NEED_LOCK	0x0100
+#define AUD_ARG_NEED_COPY	0x0200
+
+struct sysarg {
+	int		sa_type;
+	size_t		sa_size;
+
+	/* For ARRAY type arguments, sa_ref is the index
+	 * of the argument containing the number of array
+	 * items (must be of type IMMEDIATE).
+	 * sa_max contains the max number of items in this array.
+	 *
+	 * For VECTOR type arguments, sa_ref is the type
+	 * of the vector elements.
+	 */
+	unsigned int	sa_ref;
+	unsigned int	sa_max;
+
+	/* Various flags */
+	unsigned int	sa_flags;
+};
+
+/* After copying arguments to kernel space, we store
+ * them in this struct.
+ * Note that pointers must be the first item in every
+ * struct within union u.
+ */
+struct sysarg_data {
+	int			at_type;
+	unsigned int		at_flags;
+	union {
+	    u_int64_t		integer;
+	    struct {
+		char *		ptr;
+		size_t		len;
+	    } data;
+	    struct {
+		char *		name;
+		size_t		len;
+		struct dentry *	dentry;
+		unsigned char	exists : 1;
+	    } path;
+	    struct {
+		struct sysarg_data *elements;
+		size_t		count;
+	    } vector;
+	} u;
+};
+#define at_intval	u.integer
+#define at_strval	u.data.ptr
+#define at_data		u.data
+#define at_path		u.path
+#define at_vector	u.vector
+
+
+struct sysent {
+	unsigned int	sy_narg;
+	struct sysarg	sy_args[AUDIT_MAXARGS];
+};
+
+/*
+ * This struct contains all information on a system
+ * call.
+ */
+struct aud_syscall_data {
+	int			personality;
+	int			arch;
+	int			major, minor;
+	int			result;
+	int			flags;
+	struct sysent *		entry;
+	struct pt_regs *	regs;
+	u_int64_t		raw_args[AUDIT_MAXARGS];
+	struct sysarg_data	args[AUDIT_MAXARGS];
+};
+
+struct aud_event_data {
+	char			name[AUD_MAX_EVNAME];
+	struct aud_syscall_data	*syscall;
+	struct sk_buff *	netconf;
+	int			exit_status;
+};
+
+/*
+ * Scratch memory information.
+ *
+ * When we need to audit a system call made by a thread
+ * sharing its VM with other processes, there is a risk that
+ * one of these other processes modifies the arguments while
+ * we're in the kernel (so that we don't record the actual set
+ * of arguments used).
+ *
+ * The same is true if one of the system call arguments resides
+ * in a VM area that is shared memory, or a mapped file.
+ *
+ * In this case, arguments are copied to an area of scratch memory
+ * within the process' address space. This is sufficient for the
+ * non shared mm case, because the process cannot mess with its
+ * memory, being inside a system call.
+ *
+ * For the shared mm case, we grab the process' VM semaphore
+ * for the duration of the system call.
+ */
+#define AUDIT_MAX_SCRATCH_PAGES	2
+struct aud_process {
+	struct list_head	list;
+
+	uid_t			audit_uid;
+	unsigned int		audit_id;
+
+	/* scratch memory */
+	struct mm_struct *	mm;
+	struct mm_struct *	locked_mm;
+	unsigned long		vm_addr;
+	size_t			vm_size,
+				vm_used;
+	struct vm_area_struct *	vm_area;
+	struct page *		vm_page[AUDIT_MAX_SCRATCH_PAGES];
+	unsigned long		vm_phys[AUDIT_MAX_SCRATCH_PAGES];
+
+	unsigned long		flags;
+	/* Auditing suspended? */
+	unsigned char		suspended;
+
+	/* Data on system call currently in progress */
+	struct aud_syscall_data	syscall;
+};
+
+#define AUD_F_SUSPENDED		0x0001
+#define AUD_F_VM_LOCKED		0x0002
+#define AUD_F_FS_LOCKED_R	0x0008
+#define AUD_F_FS_LOCKED_W	0x0010
+
+
+extern int		audit_debug;
+extern int		audit_all_processes;
+extern unsigned int	audit_max_messages;
+extern int		audit_allow_suspend;
+extern int		audit_message_enabled;
+extern int		audit_paranoia;
+extern int		audit_arch;
+
+/* VFS interface */
+int			auditf_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+ssize_t			auditf_read(struct file *, char *, size_t, loff_t *);
+ssize_t			auditf_write(struct file *, const char *, size_t, loff_t *);
+int			auditf_open(struct inode *, struct file *);
+int			auditf_release(struct inode *, struct file *);
+unsigned int		auditf_poll(struct file *, struct poll_table_struct *);
+
+/* Policy handling */
+int			audit_policy_clear(void);
+int			audit_policy_set(void *);
+int			audit_policy_check(int, struct aud_event_data *);
+int			audit_policy_ignore(int);
+
+/* Message utility functions */
+int			audit_msg_enable(void);
+void			audit_msg_disable(void);
+struct aud_msg_head *	audit_msg_new(struct aud_process *, int, const char *, size_t);
+struct aud_msg_head *	audit_msg_get(int, size_t);
+int			audit_msg_poll(struct file *,
+					struct poll_table_struct *);
+void			audit_msg_release(struct aud_msg_head *);
+void			audit_msg_insert(struct aud_msg_head *);
+int			audit_msg_fork(struct aud_process *, pid_t);
+int			audit_msg_exit(struct aud_process *, const char *,
+	       				long);
+int			audit_msg_syscall(struct aud_process *, const char *,
+					struct aud_syscall_data *);
+int			audit_msg_result(struct aud_process *, const char *,
+	       				int);
+int			audit_msg_login(struct aud_process *, const char *,
+	       				struct audit_login *);
+int			audit_msg_netlink(struct aud_process *, const char *,
+					struct sk_buff *, int);
+
+int			audit_attach(int);
+int			audit_detach(void);
+int			audit_suspend(void);
+int			audit_resume(void);
+int			audit_setauditid(void);
+int			audit_login(void *);
+int			audit_user_message(void *);
+
+void			audit_init_syscall_table(void);
+struct sysent *		audit_get_syscall_entry(int);
+int			audit_get_args(struct pt_regs *,
+				struct aud_syscall_data *);
+long			audit_get_result(struct pt_regs *);
+int			audit_update_arg(struct aud_syscall_data *,
+				unsigned int, unsigned long);
+struct sysarg_data *	audit_get_argument(struct aud_syscall_data *,
+				unsigned int);
+int			audit_copy_arguments(struct aud_syscall_data *);
+void			audit_release_arguments(struct aud_process *);
+int			audit_encode_args(void *, size_t,
+				struct aud_syscall_data *);
+
+int			audit_filter_add(struct audit_filter *);
+int			audit_filter_eval(struct aud_filter *,
+				struct aud_event_data *);
+struct aud_filter *	audit_filter_get(unsigned int);
+void			audit_filter_put(struct aud_filter *);
+int			audit_filter_clear(void);
+
+void			audit_release_scratch_vm(struct aud_process *);
+int			audit_lock_arguments(struct aud_syscall_data *);
+
+struct aud_file_object *audit_fileset_add(char *);
+void			audit_fileset_release(struct aud_file_object *);
+int			audit_fileset_match(struct aud_file_object *,
+				struct sysarg_data *);
+void			audit_fileset_unlock(int invalidate);
+
+extern int		audit_sysctl_register(void);
+extern void		audit_sysctl_unregister(void);
+
+extern int		audit_register_ioctl_converters(void);
+extern int		audit_unregister_ioctl_converters(void);
+
+/*
+ * Function hooks
+ */
+#ifdef CONFIG_AUDIT_MODULE
+struct audit_hooks {
+	int		(*intercept)(struct pt_regs *);
+	void		(*result)(struct pt_regs *);
+	void		(*fork)(struct task_struct *, struct task_struct *);
+	void		(*exit)(struct task_struct *, long);
+	void		(*netlink_msg)(struct sk_buff *, int);
+};
+
+extern int		audit_register(struct audit_hooks *);
+extern void		audit_unregister(void);
+#endif
+
+/*
+ * Kill a process.
+ * This is called when for some reason we were unable to audit a
+ * system call (e.g. because the address was bad).
+ *
+ * This is roughly the equivalent of sig_exit
+ */
+static __inline__ void
+audit_kill_process(int error)
+{
+	printk(KERN_NOTICE "audit_intercept: error %d, killing task\n", -error);
+	sigaddset(&current->pending.signal, SIGKILL);
+	recalc_sigpending(current);
+	current->flags |= PF_SIGNALED;
+	complete_and_exit(NULL, SIGKILL);
+}
+
+/*
+ * See if the process doing this call is 32bit or 64bit
+ */
+static __inline__ unsigned int
+audit_syscall_word_size(struct aud_syscall_data *sc)
+{
+#ifdef CONFIG_PPC64
+	if (sc->arch == AUDIT_ARCH_PPC64)
+		return 64;
+#elif defined(CONFIG_X86_64)
+	if (sc->arch == AUDIT_ARCH_X86_64)
+		return 64;
+#elif defined(CONFIG_S390X)
+	if (sc->arch == AUDIT_ARCH_S390X)
+		return 64;
+#elif defined(CONFIG_IA64)
+	if (sc->arch == AUDIT_ARCH_IA64)
+		return 64;
+#endif
+	return 32;
+}
+
+#endif /* AUDITPRIVATE_H */
diff -Naur linux-2.4.21/drivers/audit/control.c audit/drivers/audit/control.c
--- linux-2.4.21/drivers/audit/control.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/control.c	2003-09-30 11:47:01.000000000 +0200
@@ -0,0 +1,1111 @@
+/*
+ * control.c
+ *
+ * Linux Audit Subsystem
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/miscdevice.h>
+#include <linux/personality.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/unistd.h>
+#include <linux/audit.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/ipc.h>
+
+#include "audit-private.h"
+
+#define AUDIT_VERSION		"0.1"
+#define AUDIT_MINOR		224
+
+static int			audit_id = 0;
+static struct aud_policy	audit_policy[__AUD_MAX_POLICY];
+
+static DECLARE_RWSEM(audit_lock);
+
+/* These are accessible through sysctl */
+int				audit_debug = 0;
+int				audit_all_processes = 0;
+unsigned int			audit_max_messages = 1024;
+int				audit_allow_suspend = 1;
+int				audit_paranoia = 1;
+
+static int	__audit_attach(struct task_struct *, int);
+static void	audit_attach_all(void);
+static void	audit_detach_all(void);
+
+static struct file_operations audit_fops = {
+	read:    &auditf_read,
+	write:   &auditf_write,
+	ioctl:   &auditf_ioctl,
+	release: &auditf_release,
+	open:    &auditf_open,
+	poll:    &auditf_poll
+};
+
+static struct miscdevice audit_dev = {
+	AUDIT_MINOR,
+	"audit",
+	&audit_fops
+};
+
+
+#ifdef CONFIG_AUDIT_MODULE
+static int	__audit_intercept(struct pt_regs *);
+static void	__audit_result(struct pt_regs *);
+static void	__audit_fork(struct task_struct *, struct task_struct *);
+static void	__audit_exit(struct task_struct *, long code);
+static void	__audit_netlink_msg(struct sk_buff *, int);
+
+#define audit_intercept	__audit_intercept
+#define	audit_result	__audit_result
+#define audit_exit	__audit_exit
+#define audit_fork	__audit_fork
+#define audit_netlink_msg __audit_netlink_msg
+
+static struct audit_hooks audit_hooks = {
+	__audit_intercept,
+	__audit_result,
+	__audit_fork,
+	__audit_exit,
+	__audit_netlink_msg,
+};
+#endif
+
+
+static int __init
+init_audit(void)
+{
+	memset(audit_policy, 0, sizeof(audit_policy));
+
+	/* We cannot simply use the NR_syscalls define in linux/audit.h
+	 * because user space may use a different header with different
+	 * values. */
+	if (__AUD_POLICY_LAST_SYSCALL <= NR_syscalls) {
+		printk(KERN_ERR "not enough syscall slots reserved, "
+				"please change __AUD_POLICY_LAST_SYSCALL "
+				"and recompile.\n");
+		return -EINVAL;
+	}
+
+	audit_init_syscall_table();
+
+	if (misc_register(&audit_dev) < 0) {
+		printk(KERN_INFO "audit: unable to register device\n");
+		return -EIO;
+	}
+
+	if (audit_sysctl_register() < 0)
+		goto fail_unregister;
+
+	if (audit_register_ioctl_converters() < 0)
+		goto fail_unregister;
+
+#ifdef CONFIG_AUDIT_MODULE
+	if (audit_register(&audit_hooks) < 0)
+		goto fail_unregister;
+#endif
+
+	printk(KERN_INFO "audit subsystem ver %s initialized\n",
+		AUDIT_VERSION);
+
+	return (0);
+
+fail_unregister:
+	audit_unregister_ioctl_converters();
+	audit_sysctl_unregister();
+	misc_deregister(&audit_dev);
+	return -EIO;
+}
+
+static void __exit 
+exit_audit(void)
+{
+	/* Detach all audited processes */
+	audit_detach_all();
+
+#ifdef CONFIG_AUDIT_MODULE
+	audit_unregister();
+#endif
+	audit_unregister_ioctl_converters();
+	misc_deregister(&audit_dev);
+	audit_sysctl_unregister();
+	audit_policy_clear();
+	audit_filter_clear();
+}
+
+int
+auditf_open(struct inode *inode, struct file *file)
+{
+	struct aud_context *ctx;
+	int error = 0;
+
+	DPRINTF("opened by pid %d\n", current->pid);
+	if ((ctx = kmalloc(sizeof(*ctx), GFP_KERNEL)) == NULL) {
+		printk(KERN_ERR "audit: Failed to allocate kernel memory.\n");
+		return -ENOBUFS;
+	}
+
+	memset(ctx, 0, sizeof(*ctx));
+	file->private_data = ctx;
+
+	MOD_INC_USE_COUNT;
+	return (error);
+}
+
+int
+auditf_ioctl(struct inode *inode, struct file *file,
+			unsigned int cmd, unsigned long arg)
+{
+	struct aud_context *ctx = (struct aud_context *) file->private_data;
+	int error = 0;
+
+	DPRINTF("ctx=%p, cmd=0x%x\n", ctx, cmd);
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	down_write(&audit_lock);
+	switch (cmd) {
+	case AUIOCIAMAUDITD:
+		error = audit_msg_enable();
+		if (error >= 0) {
+			printk(KERN_NOTICE
+				"Audit daemon registered (process %d)\n",
+				current->pid);
+			/* Suspend auditing for this process */
+			if (current->audit)
+				((struct aud_process *) current->audit)->suspended++;
+			ctx->reader = 1;
+		}
+		if (audit_all_processes)
+			audit_attach_all();
+		break;
+	case AUIOCATTACH:
+		/* Attach process. If we're rhe audit daemon,
+		 * suspend auditing for us. */
+		error = audit_attach(ctx->reader);
+		break;
+	case AUIOCDETACH:
+		error = audit_detach();
+		break;
+	case AUIOCSUSPEND:
+		error = audit_suspend();
+		break;
+	case AUIOCRESUME:
+		error = audit_resume();
+		break;
+	case AUIOCCLRPOLICY:
+		error = audit_policy_clear();
+		break;
+	case AUIOCCLRFILTER:
+		error = audit_filter_clear();
+		break;
+	case AUIOCSETFILTER:
+		error = audit_filter_add((void *) arg);
+		break;
+	case AUIOCSETPOLICY:
+		error = audit_policy_set((void *) arg);
+		break;
+	case AUIOCSETAUDITID:
+		error = audit_setauditid();
+		break;
+	case AUIOCLOGIN:
+		error = audit_login((void *) arg);
+		break;
+	case AUIOCUSERMESSAGE:
+		error = audit_user_message((void *) arg);
+		break;
+
+	default:
+		error = -EINVAL;
+		break;
+	}
+
+	up_write(&audit_lock);
+	DPRINTF("done, result=%d\n", error);
+	return (error);
+}
+
+unsigned int
+auditf_poll(struct file *file, struct poll_table_struct *wait)
+{
+	if (audit_msg_poll(file, wait))
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+/*
+ * Compute statistics
+ */
+ssize_t
+auditf_read(struct file *filp, char *buf, size_t count, loff_t *off)
+{
+	struct aud_context *ctx = (struct aud_context *) filp->private_data;
+	struct aud_msg_head *msgh;
+	size_t len, max_len, copied = 0;
+	int block, nmsgs = 0;
+
+	DPRINTF("called.\n");
+	if (!ctx->reader)
+		return -EPERM;
+
+	/* Get messages from the message queue.
+	 * The first time around, extract the first message, no
+	 * matter its size.
+	 * For subsequent messages, make sure it fits into the buffer.
+	 */
+	block = !(filp->f_flags & O_NONBLOCK);
+	max_len = 0;
+
+	while (copied < count) {
+		msgh = audit_msg_get(block, max_len);
+		if (IS_ERR(msgh)) {
+			if (copied)
+				break;
+			return PTR_ERR(msgh);
+		}
+
+		if ((len = msgh->body.msg_size) > count - copied) {
+			printk(KERN_NOTICE "auditf_read: truncated audit message (%u > %u; max_len=%u)\n",
+					len, count - copied, max_len);
+			len = count - copied;
+		}
+
+		if (audit_debug > 1) {
+			DPRINTF("copying msg %d type %d size %d\n",
+				msgh->body.msg_seqnr, msgh->body.msg_type, len);
+		}
+		if (copy_to_user(buf + copied, &msgh->body, len)) {
+			printk(KERN_ERR "Dropped audit message when "
+					"copying to audit daemon\n");
+			audit_msg_release(msgh);
+			return -EFAULT;
+		}
+		audit_msg_release(msgh);
+		copied += len;
+		nmsgs++;
+
+		max_len = count - copied;
+		block = 0;
+	}
+
+	DPRINTF("copied %d messages, %u bytes total\n", nmsgs, copied);
+	return copied;
+}
+
+ssize_t
+auditf_write(struct file *filp, const char *buf, size_t count, loff_t *off)
+{
+	return (-ENOTSUPP);
+}
+
+int
+auditf_release(struct inode *inode, struct file *filp)
+{
+	struct aud_context *ctx = filp->private_data;
+
+	DPRINTF("called.\n");
+
+	if (ctx->reader) {
+		struct aud_msg_head	*msgh;
+
+		DPRINTF("Audit daemon closed audit file; auditing disabled\n");
+		audit_msg_disable();
+
+		/* Drop all messages already queued */
+		while (1) {
+			msgh = audit_msg_get(0, 0);
+			if (IS_ERR(msgh))
+				break;
+			audit_msg_release(msgh);
+		}
+
+		/* When we announced being auditd, our
+		 * suspend count was bumped */
+		audit_resume();
+	}
+
+	filp->private_data = NULL;
+	kfree(ctx);
+
+	MOD_DEC_USE_COUNT;
+	return (0);
+}
+
+/*
+ * Process intercepted system call and result
+ */
+static void
+__audit_syscall_return(struct aud_process *pinfo, int result)
+{
+	struct aud_event_data	ev;
+	struct aud_syscall_data *sc = &pinfo->syscall;
+	int			action, error;
+
+	/* System call ignored, or not supported */
+	if (sc->entry == NULL)
+		return;
+
+	/* Work-around setfs[ug]id weirdness - these syscalls
+	 * always return the previous uid/gid instead of
+	 * an error code. */
+	switch (sc->major) {
+	case __NR_setfsuid:
+#ifdef __NR_setfsuid32
+	case __NR_setfsuid32:
+#endif
+		if (current->fsuid != (uid_t) sc->raw_args[0])
+			result = -EPERM;
+		break;
+	case __NR_setfsgid:
+#ifdef __NR_setfsgid32
+	case __NR_setfsgid32:
+#endif
+		if (current->fsgid != (gid_t) sc->raw_args[0])
+			result = -EPERM;
+		break;
+	default: ;
+	}
+
+	sc->result = result;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.syscall = sc;
+	action = audit_policy_check(sc->major, &ev);
+
+	if ((action & AUDIT_LOG) && audit_message_enabled) {
+		sc->flags = action;
+		error = audit_msg_syscall(pinfo, ev.name, sc);
+		/* ENODEV means the audit daemon has gone away.
+		 * continue as if we weren't auditing */
+		if (error < 0 && error != -ENODEV) {
+			printk("audit: error %d when processing syscall %d\n",
+					error, sc->major);
+		}
+	}
+
+	/* If we copied any system call arguments to user
+	 * space, release them now.
+	 */
+	audit_release_arguments(pinfo);
+
+	/* For now, we always invalidate the fileset's cached
+	 * dentry pointers.
+	 * We could optimize this (e.g. open(2) without O_CREAT
+	 * does not change the file system)
+	 */
+	audit_fileset_unlock(1);
+
+	memset(sc, 0, sizeof(*sc));
+}
+
+
+/*
+ * This function is executed in the context of the parent
+ * process, with the child process still sleeping
+ */
+void
+audit_fork(struct task_struct *parent, struct task_struct *child)
+{
+	struct aud_process *parent_info, *pinfo;
+
+	DPRINTF("called.\n");
+
+	/* pointer and flags copied from parent */
+	child->audit = NULL;
+	child->ptrace &= ~PT_AUDITED;
+
+	if ((parent_info = parent->audit) == NULL)
+		return;
+
+	if (__audit_attach(child, 0) != 0) {
+		printk(KERN_ERR "audit: failed to enable auditing for child process!\n");
+		return;
+	}
+	pinfo = child->audit;
+
+	pinfo->audit_id = parent_info->audit_id;
+	pinfo->audit_uid = parent_info->audit_uid;
+}
+
+void
+audit_exit(struct task_struct *p, long code)
+{
+	struct aud_process *pinfo;
+	int		action;
+
+	/* Notify auditd that we're gone */
+	if ((pinfo = p->audit) != NULL) {
+		DPRINTF("process exiting, code=%ld\n", code);
+		if (!pinfo->suspended) {
+			struct aud_event_data	ev;
+
+			__audit_syscall_return(pinfo, 0);
+
+			memset(&ev, 0, sizeof(ev));
+			ev.exit_status = code;
+			action = audit_policy_check(AUD_POLICY_EXIT, &ev);
+			if (action & AUDIT_LOG)
+				audit_msg_exit(pinfo, ev.name, code);
+		}
+		audit_detach();
+	}
+}
+
+/*
+ * Intercept system call
+ */
+int
+audit_intercept(struct pt_regs *regs)
+{
+	struct aud_syscall_data	*sc;
+	struct aud_process	*pinfo;
+	int			error;
+
+	/* Oops, process not attached? */
+	if ((pinfo = current->audit) == NULL) {
+		printk(KERN_NOTICE "audit_intercept: current->audit == NULL, weird\n");
+		return 0;
+	}
+
+	/* Check if we have system call data we haven't processed
+	 * yet, in case there was no call to audit_result.
+	 * This happens e.g. for execve(). */
+	__audit_syscall_return(pinfo, 0);
+
+	if (pinfo->suspended || !audit_message_enabled)
+		return 0;
+
+	sc = &pinfo->syscall;
+	sc->regs = regs;
+	sc->personality = personality(current->personality);
+	if ((error = audit_get_args(regs, sc)) < 0)
+		goto failed;
+
+	/* Don't dig any deeper if we're not interested in this call */
+	if (!sc->entry)
+		return 0;
+
+	/* Raw, unoptimized -
+	 *
+	 * We need to protect against two-man con games here,
+	 * where one thread enters audit_intercept with say
+	 * a pathname of "/ftc/bar", which we don't audit, and
+	 * a second thread modifies that to "/etc/bar" before
+	 * we actually call the real syscall.
+	 *
+	 * This is where the "auditing by system call intercept"
+	 * concept breaks down quite badly; but that is the price
+	 * you pay for an unintrusive patch.
+	 */
+	if (audit_paranoia) {
+		switch (sc->major) {
+#ifdef __NR_ipc
+		case __NR_ipc:
+			if (sc->minor != SHMAT && sc->minor != SHMDT)
+				goto lock_args;
+			/* fallthru */
+#else
+		case __NR_shmat:
+		case __NR_shmdt:
+#endif
+		case __NR_mmap:
+		case __NR_munmap:
+		case __NR_mremap:
+		case __NR_mprotect:
+		case __NR_io_setup:
+		case __NR_madvise:
+		case __NR_mlock:
+		case __NR_mlockall:
+		case __NR_munlock:
+			/* These calls take the MM semaphore, so
+			 * we better not do the same */
+			break;
+		case __NR_execve:
+			/* Same as above, except we need to preserve
+			 * arguments for posterity. */
+			error = audit_copy_arguments(sc);
+			break;
+		default:
+		lock_args:
+			error = audit_lock_arguments(sc);
+			break;
+		}
+	} else {
+		/* For some system calls, we need to copy one or more arguments
+		 * before the call itself:
+		 * execve	Never returns, and by the time we get around to
+		 *		assembling the audit message, the process image
+		 *		is gone.
+		 * unlink	Resolve pathnames before file is gone
+		 * rename	First pathname needs to be resolved before
+		 *		the call; afterwards it's gone already.
+		 * chroot	Pathname must be interpreted relative to
+		 *		original fs->root.
+		 */
+		switch (sc->major) {
+		case __NR_execve:
+		case __NR_unlink:
+		case __NR_chroot:
+			error = audit_copy_arguments(sc);
+			break;
+		case __NR_rename:
+			error = PTR_ERR(audit_get_argument(sc, 0));
+		}
+	}
+
+	if (error >= 0)
+		return 0;
+
+failed:	/* An error occurred while copying arguments from user
+	 * space. This could either be a simple address fault
+	 * (which is alright in some cases, and should just elicit
+	 * an error), or it's an internal problem of the audit
+	 * subsystem.
+	 */
+	/* For now, we choose to kill the task. If audit is a module,
+	 * we return and let the stub handler do this (because we
+	 * need to release the stub lock first)
+	 */
+#ifndef CONFIG_AUDIT_MODULE
+	audit_kill_process(error);
+	/* NOTREACHED */
+#else
+	return error;
+#endif
+}
+
+/*
+ * Intercept system call result
+ */
+void
+audit_result(struct pt_regs *regs)
+{
+	struct aud_process *pinfo;
+
+	if (!audit_message_enabled)
+		return;
+
+	if ((pinfo = current->audit) == NULL)
+		return;
+
+	/* report return value to audit daemon */
+	__audit_syscall_return(pinfo, audit_get_result(regs));
+}
+
+/*
+ * Netlink message - probably network configuration change
+ */
+void
+audit_netlink_msg(struct sk_buff *skb, int res)
+{
+	struct nlmsghdr		*nlh;
+	struct aud_event_data	ev;
+	struct aud_process	*pinfo;
+	int			action;
+
+	DPRINTF("called\n");
+
+	if (!audit_message_enabled)
+		return;
+
+	/* Ignore netlink replies for now */
+	nlh = (struct nlmsghdr *) skb->data;
+	if (!(nlh->nlmsg_flags & NLM_F_REQUEST))
+		return;
+
+	if (!(pinfo = current->audit) || pinfo->suspended)
+		return;
+
+	memset(&ev, 0, sizeof(ev));
+	ev.netconf = skb;
+
+	action = audit_policy_check(AUD_POLICY_NETLINK, &ev);
+
+	if (action & AUDIT_LOG)
+		audit_msg_netlink(pinfo, ev.name, skb, res);
+}
+
+/*
+ * Clear the audit policy table.
+ * We hold the audit_lock when we get here.
+ */
+int
+audit_policy_clear(void)
+{
+	struct aud_policy *policy = audit_policy;
+	int		i;
+
+	for (i = 0; i < __AUD_MAX_POLICY; i++, policy++) {
+		audit_filter_put(policy->filter);
+		policy->action = AUDIT_IGNORE;
+		policy->filter = NULL;
+	}
+	return 0;
+}
+
+/*
+ * Set an audit policy
+ * We hold the audit_lock when we get here.
+ */
+int
+audit_policy_set(void *arg)
+{
+	struct aud_policy	*policy;
+	struct audit_policy	pol;
+	struct aud_filter	*f = NULL;
+
+	if (!arg)
+		return -EINVAL;
+
+	if (copy_from_user(&pol, arg, sizeof(pol)) != 0)
+		return -EFAULT;
+
+	DPRINTF("code %u, action %d, filter %d\n",
+		       	pol.code, pol.action, pol.filter);
+	if (pol.code >= __AUD_MAX_POLICY)
+		return -EINVAL;
+
+	policy = &audit_policy[pol.code];
+	if (pol.filter > 0 && !(f = audit_filter_get(pol.filter)))
+		return -EINVAL;
+
+	audit_filter_put(policy->filter);
+	policy->action = pol.action;
+	policy->filter = f;
+	return 0;
+}
+
+/*
+ * Check whether we ignore this system call.
+ * Called to find out whether we should bother with
+ * decoding arguments etc.
+ */
+int
+audit_policy_ignore(int code)
+{
+	struct aud_policy *policy;
+	int		result = 1;
+
+	if (0 <= code && code < __AUD_MAX_POLICY) {
+		down_read(&audit_lock);
+
+		policy = &audit_policy[code];
+		if (policy->filter
+		 || policy->action != AUDIT_IGNORE)
+			result = 0;
+
+		up_read(&audit_lock);
+	}
+
+	return result;
+}
+
+/*
+ * Check policy
+ */
+static int
+__audit_policy_check(int code, struct aud_event_data *ev)
+{
+	struct aud_policy *policy;
+	int		result = AUDIT_IGNORE;
+
+	if (0 <= code && code < __AUD_MAX_POLICY) {
+		policy = &audit_policy[code];
+		if (policy->filter)
+			result = audit_filter_eval(policy->filter, ev);
+		else
+			result = policy->action;
+	}
+
+	return result;
+}
+
+int
+audit_policy_check(int code, struct aud_event_data *ev)
+{
+	int	result;
+
+	down_read(&audit_lock);
+	result = __audit_policy_check(code, ev);
+	up_read(&audit_lock);
+
+	return result;
+}
+
+/*
+ * Attach/detach audit context to process
+ */
+static int
+__audit_attach(struct task_struct *task, int suspended)
+{
+	struct aud_process *pinfo;
+	int		res = 0;
+
+	if ((pinfo = kmalloc(sizeof(*pinfo), GFP_KERNEL)) == NULL)
+		return -ENOBUFS;
+
+	task_lock(task);
+	if (task->audit) {
+		DPRINTF("Cannot attach process %d; auditing already enabled\n", task->pid);
+		kfree(pinfo);
+		res = -EBUSY;
+	} else {
+		DPRINTF("Attaching process %d\n", task->pid);
+
+		memset(pinfo, 0, sizeof(*pinfo));
+		pinfo->audit_uid = (uid_t) -1;
+		pinfo->suspended = suspended;
+
+		/* turn on syscall intercept */
+		task->audit = pinfo;
+		task->ptrace |= PT_AUDITED;
+	}
+	task_unlock(task);
+	return res;
+}
+
+int
+audit_attach(int suspended)
+{
+	/* Don't allow attach if auditd is not there
+	 *
+	 * XXX: For more robustness, shouldn't we allow the attach to
+	 * succeed even if the daemon isn't running? This may happen
+	 * if it was restarted due to a crash.
+	 */
+	if (!audit_message_enabled)
+		return -ENODEV;
+
+	return __audit_attach(current, suspended);
+}
+
+static int
+__audit_detach(task_t *task)
+{
+	struct aud_process *pinfo;
+	int		res = 0;
+
+	task_lock(task);
+	if ((pinfo = task->audit) == NULL) {
+		res = -EUNATCH;
+	} else {
+		/* turn off system call intercept */
+		task->ptrace &= ~PT_AUDITED;
+		task->audit = NULL;
+		wmb();
+
+		/* Free any memory we may have allocated for
+	   	 * argument data, and release VM scratch memory */
+		audit_release_arguments(pinfo);
+		audit_release_scratch_vm(pinfo);
+		kfree(pinfo);
+	}
+	task_unlock(task);
+	return res;
+}
+
+int
+audit_detach(void)
+{
+	DPRINTF("detaching process %d\n", current->pid);
+	return __audit_detach(current);
+}
+
+/*
+ * Attach/detach all processes
+ */
+void
+audit_attach_all(void)
+{
+	task_t	*p;
+
+	read_lock(&tasklist_lock);
+	for_each_task(p) {
+		if (p->audit == NULL
+		 && p != current
+		 && p->mm != NULL
+		 && p->pid != 1)
+			__audit_attach(p, 0);
+	}
+	read_unlock(&tasklist_lock);
+}
+
+void
+audit_detach_all(void)
+{
+	task_t	*p;
+
+	read_lock(&tasklist_lock);
+	for_each_task(p) {
+		__audit_detach(p);
+	}
+	read_unlock(&tasklist_lock);
+}
+
+/*
+ * Suspend system call auditing for this process
+ */
+int
+audit_suspend(void)
+{
+	struct aud_process *pinfo;
+
+	DPRINTF("process %d suspends auditing\n", current->pid);
+	if ((pinfo = current->audit) == NULL)
+		return -EUNATCH;
+	if (!audit_allow_suspend)
+		return -EACCES;
+	pinfo->suspended++;
+	return 0;
+}
+
+/*
+ * Resume auditing
+ */
+int
+audit_resume(void)
+{
+	struct aud_process *pinfo;
+
+	DPRINTF("process %d resumes auditing\n", current->pid);
+	if ((pinfo = current->audit) == NULL)
+		return -EUNATCH;
+	pinfo->suspended--;
+	return 0;
+}
+
+/*
+ * Assign an audit ID
+ */
+int
+audit_setauditid(void)
+{
+	struct aud_process 	*pinfo;
+
+	if (!(pinfo = current->audit))
+		return -EUNATCH;
+
+	if (pinfo->audit_id > 0)
+		return -EACCES;
+
+	/* XXX protect against counter wrap-around? */
+	pinfo->audit_id = audit_id++;
+
+	DPRINTF("process %d assigned audit id %d\n",
+		       	current->pid, pinfo->audit_id);
+	return 0;
+}
+
+/*
+ * Process login message from user land
+ */
+int
+audit_login(void *arg)
+{
+	struct aud_process	*pinfo;
+	struct audit_login	*login;
+	struct aud_event_data	ev;
+	int			action, err;
+
+	if (!(pinfo = current->audit))
+		return -EUNATCH;
+
+	/* Make sure LOGIN works just once */
+	if (pinfo->audit_uid != (uid_t) -1)
+		return -EACCES;
+
+	if (!(login = kmalloc(sizeof(*login), GFP_KERNEL)))
+		return -ENOBUFS;
+
+	err = -EFAULT;
+	if (copy_from_user(login, arg, sizeof(*login)))
+		goto out;
+
+	err = -EINVAL;
+	if (login->uid == (uid_t) -1)
+		goto out;
+
+	/* Copy the login uid and keep it */
+	pinfo->audit_uid = login->uid;
+
+	/* Notify audit daemon */
+	memset(&ev, 0, sizeof(ev));
+	strcpy(ev.name, "AUDIT_login");
+
+	action = __audit_policy_check(AUD_POLICY_LOGIN, &ev);
+	if (action & AUDIT_LOG)
+		err = audit_msg_login(pinfo, ev.name, login);
+	else
+		err = 0;
+
+out:
+	kfree(login);
+	return err;
+}
+
+/*
+ * Pass an audit message generated by user space, and fill in
+ * the blanks
+ */
+int
+audit_user_message(void *arg)
+{
+	struct aud_process	*pinfo;
+	struct aud_msg_head	*msgh;
+	struct audit_message	user_msg;
+	struct aud_event_data	ev;
+	int			action;
+
+	/* Beware, may be NULL. We still want to allow
+	 * un-audited processes to log audit messages. */
+	pinfo = current->audit;
+
+	if (copy_from_user(&user_msg, arg, sizeof(user_msg)))
+		return -EFAULT;
+
+	if (user_msg.msg_type < AUDIT_MSG_USERBASE)
+		return -EACCES;
+
+	memset(&ev, 0, sizeof(ev));
+	strncpy(ev.name, user_msg.msg_evname, sizeof(ev.name)-1);
+
+	action = __audit_policy_check(AUD_POLICY_USERMSG, &ev);
+	if (!(action & AUDIT_LOG))
+		return 0;
+
+	msgh = audit_msg_new(pinfo, user_msg.msg_type,
+				user_msg.msg_evname,
+				user_msg.msg_size);
+	if (IS_ERR(msgh))
+		return PTR_ERR(msgh);
+
+	if (copy_from_user(&msgh->body.msg_data, user_msg.msg_data, user_msg.msg_size)) {
+		audit_msg_release(msgh);
+		return -EFAULT;
+	}
+
+	audit_msg_insert(msgh);
+	return 0;
+}
+
+/*
+ * Debugging stuff
+ */
+#ifdef AUDIT_DEBUG_LOCKS
+void
+_debug_locks(char *ltype, char *var, int lock)
+{
+	#define NLOCKS 6
+	static spinlock_t _debug_lock = SPIN_LOCK_UNLOCKED;
+	static int max_cpu = 0;
+	static char locks[NR_CPUS][NLOCKS];
+	static char out[NR_CPUS * NLOCKS + NR_CPUS];
+	int locknum, cpu, p;
+
+	do_spin_lock(&_debug_lock);
+
+	cpu = current->cpu;
+	if (cpu > max_cpu) max_cpu = cpu;
+
+	/* get lock state before update */
+	for (p=0; p <= max_cpu; ++p) {
+		int l;
+		for (l=0; l<NLOCKS; ++l) {
+			int c;
+
+			c = locks[p][l];
+			if (!c) c='.';
+
+			out[p * (NLOCKS+1) + l] = c;
+		}
+		out[p*(NLOCKS+1)+NLOCKS] = '|';
+	}
+	out[max_cpu * NLOCKS + max_cpu - 1] = 0;
+
+	if (!strcmp(var, "&audit_lock")) {
+		locknum = 0;
+	} else if (!strcmp(var, "&audit_message_lock")) {
+		locknum = 1;
+	} else if (!strcmp(var, "&hook_lock")) {
+		locknum = 2;
+	} else if (!strcmp(var, "&tasklist_lock")) {
+		locknum = 3;
+	} else if (!strcmp(var, "task")) {
+		locknum = 4;
+	} else {
+		locknum = 5;
+		printk(KERN_DEBUG "unknown lock %s %s %d\n", ltype, var, lock);
+	}
+
+	/* mark changed lock w/ capital letter */
+	if (lock) {
+		int c='l';
+		int cp='L';
+
+		if (locks[cpu][locknum]) printk(KERN_DEBUG "double lock?\n");
+
+		if        (!strcmp(ltype, "read")) {
+			c='r'; cp='R';
+		} else if (!strcmp(ltype, "write")) {
+			c='w'; cp='W';
+		} else if (!strcmp(ltype, "spin")) {
+			c='s'; cp='S';
+		} else if (!strcmp(ltype, "task")) {
+			c='t'; cp='T';
+		}
+		
+		locks[cpu][locknum] = c;
+		out[cpu * (NLOCKS+1) + locknum] = cp;
+	} else {
+		if (!locks[cpu][locknum]) printk(KERN_DEBUG "double unlock?\n");
+		locks[cpu][locknum] = 0;
+		out[cpu * (NLOCKS+1) + locknum] = '-';
+	}
+
+	printk(KERN_DEBUG "lock state: [%s]\n", out);
+	do_spin_unlock(&_debug_lock);
+}
+#endif
+
+
+module_init(init_audit);
+module_exit(exit_audit);
+
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
diff -Naur linux-2.4.21/drivers/audit/debug-locks.h audit/drivers/audit/debug-locks.h
--- linux-2.4.21/drivers/audit/debug-locks.h	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/debug-locks.h	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,58 @@
+/*
+ * Debug locks
+ *
+ */
+
+#ifndef DEBUG_LOCKS_H
+#define DEBUG_LOCKS_H
+
+#ifdef AUDIT_DEBUG_LOCKS
+
+extern void		_debug_locks(char *type, char *var, int lock);
+
+static inline void	do_read_lock(rwlock_t *lk) { read_lock(lk); }
+static inline void	do_read_unlock(rwlock_t *lk) { read_unlock(lk); }
+static inline void	do_write_lock(rwlock_t *lk) { write_lock(lk); }
+static inline void	do_write_unlock(rwlock_t *lk) { write_unlock(lk); }
+static inline void	do_spin_lock(spinlock_t *lk) { spin_lock(lk); }
+static inline void	do_spin_unlock(spinlock_t *lk) { spin_unlock(lk); }
+static inline void	do_down_read(struct rw_semaphore *lk) { down_read(lk); }
+static inline void	do_up_read(struct rw_semaphore *lk) { up_read(lk); }
+static inline void	do_down_write(struct rw_semaphore *lk) { down_write(lk); }
+static inline void	do_up_write(struct rw_semaphore *lk) { up_write(lk); }
+
+#define wrap_lock(f, lk) do { \
+		printk(KERN_DEBUG "[%d,%d] / " #f "(" #lk "): %s:%s:%d\n", \
+			current->cpu, current->pid, \
+			__FILE__, __FUNCTION__, __LINE__); \
+		_debug_locks(#f, #lk, 1); \
+		do_##f(lk); \
+		printk(KERN_DEBUG "[%d,%d] \\ " #f "(" #lk "): %s:%s:%d\n", \
+			current->cpu, current->pid, \
+			__FILE__, __FUNCTION__, __LINE__); \
+	} while (0)
+#define wrap_unlock(f, lk) do { \
+		printk(KERN_DEBUG "[%d,%d] - " #f "(" #lk "): %s:%s:%d\n", \
+			current->cpu, current->pid, \
+			__FILE__, __FUNCTION__, __LINE__); \
+		_debug_locks(#f, #lk, 0); \
+		do_##f(lk); \
+	} while (0)
+
+#undef read_lock
+#undef read_unlock
+#undef write_lock
+#undef write_unlock
+#undef spin_lock
+#undef spin_unlock
+
+#define read_lock(lk)		wrap_lock(read_lock, lk)
+#define read_unlock(lk)		wrap_unlock(read_unlock, lk)
+#define write_lock(lk)		wrap_lock(write_lock, lk)
+#define write_unlock(lk)	wrap_unlock(write_unlock, lk)
+#define spin_lock(lk)		wrap_lock(spin_lock, lk)
+#define spin_unlock(lk)		wrap_unlock(spin_unlock, lk)
+
+#endif /* AUDIT_DEBUG_LOCKS */
+
+#endif /* DEBUG_LOCKS_H */
diff -Naur linux-2.4.21/drivers/audit/fileset.c audit/drivers/audit/fileset.c
--- linux-2.4.21/drivers/audit/fileset.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/fileset.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,201 @@
+/*
+ * fileset.c - handle set of files we're supposed to monitor
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/miscdevice.h>
+#include <linux/audit.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+
+#include "audit-private.h"
+
+#undef DEBUG_FILESET
+#ifdef DEBUG_FILTER
+# define DEBUG_FILESET
+#endif
+
+struct aud_file_object {
+	struct list_head	link;
+	atomic_t		refcnt;
+	unsigned int		seq;
+	char *			name;
+	struct vfsmount *	vfsmnt;
+	struct dentry *		dentry;
+};
+
+static LIST_HEAD(audit_fileset);
+static DECLARE_MUTEX(fileset_lock);
+static struct task_struct *	lock_holder;
+
+static void			audit_fileset_invalidate(void);
+
+/*
+ * Lock the fileset
+ */
+int
+audit_fileset_lock(void)
+{
+	if (lock_holder == current)
+		return 0;
+	down(&fileset_lock);
+	lock_holder = current;
+	return 1;
+}
+
+void
+audit_fileset_unlock(int invalidate)
+{
+	if (lock_holder != current)
+		return;
+	if (invalidate)
+		audit_fileset_invalidate();
+	lock_holder = NULL;
+	up(&fileset_lock);
+}
+
+struct aud_file_object *
+audit_fileset_add(char *path)
+{
+	struct aud_file_object	*obj;
+
+	obj = (struct aud_file_object *) kmalloc(sizeof(*obj), GFP_KERNEL);
+	if (obj == NULL)
+		return NULL;
+
+	memset(obj, 0, sizeof(*obj));
+	INIT_LIST_HEAD(&obj->link);
+	atomic_set(&obj->refcnt, 1);
+	obj->name = path;
+
+	list_add(&obj->link, &audit_fileset);
+
+	return obj;
+}
+
+void
+audit_fileset_release(struct aud_file_object *obj)
+{
+	if (atomic_dec_and_test(&obj->refcnt) != 0)
+		return;
+	list_del(&obj->link);
+	kfree(obj->name);
+	kfree(obj);
+}
+
+int
+audit_fileset_match(struct aud_file_object *obj, struct sysarg_data *tgt)
+{
+	struct dentry	*dp = tgt->at_path.dentry;
+	const char	*path = tgt->at_path.name;
+	int		need_to_unlock, match = 0;
+
+#ifdef DEBUG_FILESET
+	DPRINTF("obj=%s, path=%s, dp=%p\n", obj->name, path, dp);
+#endif
+	need_to_unlock = audit_fileset_lock();
+	if (dp == NULL)
+		return 1;
+	if (obj->dentry == NULL) {
+		struct nameidata	nd;
+
+		memset(&nd, 0, sizeof(nd));
+		if (!path_init(obj->name, LOOKUP_POSITIVE, &nd))
+			goto out;
+		if (path_walk(obj->name, &nd) < 0)
+			goto out;
+		obj->vfsmnt = mntget(nd.mnt);
+		obj->dentry = dget(nd.dentry);
+		path_release(&nd);
+	}
+
+#ifdef DEBUG_FILESET
+	DPRINTF("obj->dentry=%p, obj->inode=%p, dp->inode=%p\n",
+			obj->dentry,
+			obj->dentry->d_inode,
+			dp->d_inode);
+#endif
+
+	/* See if the name matches */
+	if (path && !strcmp(obj->name, path)) {
+		match = 1;
+		goto out;
+	} else
+	if (obj->dentry->d_inode == dp->d_inode) {
+		/* This is a hard link. Replace the name we log
+		 * with the real name */
+
+#ifdef DEBUG_FILESET
+		DPRINTF("is a hardlink, replacing %s -> %s\n", path, obj->name);
+#endif
+
+		/* We're lucky - we know that the path buffer
+		 * has a size of PATH_MAX */
+		strncpy(tgt->at_path.name, obj->name, PATH_MAX-1);
+		tgt->at_path.name[PATH_MAX-1] = '\0';
+		tgt->at_path.len = strlen(tgt->at_path.name);
+
+		match = 1;
+		goto out;
+	}
+
+	/* See if the name of any parent directory
+	 * matches the filter entry. */
+       	while (!match && dp && dp->d_parent != dp) {
+		dp = dp->d_parent;
+		match = (obj->dentry == dp);
+	}
+
+out:	if (need_to_unlock && !match)
+		audit_fileset_unlock(0);
+	return match;
+}
+
+void
+audit_fileset_invalidate(void)
+{
+	struct aud_file_object	*obj;
+	struct list_head	*pos;
+
+	list_for_each(pos, &audit_fileset) {
+		obj = list_entry(pos, struct aud_file_object, link);
+		if (obj->dentry)
+			dput(obj->dentry);
+		obj->dentry = NULL;
+		if (obj->vfsmnt)
+			mntput(obj->vfsmnt);
+		obj->vfsmnt = NULL;
+	}
+}
+
diff -Naur linux-2.4.21/drivers/audit/filter.c audit/drivers/audit/filter.c
--- linux-2.4.21/drivers/audit/filter.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/filter.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,786 @@
+/*
+ * filter.c - system call filtering for the audit subsystem
+ *
+ * For all functions in this file, the caller must hold the global
+ * audit_lock, either read locked (for audit_filter_eval), or write
+ * locked (for all other calls).
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/miscdevice.h>
+#include <linux/audit.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+
+#include "audit-private.h"
+
+struct aud_filter {
+	struct list_head	af_link;
+	atomic_t		af_refcnt;
+
+	unsigned short		af_num;
+	unsigned short		af_op;
+
+	char *			af_evname;
+
+	union {
+	    struct f_apply {
+	    	unsigned int		target;
+	    	struct aud_filter *	predicate;
+	    } apply;
+	    struct f_bool {
+		struct aud_filter *	left;
+		struct aud_filter *	right;
+	    } bool;
+	    struct f_return {
+		unsigned int		action;
+	    } freturn;
+	    struct f_intop {
+		u_int64_t		value, mask;
+	    } intop;
+	    struct f_strop {
+		char *			value;
+		int			len;
+	    } strop;
+	    struct f_fileop {
+		struct aud_file_object *file;
+	    } fileop;
+	} u;
+};
+#define af_left		u.bool.left
+#define af_right	u.bool.right
+#define af_target	u.apply.target
+#define af_predicate	u.apply.predicate
+#define af_action	u.freturn.action
+#define af_intval	u.intop.value
+#define af_intmask	u.intop.mask
+#define af_strval	u.strop.value
+#define af_strlen	u.strop.len
+#define af_file		u.fileop.file
+
+static LIST_HEAD(filter_list);
+
+
+static void	__audit_filter_free(struct aud_filter *f);
+
+/*
+ * Find an audit filter and bump the reference count.
+ * Must be locked when we get here
+ */
+static struct aud_filter *
+__audit_filter_find(unsigned int num)
+{
+	struct aud_filter	*f;
+	struct list_head	*pos;
+
+	list_for_each(pos, &filter_list) {
+		f = list_entry(pos, struct aud_filter, af_link);
+		if (f->af_num == num)
+			return f;
+	}
+	return NULL;
+}
+
+/*
+ * Find an audit filter and bump the reference count.
+ * Must be locked when we get here
+ */
+struct aud_filter *
+audit_filter_get(unsigned int num)
+{
+	struct aud_filter	*f;
+
+	if ((f = __audit_filter_find(num)) != NULL)
+		atomic_inc(&f->af_refcnt);
+	return f;
+}
+
+/*
+ * Release a reference on a filter object
+ */
+static void
+__audit_filter_put(struct aud_filter *f, list_t *free_list)
+{
+	if (f && atomic_dec_and_test(&f->af_refcnt)) {
+		/* Remove from linked list */
+		list_del_init(&f->af_link);
+		if (free_list) {
+			list_add(&f->af_link, free_list);
+		} else {
+			__audit_filter_free(f);
+		}
+	}
+}
+
+void
+audit_filter_put(struct aud_filter *f)
+{
+	__audit_filter_put(f, NULL);
+}
+
+/*
+ * Free a filter object, recursively.
+ */
+static void
+__audit_filter_free(struct aud_filter *f)
+{
+	LIST_HEAD(free_list);
+	list_t	*pos;
+
+	do {
+		switch (f->af_op) {
+		case AUD_FILT_OP_AND:
+		case AUD_FILT_OP_OR:
+			__audit_filter_put(f->af_right, &free_list);
+			/* fallthru */
+		case AUD_FILT_OP_NOT:
+			__audit_filter_put(f->af_left, &free_list);
+			break;
+		case AUD_FILT_OP_APPLY:
+			__audit_filter_put(f->af_predicate, &free_list);
+			break;
+		case AUD_FILT_OP_STREQ:
+			if (f->af_strval)
+				kfree(f->af_strval);
+			break;
+		case AUD_FILT_OP_PREFIX:
+			if (f->af_file)
+				audit_fileset_release(f->af_file);
+			break;
+		}
+
+		memset(f, 0, sizeof(*f));
+		kfree(f);
+
+		/* See if detaching filter sub-expressions
+		 * released the last reference as well. If so,
+		 * delete these sub-expressions too. */
+		if ((pos = list_first(&free_list)) != NULL) {
+			f = list_entry(pos, struct aud_filter, af_link);
+			list_del_init(&f->af_link);
+		} else {
+			f = NULL;
+		}
+	} while (f != NULL);
+}
+
+/*
+ * Create a new filter object, given the user
+ * supplied data
+ */
+int
+audit_filter_add(struct audit_filter *data)
+{
+	struct audit_filter	copy;
+	struct aud_filter	*f = NULL;
+	char			*string = NULL;
+	size_t			namelen;
+	int			err, n;
+
+	if (copy_from_user(&copy, data, sizeof(*data)))
+		return -EFAULT;
+	data = &copy;
+
+	err = -EEXIST;
+	if (__audit_filter_find(data->num))
+		goto err;
+
+	err = -ENOBUFS;
+	if ((namelen = strnlen(data->event, sizeof(data->event))) != 0)
+		namelen++;
+	if (!(f = kmalloc(sizeof(*f) + namelen, GFP_KERNEL)))
+		goto err;
+
+	memset(f, 0, sizeof(*f));
+	f->af_num = data->num;
+	f->af_op  = data->op;
+	if (namelen) {
+		f->af_evname = (char *) f + sizeof(*f);
+		memcpy(f->af_evname, data->event, namelen);
+	}
+
+	err = -EINVAL;
+	switch (f->af_op) {
+	case AUD_FILT_OP_AND:
+	case AUD_FILT_OP_OR:
+		if (!(f->af_left  = audit_filter_get(data->u.bool.filt1))
+		 || !(f->af_right = audit_filter_get(data->u.bool.filt2)))
+			goto err;
+		break;
+	case AUD_FILT_OP_NOT:
+		if (!(f->af_left  = audit_filter_get(data->u.bool.filt1)))
+			goto err;
+		break;
+	case AUD_FILT_OP_APPLY:
+		f->af_target = data->u.apply.target;
+		f->af_predicate = audit_filter_get(data->u.apply.filter);
+		if (!f->af_predicate)
+			goto err;
+		break;
+	case AUD_FILT_OP_RETURN:
+		f->af_action = data->u.freturn.action;
+		break;
+	case AUD_FILT_OP_TRUE:
+	case AUD_FILT_OP_FALSE:
+	case AUD_FILT_OP_EQ:
+	case AUD_FILT_OP_NE:
+	case AUD_FILT_OP_GT:
+	case AUD_FILT_OP_GE:
+	case AUD_FILT_OP_LE:
+	case AUD_FILT_OP_LT:
+		f->af_intval = data->u.integer.value;
+		break;
+	case AUD_FILT_OP_MASK:
+		f->af_intval = data->u.integer.value;
+		f->af_intmask = data->u.integer.mask;
+		break;
+	case AUD_FILT_OP_STREQ:
+	case AUD_FILT_OP_PREFIX:
+		n = strnlen_user(data->u.string.value, PATH_MAX);
+		err = -ENOBUFS;
+		if (n > PATH_MAX || !(string = kmalloc(n, GFP_KERNEL)))
+			goto err;
+		err = -EFAULT;
+		if (copy_from_user(string, data->u.string.value, n))
+			goto err_stringfree;
+		if (f->af_op == AUD_FILT_OP_STREQ) {
+			f->af_strval = string;
+			f->af_strlen = n-1;
+		} else {
+			err = -ENOBUFS;
+			f->af_file = audit_fileset_add(string);
+			if (!f->af_file)
+				goto err_stringfree;
+		}
+		break;
+	default:
+		goto err;
+	}
+
+	list_add(&f->af_link, &filter_list);
+	atomic_inc(&f->af_refcnt);
+	return 0;
+
+err_stringfree:
+	kfree(string);
+
+err:	if (f)
+		__audit_filter_free(f);
+	return err;
+}
+
+int
+audit_filter_clear(void)
+{
+	struct list_head	*pos, *next;
+	struct aud_filter	*f;
+
+	list_for_each_safe(pos, next, &filter_list) {
+		f = list_entry(pos, struct aud_filter, af_link);
+		list_del_init(&f->af_link);
+		audit_filter_put(f);
+	}
+	return 0;
+}
+
+/*
+ * Check if the given operation is a predicate
+ */
+static inline int
+__is_predicate(struct aud_filter *f)
+{
+	if (!f)
+		return 0;
+	switch (f->af_op) {
+	case AUD_FILT_OP_AND:
+	case AUD_FILT_OP_OR:
+	case AUD_FILT_OP_NOT:
+	case AUD_FILT_OP_APPLY:
+		return 0;
+	}
+	return 1;
+}
+
+/*
+ * Make sure the data is type compatible with the predicate
+ */
+static inline int
+__audit_predicate_type_compatible(unsigned int op, unsigned int type)
+{
+	if (AUD_FILT_ARGTYPE_INT(op)
+	 && type != AUDIT_ARG_IMMEDIATE
+	 && type != AUDIT_ARG_NULL)
+		return 0;
+
+	if (AUD_FILT_ARGTYPE_STR(op)
+	 && type != AUDIT_ARG_STRING
+	 && type != AUDIT_ARG_PATH)
+		return 0;
+
+	return 1;
+}
+
+/*
+ * Fill in the specified target
+ */
+static struct sysarg_data *
+__audit_get_target(unsigned int n,
+			struct aud_event_data *ev,
+		       	struct sysarg_data *tgt,
+			struct sysarg_data *parent_target)
+{
+	/* Most common case by far */
+	tgt->at_type = AUDIT_ARG_IMMEDIATE;
+	tgt->at_flags = 0;
+
+	/* Process attributes */
+	if (AUD_FILT_TGT_PROCESS_ATTR(n)) {
+		switch (n) {
+		case AUD_FILT_TGT_UID:
+			tgt->at_intval = current->uid;
+			break;
+		case AUD_FILT_TGT_GID:
+			tgt->at_intval = current->gid;
+			break;
+		case AUD_FILT_TGT_DUMPABLE:
+#ifdef is_dumpable
+			tgt->at_intval = is_dumpable(current);
+#else
+			tgt->at_intval = current->mm && current->mm->dumpable;
+#endif
+			break;
+		case AUD_FILT_TGT_EXIT_CODE:
+			tgt->at_intval = ev->exit_status;
+			tgt->at_flags |= AUD_ARG_SIGNED;
+			break;
+		case AUD_FILT_TGT_LOGIN_UID:
+			task_lock(current);
+			if (current->audit == NULL) {
+				task_unlock(current);
+				goto invalid;
+			}
+			tgt->at_intval = ((struct aud_process *) current->audit)->audit_uid;
+			task_unlock(current);
+			break;
+		default:
+			goto invalid;
+		}
+		return tgt;
+	}
+
+	/* System call attributes */
+	if (AUD_FILT_TGT_SYSCALL_ATTR(n)) {
+		struct aud_syscall_data	*sc;
+
+		if ((sc = ev->syscall) == NULL)
+			goto invalid;
+		if (n == AUD_FILT_TGT_MINOR_CODE) {
+			tgt->at_intval = sc->minor;
+		} else
+		if (n == AUD_FILT_TGT_RETURN_CODE) {
+			tgt->at_flags = AUD_ARG_SIGNED;
+			tgt->at_intval = sc->result;
+		} else
+		if (n == AUD_FILT_TGT_USERMSG_EVNAME) {
+			if (ev->name == NULL)
+				goto invalid;
+			tgt->at_type = AUDIT_ARG_STRING;
+			tgt->at_strval = ev->name;
+		} else {
+			tgt = audit_get_argument(sc, n);
+		}
+		return tgt;
+	}
+
+	/* File attributes */
+	if (AUD_FILT_TGT_FILE_ATTR(n)) {
+		struct dentry	*dentry, *up;
+		struct inode	*inode;
+
+		if (parent_target == NULL
+		 || parent_target->at_type != AUDIT_ARG_PATH)
+			goto invalid;
+
+		dentry = parent_target->at_path.dentry;
+		while (!(inode = dentry->d_inode)) {
+			up = dentry->d_parent;
+			if (dentry == up)
+				goto invalid;
+			dentry = up;
+		}
+
+		switch (n) {
+		case AUD_FILT_TGT_FILE_DEV:
+			tgt->at_intval = inode->i_dev;
+			break;
+		case AUD_FILT_TGT_FILE_INO:
+			tgt->at_intval = inode->i_ino;
+			break;
+		case AUD_FILT_TGT_FILE_MODE:
+			tgt->at_intval = inode->i_mode;
+			break;
+		case AUD_FILT_TGT_FILE_UID:
+			tgt->at_intval = inode->i_uid;
+			break;
+		case AUD_FILT_TGT_FILE_GID:
+			tgt->at_intval = inode->i_gid;
+			break;
+		case AUD_FILT_TGT_FILE_RDEV_MAJOR:
+			if (!S_ISCHR(inode->i_mode) && !S_ISBLK(inode->i_mode))
+				break;
+			tgt->at_intval = MAJOR(inode->i_rdev);
+			break;
+		case AUD_FILT_TGT_FILE_RDEV_MINOR:
+			if (!S_ISCHR(inode->i_mode) && !S_ISBLK(inode->i_mode))
+				break;
+			tgt->at_intval = MINOR(inode->i_rdev);
+			break;
+		default:
+			goto invalid;
+		}
+		return tgt;
+	}
+
+	/* Socket attributes */
+	if (AUD_FILT_TGT_SOCK_ATTR(n)) {
+		struct dentry	*dentry;
+		struct inode	*inode;
+		struct socket	*sock;
+
+		if (parent_target) {
+			dentry = parent_target->at_path.dentry;
+			DPRINTF("sock attr: type=%d, dentry=%p, inode=%p\n",
+				parent_target->at_type,
+				dentry,
+				dentry? dentry->d_inode : NULL);
+		}
+		if (parent_target == NULL
+		 || parent_target->at_type != AUDIT_ARG_PATH
+		 || !(dentry = parent_target->at_path.dentry)
+		 || !(inode = dentry->d_inode)
+		 || !inode->i_sock)
+			goto invalid;
+
+		sock = &inode->u.socket_i;
+		if (!sock->ops)
+			goto invalid;
+
+		switch (n) {
+		case AUD_FILT_TGT_SOCK_FAMILY:
+			tgt->at_intval = sock->ops->family;
+			break;
+		case AUD_FILT_TGT_SOCK_TYPE:
+			tgt->at_intval = sock->type;
+			break;
+		default:
+			goto invalid;
+		}
+		return tgt;
+	}
+
+	/* rtnetlink attributes */
+	if (AUD_FILT_TGT_NETLINK_ATTR(n)) {
+		struct sk_buff	*skb;
+		struct nlmsghdr	*nlh;
+
+		if ((skb = ev->netconf) == NULL
+		 || (skb->len < NLMSG_LENGTH(sizeof(struct rtgenmsg))))
+			goto invalid;
+
+		nlh = (struct nlmsghdr *) skb->data;
+		switch (n) {
+		case AUD_FILT_TGT_NETLINK_TYPE:
+			tgt->at_intval = nlh->nlmsg_type;
+			break;
+		case AUD_FILT_TGT_NETLINK_FLAGS:
+			tgt->at_intval = nlh->nlmsg_flags;
+			break;
+		case AUD_FILT_TGT_NETLINK_FAMILY:
+			tgt->at_intval = ((struct rtgenmsg *)NLMSG_DATA(nlh))->rtgen_family;
+			break;
+		default:
+			goto invalid;
+		}
+		return tgt;
+	}
+
+invalid:
+	return ERR_PTR(-EINVAL);
+}
+
+/*
+ * Print target value
+ */
+#ifdef DEBUG_FILTER
+static void
+__audit_print_target(struct sysarg_data *tgt)
+{
+	if (!audit_debug)
+		return;
+
+	switch (tgt->at_type) {
+	case AUDIT_ARG_IMMEDIATE:
+		if (tgt->at_flags & AUD_ARG_SIGNED)
+			DPRINTF("  value is [signed] %lld\n", tgt->at_intval);
+		else
+			DPRINTF("  value is [unsigned] %llu\n", tgt->at_intval);
+		break;
+	case AUDIT_ARG_STRING:
+		DPRINTF("  value is [string] %s\n", tgt->at_path.name);
+		break;
+	case AUDIT_ARG_PATH:
+		DPRINTF("  value is [path] %s\n", tgt->at_strval);
+		break;
+	case AUDIT_ARG_POINTER:
+		DPRINTF("  value is [pointer] size=%u\n", tgt->at_data.len);
+		break;
+	case AUDIT_ARG_NULL:
+		DPRINTF("  value is [null]\n");
+		break;
+	default:
+		DPRINTF("  value is [unknown]\n");
+	}
+}
+#endif
+
+/*
+ * Comparison operation
+ */
+#define __audit_compare(tgt, op, f) \
+do { \
+	if ((tgt)->at_flags & AUD_ARG_SIGNED) \
+		return (long) ((tgt)->at_intval) op (long) ((f)->af_intval); \
+	else \
+		return ((tgt)->at_intval) op ((f)->af_intval); \
+} while (0)
+
+/*
+ * Evaluate predicate
+ */
+static int
+__audit_predicate_eval(struct aud_filter *f,
+	       		struct aud_event_data *ev,
+			struct sysarg_data *tgt)
+{
+	char	*str;
+
+	if (f->af_op == AUD_FILT_OP_TRUE)
+		return AUDIT_LOG;
+	if (f->af_op == AUD_FILT_OP_FALSE)
+		return 0;
+
+	if (tgt == NULL)
+		return -EINVAL;
+
+	if (!__audit_predicate_type_compatible(f->af_op, tgt->at_type))
+		return -EINVAL;
+
+	switch (f->af_op) {
+	case AUD_FILT_OP_EQ: __audit_compare(tgt, ==, f);
+	case AUD_FILT_OP_NE: __audit_compare(tgt, !=, f);
+	case AUD_FILT_OP_LT: __audit_compare(tgt, <,  f);
+	case AUD_FILT_OP_LE: __audit_compare(tgt, <=, f);
+	case AUD_FILT_OP_GE: __audit_compare(tgt, >=, f);
+	case AUD_FILT_OP_GT: __audit_compare(tgt, >,  f);
+	case AUD_FILT_OP_MASK:
+		return ((tgt->at_intval & f->af_intmask) == f->af_intval);
+	case AUD_FILT_OP_STREQ:
+		str = tgt->at_strval;
+		return !strcmp(str, f->af_strval);
+	case AUD_FILT_OP_PREFIX:
+		return audit_fileset_match(f->af_file, tgt);
+	}
+
+	DPRINTF("Unsupported filter op %d\n", f->af_op);
+	return -EINVAL;
+}
+
+/*
+ * Recursively evaluate a filter expression
+ *
+ * If the user space did its job correctly, operations on a set
+ * of values get stored so that the left term of the OR expression
+ * is the comparison, so we don't have to recurse.
+ */
+#define DEF_VISITOR_STACK	8
+#define DEF_TARGET_STACK	8
+struct visitor {
+	struct visitor *	prev;
+	struct aud_filter *	node;
+	struct sysarg_data *	target;
+};
+
+int
+audit_filter_eval(struct aud_filter *filt, struct aud_event_data *ev)
+{
+	struct sysarg_data tstack[DEF_TARGET_STACK], *tstack_top, *tgt = NULL;
+	struct visitor	vstack[DEF_VISITOR_STACK], *vstack_top;
+	unsigned int	nvisitors = 0, ntargets = 0;
+	struct visitor	*v = NULL, *vnew;
+	int		r, res = 0, flags = 0;
+
+	tstack_top = tstack + DEF_TARGET_STACK;
+	vstack_top = vstack + DEF_VISITOR_STACK;
+
+	while (filt) {
+		/* Special case for expanded sets */
+		while (filt->af_op == AUD_FILT_OP_OR && __is_predicate(filt->af_left)) {
+			if ((r = __audit_predicate_eval(filt->af_left, ev, tgt)) < 0)
+				goto error;
+			flags |= r;
+			if (r & AUDIT_LOG)
+				goto up;
+			filt = filt->af_right;
+		}
+
+		/* Apply predicate to target? */
+		if (filt->af_op == AUD_FILT_OP_APPLY) {
+			if (ntargets >= DEF_TARGET_STACK) {
+				printk(KERN_NOTICE "%s: Too many nested "
+					       "targets in filter expression\n",
+					       __FUNCTION__);
+				goto einval;
+			}
+			tgt = __audit_get_target(filt->af_target, ev,
+						&tstack[ntargets], tgt);
+
+			if (IS_ERR(tgt)) {
+				r = PTR_ERR(tgt);
+				if (r == -EINVAL) {
+					printk(KERN_NOTICE
+						"Filter target 0x%x not known or not "
+						"supported in this context\n",
+					       	filt->af_target);
+				}
+				goto error;
+			}
+			if (tgt == &tstack[ntargets])
+				ntargets++;
+			filt = filt->af_predicate;
+#ifdef DEBUG_FILTER
+			__audit_print_target(tgt);
+#endif
+			continue;
+		}
+
+		/* Deal with AND OR NOT expressions.
+		 * First, we need to push the current node onto the
+		 * visitor stack, then we continue evaluating the left
+		 * term.
+		 */
+		if (!__is_predicate(filt)) {
+			/* Need to push this node */
+			if (nvisitors < DEF_VISITOR_STACK) {
+				vnew = &vstack[nvisitors++];
+			} else
+			if (!(vnew = (struct visitor *) kmalloc(sizeof(*v), GFP_KERNEL)))
+				goto enomem;
+			memset(vnew, 0, sizeof(*vnew));
+			vnew->prev = v;
+			vnew->node = filt;
+			vnew->target = tgt;
+			v = vnew;
+
+			filt = filt->af_left;
+			continue;
+		}
+
+		/* We have a predicate. Evaluate */
+		if ((r = __audit_predicate_eval(filt, ev, tgt)) < 0)
+			goto error;
+		flags |= r;
+
+up:
+		/* Combine with result of parent node */
+		filt = NULL;
+		while (v != NULL && filt == NULL) {
+			switch (v->node->af_op) {
+			case AUD_FILT_OP_NOT:
+				r ^= AUDIT_LOG;
+				break;
+			case AUD_FILT_OP_OR:
+				/* Left hand term evaluated to false,
+				 * try right hand */
+				if (!(r & AUDIT_LOG))
+					filt = v->node->af_right;
+				break;
+			case AUD_FILT_OP_AND:
+				/* Left hand term evaluated to true,
+				 * try right hand as well */
+				if (r & AUDIT_LOG)
+					filt = v->node->af_right;
+				break;
+			case AUD_FILT_OP_APPLY:
+				break;
+			}
+
+			/* Pop the visitor node */
+			vnew = v->prev;
+			if (vstack <= v && v < vstack_top) {
+				nvisitors--;
+			} else {
+				kfree(v);
+			}
+			v = vnew;
+
+			/* If the target goes out of scope, release it */
+			if (v && v->target != tgt && tstack <= tgt && tgt < tstack_top) {
+				tgt = v->target;
+				ntargets--;
+			}
+		}
+
+		res = r;
+	}
+
+	return res;
+
+enomem:	r = -ENOMEM;
+	goto error;
+einval:	r = -EINVAL;
+
+error:	/* Unwind the stack of visitors */
+	while (v) {
+		vnew = v->prev;
+		if (vstack <= v && v < vstack_top)
+			break;
+		kfree(v);
+		v = vnew;
+	}
+	return r;
+}
diff -Naur linux-2.4.21/drivers/audit/ioctl.c audit/drivers/audit/ioctl.c
--- linux-2.4.21/drivers/audit/ioctl.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/ioctl.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,310 @@
+/*
+ * ioctl.c
+ *
+ * Perform 32->64 ioctl conversion
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Copyright (C) 2003, International Business Machines Corp.
+ *
+ * Written by Jerone Young.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/unistd.h>
+#include <linux/audit.h>
+#include <linux/ioctl.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+
+#include "audit-private.h"
+
+#if defined(CONFIG_PPC64) || defined(CONFIG_X86_64)
+
+static int	do_audit_usermsg_ioctl(unsigned int fd, unsigned int cmd, 
+				unsigned long arg, struct file * file);
+static int	do_audit_filter_ioctl(unsigned int fd, unsigned int cmd, 
+				unsigned long arg, struct file * file);
+static int	do_audit_policy_ioctl(unsigned int fd, unsigned int cmd, 
+				unsigned long arg, struct file * file);
+static int	do_audit_login_ioctl(unsigned int fd, unsigned int cmd, 
+				unsigned long arg, struct file * file);
+
+extern int register_ioctl32_conversion(unsigned int cmd,
+					int (*handler)(unsigned int,
+							unsigned int,
+							unsigned long,
+							struct file *));
+extern int unregister_ioctl32_conversion(unsigned int cmd);
+
+
+struct audit_message32 {
+	u32	msg_type;
+	char	msg_evname[AUD_MAX_EVNAME];
+	u32	msg_data;
+	u32	msg_size;
+};
+
+struct audit_filter32 {
+	unsigned short  num;
+	unsigned short  op;
+	char event[AUD_MAX_EVNAME];
+	union {
+		struct {
+			unsigned short	target;
+			unsigned short	filter;
+		} apply;
+		struct {
+			unsigned short filt1,filt2;
+		} bool;
+		struct {
+			u32 action;
+		} freturn;
+		struct {
+			u_int64_t value;
+			u_int64_t mask;
+		} integer;
+		struct {
+			u32 value;
+		} string;
+	} u;
+};
+
+#define AUIOCUSERMESSAGE_32        _IOR(AUD_MAGIC, 111, struct audit_message32)
+
+static int 
+do_audit_usermsg_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, 
+			struct file * file)
+{
+	int ret, i;
+	mm_segment_t old_fs;
+	struct audit_message aum;
+	struct audit_message32 copy;
+
+	if (copy_from_user(&copy, (struct audit_message32 *) arg, 
+			sizeof(struct audit_message32)))
+		return -EFAULT;
+		
+	aum.msg_type = copy.msg_type;
+	for (i = 0; i < AUD_MAX_EVNAME; i++) 
+		aum.msg_evname[i] = copy.msg_evname[i];
+	aum.msg_data = (void *) copy.msg_data;
+	aum.msg_size = (size_t) copy.msg_size;
+
+	/* Make sure the pointer is good. */
+	if (!access_ok(VERIFY_READ, aum.msg_data, aum.msg_size))
+		return -EFAULT;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, AUIOCUSERMESSAGE, (unsigned long) &aum);
+	set_fs(old_fs);
+
+	return ret;
+}
+
+static int 
+do_audit_filter_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, 
+			struct file * file)
+{
+	int ret, i;
+	mm_segment_t old_fs;
+	struct audit_filter auf;
+	struct audit_filter32 copy;
+
+	if (copy_from_user(&copy, (struct audit_filter32 *) arg, 
+			sizeof(struct audit_filter32)))
+		return -EFAULT;
+	
+	auf.num = copy.num;
+	auf.op = copy.op;
+	for (i = 0; i < AUD_MAX_EVNAME; i++) 
+		auf.event[i] = copy.event[i];
+	switch (copy.op) {
+	case AUD_FILT_OP_AND:
+	case AUD_FILT_OP_OR:
+	case AUD_FILT_OP_NOT:
+		auf.u.bool.filt1 = copy.u.bool.filt1;
+		auf.u.bool.filt2 = copy.u.bool.filt2;
+		break;
+	case AUD_FILT_OP_APPLY:
+		auf.u.apply.target = copy.u.apply.target;
+		auf.u.apply.filter = copy.u.apply.filter;
+		break;
+	case AUD_FILT_OP_RETURN:
+		auf.u.freturn.action = copy.u.freturn.action;
+		break;
+	case AUD_FILT_OP_EQ:
+	case AUD_FILT_OP_NE:
+	case AUD_FILT_OP_GT:
+	case AUD_FILT_OP_GE:
+	case AUD_FILT_OP_LE:
+	case AUD_FILT_OP_LT:
+	case AUD_FILT_OP_MASK:
+		auf.u.integer.value = copy.u.integer.value;
+		auf.u.integer.mask = copy.u.integer.mask;
+		break;
+	case AUD_FILT_OP_STREQ:
+	case AUD_FILT_OP_PREFIX:
+		auf.u.string.value = (char *) copy.u.string.value;
+		break;
+	default:
+		break;
+	}
+	
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long) &auf);
+	set_fs(old_fs);
+
+	return ret;
+}
+
+static int 
+do_audit_policy_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg, 
+			struct file * file)
+{	
+	int ret;
+	mm_segment_t old_fs;
+	struct audit_policy pol;
+	
+	if (copy_from_user(&pol, (struct audit_policy *) arg, 
+			sizeof(struct audit_policy)))
+		return -EFAULT;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long) &pol);
+	set_fs(old_fs);
+	
+	return ret;
+}
+
+static int 
+do_audit_login_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg,
+			struct file * file)
+{
+	int ret;
+	mm_segment_t old_fs;
+	struct audit_login lg;
+
+	if (copy_from_user(&lg, (struct audit_login *) arg, 
+			sizeof(struct audit_login)))
+		return -EFAULT;
+
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = sys_ioctl(fd, cmd, (unsigned long) &lg);
+	set_fs(old_fs);
+
+	return ret;	
+}
+
+static struct {
+	int		code;
+	int		(*handler)(unsigned int,
+				   unsigned int,
+				   unsigned long,
+				   struct file *);
+} converters[] = {
+	/* compatible */
+	{ AUIOCATTACH,		NULL },
+	{ AUIOCDETACH,		NULL },
+	{ AUIOCSUSPEND,		NULL },
+	{ AUIOCRESUME,		NULL },
+	{ AUIOCCLRPOLICY,	NULL },
+	{ AUIOCIAMAUDITD,	NULL },
+	{ AUIOCSETAUDITID,	NULL },
+	{ AUIOCCLRFILTER,	NULL },
+
+	/* need handler */
+	{ AUIOCLOGIN,		do_audit_login_ioctl },
+	{ AUIOCSETPOLICY,	do_audit_policy_ioctl },
+	{ AUIOCUSERMESSAGE,	do_audit_usermsg_ioctl },
+	{ AUIOCUSERMESSAGE_32,	do_audit_usermsg_ioctl },
+	{ AUIOCSETFILTER,	do_audit_filter_ioctl },
+
+	{ -1 }
+};
+
+/*
+ * Register ioctl converters
+ */
+int
+audit_register_ioctl_converters(void)
+{
+	int	i, err = 0;
+
+	for (i = 0; err >= 0 && converters[i].code != -1; i++) {
+		err = register_ioctl32_conversion(converters[i].code,
+				converters[i].handler);
+	}
+
+	if (err < 0)
+		printk(KERN_ERR "audit: Failed to register ioctl32 "
+				"conversion handlers\n");
+	return err;
+}
+
+/*
+ * Unregister ioctl converters
+ */
+int
+audit_unregister_ioctl_converters(void)
+{
+	int	i, err = 0;
+
+	for (i = 0; err >= 0 && converters[i].code != -1; i++) {
+		err = unregister_ioctl32_conversion(converters[i].code);
+	}
+
+	if (err < 0)
+		printk(KERN_ERR "audit: Failed to unregister ioctl32 "
+				"conversion handlers\n");
+	return err;
+}
+
+#else
+
+/*
+ * No ioctl conversion on other platforms
+ */
+int
+audit_register_ioctl_converters(void)
+{
+	return 0;
+}
+
+int
+audit_unregister_ioctl_converters(void)
+{
+	return 0;
+}
+
+#endif
diff -Naur linux-2.4.21/drivers/audit/message.c audit/drivers/audit/message.c
--- linux-2.4.21/drivers/audit/message.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/message.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,438 @@
+/*
+ * message.c
+ *
+ * Linux Audit Subsystem, message passing stuff
+ *
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+#include <linux/audit.h>
+
+#include "audit-private.h"
+
+/* This looks a lot like wait_queue handling, but I couldn't
+ * get add_wait_queue_exclusive() to work... */
+struct audit_writer {
+	list_t		entry;
+	task_t *	task;
+	int		status;
+};
+
+int				audit_message_enabled;
+static spinlock_t		audit_message_lock = SPIN_LOCK_UNLOCKED;
+static unsigned int		audit_message_count;
+static unsigned int		audit_message_seq;
+static DECLARE_WAIT_QUEUE_HEAD(audit_reader_wait);
+static LIST_HEAD(audit_messages);
+static LIST_HEAD(audit_writers);
+
+/*
+ * Enable/disable audit messages
+ */
+int
+audit_msg_enable(void)
+{
+	int	res = -EBUSY;
+
+	spin_lock(&audit_message_lock);
+	if (!audit_message_enabled) {
+		audit_message_enabled = 1;
+		res = 0;
+		mb();
+	}
+	spin_unlock(&audit_message_lock);
+	return res;
+}
+
+/*
+ * Wake up the first writer
+ */
+static inline int
+__audit_wake_writer(int res)
+{
+	struct audit_writer *w = NULL;
+	list_t *first;
+
+	if (!(first = list_first(&audit_writers)))
+		return 0;
+
+	w = list_entry(first, struct audit_writer, entry);
+	w->status = res;
+	list_del_init(&w->entry);
+	wake_up_process(w->task);
+
+	return 1;
+}
+
+void
+audit_msg_disable(void)
+{
+	spin_lock(&audit_message_lock);
+	audit_message_enabled = 0;
+
+	/* Inform all processes waiting to deliver
+	 * messages to the audit daemon that we're
+	 * no longer interested */
+	while (__audit_wake_writer(-ENODEV))
+		;
+
+	spin_unlock(&audit_message_lock);
+	mb();
+
+	/* There shouldn't be any processes left on the
+	 * reader queue, but we wake them up anyway */
+	wake_up(&audit_reader_wait);
+}
+
+/*
+ * Allocate a new audit message head
+ *
+ * Beware - pinfo may be NULL when called from
+ * audit_user_message().
+ */
+struct aud_msg_head *
+audit_msg_new(struct aud_process *pinfo, int type,
+	       const char *evname, size_t size)
+{
+	struct aud_msg_head *msgh;
+	struct aud_message *msg;
+	unsigned int seqno;
+
+	spin_lock(&audit_message_lock);
+	if (audit_message_count < audit_max_messages) {
+		audit_message_count++;
+	} else {
+		struct audit_writer writer;
+
+		if (!audit_message_enabled) {
+			spin_unlock(&audit_message_lock);
+			return ERR_PTR(-ENODEV);
+		}
+		writer.task = current;
+		list_add_tail(&writer.entry, &audit_writers);
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+
+		spin_unlock(&audit_message_lock);
+		schedule();
+
+		if (writer.status < 0)
+			return ERR_PTR(writer.status);
+
+		spin_lock(&audit_message_lock);
+
+		/* When we get here, we know we are permitted
+		 * to allocate a message.
+		 * audit_message_count has already been adjusted. */
+	}
+
+	if (audit_message_count > audit_max_messages)
+		printk(KERN_WARNING "Oops, too many audit messages\n");
+
+	seqno = audit_message_seq++;
+	spin_unlock(&audit_message_lock);
+
+	while (1) {
+		msgh = kmalloc(sizeof(*msgh) + size, GFP_KERNEL);
+		if (msgh != NULL)
+			break;
+		schedule_timeout(HZ / 2);
+	}
+
+	INIT_LIST_HEAD(&msgh->list);
+	memset(&msgh->body, 0, sizeof(msgh->body));
+
+	msg = &msgh->body;
+	msg->msg_pid		= current->pid;
+	msg->msg_type		= type;
+	msg->msg_size		= sizeof(msgh->body) + size;
+	msg->msg_seqnr		= seqno;
+	msg->msg_timestamp	= CURRENT_TIME;
+	msg->msg_arch		= audit_arch;
+
+	msg->msg_audit_id	= pinfo? pinfo->audit_id : -1;
+	msg->msg_login_uid	= pinfo? pinfo->audit_uid : -1;
+	msg->msg_euid		= current->euid;
+	msg->msg_ruid		= current->uid;
+	msg->msg_suid		= current->suid;
+	msg->msg_fsuid		= current->fsuid;
+	msg->msg_egid		= current->egid;
+	msg->msg_rgid		= current->gid;
+	msg->msg_sgid		= current->sgid;
+	msg->msg_fsgid		= current->fsgid;
+
+	if (evname && *evname)
+		strncpy(msg->msg_evname, evname, sizeof(msg->msg_evname));
+
+	return msgh;
+}
+
+void
+audit_msg_insert(struct aud_msg_head *msgh)
+{
+	spin_lock(&audit_message_lock);
+	list_add_tail(&msgh->list, &audit_messages);
+	spin_unlock(&audit_message_lock);
+
+	/* Wake up the next reader in the queue */
+	wake_up(&audit_reader_wait);
+}
+
+int
+audit_msg_poll(struct file *file, struct poll_table_struct *wait)
+{
+	unsigned int ret = 0;
+
+	poll_wait(file, &audit_reader_wait, wait);
+
+	spin_lock(&audit_message_lock);
+	if (!list_empty(&audit_messages))
+		ret = 1;
+	spin_unlock(&audit_message_lock);
+
+	return (ret);
+}
+
+/*
+ * Get the next message from the queue
+ */
+struct aud_msg_head *
+audit_msg_get(int block, size_t max_len)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct aud_msg_head *msgh;
+
+	/* Wait non-exclusively */
+	add_wait_queue(&audit_reader_wait, &wait);
+	spin_lock(&audit_message_lock);
+	while (1) {
+		msgh = (struct aud_msg_head *) list_first(&audit_messages);
+		if (msgh != NULL && (max_len == 0 || msgh->body.msg_size < max_len)) {
+			list_del_init(&msgh->list);
+			break;
+		}
+
+		msgh = ERR_PTR(-EAGAIN);
+		if (!block)
+			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_unlock(&audit_message_lock);
+		schedule();
+		spin_lock(&audit_message_lock);
+
+		msgh = ERR_PTR(-ERESTARTSYS);
+		if (signal_pending(current))
+			break;
+	}
+
+	spin_unlock(&audit_message_lock);
+
+	current->state = TASK_RUNNING;
+	remove_wait_queue(&audit_reader_wait, &wait);
+
+	return msgh;
+}
+
+void
+audit_msg_release(struct aud_msg_head *msgh)
+{
+	spin_lock(&audit_message_lock);
+	if (msgh)
+		list_del(&msgh->list);
+
+	/* Wake up the next writer in the queue */
+	if (!__audit_wake_writer(0))
+		audit_message_count--;
+
+	spin_unlock(&audit_message_lock);
+
+
+	if (msgh)
+		kfree(msgh);
+}
+
+/*
+ * Send a login message
+ */
+int
+audit_msg_login(struct aud_process *pinfo, const char *evname,
+	       	struct audit_login *login)
+{
+	struct aud_msg_head	*msgh;
+	struct aud_msg_login	*login_msg;
+
+	msgh = audit_msg_new(pinfo, AUDIT_MSG_LOGIN, evname,
+		       	sizeof(*login_msg));
+	if (IS_ERR(msgh))
+		return PTR_ERR(msgh);
+
+	login_msg = (struct aud_msg_login *) &msgh->body.msg_data;
+	memset(login_msg, 0, sizeof(*login_msg));
+	login_msg->uid = login->uid;
+	memcpy(login_msg->hostname, login->hostname, sizeof(login->hostname));
+	memcpy(login_msg->address, login->address, sizeof(login->address));
+	memcpy(login_msg->terminal, login->terminal, sizeof(login->terminal));
+
+	/* fill in the executable name */
+	if (current->mm) {
+		struct vm_area_struct	*mmap;
+		struct file		*file;
+		char			*str;
+		int			len;
+
+		down_read(&current->mm->mmap_sem);
+		if ((mmap = current->mm->mmap)
+		 && (file = mmap->vm_file)
+		 && file->f_dentry) {
+			str = d_path(file->f_dentry, file->f_vfsmnt,
+					login_msg->executable,
+					sizeof(login_msg->executable));
+
+			if (str && str != login_msg->executable) {
+				len = strlen(str);
+				memmove(login_msg->executable, str, len+1);
+			}
+		}
+		up_read(&current->mm->mmap_sem);
+	}
+
+	audit_msg_insert(msgh);
+	return 0;
+}
+
+/*
+ * Send an exit event to user land
+ */
+int
+audit_msg_exit(struct aud_process *pinfo, const char *evname, long code)
+{
+	struct aud_msg_head	*msgh;
+	struct aud_msg_exit	*exit_msg;
+
+	msgh = audit_msg_new(pinfo, AUDIT_MSG_EXIT,
+		       		evname, sizeof(*exit_msg));
+	if (IS_ERR(msgh))
+		return PTR_ERR(msgh);
+
+	exit_msg = (struct aud_msg_exit *) &msgh->body.msg_data;
+	exit_msg->code = code;
+
+	audit_msg_insert(msgh);
+	return 0;
+}
+
+/*
+ * Log a system call, along with all arguments
+ */
+int
+audit_msg_syscall(struct aud_process *pinfo,
+		  const char *evname,
+	          struct aud_syscall_data *syscall)
+{
+	struct aud_msg_head	*msgh;
+	struct aud_msg_syscall	*syscall_msg;
+	int			len;
+
+	DPRINTF("called, syscall %d/%d, pid %d\n",
+		 syscall->major, syscall->minor, current->pid);
+
+	len = audit_encode_args(NULL, 0, syscall);
+	if (len < 0)
+		return len;
+
+	/* Allocate aud_process with message buffer and
+	 * additional space for arguments. */
+	msgh = audit_msg_new(pinfo, AUDIT_MSG_SYSCALL,
+				evname, sizeof(*syscall_msg)+len);
+	if (IS_ERR(msgh)) {
+		DPRINTF("audit_msg_new failed, err=%ld\n", PTR_ERR(msgh));
+		return PTR_ERR(msgh);
+	}
+
+	msgh->body.msg_arch = syscall->arch;
+	syscall_msg = (struct aud_msg_syscall *) &msgh->body.msg_data;
+	syscall_msg->personality = syscall->personality;
+	syscall_msg->major = syscall->major;
+	syscall_msg->minor = syscall->minor;
+	syscall_msg->result = syscall->result;
+	syscall_msg->length = len;
+
+	/* Encode arguments now */
+	len = audit_encode_args(syscall_msg->data, len, syscall);
+	if (len < 0) {
+		DPRINTF("Failed to encode args (pass #2, err=%d)\n", len);
+		audit_msg_release(msgh);
+		return len;
+	}
+
+	audit_msg_insert(msgh);
+	return 0;
+}
+
+/*
+ * Log an rtnetlink call
+ */
+int
+audit_msg_netlink(struct aud_process *pinfo, const char *evname,
+	       		struct sk_buff *skb, int result)
+{
+	struct aud_msg_head	*msgh;
+	struct aud_msg_netlink	*netlink_msg;
+	unsigned int		len;
+
+	DPRINTF("called.\n");
+
+	len = skb->len;
+	msgh = audit_msg_new(pinfo, AUDIT_MSG_NETLINK, evname,
+				sizeof(*netlink_msg) + len);
+	if (IS_ERR(msgh)) {
+		DPRINTF("audit_msg_new failed, err=%ld\n", PTR_ERR(msgh));
+		return PTR_ERR(msgh);
+	}
+
+	netlink_msg = (struct aud_msg_netlink *) &msgh->body.msg_data;
+	skb_copy_bits(skb, 0, netlink_msg->data, len);
+	netlink_msg->groups = NETLINK_CB(skb).groups;
+	netlink_msg->dst_groups = NETLINK_CB(skb).dst_groups;
+	netlink_msg->result = result;
+	netlink_msg->length = len;
+
+	audit_msg_insert(msgh);
+	return 0;
+}
diff -Naur linux-2.4.21/drivers/audit/stubs.c audit/drivers/audit/stubs.c
--- linux-2.4.21/drivers/audit/stubs.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/stubs.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,122 @@
+/*
+ * Audit subsystem module hooks
+ *
+ * Copyright (C) 2003, SuSE Linux AG
+ * Written by okir@suse.de
+ */
+
+#define __NO_VERSION__
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/syscall.h>
+#include <linux/audit.h>
+#include <asm/semaphore.h>
+
+#include <linux/audit.h>
+
+#define DONT_DEBUG_LOCKS
+#include "audit-private.h"
+
+unsigned int			audit_intercept_enabled = 0;
+
+#ifdef CONFIG_AUDIT_MODULE
+
+static struct audit_hooks	audit;
+static DECLARE_RWSEM(hook_lock);
+
+int
+audit_register(struct audit_hooks *hooks)
+{
+	int	res = 0;
+
+	if (!hooks->intercept
+	 || !hooks->result
+	 || !hooks->fork
+	 || !hooks->exit)
+		return -EINVAL;
+
+	down_write(&hook_lock);
+	if (audit.intercept) {
+		res = -EEXIST;
+	} else {
+		audit = *hooks;
+		audit_intercept_enabled = 1;
+	}
+	mb();
+	up_write(&hook_lock);
+
+	return res;
+}
+
+void
+audit_unregister(void)
+{
+	down_write(&hook_lock);
+	memset(&audit, 0, sizeof(audit));
+	audit_intercept_enabled = 0;
+	mb();
+	up_write(&hook_lock);
+}
+
+int
+audit_intercept(struct pt_regs *regs)
+{
+	int res = 0;
+
+	down_read(&hook_lock);
+	if (audit.intercept)
+		res = audit.intercept(regs);
+	up_read(&hook_lock);
+	if (res < 0)
+		audit_kill_process(res);
+	return res;
+}
+
+void
+audit_result(struct pt_regs *regs)
+{
+	down_read(&hook_lock);
+	if (audit.result)
+		audit.result(regs);
+	up_read(&hook_lock);
+}
+
+void
+audit_fork(struct task_struct *parent, struct task_struct *child)
+{
+	down_read(&hook_lock);
+	if (audit.fork)
+		audit.fork(parent, child);
+	up_read(&hook_lock);
+}
+
+void
+audit_exit(struct task_struct *task, long code)
+{
+	down_read(&hook_lock);
+	if (audit.exit)
+		audit.exit(task, code);
+	up_read(&hook_lock);
+}
+
+void
+audit_netlink_msg(struct sk_buff *skb, int res)
+{
+	down_read(&hook_lock);
+	if (audit.netlink_msg)
+		audit.netlink_msg(skb, res);
+	up_read(&hook_lock);
+}
+
+EXPORT_SYMBOL(audit_register);
+EXPORT_SYMBOL(audit_unregister);
+
+#endif /* CONFIG_AUDIT_MODULE */
diff -Naur linux-2.4.21/drivers/audit/syscall-i386.c audit/drivers/audit/syscall-i386.c
--- linux-2.4.21/drivers/audit/syscall-i386.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall-i386.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,151 @@
+/*
+ * syscall-i386.c
+ *
+ * i386 specific system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+extern struct sysent	linux_sysent[];
+extern int		audit_get_socketargs(struct aud_syscall_data *);
+extern int		audit_get_ipcargs(struct aud_syscall_data *);
+extern int		audit_get_ioctlargs(struct aud_syscall_data *);
+
+int			audit_arch = AUDIT_ARCH_I386;
+
+/*
+ * Given the set of registers, extract the syscall code and
+ * arguments array
+ */
+int
+audit_get_args(struct pt_regs *regs, struct aud_syscall_data *sc)
+{
+	struct sysent	*entry;
+	int		code;
+
+	sc->entry = NULL;
+	sc->major = 0;
+	sc->minor = 0;
+	sc->arch  = AUDIT_ARCH_I386;
+
+	if (regs == NULL)
+		return -EPERM;
+
+	code = regs->orig_eax;
+
+	/* XXX should we try to log calls to invalid syscalls? */
+	if (code < 0 || code >= NR_syscalls)
+		return -ENOSYS;
+
+	if (audit_policy_ignore(code))
+		return 0;
+
+	entry = &linux_sysent[code];
+	switch (entry->sy_narg) {
+	case 6:	sc->raw_args[5] = regs->ebp; /* correct? */
+	case 5:	sc->raw_args[4] = regs->edi;
+	case 4: sc->raw_args[3] = regs->esi;
+	case 3: sc->raw_args[2] = regs->edx;
+	case 2: sc->raw_args[1] = regs->ecx;
+	case 1: sc->raw_args[0] = regs->ebx;
+	case 0: break;
+	default:
+		printk("audit: invalid argument count?!\n");
+		BUG();
+	}
+
+	sc->major = code;
+	sc->entry = entry;
+
+	/* Special treatment for special functions */
+	switch (code) {
+	case __NR_truncate64:
+	case __NR_ftruncate64:
+		/* 64bit values are actually passed as two 32bit
+		 * registers, lower one first */
+		sc->raw_args[1] |= ((u_int64_t) regs->edx) << 32;
+		break;
+	case __NR_ioctl:
+		return audit_get_ioctlargs(sc);
+	case __NR_socketcall:
+		return audit_get_socketargs(sc);
+	case __NR_ipc:
+		return audit_get_ipcargs(sc);
+	}
+
+	return 0;
+}
+
+/*
+ * Update system call register
+ */
+int
+audit_update_arg(struct aud_syscall_data *sc, unsigned int n, unsigned long newval)
+{
+	struct pt_regs	*regs = sc->regs;
+	int		code = sc->major;
+
+	if (n > 5)
+		return -EINVAL;
+	if (code == __NR_ioctl || code == __NR_socketcall || code == __NR_ipc)
+		return -EINVAL;
+	sc->raw_args[n] = newval;
+	switch (n) {
+	case 5: regs->ebp = newval; break;
+	case 4: regs->edi = newval; break;
+	case 3: regs->esi = newval; break;
+	case 2: regs->edx = newval; break;
+	case 1: regs->ecx = newval; break;
+	case 0: regs->ebx = newval; break;
+	}
+
+	return 0;
+}
+
+/*
+ * Get the return value of a system call
+ */
+long
+audit_get_result(struct pt_regs *regs)
+{
+	return regs->eax;
+}
diff -Naur linux-2.4.21/drivers/audit/syscall-ppc64.c audit/drivers/audit/syscall-ppc64.c
--- linux-2.4.21/drivers/audit/syscall-ppc64.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall-ppc64.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,195 @@
+/*
+ * syscall-ppc64.c
+ *
+ * ppc64 specific system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 Paul Mackerras, IBM Corp.
+ *
+ * Derived from syscall-i386.c:
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+/* XXX must be the same as in syscall.c, yuck */
+#define MAX_SOCKETCALL	20
+
+extern struct sysent	socketcall_sysent[];
+extern int		audit_get_socketargs(struct aud_syscall_data *);
+extern int		audit_get_ipcargs(struct aud_syscall_data *);
+static int		audit_get_socketargs32(struct aud_syscall_data *sc);
+
+int			audit_arch = AUDIT_ARCH_PPC64;
+
+
+/*
+ * Given the set of registers, extract the syscall code and
+ * arguments array
+ */
+int
+audit_get_args(struct pt_regs *regs, struct aud_syscall_data *syscall)
+{
+	struct sysent	*entry;
+	int		code;
+	int		i;
+
+	syscall->entry = NULL;
+	syscall->major = 0;
+	syscall->minor = 0;
+
+	if (regs == NULL)		/* "can't happen"  -- paulus. */
+		return -EPERM;
+
+	code = regs->gpr[0];
+
+	/* XXX may need to define and use a linux_sysent_32
+	   for 32-bit processes  -- paulus. */
+	entry = audit_get_syscall_entry(code);
+
+	/* XXX should we try to log calls to invalid syscalls? */
+	if (entry == NULL)
+		return -ENOSYS;
+
+	if (audit_policy_ignore(code))
+		return 0;
+
+	syscall->major = code;
+	syscall->entry = entry;
+
+	if (current->thread.flags & PPC_FLAG_32BIT) {
+		/* 32-bit process */
+		syscall->arch  = AUDIT_ARCH_PPC;
+		for (i = 0; i < entry->sy_narg; ++i) {
+			unsigned long val = regs->gpr[3+i] & 0xFFFFFFFFUL;
+
+			/* sign-extend if necessary */
+			if (entry->sy_args[i].sa_flags & AUD_ARG_SIGNED)
+				val = (int) val;
+			syscall->raw_args[i] = val;
+		}
+
+		/* Special treatment for socket call */
+		if (code == __NR_socketcall)
+			return audit_get_socketargs32(syscall);
+
+		if (code == __NR_truncate64
+		 || code == __NR_ftruncate64) {
+			syscall->raw_args[1] =
+				(((u_int64_t) regs->gpr[4]) << 32) | regs->gpr[5];
+		}
+	} else {
+		/* 64-bit process */
+		syscall->arch  = AUDIT_ARCH_PPC64;
+		for (i = 0; i < entry->sy_narg; ++i)
+			syscall->raw_args[i] = regs->gpr[3+i];
+
+		/* Special treatment for socket call */
+		if (code == __NR_socketcall)
+			return audit_get_socketargs(syscall);
+	}
+
+	/* Special treatment for IPC syscalls */
+	if (code == __NR_ipc)
+		return audit_get_ipcargs(syscall);
+	return 0;
+}
+
+/*
+ * Update system call register
+ */
+int
+audit_update_arg(struct aud_syscall_data *sc, unsigned int n, unsigned long newval)
+{
+	struct pt_regs	*regs = sc->regs;
+	int		code = sc->major;
+
+	if (n > 5)
+		return -EINVAL;
+	if (code == __NR_ioctl || code == __NR_socketcall || code == __NR_ipc)
+		return -EINVAL;
+
+	sc->raw_args[n] = newval;
+	regs->gpr[3+n] = newval;
+	return 0;
+}
+
+/*
+ * Get the return value of a system call
+ */
+long int
+audit_get_result(struct pt_regs *regs)
+{
+	return regs->result;
+}
+
+int
+audit_get_socketargs32(struct aud_syscall_data *sc)
+{
+	unsigned int	minor = sc->raw_args[0];
+	struct sysent	*entry;
+	unsigned int	argsize;
+	unsigned int	args[AUDIT_MAXARGS];
+	int		i;
+
+	sc->minor = minor;
+	if (minor >= MAX_SOCKETCALL) {
+		/* XXX mark as invalid? */
+		return 0;
+	}
+
+	entry = &socketcall_sysent[minor];
+
+	argsize = entry->sy_narg * sizeof(unsigned int);
+	if (copy_from_user(args, (void *)sc->raw_args[1], argsize))
+		return 0;
+
+	for (i = 0; i < entry->sy_narg; ++i) {
+		unsigned long val = args[i];
+
+		/* sign-extend if necessary */
+		if (entry->sy_args[i].sa_flags & AUD_ARG_SIGNED)
+			val = (int) val;
+		sc->raw_args[i] = val;
+	}
+
+	sc->entry = entry;
+
+	return 0;
+}
diff -Naur linux-2.4.21/drivers/audit/syscall-s390.c audit/drivers/audit/syscall-s390.c
--- linux-2.4.21/drivers/audit/syscall-s390.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall-s390.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,141 @@
+/*
+ * syscall-s390.c
+ *
+ * s390 specific system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+extern struct sysent	linux_sysent[];
+extern int		audit_get_socketargs(struct aud_syscall_data *);
+extern int		audit_get_ipcargs(struct aud_syscall_data *);
+
+int			audit_arch = AUDIT_ARCH_S390;
+
+/*
+ * Given the set of registers, extract the syscall code and
+ * arguments array
+ */
+int
+audit_get_args(struct pt_regs *regs, struct aud_syscall_data *syscall)
+{
+	struct sysent	*entry;
+	unsigned long	code;
+
+	syscall->entry = NULL;
+	syscall->major = 0;
+	syscall->minor = 0;
+	syscall->arch  = AUDIT_ARCH_S390;
+
+	if (regs == NULL)
+		return -EPERM;
+
+	code = regs->gprs[2];
+
+	/* XXX should we try to log calls to invalid syscalls? */
+	if (code >= NR_syscalls)
+		return -ENOSYS;
+
+	if (audit_policy_ignore(code))
+		return 0;
+
+	entry = &linux_sysent[code];
+	switch (entry->sy_narg) {
+	case 6: syscall->raw_args[5] = regs->gprs[7];
+	case 5:	syscall->raw_args[4] = regs->gprs[6];
+	case 4: syscall->raw_args[3] = regs->gprs[5];
+	case 3: syscall->raw_args[2] = regs->gprs[4];
+	case 2: syscall->raw_args[1] = regs->gprs[3];
+	case 1: syscall->raw_args[0] = regs->orig_gpr2;
+	case 0: break;
+	default:
+		printk("audit: invalid argument count?!\n");
+		BUG();
+	}
+
+	syscall->major = code;
+	syscall->entry = entry;
+
+	/* Special treatment for special functions */
+	if (code == __NR_truncate64
+	 || code == __NR_ftruncate64) {
+		syscall->raw_args[1] = (((u_int64_t)  regs->gprs[3]) << 32) |  regs->gprs[4];
+		return 0;
+	}
+	if (code == __NR_socketcall)
+		return audit_get_socketargs(syscall);
+	if (code == __NR_ipc)
+		return audit_get_ipcargs(syscall);
+
+	return 0;
+}
+
+/*
+ * Update system call register
+ */
+int
+audit_update_arg(struct aud_syscall_data *sc, unsigned int n, unsigned long newval)
+{
+	struct pt_regs	*regs = sc->regs;
+	int		code = sc->major;
+
+	if (n > 5)
+		return -EINVAL;
+	if (code == __NR_ioctl || code == __NR_socketcall || code == __NR_ipc)
+		return -EINVAL;
+
+	sc->raw_args[n] = newval;
+	if (n == 0)
+		regs->orig_gpr2 = newval;
+	else
+		regs->gprs[2+n] = newval;
+	return 0;
+}
+
+/*
+ * Get the return value of a system call
+ */
+long
+audit_get_result(struct pt_regs *regs)
+{
+	return regs->gprs[2];
+}
diff -Naur linux-2.4.21/drivers/audit/syscall-s390x.c audit/drivers/audit/syscall-s390x.c
--- linux-2.4.21/drivers/audit/syscall-s390x.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall-s390x.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,136 @@
+/*
+ * syscall-s390x.c
+ *
+ * s390x specific system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+extern struct sysent	linux_sysent[];
+extern int		audit_get_socketargs(struct aud_syscall_data *);
+extern int		audit_get_ipcargs(struct aud_syscall_data *);
+
+int			audit_arch = AUDIT_ARCH_S390;
+
+/*
+ * Given the set of registers, extract the syscall code and
+ * arguments array
+ */
+int
+audit_get_args(struct pt_regs *regs, struct aud_syscall_data *syscall)
+{
+	struct sysent	*entry;
+	unsigned long	code;
+
+	syscall->entry = NULL;
+	syscall->major = 0;
+	syscall->minor = 0;
+	syscall->arch  = AUDIT_ARCH_S390X;
+
+	if (regs == NULL)
+		return -EPERM;
+
+	code = regs->gprs[2];
+
+	/* XXX should we try to log calls to invalid syscalls? */
+	if (code >= NR_syscalls)
+		return -ENOSYS;
+
+	if (audit_policy_ignore(code))
+		return 0;
+
+	entry = &linux_sysent[code];
+	switch (entry->sy_narg) {
+	case 6: syscall->raw_args[5] = regs->gprs[7];
+	case 5:	syscall->raw_args[4] = regs->gprs[6];
+	case 4: syscall->raw_args[3] = regs->gprs[5];
+	case 3: syscall->raw_args[2] = regs->gprs[4];
+	case 2: syscall->raw_args[1] = regs->gprs[3];
+	case 1: syscall->raw_args[0] = regs->orig_gpr2;
+	case 0: break;
+	default:
+		printk("audit: invalid argument count?!\n");
+		BUG();
+	}
+
+	syscall->major = code;
+	syscall->entry = entry;
+
+	/* Special treatment for special functions */
+	if (code == __NR_socketcall)
+		return audit_get_socketargs(syscall);
+	if (code == __NR_ipc)
+		return audit_get_ipcargs(syscall);
+
+	return 0;
+}
+
+/*
+ * Update system call register
+ */
+int
+audit_update_arg(struct aud_syscall_data *sc, unsigned int n, unsigned long newval)
+{
+	struct pt_regs	*regs = sc->regs;
+	int		code = sc->major;
+
+	if (n > 5)
+		return -EINVAL;
+	if (code == __NR_ioctl || code == __NR_socketcall || code == __NR_ipc)
+		return -EINVAL;
+
+	sc->raw_args[n] = newval;
+	if (n == 0)
+		regs->orig_gpr2 = newval;
+	else
+		regs->gprs[2+n] = newval;
+	return 0;
+}
+
+/*
+ * Get the return value of a system call
+ */
+long
+audit_get_result(struct pt_regs *regs)
+{
+	return regs->gprs[2];
+}
diff -Naur linux-2.4.21/drivers/audit/syscall-x86_64.c audit/drivers/audit/syscall-x86_64.c
--- linux-2.4.21/drivers/audit/syscall-x86_64.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall-x86_64.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,353 @@
+/*
+ * syscall-x86_64.c
+ *
+ * x86_64 specific system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 Max Asbock, IBM Corp.
+ *
+ * Derived from syscall-i386.c and syscall.c:
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+#include <asm/ia32_unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+extern struct sysent	linux_sysent[];
+extern int		audit_get_socketargs(struct aud_syscall_data *);
+extern int		audit_get_ipcargs(struct aud_syscall_data *);
+extern int		audit_get_ioctlargs(struct aud_syscall_data *);
+
+int			audit_arch = AUDIT_ARCH_X86_64;
+
+
+/* copied from syscall.c, need to consolidate */
+#define T_void          { AUDIT_ARG_END }
+#define T_immediate(T)  { AUDIT_ARG_IMMEDIATE, sizeof(T) }
+#define T_signedimm(T)  { AUDIT_ARG_IMMEDIATE, sizeof(T), .sa_flags = AUD_ARG_SIGNED }
+#define T_pointer(T)    { AUDIT_ARG_POINTER, sizeof(T) }
+#define T_string        { AUDIT_ARG_STRING }
+#define T_path          { AUDIT_ARG_PATH }
+#define T_path_parent   { AUDIT_ARG_PATH, .sa_flags = AUD_ARG_DIRNAME }
+#define T_filedesc      { AUDIT_ARG_FILEDESC }
+#define T_int           T_signedimm(int)
+#define T_uint          T_immediate(int)
+#define T_long          T_signedimm(long)
+#define T_ulong         T_immediate(long)
+#define T_off_t         T_signedimm(off_t)
+#define T_loff_t        T_signedimm(loff_t)
+#define T_mode_t        T_immediate(mode_t)
+#define T_size_t        T_immediate(size_t)
+#define T_dev_t         T_immediate(dev_t)
+#define T_pid_t         T_immediate(pid_t)
+#define T_uid_t         T_immediate(uid_t)
+#define T_gid_t         T_immediate(gid_t)
+#define T_u16_t         { AUDIT_ARG_IMMEDIATE, 2 }
+#define T_u32_t         { AUDIT_ARG_IMMEDIATE, 4 }
+#define T_u64_t         { AUDIT_ARG_IMMEDIATE, 8 }
+#define T_any_ptr       { AUDIT_ARG_POINTER, 0 }
+#define T_timeval_t     T_pointer(struct timeval)
+#define T_timezone_t    T_pointer(struct timezone)
+#define T_timex_t       T_pointer(struct timex)
+#define T_caphdr_t      T_pointer(struct __user_cap_header_struct)
+#define T_capdata_t     T_pointer(struct __user_cap_data_struct)
+#define T_sysctl_t      T_pointer(struct __sysctl_args)
+#define T_rlimit_t      T_pointer(struct rlimit)
+#define T_socklen_t     T_immediate(socklen_t)
+#define T_sigset_t      T_pointer(sigset_t)
+#define socklen_t       int     /* socklen_t is a user land thing */
+#define T_array(itype, index, max) \
+                        { AUDIT_ARG_ARRAY, sizeof(itype), index, max }
+#define T_opaque_t(idx) { AUDIT_ARG_ARRAY, 1, idx+1, 256 }
+
+
+/* x86-64 supports 64-bit and 32-bit syscalls. They use different codes
+ * therefore we need a second table
+ */
+static struct sysent linux_sysent_ia32[IA32_NR_syscalls] = {
+[__NR_ia32_fork]	= { 0, { T_void } },
+[__NR_ia32_vfork]	= { 0, { T_void } },
+[__NR_ia32_clone]	= { 1, { T_uint } },
+[__NR_ia32_execve]	= { 3, { T_path, T_any_ptr, T_any_ptr } },
+[__NR_ia32_exit]	= { 1, { T_int } },
+[__NR_ia32_ptrace]	= { 4, { T_uint, T_pid_t, T_any_ptr, T_any_ptr } },
+[__NR_ia32_uselib]	= { 1, { T_path } },
+[__NR_ia32_kill]	= { 2, { T_pid_t, T_int } },
+[__NR_ia32_tkill]	= { 2, { T_pid_t, T_int } },
+
+[__NR_ia32_setuid]	= { 1, { T_u16_t } },
+[__NR_ia32_setgid]	= { 1, { T_u16_t } },
+[__NR_ia32_setreuid]	= { 2, { T_u16_t, T_u16_t } },
+[__NR_ia32_setregid]	= { 2, { T_u16_t, T_u16_t } },
+[__NR_ia32_setresuid]	= { 3, { T_u16_t, T_u16_t, T_u16_t } },
+[__NR_ia32_setresgid]	= { 3, { T_u16_t, T_u16_t, T_u16_t } },
+[__NR_ia32_setfsuid]	= { 1, { T_u16_t } },
+[__NR_ia32_setfsgid]	= { 1, { T_u16_t } },
+[__NR_ia32_setgroups]	= { 2, { T_size_t, T_array(__u16, 0, NGROUPS) } },
+[__NR_ia32_setuid32]	= { 1, { T_uid_t } },
+[__NR_ia32_setgid32]	= { 1, { T_gid_t } },
+[__NR_ia32_setreuid32]	= { 2, { T_uid_t, T_uid_t } },
+[__NR_ia32_setregid32]	= { 2, { T_gid_t, T_gid_t } },
+[__NR_ia32_setresuid32]	= { 3, { T_uid_t, T_uid_t, T_uid_t } },
+[__NR_ia32_setresgid32]	= { 3, { T_gid_t, T_gid_t, T_gid_t } },
+[__NR_ia32_setfsuid32]	= { 1, { T_uid_t } },
+[__NR_ia32_setfsgid32]	= { 1, { T_uid_t } },
+[__NR_ia32_setgroups32]	= { 2, { T_size_t, T_array(gid_t, 0, NGROUPS) } },
+[__NR_ia32_capset]	= { 2, { T_caphdr_t, T_capdata_t } },
+
+[__NR_ia32_umask]		= { 1, { T_mode_t } },
+[__NR_ia32_chroot]		= { 1, { T_path } },
+[__NR_ia32_chdir]		= { 1, { T_path } },
+[__NR_ia32_fchdir]		= { 1, { T_filedesc } },
+[__NR_ia32_setrlimit]		= { 2, { T_int, T_rlimit_t } },
+[__NR_ia32_setpriority]		= { 2, { T_int, T_int } },
+[__NR_ia32_sched_setaffinity]	= { 3, { T_pid_t, T_int, T_pointer(long) } },
+[__NR_ia32_sched_setparam]	= { 2, { T_pid_t, T_pointer(struct sched_param) } },
+[__NR_ia32_sched_setscheduler]	= { 3, { T_pid_t, T_int, T_pointer(struct sched_param) } },
+[__NR_ia32_brk]			= { 1, { T_any_ptr } },
+[__NR_ia32_signal]		= { 2, { T_int, T_any_ptr } },
+[__NR_ia32_rt_sigreturn]	= { 1, { T_long } },
+[__NR_ia32_rt_sigaction]	= { 3, { T_int, T_pointer(struct sigaction), T_any_ptr } },
+[__NR_ia32_rt_sigprocmask]	= { 3, { T_int, T_sigset_t, T_any_ptr } },
+[__NR_ia32_rt_sigpending]	= { 1, { T_any_ptr } },
+[__NR_ia32_rt_sigtimedwait]	= { 3, { T_sigset_t, T_any_ptr, T_pointer(struct timespec) } },
+[__NR_ia32_rt_sigqueueinfo]	= { 1, { T_void } },
+[__NR_ia32_rt_sigsuspend]	= { 1, { T_pointer(sigset_t) } },
+[__NR_ia32_setitimer]		= { 3, { T_int, T_any_ptr, T_any_ptr } },
+[__NR_ia32_setpgid]		= { 2, { T_pid_t, T_pid_t } },
+[__NR_ia32_setsid]		= { 1, { T_void } },
+
+[__NR_ia32_settimeofday]	= { 2, { T_timeval_t, T_timezone_t } },
+[__NR_ia32_adjtimex]		= { 1, { T_timex_t } },
+[__NR_ia32_stime]		= { 1, { T_pointer(int) } },
+[__NR_ia32__sysctl]		= { 1, { T_sysctl_t } },
+[__NR_ia32_sethostname]		= { 2, { T_array(char, 1, 256), T_size_t } },
+[__NR_ia32_setdomainname]	= { 2, { T_array(char, 1, __NEW_UTS_LEN), T_size_t } },
+[__NR_ia32_reboot]		= { 4, { T_int, T_int, T_int, T_any_ptr } },
+[__NR_ia32_create_module]	= { 2, { T_string, T_size_t } },
+[__NR_ia32_init_module]		= { 2, { T_string, T_any_ptr } },
+[__NR_ia32_query_module]	= { 5, { T_string, T_int, T_any_ptr, T_size_t, T_any_ptr } },
+[__NR_ia32_delete_module]	= { 1, { T_string } },
+[__NR_ia32_mount]		= { 5, { T_string, T_path, T_string, T_long, T_any_ptr } },
+[__NR_ia32_umount]		= { 1, { T_path } },
+[__NR_ia32_umount2]		= { 2, { T_path, T_int } },
+[__NR_ia32_swapon]		= { 2, { T_path, T_int } },
+[__NR_ia32_swapoff]		= { 1, { T_path } },
+[__NR_ia32_ioperm]		= { 3, { T_long, T_long, T_int } },
+[__NR_ia32_iopl]		= { 1, { T_int } },
+[__NR_ia32_syslog]		= { 3, { T_int, T_any_ptr, T_int } },
+
+[__NR_ia32_open]		= { 3, { T_path, T_int, T_mode_t } },
+[__NR_ia32_read]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_write]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_close]		= { 1, { T_filedesc } },
+[__NR_ia32_readv]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_writev]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_readdir]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_sendfile]		= { 4, { T_filedesc, T_filedesc, T_off_t, T_size_t } },
+[__NR_ia32_access]		= { 2, { T_path, T_int } },
+[__NR_ia32_creat]		= { 2, { T_path, T_mode_t } },
+[__NR_ia32_mkdir]		= { 2, { T_path_parent, T_mode_t } },
+[__NR_ia32_mknod]		= { 3, { T_path_parent, T_mode_t, T_dev_t } },
+[__NR_ia32_link]		= { 2, { T_path, T_path_parent } },
+[__NR_ia32_symlink]		= { 2, { T_string, T_path_parent } },
+[__NR_ia32_rename]		= { 2, { T_path, T_path_parent } },
+[__NR_ia32_unlink]		= { 1, { T_path } },
+[__NR_ia32_rmdir]		= { 1, { T_path } },
+[__NR_ia32_utime]		= { 2, { T_path, T_pointer(struct utimbuf) } },
+[__NR_ia32_chmod]		= { 2, { T_path, T_mode_t } },
+[__NR_ia32_chown]		= { 3, { T_path, T_u16_t, T_u16_t } },
+[__NR_ia32_chown32]		= { 3, { T_path, T_uid_t, T_gid_t } },
+[__NR_ia32_lchown]		= { 3, { T_path_parent, T_u16_t, T_u16_t } },
+[__NR_ia32_lchown32]		= { 3, { T_path_parent, T_uid_t, T_gid_t } },
+[__NR_ia32_fchown]		= { 3, { T_filedesc, T_u16_t, T_u16_t } },
+[__NR_ia32_fchown32]		= { 3, { T_filedesc, T_uid_t, T_gid_t } },
+[__NR_ia32_fchmod]		= { 2, { T_filedesc, T_mode_t } },
+[__NR_ia32_truncate]		= { 2, { T_path, T_size_t } },
+[__NR_ia32_truncate64]		= { 2, { T_path, T_u64_t } },
+[__NR_ia32_ftruncate]		= { 2, { T_filedesc, T_size_t } },
+[__NR_ia32_ftruncate64]		= { 2, { T_filedesc, T_u64_t } },
+[__NR_ia32_setxattr]		= { 5, { T_path, T_string, T_array(char, 3, 2046), T_size_t, T_int } },
+[__NR_ia32_lsetxattr]		= { 5, { T_path_parent, T_string, T_array(char, 3, 2046), T_size_t, T_int } },
+[__NR_ia32_fsetxattr]		= { 5, { T_filedesc, T_string, T_array(char, 3, 2046), T_size_t, T_int } },
+[__NR_ia32_getxattr]		= { 3, { T_path, T_any_ptr, T_size_t } },
+[__NR_ia32_lgetxattr]		= { 3, { T_path_parent, T_any_ptr, T_size_t } },
+[__NR_ia32_fgetxattr]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_listxattr]		= { 3, { T_path, T_any_ptr, T_size_t } },
+[__NR_ia32_llistxattr]		= { 3, { T_path_parent, T_any_ptr, T_size_t } },
+[__NR_ia32_flistxattr]		= { 3, { T_filedesc, T_any_ptr, T_size_t } },
+[__NR_ia32_removexattr]		= { 2, { T_path, T_string } },
+[__NR_ia32_lremovexattr]	= { 2, { T_path_parent, T_string } },
+[__NR_ia32_fremovexattr]	= { 2, { T_filedesc, T_string } },
+
+[__NR_ia32_socketcall]	= { 2, { T_int, T_any_ptr } },
+
+[__NR_ia32_ipc]		= { 6, { T_int, T_long, T_long, T_long, T_long, T_long } },
+
+[__NR_ia32_ioctl]	= { 3, { T_filedesc, T_int, T_any_ptr } },
+
+};
+
+/*
+ * Given the set of registers, extract the syscall code and
+ * arguments array
+ */
+int
+audit_get_args(struct pt_regs *regs, struct aud_syscall_data *sc)
+{
+	struct sysent	*entry;
+	int		code;
+	int		ia32_thread = 0;
+	int 		nr_syscalls = NR_syscalls;
+
+	if (current->thread.flags & THREAD_IA32) {
+		ia32_thread = 1;
+		nr_syscalls = IA32_NR_syscalls;
+	}
+
+	sc->entry = NULL;
+	sc->major = 0;
+	sc->minor = 0;
+
+	if (regs == NULL)
+		return -EPERM;
+
+	code = regs->orig_rax;
+
+	/* XXX should we try to log calls to invalid syscalls? */
+	if (code < 0 || code >= nr_syscalls)
+		return -ENOSYS;
+
+	if (audit_policy_ignore(code))
+		return 0;
+
+	if (ia32_thread) {
+		sc->arch = AUDIT_ARCH_I386;
+		entry = &linux_sysent_ia32[code];
+	} else {
+		sc->arch = AUDIT_ARCH_X86_64;
+		entry = &linux_sysent[code];
+	}
+
+	switch (entry->sy_narg) {
+	case 6:	sc->raw_args[5] = regs->r9;  /* correct? */
+	case 5:	sc->raw_args[4] = regs->r8;
+	case 4: sc->raw_args[3] = regs->r10;
+	case 3: sc->raw_args[2] = regs->rdx;
+	case 2: sc->raw_args[1] = regs->rsi;
+	case 1: sc->raw_args[0] = regs->rdi;
+	case 0: break;
+	default:
+		printk("audit: invalid argument count?!\n");
+		BUG();
+	}
+
+	sc->major = code;
+	sc->entry = entry;
+
+	/* Special treatment for special functions */
+
+	if (ia32_thread) {
+		switch (code) {
+		case __NR_ia32_truncate64:
+		case __NR_ia32_ftruncate64:
+			/* 64bit values are actually passed as two 32bit
+		 	* registers, lower one first */
+			sc->raw_args[1] |= ((u_int64_t) regs->rdx) << 32;
+			break;
+
+/* the audit_get_[socket|ipc]args() are ifdef'ed in syscall.c */
+#if 0
+		case __NR_ia32_socketcall:
+			return audit_get_socketargs(sc);
+		case __NR_ia32_ipc:
+			return audit_get_ipcargs(sc);
+#endif
+		}
+	}
+
+	if (code == __NR_ioctl || code == __NR_ia32_ioctl)
+		return audit_get_ioctlargs(sc);
+
+	return 0;
+}
+
+/*
+ * Update system call register
+ */
+int
+audit_update_arg(struct aud_syscall_data *sc, unsigned int n, unsigned long newval)
+{
+	struct pt_regs	*regs = sc->regs;
+	int		code = sc->major;
+
+	if (current->thread.flags & THREAD_IA32) {
+		if ( code == __NR_ia32_ioctl 
+		     ||  code == __NR_ia32_socketcall
+		     ||  code == __NR_ia32_ipc)
+			return -EINVAL;
+	} else {
+		if ( code == __NR_ioctl )
+			return -EINVAL;
+	}
+
+	if (n > 5)
+		return -EINVAL;
+
+	sc->raw_args[n] = newval;
+	switch (n) {
+	case 5: regs->r9  = newval; break;
+	case 4: regs->r8  = newval; break;
+	case 3: regs->r10 = newval; break;
+	case 2: regs->rdx = newval; break;
+	case 1: regs->rsi = newval; break;
+	case 0: regs->rdi = newval; break;
+	}
+
+	return 0;
+}
+
+/*
+ * Get the return value of a system call
+ */
+long
+audit_get_result(struct pt_regs *regs)
+{
+	return regs->rax;
+}
diff -Naur linux-2.4.21/drivers/audit/syscall.c audit/drivers/audit/syscall.c
--- linux-2.4.21/drivers/audit/syscall.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/syscall.c	2003-09-30 11:47:01.000000000 +0200
@@ -0,0 +1,621 @@
+/*
+ * syscall.c
+ *
+ * Common system call information for the audit daemon.
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ * Written by okir@suse.de.
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sys.h>
+#include <linux/utsname.h>
+#include <linux/utime.h>
+#include <linux/sysctl.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/if_frad.h>
+#include <linux/route.h>
+
+#include <linux/ipc.h>
+#include <linux/msg.h>
+#include <linux/shm.h>
+#include <asm/ipc.h>
+
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/semaphore.h>
+#include <asm/unistd.h>
+
+#include <linux/audit.h>
+#include "audit-private.h"
+
+#define MAX_SOCKETCALL	20
+#define MAX_IPCCALL	24
+
+/*
+ * Maximum of bytes we copy when logging ioctl's third argument
+ */
+#define MAX_IOCTL_COPY	256
+
+/*
+ * Note - using [__NR_xxx] to initialize the array makes us more
+ * hardware independent. Platform specific syscalls can easily
+ * be included in #ifdef __NR_foobar/#endif.
+ * The only remaining problem is platforms with more than one
+ * exec domain by default.
+ */
+#define f(name, args...) [__NR_##name] = { 0, { args , { AUDIT_ARG_END } } }
+
+#define T_void		{ AUDIT_ARG_END }
+#define T_immediate(T)	{ AUDIT_ARG_IMMEDIATE, sizeof(T) }
+#define T_signedimm(T)	{ AUDIT_ARG_IMMEDIATE, sizeof(T), .sa_flags = AUD_ARG_SIGNED }
+#define T_pointer(T)	{ AUDIT_ARG_POINTER, sizeof(T) }
+#define T_pointer_rw(T)	{ AUDIT_ARG_POINTER, sizeof(T), .sa_flags = AUD_ARG_INOUT }
+#define T_string	{ AUDIT_ARG_STRING }
+#define T_path		{ AUDIT_ARG_PATH }
+#define T_path_parent	{ AUDIT_ARG_PATH, .sa_flags = AUD_ARG_DIRNAME }
+#define T_filedesc	{ AUDIT_ARG_FILEDESC }
+#define T_int		T_signedimm(int)
+#define T_uint		T_immediate(int)
+#define T_long		T_signedimm(long)
+#define T_ulong		T_immediate(long)
+#define T_off_t		T_signedimm(off_t)
+#define T_loff_t	T_signedimm(loff_t)
+#define T_mode_t	T_immediate(mode_t)
+#define T_size_t	T_immediate(size_t)
+#define T_dev_t		T_immediate(dev_t)
+#define T_pid_t		T_signedimm(pid_t)
+#define T_uid_t		T_immediate(uid_t)
+#define T_gid_t		T_immediate(gid_t)
+#define T_u16_t		{ AUDIT_ARG_IMMEDIATE, 2 }
+#define T_u32_t		{ AUDIT_ARG_IMMEDIATE, 4 }
+#define T_u64_t		{ AUDIT_ARG_IMMEDIATE, 8 }
+#define T_any_ptr	{ AUDIT_ARG_POINTER, 0 }
+#define T_timeval_t	T_pointer(struct timeval)
+#define T_timezone_t	T_pointer(struct timezone)
+#define T_caphdr_t	T_pointer(struct __user_cap_header_struct)
+#define T_capdata_t	T_pointer(struct __user_cap_data_struct)
+#define T_sysctl_t	T_pointer(struct __sysctl_args)
+#define T_rlimit_t	T_pointer(struct rlimit)
+#define T_socklen_t	T_immediate(socklen_t)
+#define T_sigset_t	T_pointer(sigset_t)
+#define socklen_t	int	/* socklen_t is a user land thing */
+#define T_array(itype, index, max) \
+			{ AUDIT_ARG_ARRAY, sizeof(itype), index, max }
+#define T_opaque_t(idx)	{ AUDIT_ARG_ARRAY, 1, idx+1, 256 }
+#define T_argv		{ AUDIT_ARG_VECTOR, sizeof(char *), .sa_ref = AUDIT_ARG_STRING }
+
+struct sysent linux_sysent[NR_syscalls+1] = {
+f(fork,		T_void),
+f(vfork,	T_void),
+f(clone,	T_uint),
+f(execve,	T_path, T_argv, T_any_ptr),
+f(exit,		T_int),
+f(ptrace,	T_uint, T_pid_t, T_any_ptr, T_any_ptr),
+f(uselib,	T_path),
+f(kill,		T_pid_t, T_int),
+f(tkill,	T_pid_t, T_int),
+
+/*
+ * Calls related to process privilege
+ */
+#ifdef __NR_setuid32
+f(setuid,	T_u16_t),
+f(setgid,	T_u16_t),
+f(setreuid,	T_u16_t, T_u16_t),
+f(setregid,	T_u16_t, T_u16_t),
+f(setresuid,	T_u16_t, T_u16_t, T_u16_t),
+f(setresgid,	T_u16_t, T_u16_t, T_u16_t),
+f(setfsuid,	T_u16_t),
+f(setfsgid,	T_u16_t),
+f(setgroups,	T_size_t, T_array(__u16, 0, NGROUPS)),
+f(setuid32,	T_uid_t),
+f(setgid32,	T_gid_t),
+f(setreuid32,	T_uid_t, T_uid_t),
+f(setregid32,	T_gid_t, T_gid_t),
+f(setresuid32,	T_uid_t, T_uid_t, T_uid_t),
+f(setresgid32,	T_gid_t, T_gid_t, T_gid_t),
+f(setfsuid32,	T_uid_t),
+f(setfsgid32,	T_uid_t),
+f(setgroups32,	T_size_t, T_array(gid_t, 0, NGROUPS)),
+#else
+/* architectures where uids and gids have been 32 bits all along */
+f(setuid,	T_uid_t),
+f(setgid,	T_gid_t),
+f(setreuid,	T_uid_t, T_uid_t),
+f(setregid,	T_gid_t, T_gid_t),
+f(setresuid,	T_uid_t, T_uid_t, T_uid_t),
+f(setresgid,	T_gid_t, T_gid_t, T_gid_t),
+f(setfsuid,	T_uid_t),
+f(setfsgid,	T_uid_t),
+f(setgroups,	T_size_t, T_array(gid_t, 0, NGROUPS)),
+#endif
+f(capset,	T_caphdr_t, T_capdata_t),
+
+/*
+ * Other per-process state
+ */
+f(umask,	T_mode_t),
+f(chroot,	T_path),
+f(chdir,	T_path),
+f(fchdir,	T_filedesc),
+f(setrlimit,	T_int, T_rlimit_t),
+f(setpriority,	T_int, T_int),
+f(sched_setaffinity, T_pid_t, T_int, T_pointer(long)),
+f(sched_setparam, T_pid_t, T_pointer(struct sched_param)),
+f(sched_setscheduler, T_pid_t, T_int, T_pointer(struct sched_param)),
+f(brk,		T_any_ptr),
+#ifdef __NR_signal
+f(signal,	T_int, T_any_ptr),
+#endif
+#ifdef __NR_sigaction
+f(sigaction,	T_int, T_pointer(struct sigaction), T_any_ptr),
+f(sigprocmask,	T_int, T_sigset_t, T_any_ptr),
+f(sigpending,	T_any_ptr),
+f(sigsuspend,	T_pointer(sigset_t)),
+f(sigreturn,	T_long),
+#endif
+f(sigaltstack,	T_pointer(stack_t), T_any_ptr),
+f(rt_sigaction,	T_int, T_pointer(struct sigaction), T_any_ptr),
+f(rt_sigprocmask, T_int, T_sigset_t, T_any_ptr),
+f(rt_sigpending,T_any_ptr),
+f(rt_sigqueueinfo,T_void),
+f(rt_sigreturn,	T_long),
+f(rt_sigsuspend,T_pointer(sigset_t)),
+f(rt_sigtimedwait,T_sigset_t, T_any_ptr, T_pointer(struct timespec)),
+f(setitimer,	T_int, T_any_ptr, T_any_ptr),
+f(setpgid,	T_pid_t, T_pid_t),
+f(setsid,	T_void),
+
+
+/*
+ * Calls related to global machine state
+ */
+f(settimeofday,	T_timeval_t, T_timezone_t),
+f(adjtimex,	T_pointer_rw(struct timex)),
+#ifdef __NR_stime
+f(stime,	T_pointer(int)),
+#endif
+f(_sysctl,	T_sysctl_t),
+f(sethostname,	T_array(char, 1, 256), T_size_t),
+f(setdomainname, T_array(char, 1, __NEW_UTS_LEN), T_size_t),
+f(reboot,	T_int, T_int, T_int, T_any_ptr),
+f(create_module,T_string, T_size_t),
+f(init_module,	T_string, T_any_ptr),
+f(query_module,	T_string, T_int, T_any_ptr, T_size_t, T_any_ptr),
+f(delete_module,T_string),
+f(mount,	T_string, T_path, T_string, T_long, T_any_ptr),
+#ifdef __NR_umount
+f(umount,	T_path),
+#endif
+f(umount2,	T_path, T_int),
+f(swapon,	T_path, T_int),
+f(swapoff,	T_path),
+#ifdef __NR_ioperm
+f(ioperm,	T_long, T_long, T_int),
+#endif
+#ifdef __NR_iopl
+f(iopl,		T_int),
+#endif
+f(syslog,	T_int, T_any_ptr, T_int),
+#ifdef __NR_pciconfig_write
+f(pciconfig_read, T_ulong, T_ulong, T_ulong, T_ulong, T_any_ptr),
+f(pciconfig_write, T_ulong, T_ulong, T_ulong, T_ulong, T_any_ptr),
+f(pciconfig_iobase, T_long, T_ulong, T_ulong),
+#endif
+
+/*
+ * File system operations
+ */
+f(open,		T_path, T_int, T_mode_t),
+f(read,		T_filedesc, T_any_ptr, T_size_t),
+f(write,	T_filedesc, T_any_ptr, T_size_t),
+f(close,	T_filedesc),
+#ifdef __NR_readv
+f(readv,	T_filedesc, T_any_ptr, T_size_t),
+f(writev,	T_filedesc, T_any_ptr, T_size_t),
+#endif
+#ifdef __NR_readdir
+f(readdir,	T_filedesc, T_any_ptr, T_size_t),
+#endif
+#ifdef __NR_sendfile
+f(sendfile,	T_filedesc, T_filedesc, T_pointer(off_t), T_size_t),
+#endif
+
+f(access,	T_path, T_int),
+f(creat,	T_path, T_mode_t),
+f(mkdir,	T_path_parent, T_mode_t),
+f(mknod,	T_path_parent, T_mode_t, T_dev_t),
+f(link,		T_path_parent, T_path_parent),
+f(symlink,	T_string, T_path_parent),
+f(rename,	T_path_parent, T_path_parent),
+f(unlink,	T_path_parent),
+f(rmdir,	T_path_parent),
+f(utime,	T_path, T_pointer(struct utimbuf)),
+f(chmod,	T_path, T_mode_t),
+#ifdef __NR_chown32
+f(chown,	T_path, T_u16_t, T_u16_t),
+f(chown32,	T_path, T_uid_t, T_gid_t),
+f(lchown,	T_path_parent, T_u16_t, T_u16_t),
+f(lchown32,	T_path_parent, T_uid_t, T_gid_t),
+f(fchown,	T_filedesc, T_u16_t, T_u16_t),
+f(fchown32,	T_filedesc, T_uid_t, T_gid_t),
+f(fchmod,	T_filedesc, T_mode_t),
+#else
+f(chown,	T_path, T_uid_t, T_uid_t),
+f(lchown,	T_path_parent, T_uid_t, T_uid_t),
+f(fchown,	T_filedesc, T_uid_t, T_uid_t),
+f(fchmod,	T_filedesc, T_mode_t),
+#endif
+f(truncate,	T_path, T_size_t),
+#ifdef __NR_truncate64
+f(truncate64,	T_path, T_u64_t),
+#endif
+f(ftruncate,	T_filedesc, T_size_t),
+#ifdef __NR_ftruncate64
+f(ftruncate64,	T_filedesc, T_u64_t),
+#endif
+f(setxattr,	T_path, T_string, T_array(char, 3, 2046), T_size_t, T_int),
+f(lsetxattr,	T_path_parent, T_string, T_array(char, 3, 2046), T_size_t, T_int),
+f(fsetxattr,	T_filedesc, T_string, T_array(char, 3, 2046), T_size_t, T_int),
+f(getxattr,	T_path, T_string, T_any_ptr, T_size_t),
+f(lgetxattr,	T_path_parent, T_string, T_any_ptr, T_size_t),
+f(fgetxattr,	T_filedesc, T_string, T_any_ptr, T_size_t),
+f(listxattr,	T_path, T_any_ptr, T_size_t),
+f(llistxattr,	T_path_parent, T_any_ptr, T_size_t),
+f(flistxattr,	T_filedesc, T_any_ptr, T_size_t),
+f(removexattr,	T_path, T_string),
+f(lremovexattr,	T_path_parent, T_string),
+f(fremovexattr,	T_filedesc, T_string),
+
+/*
+ * Network stuff
+ */
+#ifdef __NR_socketcall
+f(socketcall,	T_int, T_any_ptr),
+#else
+f(socket,	T_int, T_int, T_int),
+f(connect,	T_filedesc, T_opaque_t(2), T_socklen_t),
+f(accept,	T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+f(sendto,	T_filedesc, T_any_ptr, T_size_t, T_int, T_opaque_t(5), T_socklen_t),
+f(recvfrom,	T_filedesc, T_any_ptr, T_size_t, T_int, T_any_ptr, T_pointer(socklen_t)),
+f(sendmsg,	T_filedesc, T_pointer(struct msghdr), T_int),
+f(recvmsg,	T_filedesc, T_pointer(struct msghdr), T_int),
+f(shutdown,	T_filedesc, T_int),
+f(bind,		T_filedesc, T_opaque_t(2), T_socklen_t),
+f(listen,	T_filedesc, T_int),
+f(getsockname,	T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+f(getpeername,	T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+f(socketpair,	T_int, T_int, T_int, T_any_ptr),
+f(setsockopt,	T_filedesc, T_int, T_int, T_opaque_t(4), T_socklen_t),
+f(getsockopt,	T_filedesc, T_int, T_int, T_opaque_t(4), T_socklen_t),
+#endif
+
+/*
+ * SysV IPC
+ */
+#ifdef __NR_ipc
+f(ipc,		T_int, T_long, T_long, T_long, T_long, T_long),
+#else
+f(shmget,	T_int, T_int, T_int),
+f(shmat,	T_int, T_any_ptr, T_int, T_pointer(unsigned long)),
+f(shmdt,	T_any_ptr),
+f(shmctl,	T_int, T_int, T_int, T_any_ptr),
+f(semget,	T_int, T_int, T_int),
+f(semop,	T_int, T_array(struct sembuf, 2, SEMOPM), T_int),
+f(semctl,	T_int, T_int, T_pointer(struct shmid_ds)),
+f(msgget,	T_int, T_int),
+f(msgsnd,	T_int, T_opaque_t(2), T_size_t, T_int),
+f(msgrcv,	T_int, T_opaque_t(2), T_size_t, T_long, T_int),
+f(msgctl,	T_int, T_int, T_pointer(struct msqid_ds)),
+#endif
+
+/*
+ * ioctl.
+ * The third ioctl argument is frobbed in audit_get_ioctlargs below
+ */
+f(ioctl,	T_filedesc, T_uint, T_any_ptr),
+
+};
+
+/*
+ * Initialize system call tables
+ */
+void
+audit_init_syscall_table(void)
+{
+	unsigned int m, n;
+
+	/* Loop over list of syscalls and fill in the number of
+	 * arguments */
+	for (m = 0; m < NR_syscalls; m++) {
+		struct sysent *entry = &linux_sysent[m];
+
+		for (n = 0; n < AUDIT_MAXARGS; n++) {
+			if (entry->sy_args[n].sa_type == AUDIT_ARG_END)
+				break;
+		}
+		entry->sy_narg = n;
+	}
+}
+
+/*
+ * Get syscall information
+ */
+struct sysent *
+audit_get_syscall_entry(int code)
+{
+	if (code < 0 || code >= NR_syscalls)
+		return NULL;
+
+	return &linux_sysent[code];
+}
+
+
+#define sc(N, args...)	{ (N), { args } }
+
+/*
+ * Architecture specific, but used by more than one architecture
+ *
+ * First, handle sys_socketcall
+ *
+ * The system call is
+ *	socketcall(int cmd, long *args)
+ * where the number of arguments pointed to by args is implicit.
+ * To make tracing socketcalls possible, we break up the argument
+ * array (at least for those calls we understand).
+ * The array below defines the arguments for all these socketcalls.
+ * Note that the first argument (i.e. #cmd) remains unchanged; the
+ * contents of the #args array are pasted after that.
+ */
+#ifdef __NR_socketcall
+struct sysent	socketcall_sysent[MAX_SOCKETCALL] = {
+[SYS_SOCKET]		= sc(3, T_int, T_int, T_int),
+[SYS_BIND]		= sc(3, T_filedesc, T_opaque_t(2), T_socklen_t),
+[SYS_CONNECT]		= sc(3, T_filedesc, T_opaque_t(2), T_socklen_t),
+[SYS_LISTEN]		= sc(2, T_filedesc, T_int),
+[SYS_ACCEPT]		= sc(3, T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+[SYS_GETSOCKNAME]	= sc(3, T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+[SYS_GETPEERNAME]	= sc(3, T_filedesc, T_any_ptr, T_pointer(socklen_t)),
+[SYS_SOCKETPAIR]	= sc(4, T_int, T_int, T_int, T_any_ptr),
+[SYS_SHUTDOWN]		= sc(2, T_filedesc, T_int),
+[SYS_SETSOCKOPT]	= sc(5, T_filedesc, T_int, T_int,
+				T_opaque_t(4), T_socklen_t),
+[SYS_GETSOCKOPT]	= sc(5, T_filedesc, T_int, T_int,
+				T_any_ptr, T_pointer(socklen_t)),
+[SYS_SEND]		= sc(4, T_filedesc, T_any_ptr, T_size_t, T_int),
+[SYS_RECV]		= sc(4, T_filedesc, T_any_ptr, T_size_t, T_int),
+[SYS_SENDTO]		= sc(4, T_filedesc, T_any_ptr, T_size_t, T_int,
+				T_opaque_t(5), T_socklen_t),
+[SYS_RECVFROM]		= sc(4, T_filedesc, T_any_ptr, T_size_t, T_int,
+				T_any_ptr, T_pointer(socklen_t)),
+[SYS_SENDMSG]		= sc(3, T_filedesc, T_pointer(struct msghdr), T_int),
+[SYS_RECVMSG]		= sc(3, T_filedesc, T_pointer(struct msghdr), T_int),
+};
+
+int
+audit_get_socketargs(struct aud_syscall_data *sc)
+{
+	unsigned int	minor = sc->raw_args[0];
+	struct sysent	*entry;
+	unsigned int	n, argsize;
+	long		args[AUDIT_MAXARGS];
+
+	sc->minor = minor;
+	if (minor >= MAX_SOCKETCALL) {
+		/* XXX mark as invalid? */
+		return 0;
+	}
+
+	entry = &socketcall_sysent[minor];
+
+	argsize = entry->sy_narg * sizeof(long);
+	if (copy_from_user(args, (void *)(unsigned long) sc->raw_args[1], argsize))
+		return 0;
+
+	/* Can't memcpy here because raw_args is 64bit and args is a long,
+	 * which is not necessarily the same thing */
+	for (n = 0; n < entry->sy_narg; n++)
+		sc->raw_args[n] = args[n];
+	sc->entry = entry;
+
+	return 0;
+}
+#endif
+
+
+/*
+ * socketcall was almost too easy.
+ * Here comes sys_ipc.
+ */
+#ifdef __NR_ipc
+static int	ipc_reorder[MAX_IPCCALL+1][AUDIT_MAXARGS] = {
+[SEMOP]		= { 1, 4, 2 },
+[SEMGET]	= { 1, 2, 3 },
+[SEMCTL]	= { 1, 2, 3, 4 },
+[MSGSND]	= { 1, 4, 2, 3 },
+[MSGRCV]	= { 1, 4, 2, 5, 3 },
+[MSGGET]	= { 1, 2 },
+[MSGCTL]	= { 1, 2, 4 },
+[SHMAT]		= { 1, 4, 2, 3 },
+[SHMDT]		= { 4 },
+[SHMGET]	= { 1, 2, 3 },
+[SHMCTL]	= { 1, 2, 4 },
+};
+
+struct sysent	ipccall_sysent[MAX_IPCCALL+1] = {
+[SEMOP]			= sc(3, T_int, T_array(struct sembuf, 2, SEMOPM), T_int),
+[SEMGET]		= sc(3, T_int, T_int, T_int),
+[SEMCTL]		= sc(4, T_int, T_int, T_int, T_any_ptr),
+[MSGSND]		= sc(4, T_int, T_opaque_t(2), T_size_t, T_int),
+[MSGRCV]		= sc(5, T_int, T_opaque_t(2), T_size_t, T_long, T_int),
+[MSGGET]		= sc(2, T_int, T_int),
+[MSGCTL]		= sc(3, T_int, T_int, T_pointer(struct msqid_ds)),
+[SHMGET]		= sc(3, T_int, T_int, T_int),
+[SHMAT]			= sc(4, T_int, T_any_ptr, T_int, T_pointer(unsigned long)),
+[SHMDT]			= sc(1, T_any_ptr),
+[SHMCTL]		= sc(3, T_int, T_int, T_pointer(struct shmid_ds)),
+};
+
+int
+audit_get_ipcargs(struct aud_syscall_data *sc)
+{
+	unsigned int	minor = sc->raw_args[0];
+	struct sysent	*entry;
+	unsigned int	n, nargs = 0, version;
+	int		*reorder;
+	long		args[AUDIT_MAXARGS];
+
+	/* i386 sys_ipc has a syscall API version encoded in
+	   the top 16 bits */
+	version = minor >> 16;
+	minor &= 0xffff;
+
+	sc->minor = minor;
+	if (minor > MAX_IPCCALL) {
+		/* XXX mark as invalid? */
+		return 0;
+	}
+
+	/* Very special cases */
+	if (minor == MSGRCV && version == 0) {
+		/* backward compat, TBD */
+		return 0;
+	} else if (minor == SHMAT && version == 1) {
+		/* iBCS2 emulator, TBD */
+		return 0;
+	} else  {
+		reorder = ipc_reorder[minor];
+		while (nargs < AUDIT_MAXARGS && reorder[nargs]) {
+			args[nargs] = sc->raw_args[reorder[nargs]];
+			nargs++;
+		}
+	}
+
+	for (n = 0; n < nargs; n++)
+		sc->raw_args[n] = args[n];
+
+	entry = &ipccall_sysent[minor];
+	BUG_ON(entry->sy_narg != nargs);
+
+	sc->entry = entry;
+	return 0;
+}
+#endif
+
+/*
+ * Get ioctl arguments (well, basically we want the third argument's size,
+ * and whether it's a read or write ioctl
+ */
+static struct ioctl_info {
+	int		cmd;
+	size_t		size;
+} ioctl_info[] = {
+      { SIOCSIFNAME,		sizeof(struct ifreq),	},
+
+      { SIOCSIFFLAGS,		sizeof(struct ifreq),	},
+      { SIOCSIFADDR,		sizeof(struct ifreq),	},
+      { SIOCSIFDSTADDR,		sizeof(struct ifreq),	},
+      { SIOCSIFBRDADDR,		sizeof(struct ifreq),	},
+      { SIOCSIFNETMASK,		sizeof(struct ifreq),	},
+      { SIOCSIFMETRIC,		sizeof(struct ifreq),	},
+      { SIOCSIFMTU,		sizeof(struct ifreq),	},
+      { SIOCADDMULTI,		sizeof(struct ifreq),	},
+      { SIOCDELMULTI,		sizeof(struct ifreq),	},
+      {	SIOCADDRT,		sizeof(struct rtentry),	},
+      {	SIOCDELRT,		sizeof(struct rtentry),	},
+
+      { SIOCSIFHWADDR,		sizeof(struct ifreq),	},
+#ifdef SIOCSIFHWBROADCAST
+      { SIOCSIFHWBROADCAST,	sizeof(struct ifreq),	},
+#endif
+      { SIOCSIFMAP,		sizeof(struct ifreq),	},
+      { SIOCSIFMEM,		sizeof(struct ifreq),	},
+      { SIOCSIFENCAP,		sizeof(struct ifreq),	},
+      { SIOCSIFSLAVE,		sizeof(struct ifreq),	},
+      { SIOCSIFPFLAGS,		sizeof(struct ifreq),	},
+      { SIOCDIFADDR,		sizeof(struct ifreq),	},
+      { SIOCSIFBR,		3 * sizeof(long),	},
+      { SIOCGIFBR,		3 * sizeof(long),	},
+
+      { SIOCSARP,		sizeof(struct arpreq)	},
+      { SIOCDARP,		sizeof(struct arpreq)	},
+
+      /* SIOCDRARP, SIOCSRARP obsolete */
+#ifdef CONFIG_DLCI
+      { SIOCADDDLCI,		sizeof(struct dlci_add)	},
+      { SIOCDELDLCI,		sizeof(struct dlci_add)	},
+#endif
+
+      /* SIOCSIFLINK obsolete? */
+      { SIOCSIFLINK,		0,			},
+      { SIOCSIFTXQLEN,		sizeof(struct ifreq),	},
+      { SIOCBONDENSLAVE,	sizeof(struct ifreq),	},
+      { SIOCBONDRELEASE,	sizeof(struct ifreq),	},
+      { SIOCBONDSETHWADDR,	sizeof(struct ifreq),	},
+      { SIOCBONDCHANGEACTIVE,	sizeof(struct ifreq),	},
+      { SIOCETHTOOL,		sizeof(struct ifreq),	},
+      { SIOCSMIIREG,		sizeof(struct ifreq),	},
+
+      { -1, 0 },
+};
+
+int
+audit_get_ioctlargs(struct aud_syscall_data *sc)
+{
+	struct ioctl_info	*iop;
+	int			cmd;
+	void			*arg, *p;
+	struct sysarg_data	*tgt;
+	size_t			len = 0;
+
+	cmd = sc->raw_args[1];
+	arg = (void *) (unsigned long) sc->raw_args[2];
+	tgt = &sc->args[2];
+
+	if (arg == NULL)
+		return 0;
+
+	for (iop = ioctl_info; iop->cmd >= 0; iop++) {
+		if (iop->cmd == cmd) {
+			len = iop->size;
+			break;
+		}
+	}
+
+	if (len == 0 && (_IOC_DIR(cmd) & _IOC_WRITE)) 
+		len = _IOC_SIZE(cmd);
+
+	if (len != 0 && len < MAX_IOCTL_COPY) {
+		if ((p = kmalloc(len, GFP_USER)) == NULL)
+			return -ENOBUFS;
+		if (copy_from_user(p, arg, len)) {
+			kfree(p);
+			return -EFAULT;
+		}
+		tgt->at_type = AUDIT_ARG_POINTER;
+		tgt->at_data.ptr = p;
+		tgt->at_data.len = len;
+	}
+	return 0;
+}
diff -Naur linux-2.4.21/drivers/audit/sysctl.c audit/drivers/audit/sysctl.c
--- linux-2.4.21/drivers/audit/sysctl.c	1970-01-01 01:00:00.000000000 +0100
+++ audit/drivers/audit/sysctl.c	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,105 @@
+/*
+ * sysctl.c
+ *
+ * Linux Audit Subsystem, handle sysctl's.
+ *
+ * Copyright (C) 2003 SuSE Linux AG
+ *
+ * Written by okir@suse.de, based on ideas from systrace, by
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#if 0
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/sys.h>
+#include <linux/miscdevice.h>
+#include <linux/personality.h>
+#include <linux/poll.h>
+#include <linux/skbuff.h>
+#include <linux/netlink.h>
+#include <linux/unistd.h>
+#include <linux/audit.h>
+
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <asm/ptrace.h>
+#endif
+
+#include <linux/sysctl.h>
+#include <linux/audit.h>
+
+#include "audit-private.h"
+
+static ctl_table	audit_table[] = {
+	{ 1, "attach-all",
+	  &audit_all_processes,
+	  sizeof(audit_all_processes),
+	  0600, NULL, &proc_dointvec, NULL, },
+	{ 2, "allow-suspend",
+	  &audit_allow_suspend,
+	  sizeof(audit_allow_suspend),
+	  0600, NULL,  &proc_dointvec, NULL, },
+	{ 3, "max-messages",
+	  &audit_max_messages,
+	  sizeof(audit_max_messages),
+	  0600, NULL,  &proc_dointvec, NULL, },
+	{ 4, "paranoia",
+	  &audit_paranoia,
+	  sizeof(audit_paranoia),
+	  0600, NULL,  &proc_dointvec, NULL, },
+	{ 5, "debug",
+	  &audit_debug,
+	  sizeof(audit_debug),
+	  0600, NULL,  &proc_dointvec, NULL, },
+	{ 0 },
+};
+
+static ctl_table	audit_root[] = {
+	{ 1, "audit", NULL, 0, 0555, audit_table },
+	{ 0 }
+};
+
+static ctl_table	dev_root[] = {
+	{ CTL_KERN, "dev", NULL, 0, 0555, audit_root },
+	{ 0 }
+};
+
+static struct ctl_table_header *	sysctl_header = NULL;
+
+int
+audit_sysctl_register(void)
+{
+	sysctl_header = register_sysctl_table(dev_root, 0);
+	return 0;
+}
+
+void
+audit_sysctl_unregister(void)
+{
+	if (sysctl_header)
+		unregister_sysctl_table(sysctl_header);
+	sysctl_header = NULL;
+}
diff -Naur linux-2.4.21/include/linux/audit.h audit/include/linux/audit.h
--- linux-2.4.21/include/linux/audit.h	1970-01-01 01:00:00.000000000 +0100
+++ audit/include/linux/audit.h	2003-09-30 11:46:39.000000000 +0200
@@ -0,0 +1,338 @@
+/*
+ * audit.h
+ *
+ * Copyright (c) 2003 SuSE Linux AG
+ * Written by okir@suse.de, based on ideas from systrace, written by
+ * Niels Provos (OpenBSD) and ported to Linux by Marius Aamodt Eriksen.
+ *
+ * GPL goes here
+ */
+
+#ifndef _AUDIT_H
+#define _AUDIT_H
+
+#include <linux/limits.h>
+#include <linux/sys.h>
+
+#ifdef __KERNEL__
+#include <asm/semaphore.h>
+#endif
+
+#define AUDIT_API_VERSION	0x20030801
+
+#define AUD_MAX_HOSTNAME	256
+#define AUD_MAX_ADDRESS		256
+#define AUD_MAX_TERMINAL	256
+#define AUD_MAX_EVNAME		16
+
+/*
+ * System call intercept policy
+ */
+struct audit_policy {
+	unsigned int	code;
+	unsigned int	action;
+	unsigned int	filter;
+};
+
+#define AUDIT_IGNORE		0x0000
+#define AUDIT_LOG		0x0001
+/* Policy flags that can be set in filter rules using
+ * the return() predicate
+ */
+#define AUDIT_VERBOSE		0x0002
+
+#ifdef __KERNEL__
+#define AUDIT_RETURN		0x0100
+#define AUDIT_INVAL		0x0301
+#endif
+
+
+/*
+ * Special values for audit_policy.code
+ */
+enum {
+	__AUD_POLICY_LAST_SYSCALL = 299,
+	AUD_POLICY_FORK,
+	AUD_POLICY_EXIT,
+	AUD_POLICY_NETLINK,
+	AUD_POLICY_LOGIN,
+	AUD_POLICY_USERMSG,
+
+	__AUD_MAX_POLICY
+};
+
+/*
+ * Filter setup.
+ */
+struct audit_filter {
+	unsigned short	num;
+	unsigned short	op;
+	char		event[AUD_MAX_EVNAME];
+	union {
+	    struct {
+		unsigned short	target;
+		unsigned short	filter;
+	    } apply;
+	    struct {
+		unsigned short	filt1, filt2;
+	    } bool;
+	    struct {
+		unsigned int	action;
+	    } freturn;
+	    struct {
+		u_int64_t	value;
+		u_int64_t	mask;
+	    } integer;
+	    struct {
+		char *		value;
+	    } string;
+	} u;
+};
+
+enum {
+	/* Boolean operations */
+	AUD_FILT_OP_AND = 0,		/* pair of filters */
+	AUD_FILT_OP_OR,			/* pair of filters */
+	AUD_FILT_OP_NOT,		/* single filter */
+	AUD_FILT_OP_APPLY,		/* target + predicate filter */
+	AUD_FILT_OP_RETURN,		/* return immediately */
+	AUD_FILT_OP_TRUE,		/* always true */
+	AUD_FILT_OP_FALSE,		/* always false */
+
+	/* Filter predicates, taking one argument */
+	AUD_FILT_OP_EQ = 0x10,		/* int */
+	AUD_FILT_OP_NE,			/* int */
+	AUD_FILT_OP_GT,			/* int */
+	AUD_FILT_OP_GE,			/* int */
+	AUD_FILT_OP_LE,			/* int */
+	AUD_FILT_OP_LT,			/* int */
+	AUD_FILT_OP_MASK,		/* int */
+	AUD_FILT_OP_STREQ = 0x20,	/* string */
+	AUD_FILT_OP_PREFIX,		/* path */
+};
+#define AUD_FILT_ARGTYPE_INT(op)	(((op) >> 4) == 1)
+#define AUD_FILT_ARGTYPE_STR(op)	(((op) >> 4) == 2)
+
+enum {
+	/* target values < 128 denote syscall arguments 0 .. 127
+	 * (in case anyone ever comes up with a system call
+	 * taking 127 arguments :)
+	 */
+	AUD_FILT_TGT_USERMSG_EVNAME = 0xFD,
+	AUD_FILT_TGT_MINOR_CODE = 0xFE,
+	AUD_FILT_TGT_RETURN_CODE = 0xFF,
+
+	AUD_FILT_TGT_UID = 0x100,
+	AUD_FILT_TGT_GID,
+	AUD_FILT_TGT_DUMPABLE,
+	AUD_FILT_TGT_EXIT_CODE,
+	AUD_FILT_TGT_LOGIN_UID,
+
+	AUD_FILT_TGT_FILE_MODE = 0x200,
+	AUD_FILT_TGT_FILE_DEV,
+	AUD_FILT_TGT_FILE_INO,
+	AUD_FILT_TGT_FILE_UID,
+	AUD_FILT_TGT_FILE_GID,
+	AUD_FILT_TGT_FILE_RDEV_MAJOR,
+	AUD_FILT_TGT_FILE_RDEV_MINOR,
+
+	AUD_FILT_TGT_SOCK_FAMILY = 0x300,
+	AUD_FILT_TGT_SOCK_TYPE,
+
+	AUD_FILT_TGT_NETLINK_TYPE = 0x400,
+	AUD_FILT_TGT_NETLINK_FLAGS,
+	AUD_FILT_TGT_NETLINK_FAMILY,
+};
+#define AUD_FILT_TGT_SYSCALL_ATTR(x)	(((x) >> 8) == 0)
+#define AUD_FILT_TGT_PROCESS_ATTR(x)	(((x) >> 8) == 1)
+#define AUD_FILT_TGT_FILE_ATTR(x)	(((x) >> 8) == 2)
+#define AUD_FILT_TGT_SOCK_ATTR(x)	(((x) >> 8) == 3)
+#define AUD_FILT_TGT_NETLINK_ATTR(x)	(((x) >> 8) == 4)
+
+
+/*
+ * Login data
+ */
+struct audit_login {
+	uid_t		uid;
+	char		hostname[AUD_MAX_HOSTNAME];
+	char		address[AUD_MAX_ADDRESS];
+	char		terminal[AUD_MAX_TERMINAL];
+};
+
+/*
+ * Message passing from user space
+ */
+struct audit_message {
+	unsigned int	msg_type;
+	char		msg_evname[AUD_MAX_EVNAME];
+	void *		msg_data;
+	size_t		msg_size;
+};
+
+/*
+ * IOCTLs to configure the audit subsystem
+ */
+#define AUD_MAGIC '@'
+
+/* The _IOR's are in fact wrong; they should be _IOW's :-( */
+#define AUIOCATTACH		_IO(AUD_MAGIC, 101)
+#define AUIOCDETACH		_IO(AUD_MAGIC, 102)
+#define AUIOCSUSPEND		_IO(AUD_MAGIC, 103)
+#define AUIOCRESUME		_IO(AUD_MAGIC, 104)
+#define AUIOCCLRPOLICY		_IO(AUD_MAGIC, 105)
+#define AUIOCSETPOLICY		_IOR(AUD_MAGIC, 106, struct audit_policy)
+#define AUIOCIAMAUDITD		_IO(AUD_MAGIC, 107)
+#define AUIOCSETAUDITID		_IO(AUD_MAGIC, 108)
+#define AUIOCLOGIN		_IOR(AUD_MAGIC, 110, struct audit_login)
+#define AUIOCUSERMESSAGE	_IOR(AUD_MAGIC, 111, struct audit_message)
+#define AUIOCCLRFILTER		_IO(AUD_MAGIC, 112)
+#define AUIOCSETFILTER		_IOR(AUD_MAGIC, 113, struct audit_filter)
+
+/* Pass as ioctl(fd, AUIOCIAMAUDITD, AUDIT_TRACE_ALL) */
+#define AUDIT_TRACE_ALL		1
+
+/*
+ * This message is generated when a process forks
+ * or exits, to help auditd with book-keeping.
+ */
+struct aud_msg_child {
+        pid_t			new_pid;
+};
+
+/*
+ * This message reports system call arguments.
+ *
+ * personality	execution domain (see linux/personality.h)
+ * code		the system call code
+ * result	return value of system call
+ * length	length of data field
+ * data field	contains all arguments, TLV encoded as follows:
+ *
+ *   type	4 octets	(AUD_ARG_xxx)
+ *   length	4 octets	length of argument
+ *   ...	N octets	argument data
+ *
+ * Note that path name arguments are subjected to a realpath()
+ * style operation prior to sending them up to user land.
+ */
+struct aud_msg_syscall {
+	int		personality;
+
+	/* System call codes can have major/minor number.
+	 * for instance in the socketcall() case, major
+	 * would be __NR_socketcall, and minor would be
+	 * SYS_ACCEPT (or whatever the specific call is).
+	 */
+	int		major, minor;
+
+	int		result;
+	unsigned int	length;
+	unsigned char	data[1];	/* variable size */
+};
+
+/*
+ * The LOGIN message is generated by the kernel when
+ * a user application performs an AUIOCLOGIN ioctl.
+ */
+struct aud_msg_login {
+	unsigned int	uid;
+	char		hostname[AUD_MAX_HOSTNAME];
+	char		address[AUD_MAX_ADDRESS];
+	char		terminal[AUD_MAX_TERMINAL];
+	char		executable[PATH_MAX];
+};
+
+/*
+ * Exit message
+ */
+struct aud_msg_exit {
+	long		code;
+};
+
+/*
+ * Network config (rtnetlink) call
+ */
+struct aud_msg_netlink {
+	unsigned int	groups, dst_groups;
+	int		result;
+	unsigned int	length;
+	unsigned char	data[1];	/* variable size */
+};
+
+/* Values for msg_type */
+#define AUDIT_MSG_LOGIN		1
+#define AUDIT_MSG_SYSCALL	2
+#define AUDIT_MSG_EXIT		3
+#define AUDIT_MSG_NETLINK	4
+#define AUDIT_MSG_USERBASE	256	/* user land messages start here */
+
+/* Values for msg_arch */
+enum {
+	AUDIT_ARCH_I386,
+	AUDIT_ARCH_PPC,
+	AUDIT_ARCH_PPC64,
+	AUDIT_ARCH_S390,
+	AUDIT_ARCH_S390X,
+	AUDIT_ARCH_X86_64,
+	AUDIT_ARCH_IA64,
+};
+
+
+struct aud_message {
+	u_int32_t	msg_seqnr;
+	u_int16_t	msg_type;
+	u_int16_t	msg_arch;	
+
+	pid_t		msg_pid;
+	size_t		msg_size;
+	unsigned long	msg_timestamp;
+
+	unsigned int	msg_audit_id;
+	unsigned int	msg_login_uid;
+	unsigned int	msg_euid, msg_ruid, msg_suid, msg_fsuid;
+	unsigned int	msg_egid, msg_rgid, msg_sgid, msg_fsgid;
+
+	/* Event name */
+	char		msg_evname[AUD_MAX_EVNAME];
+
+	union {
+		char	dummy;
+	} msg_data;
+};
+
+/*
+ * Encoding of arguments passed up to auditd
+ */
+enum {
+	/* value 0 is reserved */
+	AUDIT_ARG_IMMEDIATE = 1,
+	AUDIT_ARG_POINTER,
+	AUDIT_ARG_STRING,
+	AUDIT_ARG_PATH,
+	AUDIT_ARG_NULL,
+	AUDIT_ARG_ERROR,
+	AUDIT_ARG_VECTOR,	/* for execve */
+
+#ifdef __KERNEL__
+	/* Internal use only */
+	AUDIT_ARG_ARRAY = 100,
+	AUDIT_ARG_FILEDESC,
+#endif
+
+	AUDIT_ARG_END = 0
+};
+
+#ifdef __KERNEL__
+
+struct sk_buff;
+
+extern int  audit_intercept(struct pt_regs *);
+extern void audit_result(struct pt_regs *);
+extern void audit_fork(struct task_struct *, struct task_struct *);
+extern void audit_exit(struct task_struct *, long);
+extern void audit_netlink_msg(struct sk_buff *, int);
+
+#endif /* __KERNEL__ */
+
+#endif /* _AUDIT_H */
diff -Naur linux-2.4.21/include/linux/sched.h audit/include/linux/sched.h
--- linux-2.4.21/include/linux/sched.h	2003-09-30 11:44:31.000000000 +0200
+++ audit/include/linux/sched.h	2003-09-30 11:46:39.000000000 +0200
@@ -446,6 +446,10 @@
 
 /* TASK_UNMAPPED_BASE value */
 	unsigned long map_base;
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+        void *audit;
+#endif /* CONFIG_AUDIT */
 };
 
 /*
@@ -472,6 +476,7 @@
 #define PT_DTRACE	0x00000004	/* delayed trace (used on m68k, i386) */
 #define PT_TRACESYSGOOD	0x00000008
 #define PT_PTRACE_CAP	0x00000010	/* ptracer can follow suid-exec */
+#define PT_AUDITED	0x00000020	/* being audited */
 
 #define is_dumpable(tsk)    ((tsk)->task_dumpable && (tsk)->mm && (tsk)->mm->dumpable)
 
diff -Naur linux-2.4.21/kernel/exit.c audit/kernel/exit.c
--- linux-2.4.21/kernel/exit.c	2003-09-30 11:44:18.000000000 +0200
+++ audit/kernel/exit.c	2003-09-30 11:46:39.000000000 +0200
@@ -16,6 +16,9 @@
 #ifdef CONFIG_BSD_PROCESS_ACCT
 #include <linux/acct.h>
 #endif
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif
 
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
@@ -151,7 +154,7 @@
 	/* Set the exit signal to SIGCHLD so we signal init on exit */
 	current->exit_signal = SIGCHLD;
 
-	current->ptrace = 0;
+	current->ptrace &= PT_AUDITED;
 	if ((current->policy == SCHED_OTHER) && (task_nice(current) < 0))
 		set_user_nice(current, 0);
 	/* cpus_allowed? */
@@ -442,7 +445,7 @@
 		p = current->p_cptr;
 		current->p_cptr = p->p_osptr;
 		p->p_ysptr = NULL;
-		p->ptrace = 0;
+		p->ptrace &= PT_AUDITED;
 
 		p->p_pptr = p->p_opptr;
 		p->p_osptr = p->p_pptr->p_cptr;
@@ -498,6 +501,10 @@
 		current->tux_exit();
 	}
 #endif
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	if (tsk->audit)
+		audit_exit(tsk, code);
+#endif
 	__exit_mm(tsk);
 
         if (tng_exitfunc != NULL) {
diff -Naur linux-2.4.21/kernel/fork.c audit/kernel/fork.c
--- linux-2.4.21/kernel/fork.c	2003-09-30 11:44:34.000000000 +0200
+++ audit/kernel/fork.c	2003-09-30 11:46:39.000000000 +0200
@@ -31,6 +31,10 @@
 /* tng related changes */
 int (*tng_forkfunc)(struct task_struct *) = NULL ;
 
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+#include <linux/audit.h>
+#endif /* CONFIG_AUDIT */
+
 /* The idle threads do not count.. */
 int nr_threads;
 
@@ -671,7 +675,7 @@
 	new_flags &= ~(PF_SUPERPRIV | PF_USEDFPU);
 	new_flags |= PF_FORKNOEXEC;
 	if (!(clone_flags & CLONE_PTRACE))
-		p->ptrace = 0;
+		p->ptrace &= PT_AUDITED;
 	p->flags = new_flags;
 }
 
@@ -683,7 +687,7 @@
 
 	/* lock out any potential ptracer */
 	task_lock(task);
-	if (task->ptrace) {
+	if (task->ptrace & ~PT_AUDITED) {
 		task_unlock(task);
 		return -EPERM;
 	}
@@ -739,6 +743,9 @@
 #if defined(CONFIG_TUX) || defined(CONFIG_TUX_MODULE)
 	p->tux_info = NULL;
 #endif
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+	p->audit = NULL;
+#endif
 
 	retval = -EAGAIN;
 	/*
@@ -914,6 +921,11 @@
 		(*tng_forkfunc)((struct task_struct *) p) ;
 	}
 
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+        if (current->audit != NULL)
+              audit_fork(current, p);
+#endif /* CONFIG_AUDIT */
+
 	wake_up_forked_process(p);	/* do this last */
 	++total_forks;
 	if (clone_flags & CLONE_VFORK)
diff -Naur linux-2.4.21/kernel/ksyms.c audit/kernel/ksyms.c
--- linux-2.4.21/kernel/ksyms.c	2003-09-30 11:44:35.000000000 +0200
+++ audit/kernel/ksyms.c	2003-09-30 11:46:39.000000000 +0200
@@ -113,6 +113,7 @@
 EXPORT_SYMBOL(exit_fs);
 EXPORT_SYMBOL(exit_sighand);
 EXPORT_SYMBOL(unshare_files);
+EXPORT_SYMBOL(mmput);
 
 /* internal kernel memory management */
 EXPORT_SYMBOL(__alloc_pages);
diff -Naur linux-2.4.21/net/core/rtnetlink.c audit/net/core/rtnetlink.c
--- linux-2.4.21/net/core/rtnetlink.c	2003-09-30 11:44:21.000000000 +0200
+++ audit/net/core/rtnetlink.c	2003-09-30 11:46:39.000000000 +0200
@@ -408,6 +408,7 @@
 		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
 		if (rlen > skb->len)
 			rlen = skb->len;
+		err = 0;
 		if (rtnetlink_rcv_msg(skb, nlh, &err)) {
 			/* Not error, but we must interrupt processing here:
 			 *   Note, that in this case we do not pull message
@@ -418,6 +419,11 @@
 			netlink_ack(skb, nlh, err);
 		} else if (nlh->nlmsg_flags&NLM_F_ACK)
 			netlink_ack(skb, nlh, 0);
+
+#if defined(CONFIG_AUDIT) || defined(CONFIG_AUDIT_MODULE)
+		audit_netlink_msg(skb, err);
+#endif
+
 		skb_pull(skb, rlen);
 	}
 
