summary refs log tree commit diff
diff options
context:
space:
mode:
author <dwmw2@shinybook.infradead.org>2005-04-29 16:08:28 +0100
committer <dwmw2@shinybook.infradead.org>2005-04-29 16:08:28 +0100
commit2fd6f58ba6efc82ea2c9c2630f7ff5ed9eeaf34a (patch)
tree87cf236a78ad242ae01f1b71c289131e6d1c0662
parentea3834d9fb348fb1144ad3affea22df933eaf62e (diff)
downloadlinux-2fd6f58ba6efc82ea2c9c2630f7ff5ed9eeaf34a.tar.gz
[AUDIT] Don't allow ptrace to fool auditing, log arch of audited syscalls.
We were calling ptrace_notify() after auditing the syscall and arguments,
but the debugger could have _changed_ them before the syscall was actually
invoked. Reorder the calls to fix that.

While we're touching ever call to audit_syscall_entry(), we also make it
take an extra argument: the architecture of the syscall which was made,
because some architectures allow more than one type of syscall.

Also add an explicit success/failure flag to audit_syscall_exit(), for
the benefit of architectures which return that in a condition register
rather than only returning a single register.

Change type of syscall return value to 'long' not 'int'.

Signed-off-by: David Woodhouse <dwmw2@infradead.org>
-rw-r--r--arch/i386/kernel/ptrace.c19
-rw-r--r--arch/ia64/kernel/ptrace.c21
-rw-r--r--arch/mips/kernel/ptrace.c38
-rw-r--r--arch/ppc64/kernel/ptrace.c15
-rw-r--r--arch/s390/kernel/ptrace.c21
-rw-r--r--arch/x86_64/kernel/ptrace.c13
-rw-r--r--include/linux/audit.h48
-rw-r--r--kernel/auditsc.c22
8 files changed, 139 insertions, 58 deletions
diff --git a/arch/i386/kernel/ptrace.c b/arch/i386/kernel/ptrace.c
index b2f17640ceff..5606ec7a5c2b 100644
--- a/arch/i386/kernel/ptrace.c
+++ b/arch/i386/kernel/ptrace.c
@@ -682,24 +682,18 @@ void do_syscall_trace(struct pt_regs *regs, int entryexit)
 	/* do the secure computing check first */
 	secure_computing(regs->orig_eax);
 
-	if (unlikely(current->audit_context)) {
-		if (!entryexit)
-			audit_syscall_entry(current, regs->orig_eax,
-					    regs->ebx, regs->ecx,
-					    regs->edx, regs->esi);
-		else
-			audit_syscall_exit(current, regs->eax);
-	}
+	if (unlikely(current->audit_context) && entryexit)
+		audit_syscall_exit(current, AUDITSC_RESULT(regs->eax), regs->eax);
 
 	if (!(current->ptrace & PT_PTRACED))
-		return;
+		goto out;
 
 	/* Fake a debug trap */
 	if (test_thread_flag(TIF_SINGLESTEP))
 		send_sigtrap(current, regs, 0);
 
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
-		return;
+		goto out;
 
 	/* the 0x80 provides a way for the tracing parent to distinguish
 	   between a syscall stop and SIGTRAP delivery */
@@ -714,4 +708,9 @@ void do_syscall_trace(struct pt_regs *regs, int entryexit)
 		send_sig(current->exit_code, current, 1);
 		current->exit_code = 0;
 	}
+ out:
+	if (unlikely(current->audit_context) && !entryexit)
+		audit_syscall_entry(current, AUDIT_ARCH_I386, regs->orig_eax,
+				    regs->ebx, regs->ecx, regs->edx, regs->esi);
+
 }
diff --git a/arch/ia64/kernel/ptrace.c b/arch/ia64/kernel/ptrace.c
index 55789fcd7210..8dde0b16d4c8 100644
--- a/arch/ia64/kernel/ptrace.c
+++ b/arch/ia64/kernel/ptrace.c
@@ -1595,20 +1595,25 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
 		     long arg4, long arg5, long arg6, long arg7,
 		     struct pt_regs regs)
 {
-	long syscall;
+	if (test_thread_flag(TIF_SYSCALL_TRACE) 
+	    && (current->ptrace & PT_PTRACED))
+		syscall_trace();
 
 	if (unlikely(current->audit_context)) {
-		if (IS_IA32_PROCESS(&regs))
+		long syscall;
+		int arch;
+
+		if (IS_IA32_PROCESS(&regs)) {
 			syscall = regs.r1;
-		else
+			arch = AUDIT_ARCH_I386;
+		} else {
 			syscall = regs.r15;
+			arch = AUDIT_ARCH_IA64;
+		}
 
-		audit_syscall_entry(current, syscall, arg0, arg1, arg2, arg3);
+		audit_syscall_entry(current, arch, syscall, arg0, arg1, arg2, arg3);
 	}
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE)
-	    && (current->ptrace & PT_PTRACED))
-		syscall_trace();
 }
 
 /* "asmlinkage" so the input arguments are preserved... */
@@ -1619,7 +1624,7 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
 		     struct pt_regs regs)
 {
 	if (unlikely(current->audit_context))
-		audit_syscall_exit(current, regs.r8);
+		audit_syscall_exit(current, AUDITSC_RESULT(regs.r10), regs.r8);
 
 	if (test_thread_flag(TIF_SYSCALL_TRACE)
 	    && (current->ptrace & PT_PTRACED))
diff --git a/arch/mips/kernel/ptrace.c b/arch/mips/kernel/ptrace.c
index 92f2c39afe27..eaf7be9d0b0a 100644
--- a/arch/mips/kernel/ptrace.c
+++ b/arch/mips/kernel/ptrace.c
@@ -300,25 +300,38 @@ out:
 	return ret;
 }
 
+static inline int audit_arch()
+{
+#ifdef CONFIG_CPU_LITTLE_ENDIAN
+#ifdef CONFIG_MIPS64
+	if (!(current->thread.mflags & MF_32BIT_REGS))
+		return AUDIT_ARCH_MIPSEL64;
+#endif /* MIPS64 */
+	return AUDIT_ARCH_MIPSEL;
+
+#else /* big endian... */
+#ifdef CONFIG_MIPS64
+	if (!(current->thread.mflags & MF_32BIT_REGS))
+		return AUDIT_ARCH_MIPS64;
+#endif /* MIPS64 */
+	return AUDIT_ARCH_MIPS;
+
+#endif /* endian */
+}
+
 /*
  * Notification of system call entry/exit
  * - triggered by current->work.syscall_trace
  */
 asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
 {
-	if (unlikely(current->audit_context)) {
-		if (!entryexit)
-			audit_syscall_entry(current, regs->regs[2],
-			                    regs->regs[4], regs->regs[5],
-			                    regs->regs[6], regs->regs[7]);
-		else
-			audit_syscall_exit(current, regs->regs[2]);
-	}
+	if (unlikely(current->audit_context) && entryexit)
+		audit_syscall_exit(current, AUDITSC_RESULT(regs->regs[2]), regs->regs[2]);
 
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
-		return;
+		goto out;
 	if (!(current->ptrace & PT_PTRACED))
-		return;
+		goto out;
 
 	/* The 0x80 provides a way for the tracing parent to distinguish
 	   between a syscall stop and SIGTRAP delivery */
@@ -334,4 +347,9 @@ asmlinkage void do_syscall_trace(struct pt_regs *regs, int entryexit)
 		send_sig(current->exit_code, current, 1);
 		current->exit_code = 0;
 	}
+ out:
+	if (unlikely(current->audit_context) && !entryexit)
+		audit_syscall_entry(current, audit_arch(), regs->regs[2],
+				    regs->regs[4], regs->regs[5],
+				    regs->regs[6], regs->regs[7]);
 }
diff --git a/arch/ppc64/kernel/ptrace.c b/arch/ppc64/kernel/ptrace.c
index 354a287c67eb..3c76333ec3a9 100644
--- a/arch/ppc64/kernel/ptrace.c
+++ b/arch/ppc64/kernel/ptrace.c
@@ -304,14 +304,17 @@ static void do_syscall_trace(void)
 
 void do_syscall_trace_enter(struct pt_regs *regs)
 {
+	if (test_thread_flag(TIF_SYSCALL_TRACE)
+	    && (current->ptrace & PT_PTRACED))
+		do_syscall_trace();
+
 	if (unlikely(current->audit_context))
-		audit_syscall_entry(current, regs->gpr[0],
+		audit_syscall_entry(current,
+				    test_thread_flag(TIF_32BIT)?AUDIT_ARCH_PPC:AUDIT_ARCH_PPC64,
+				    regs->gpr[0],
 				    regs->gpr[3], regs->gpr[4],
 				    regs->gpr[5], regs->gpr[6]);
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE)
-	    && (current->ptrace & PT_PTRACED))
-		do_syscall_trace();
 }
 
 void do_syscall_trace_leave(struct pt_regs *regs)
@@ -319,7 +322,9 @@ void do_syscall_trace_leave(struct pt_regs *regs)
 	secure_computing(regs->gpr[0]);
 
 	if (unlikely(current->audit_context))
-		audit_syscall_exit(current, regs->result);
+		audit_syscall_exit(current, 
+				   (regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
+				   regs->result);
 
 	if ((test_thread_flag(TIF_SYSCALL_TRACE)
 	     || test_thread_flag(TIF_SINGLESTEP))
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 647233c02fc8..2d546c67f7c3 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -711,18 +711,13 @@ out:
 asmlinkage void
 syscall_trace(struct pt_regs *regs, int entryexit)
 {
-	if (unlikely(current->audit_context)) {
-		if (!entryexit)
-			audit_syscall_entry(current, regs->gprs[2],
-					    regs->orig_gpr2, regs->gprs[3],
-					    regs->gprs[4], regs->gprs[5]);
-		else
-			audit_syscall_exit(current, regs->gprs[2]);
-	}
+	if (unlikely(current->audit_context) && entryexit)
+		audit_syscall_exit(current, AUDITSC_RESULT(regs->gprs[2]), regs->gprs[2]);
+
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
-		return;
+		goto out;
 	if (!(current->ptrace & PT_PTRACED))
-		return;
+		goto out;
 	ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
 				 ? 0x80 : 0));
 
@@ -735,4 +730,10 @@ syscall_trace(struct pt_regs *regs, int entryexit)
 		send_sig(current->exit_code, current, 1);
 		current->exit_code = 0;
 	}
+ out:
+	if (unlikely(current->audit_context) && !entryexit)
+		audit_syscall_entry(current, 
+				    test_thread_flag(TIF_31BIT)?AUDIT_ARCH_S390:AUDIT_ARCH_S390X,
+				    regs->gprs[2], regs->orig_gpr2, regs->gprs[3],
+				    regs->gprs[4], regs->gprs[5]);
 }
diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c
index c7011675007d..ecbccbbf5c2a 100644
--- a/arch/x86_64/kernel/ptrace.c
+++ b/arch/x86_64/kernel/ptrace.c
@@ -629,25 +629,28 @@ static void syscall_trace(struct pt_regs *regs)
 	}
 }
 
+#define audit_arch() (test_thread_flag(TIF_IA32) ? AUDIT_ARCH_I386 : AUDIT_ARCH_X86_64)
+
 asmlinkage void syscall_trace_enter(struct pt_regs *regs)
 {
 	/* do the secure computing check first */
 	secure_computing(regs->orig_rax);
 
+	if (test_thread_flag(TIF_SYSCALL_TRACE)
+	    && (current->ptrace & PT_PTRACED))
+		syscall_trace(regs);
+
 	if (unlikely(current->audit_context))
-		audit_syscall_entry(current, regs->orig_rax,
+		audit_syscall_entry(current, audit_arch(), regs->orig_rax,
 				    regs->rdi, regs->rsi,
 				    regs->rdx, regs->r10);
 
-	if (test_thread_flag(TIF_SYSCALL_TRACE)
-	    && (current->ptrace & PT_PTRACED))
-		syscall_trace(regs);
 }
 
 asmlinkage void syscall_trace_leave(struct pt_regs *regs)
 {
 	if (unlikely(current->audit_context))
-		audit_syscall_exit(current, regs->rax);
+		audit_syscall_exit(current, AUDITSC_RESULT(regs->rax), regs->rax);
 
 	if ((test_thread_flag(TIF_SYSCALL_TRACE)
 	     || test_thread_flag(TIF_SINGLESTEP))
diff --git a/include/linux/audit.h b/include/linux/audit.h
index 9b77992c4888..fad0c1dc21a9 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -24,6 +24,9 @@
 #ifndef _LINUX_AUDIT_H_
 #define _LINUX_AUDIT_H_
 
+#include <linux/sched.h>
+#include <linux/elf.h>
+
 /* Request and reply types */
 #define AUDIT_GET      1000	/* Get status */
 #define AUDIT_SET      1001	/* Set status (enable/disable/auditd) */
@@ -67,6 +70,7 @@
 #define AUDIT_FSGID	8
 #define AUDIT_LOGINUID	9
 #define AUDIT_PERS	10
+#define AUDIT_ARCH	11
 
 				/* These are ONLY useful when checking
 				 * at syscall exit time (AUDIT_AT_EXIT). */
@@ -96,6 +100,38 @@
 #define AUDIT_FAIL_PRINTK	1
 #define AUDIT_FAIL_PANIC	2
 
+/* distinguish syscall tables */
+#define __AUDIT_ARCH_64BIT 0x80000000
+#define __AUDIT_ARCH_LE	   0x40000000
+#define AUDIT_ARCH_ALPHA	(EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_ARM		(EM_ARM|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_ARMEB	(EM_ARM)
+#define AUDIT_ARCH_CRIS		(EM_CRIS|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_FRV		(EM_FRV)
+#define AUDIT_ARCH_H8300	(EM_H8_300)
+#define AUDIT_ARCH_I386		(EM_386|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_IA64		(EM_IA_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_M32R		(EM_M32R)
+#define AUDIT_ARCH_M68K		(EM_68K)
+#define AUDIT_ARCH_MIPS		(EM_MIPS)
+#define AUDIT_ARCH_MIPSEL	(EM_MIPS|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_MIPS64	(EM_MIPS|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_MIPSEL64	(EM_MIPS|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_PARISC	(EM_PARISC)
+#define AUDIT_ARCH_PARISC64	(EM_PARISC|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_PPC		(EM_PPC)
+#define AUDIT_ARCH_PPC64	(EM_PPC64|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_S390		(EM_S390)
+#define AUDIT_ARCH_S390X	(EM_S390|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_SH		(EM_SH)
+#define AUDIT_ARCH_SHEL		(EM_SH|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_SH64		(EM_SH|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_SHEL64	(EM_SH|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_SPARC	(EM_SPARC)
+#define AUDIT_ARCH_SPARC64	(EM_SPARC64|__AUDIT_ARCH_64BIT)
+#define AUDIT_ARCH_V850		(EM_V850|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_X86_64	(EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
+
 #ifndef __KERNEL__
 struct audit_message {
 	struct nlmsghdr nlh;
@@ -129,15 +165,19 @@ struct audit_buffer;
 struct audit_context;
 struct inode;
 
+#define AUDITSC_INVALID 0
+#define AUDITSC_SUCCESS 1
+#define AUDITSC_FAILURE 2
+#define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS )
 #ifdef CONFIG_AUDITSYSCALL
 /* These are defined in auditsc.c */
 				/* Public API */
 extern int  audit_alloc(struct task_struct *task);
 extern void audit_free(struct task_struct *task);
-extern void audit_syscall_entry(struct task_struct *task,
+extern void audit_syscall_entry(struct task_struct *task, int arch,
 				int major, unsigned long a0, unsigned long a1,
 				unsigned long a2, unsigned long a3);
-extern void audit_syscall_exit(struct task_struct *task, int return_code);
+extern void audit_syscall_exit(struct task_struct *task, int failed, long return_code);
 extern void audit_getname(const char *name);
 extern void audit_putname(const char *name);
 extern void audit_inode(const char *name, const struct inode *inode);
@@ -153,8 +193,8 @@ extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mo
 #else
 #define audit_alloc(t) ({ 0; })
 #define audit_free(t) do { ; } while (0)
-#define audit_syscall_entry(t,a,b,c,d,e) do { ; } while (0)
-#define audit_syscall_exit(t,r) do { ; } while (0)
+#define audit_syscall_entry(t,ta,a,b,c,d,e) do { ; } while (0)
+#define audit_syscall_exit(t,f,r) do { ; } while (0)
 #define audit_getname(n) do { ; } while (0)
 #define audit_putname(n) do { ; } while (0)
 #define audit_inode(n,i) do { ; } while (0)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 00e87ffff13b..77e92592de57 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -123,7 +123,7 @@ struct audit_context {
 	int		    major;      /* syscall number */
 	unsigned long	    argv[4];    /* syscall arguments */
 	int		    return_valid; /* return code is valid */
-	int		    return_code;/* syscall return code */
+	long		    return_code;/* syscall return code */
 	int		    auditable;  /* 1 if record should be written */
 	int		    name_count;
 	struct audit_names  names[AUDIT_NAMES];
@@ -135,6 +135,7 @@ struct audit_context {
 	uid_t		    uid, euid, suid, fsuid;
 	gid_t		    gid, egid, sgid, fsgid;
 	unsigned long	    personality;
+	int		    arch;
 
 #if AUDIT_DEBUG
 	int		    put_count;
@@ -348,6 +349,10 @@ static int audit_filter_rules(struct task_struct *tsk,
 		case AUDIT_PERS:
 			result = (tsk->personality == value);
 			break;
+		case AUDIT_ARCH:
+			if (ctx) 
+				result = (ctx->arch == value);
+			break;
 
 		case AUDIT_EXIT:
 			if (ctx && ctx->return_valid)
@@ -355,7 +360,7 @@ static int audit_filter_rules(struct task_struct *tsk,
 			break;
 		case AUDIT_SUCCESS:
 			if (ctx && ctx->return_valid)
-				result = (ctx->return_code >= 0);
+				result = (ctx->return_valid == AUDITSC_SUCCESS);
 			break;
 		case AUDIT_DEVMAJOR:
 			if (ctx) {
@@ -648,8 +653,11 @@ static void audit_log_exit(struct audit_context *context)
 	audit_log_format(ab, "syscall=%d", context->major);
 	if (context->personality != PER_LINUX)
 		audit_log_format(ab, " per=%lx", context->personality);
+	audit_log_format(ab, " arch=%x", context->arch);
 	if (context->return_valid)
-		audit_log_format(ab, " exit=%d", context->return_code);
+		audit_log_format(ab, " success=%s exit=%ld", 
+				 (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
+				 context->return_code);
 	audit_log_format(ab,
 		  " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
 		  " pid=%d loginuid=%d uid=%d gid=%d"
@@ -773,7 +781,7 @@ static inline unsigned int audit_serial(void)
  * then the record will be written at syscall exit time (otherwise, it
  * will only be written if another part of the kernel requests that it
  * be written). */
-void audit_syscall_entry(struct task_struct *tsk, int major,
+void audit_syscall_entry(struct task_struct *tsk, int arch, int major,
 			 unsigned long a1, unsigned long a2,
 			 unsigned long a3, unsigned long a4)
 {
@@ -827,6 +835,7 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
 	if (!audit_enabled)
 		return;
 
+	context->arch	    = arch;
 	context->major      = major;
 	context->argv[0]    = a1;
 	context->argv[1]    = a2;
@@ -850,13 +859,13 @@ void audit_syscall_entry(struct task_struct *tsk, int major,
  * filtering, or because some other part of the kernel write an audit
  * message), then write out the syscall information.  In call cases,
  * free the names stored from getname(). */
-void audit_syscall_exit(struct task_struct *tsk, int return_code)
+void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
 {
 	struct audit_context *context;
 
 	get_task_struct(tsk);
 	task_lock(tsk);
-	context = audit_get_context(tsk, 1, return_code);
+	context = audit_get_context(tsk, valid, return_code);
 	task_unlock(tsk);
 
 	/* Not having a context here is ok, since the parent may have
@@ -869,6 +878,7 @@ void audit_syscall_exit(struct task_struct *tsk, int return_code)
 
 	context->in_syscall = 0;
 	context->auditable  = 0;
+
 	if (context->previous) {
 		struct audit_context *new_context = context->previous;
 		context->previous  = NULL;