Commit f85a96f6 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] Light-weight Auditing Framework

From: Rik Faith <faith@redhat.com>

This patch provides a low-overhead system-call auditing framework for Linux
that is usable by LSM components (e.g., SELinux).  This is an update of the
patch discussed in this thread:

    http://marc.theaimsgroup.com/?t=107815888100001&r=1&w=2

In brief, it provides for netlink-based logging of audit records that have
been generated in other parts of the kernel (e.g., SELinux) as well as the
ability to audit system calls, either independently (using simple
filtering) or as a compliment to the audit record that another part of the
kernel generated.

The main goals were to provide system call auditing with 1) as low overhead
as possible, and 2) without duplicating functionality that is already
provided by SELinux (and/or other security infrastructures).  This
framework will work "stand-alone", but is not designed to provide, e.g.,
CAPP functionality without another security component in place.

This updated patch includes changes from feedback I have received,
including the ability to compile without CONFIG_NET (and better use of
tabs, so use -w if you diff against the older patch).

Please see http://people.redhat.com/faith/audit/ for an early example
user-space client (auditd-0.4.tar.gz) and instructions on how to try it.

My future intentions at the kernel level include improving filtering (e.g.,
syscall personality/exit codes) and syscall support for more architectures.
 First, though, I'm going to work on documentation, a (real) audit daemon,
and patches for other user-space tools so that people can play with the
framework and understand how it can be used with and without SELinux.


Update:

Light-weight Auditing Framework receive filter fixes
From: Rik Faith <faith@redhat.com>

Since audit_receive_filter() is only called with audit_netlink_sem held, it
cannot race with either audit_del_rule() or audit_add_rule(), so the
list_for_each_entry_rcu()s may be replaced by list_for_each_entry()s, and
the rcu_read_{un,}lock()s removed.  A fix for this is part of the attached
patch.

Other features of the attached patch are:

1) generalized the ability to test for inequality

2) added syscall exit status reporting and testing

3) added ability to report and test first 4 syscall arguments (this adds
   a large amount of flexibility for little cost; not implemented or tested
   on ppc64)

4) added ability to report and test personality

User-space demo program enhanced for new fields and inequality testing:
http://people.redhat.com/faith/audit/auditd-0.5.tar.gz
parent 0e8e57e3
......@@ -264,7 +264,7 @@ sysenter_past_esp:
cmpl $(nr_syscalls), %eax
jae syscall_badsys
testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp)
testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_FLAGS(%ebp)
jnz syscall_trace_entry
call *sys_call_table(,%eax,4)
movl %eax,EAX(%esp)
......@@ -287,7 +287,7 @@ ENTRY(system_call)
cmpl $(nr_syscalls), %eax
jae syscall_badsys
# system call tracing in operation
testb $_TIF_SYSCALL_TRACE,TI_FLAGS(%ebp)
testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_FLAGS(%ebp)
jnz syscall_trace_entry
syscall_call:
call *sys_call_table(,%eax,4)
......@@ -354,7 +354,7 @@ syscall_trace_entry:
# perform syscall exit tracing
ALIGN
syscall_exit_work:
testb $_TIF_SYSCALL_TRACE, %cl
testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT), %cl
jz work_pending
sti # could let do_syscall_trace() call
# schedule() instead
......
......@@ -14,6 +14,7 @@
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
......@@ -524,6 +525,15 @@ asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
__attribute__((regparm(3)))
void do_syscall_trace(struct pt_regs *regs, int entryexit)
{
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 (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
......
......@@ -95,7 +95,7 @@ _GLOBAL(DoSyscall)
#endif /* SHOW_SYSCALLS */
clrrdi r10,r1,THREAD_SHIFT
ld r10,TI_FLAGS(r10)
andi. r11,r10,_TIF_SYSCALL_TRACE
andi. r11,r10,_TIF_SYSCALL_T_OR_A
bne- 50f
cmpli 0,r0,NR_syscalls
bge- 66f
......@@ -151,7 +151,8 @@ _GLOBAL(ret_from_syscall_1)
b 22b
/* Traced system call support */
50: bl .do_syscall_trace
50: addi r3,r1,STACK_FRAME_OVERHEAD
bl .do_syscall_trace_enter
ld r0,GPR0(r1) /* Restore original registers */
ld r3,GPR3(r1)
ld r4,GPR4(r1)
......@@ -201,7 +202,7 @@ _GLOBAL(ret_from_syscall_2)
oris r10,r10,0x1000
std r10,_CCR(r1)
60: std r3,GPR3(r1) /* Update return value */
bl .do_syscall_trace
bl .do_syscall_trace_leave
b .ret_from_except
66: li r3,ENOSYS
b 57b
......@@ -234,14 +235,14 @@ _GLOBAL(ppc64_rt_sigreturn)
80: clrrdi r4,r1,THREAD_SHIFT
ld r4,TI_FLAGS(r4)
andi. r4,r4,_TIF_SYSCALL_TRACE
andi. r4,r4,_TIF_SYSCALL_T_OR_A
bne- 81f
cmpi 0,r3,0
bge .ret_from_except
b .ret_from_syscall_1
81: cmpi 0,r3,0
blt .ret_from_syscall_2
bl .do_syscall_trace
bl .do_syscall_trace_leave
b .ret_from_except
/*
......@@ -352,9 +353,9 @@ _GLOBAL(ret_from_fork)
bl .schedule_tail
clrrdi r4,r1,THREAD_SHIFT
ld r4,TI_FLAGS(r4)
andi. r4,r4,_TIF_SYSCALL_TRACE
andi. r4,r4,_TIF_SYSCALL_T_OR_A
beq+ .ret_from_except
bl .do_syscall_trace
bl .do_syscall_trace_leave
b .ret_from_except
_GLOBAL(ret_from_except)
......
......@@ -26,6 +26,7 @@
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <asm/uaccess.h>
#include <asm/page.h>
......@@ -286,12 +287,8 @@ int sys_ptrace(long request, long pid, long addr, long data)
return ret;
}
void do_syscall_trace(void)
static void do_syscall_trace(void)
{
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
/* the 0x80 provides a way for the tracing parent to distinguish
between a syscall stop and SIGTRAP delivery */
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
......@@ -307,3 +304,25 @@ void do_syscall_trace(void)
current->exit_code = 0;
}
}
void do_syscall_trace_enter(struct pt_regs *regs)
{
if (unlikely(current->audit_context))
audit_syscall_entry(current, 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(void)
{
if (unlikely(current->audit_context))
audit_syscall_exit(current, 0); /* FIXME: pass pt_regs */
if (test_thread_flag(TIF_SYSCALL_TRACE)
&& (current->ptrace & PT_PTRACED))
do_syscall_trace();
}
......@@ -78,8 +78,8 @@ ENTRY(ia32_sysenter_target)
.quad 1b,ia32_badarg
.previous
GET_THREAD_INFO(%r10)
bt $TIF_SYSCALL_TRACE,threadinfo_flags(%r10)
jc sysenter_tracesys
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%r10)
jnz sysenter_tracesys
sysenter_do_call:
cmpl $(IA32_NR_syscalls),%eax
jae ia32_badsys
......@@ -106,7 +106,7 @@ sysenter_tracesys:
CLEAR_RREGS
movq $-ENOSYS,RAX(%rsp) /* really needed? */
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 */
RESTORE_REST
movl %ebp, %ebp
......@@ -163,8 +163,8 @@ ENTRY(ia32_cstar_target)
.quad 1b,ia32_badarg
.previous
GET_THREAD_INFO(%r10)
bt $TIF_SYSCALL_TRACE,threadinfo_flags(%r10)
jc cstar_tracesys
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%r10)
jnz cstar_tracesys
cstar_do_call:
cmpl $IA32_NR_syscalls,%eax
jae ia32_badsys
......@@ -187,7 +187,7 @@ cstar_tracesys:
CLEAR_RREGS
movq $-ENOSYS,RAX(%rsp) /* really needed? */
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 */
RESTORE_REST
movl RSP-ARGOFFSET(%rsp), %r8d
......@@ -236,8 +236,8 @@ ENTRY(ia32_syscall)
this could be a problem. */
SAVE_ARGS 0,0,1
GET_THREAD_INFO(%r10)
bt $TIF_SYSCALL_TRACE,threadinfo_flags(%r10)
jc ia32_tracesys
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%r10)
jnz ia32_tracesys
ia32_do_syscall:
cmpl $(IA32_NR_syscalls),%eax
jae ia32_badsys
......@@ -251,7 +251,7 @@ ia32_tracesys:
SAVE_REST
movq $-ENOSYS,RAX(%rsp) /* really needed? */
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 */
RESTORE_REST
jmp ia32_do_syscall
......
......@@ -131,8 +131,8 @@ ENTRY(ret_from_fork)
CFI_DEFAULT_STACK
call schedule_tail
GET_THREAD_INFO(%rcx)
bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx)
jc rff_trace
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%rcx)
jnz rff_trace
rff_action:
RESTORE_REST
testl $3,CS-ARGOFFSET(%rsp) # from kernel_thread?
......@@ -143,7 +143,7 @@ rff_action:
jmp ret_from_sys_call
rff_trace:
movq %rsp,%rdi
call syscall_trace
call syscall_trace_leave
GET_THREAD_INFO(%rcx)
jmp rff_action
CFI_ENDPROC
......@@ -185,8 +185,8 @@ ENTRY(system_call)
movq %rax,ORIG_RAX-ARGOFFSET(%rsp)
movq %rcx,RIP-ARGOFFSET(%rsp)
GET_THREAD_INFO(%rcx)
bt $TIF_SYSCALL_TRACE,threadinfo_flags(%rcx)
jc tracesys
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),threadinfo_flags(%rcx)
jnz tracesys
cmpq $__NR_syscall_max,%rax
ja badsys
movq %r10,%rcx
......@@ -244,7 +244,7 @@ tracesys:
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
......@@ -254,7 +254,7 @@ tracesys:
movq %rax,RAX-ARGOFFSET(%rsp)
1: SAVE_REST
movq %rsp,%rdi
call syscall_trace
call syscall_trace_leave
RESTORE_TOP_OF_STACK %rbx
RESTORE_REST
jmp ret_from_sys_call
......@@ -297,13 +297,14 @@ int_very_careful:
sti
SAVE_REST
/* Check for syscall exit trace */
bt $TIF_SYSCALL_TRACE,%edx
jnc int_signal
testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),%edx
jz int_signal
pushq %rdi
leaq 8(%rsp),%rdi # &ptregs -> arg1
call syscall_trace
call syscall_trace_leave
popq %rdi
btr $TIF_SYSCALL_TRACE,%edi
btr $TIF_SYSCALL_AUDIT,%edi
jmp int_restore_rest
int_signal:
......
......@@ -16,6 +16,7 @@
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/security.h>
#include <linux/audit.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
......@@ -486,7 +487,7 @@ asmlinkage long sys_ptrace(long request, long pid, unsigned long addr, long data
return ret;
}
asmlinkage void syscall_trace(struct pt_regs *regs)
static void syscall_trace(struct pt_regs *regs)
{
#if 0
......@@ -496,11 +497,6 @@ asmlinkage void syscall_trace(struct pt_regs *regs)
current_thread_info()->flags, current->ptrace);
#endif
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return;
if (!(current->ptrace & PT_PTRACED))
return;
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0));
/*
......@@ -513,3 +509,25 @@ asmlinkage void syscall_trace(struct pt_regs *regs)
current->exit_code = 0;
}
}
asmlinkage void syscall_trace_enter(struct pt_regs *regs)
{
if (unlikely(current->audit_context))
audit_syscall_entry(current, 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);
if (test_thread_flag(TIF_SYSCALL_TRACE)
&& (current->ptrace & PT_PTRACED))
syscall_trace(regs);
}
......@@ -26,6 +26,7 @@
#include <linux/personality.h>
#include <linux/security.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <asm/namei.h>
#include <asm/uaccess.h>
......@@ -141,10 +142,12 @@ char * getname(const char __user * filename)
result = tmp;
if (retval < 0) {
putname(tmp);
__putname(tmp);
result = ERR_PTR(retval);
}
}
if (unlikely(current->audit_context) && !IS_ERR(result) && result)
audit_getname(result);
return result;
}
......@@ -860,6 +863,8 @@ walk_init_root(const char *name, struct nameidata *nd)
int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata *nd)
{
int retval;
nd->last_type = LAST_ROOT; /* if there are only slashes... */
nd->flags = flags;
......@@ -882,7 +887,13 @@ int fastcall path_lookup(const char *name, unsigned int flags, struct nameidata
}
read_unlock(&current->fs->lock);
current->total_link_count = 0;
return link_path_walk(name, nd);
retval = link_path_walk(name, nd);
if (unlikely(current->audit_context
&& nd && nd->dentry && nd->dentry->d_inode))
audit_inode(name,
nd->dentry->d_inode->i_ino,
nd->dentry->d_inode->i_rdev);
return retval;
}
/*
......
......@@ -151,6 +151,7 @@ static inline unsigned long current_stack_pointer(void)
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* restore singlestep on return to user mode */
#define TIF_IRET 5 /* return with iret */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
......@@ -159,9 +160,12 @@ static inline unsigned long current_stack_pointer(void)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
#define _TIF_IRET (1<<TIF_IRET)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
(0x0000FFFF & ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT))
#define _TIF_ALLWORK_MASK 0x0000FFFF /* work to do on any return to u-space */
/*
......
......@@ -97,6 +97,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_32BIT 5 /* 32 bit binary */
#define TIF_RUN_LIGHT 6 /* iSeries run light */
#define TIF_ABI_PENDING 7 /* 32/64 bit switch needed */
#define TIF_SYSCALL_AUDIT 8 /* syscall auditing active */
/* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
......@@ -107,6 +108,8 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_32BIT (1<<TIF_32BIT)
#define _TIF_RUN_LIGHT (1<<TIF_RUN_LIGHT)
#define _TIF_ABI_PENDING (1<<TIF_ABI_PENDING)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT)
#define _TIF_USER_WORK_MASK (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | \
_TIF_NEED_RESCHED)
......
......@@ -101,6 +101,7 @@ static inline struct thread_info *stack_thread_info(void)
#define TIF_NEED_RESCHED 3 /* rescheduling necessary */
#define TIF_SINGLESTEP 4 /* reenable singlestep on user return*/
#define TIF_IRET 5 /* force IRET */
#define TIF_SYSCALL_AUDIT 7 /* syscall auditing active */
#define TIF_POLLING_NRFLAG 16 /* true if poll_idle() is polling TIF_NEED_RESCHED */
#define TIF_IA32 17 /* 32bit process */
#define TIF_FORK 18 /* ret_from_fork */
......@@ -112,13 +113,15 @@ static inline struct thread_info *stack_thread_info(void)
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_IRET (1<<TIF_IRET)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_IA32 (1<<TIF_IA32)
#define _TIF_FORK (1<<TIF_FORK)
#define _TIF_ABI_PENDING (1<<TIF_ABI_PENDING)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK (0x0000FFFF & ~(_TIF_SYSCALL_TRACE|_TIF_SINGLESTEP))
#define _TIF_WORK_MASK \
(0x0000FFFF & ~(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SINGLESTEP))
/* work to do on any return to user space */
#define _TIF_ALLWORK_MASK 0x0000FFFF
......
/* audit.h -- Auditing support -*- linux-c -*-
*
* Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Written by Rickard E. (Rik) Faith <faith@redhat.com>
*
*/
#ifndef _LINUX_AUDIT_H_
#define _LINUX_AUDIT_H_
/* Request and reply types */
#define AUDIT_GET 1000 /* Get status */
#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
#define AUDIT_LIST 1002 /* List filtering rules */
#define AUDIT_ADD 1003 /* Add filtering rule */
#define AUDIT_DEL 1004 /* Delete filtering rule */
#define AUDIT_USER 1005 /* Send a message from user-space */
#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
#define AUDIT_AT_ENTRY 0x02 /* Apply rule at syscall entry */
#define AUDIT_AT_EXIT 0x04 /* Apply rule at syscall exit */
#define AUDIT_PREPEND 0x10 /* Prepend to front of list */
/* Rule actions */
#define AUDIT_NEVER 0 /* Do not build context if rule matches */
#define AUDIT_POSSIBLE 1 /* Build context if rule matches */
#define AUDIT_ALWAYS 2 /* Generate audit record if rule matches */
/* Rule structure sizes -- if these change, different AUDIT_ADD and
* AUDIT_LIST commands must be implemented. */
#define AUDIT_MAX_FIELDS 64
#define AUDIT_BITMASK_SIZE 64
#define AUDIT_WORD(nr) ((__u32)((nr)/32))
#define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32))
/* Rule fields */
/* These are useful when checking the
* task structure at task creation time
* (AUDIT_PER_TASK). */
#define AUDIT_PID 0
#define AUDIT_UID 1
#define AUDIT_EUID 2
#define AUDIT_SUID 3
#define AUDIT_FSUID 4
#define AUDIT_GID 5
#define AUDIT_EGID 6
#define AUDIT_SGID 7
#define AUDIT_FSGID 8
#define AUDIT_LOGINUID 9
#define AUDIT_PERS 10
/* These are ONLY useful when checking
* at syscall exit time (AUDIT_AT_EXIT). */
#define AUDIT_DEVMAJOR 100
#define AUDIT_DEVMINOR 101
#define AUDIT_INODE 102
#define AUDIT_EXIT 103
#define AUDIT_SUCCESS 104 /* exit >= 0; value ignored */
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
#define AUDIT_ARG2 (AUDIT_ARG0+2)
#define AUDIT_ARG3 (AUDIT_ARG0+3)
#define AUDIT_NEGATE 0x80000000
/* Status symbols */
/* Mask values */
#define AUDIT_STATUS_ENABLED 0x0001
#define AUDIT_STATUS_FAILURE 0x0002
#define AUDIT_STATUS_PID 0x0004
#define AUDIT_STATUS_RATE_LIMIT 0x0008
#define AUDIT_STATUS_BACKLOG_LIMIT 0x0010
/* Failure-to-log actions */
#define AUDIT_FAIL_SILENT 0
#define AUDIT_FAIL_PRINTK 1
#define AUDIT_FAIL_PANIC 2
#ifndef __KERNEL__
struct audit_message {
struct nlmsghdr nlh;
char data[1200];
};
#endif
struct audit_status {
__u32 mask; /* Bit mask for valid entries */
__u32 enabled; /* 1 = enabled, 0 = disbaled */
__u32 failure; /* Failure-to-log action */
__u32 pid; /* pid of auditd process */
__u32 rate_limit; /* messages rate limit (per second) */
__u32 backlog_limit; /* waiting messages limit */
__u32 lost; /* messages lost */
__u32 backlog; /* messages waiting in queue */
};
struct audit_login {
__u32 loginuid;
int msglen;
char msg[1024];
};
struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
__u32 flags; /* AUDIT_PER_{TASK,CALL}, AUDIT_PREPEND */
__u32 action; /* AUDIT_NEVER, AUDIT_POSSIBLE, AUDIT_ALWAYS */
__u32 field_count;
__u32 mask[AUDIT_BITMASK_SIZE];
__u32 fields[AUDIT_MAX_FIELDS];
__u32 values[AUDIT_MAX_FIELDS];
};
#ifdef __KERNEL__
#ifdef CONFIG_AUDIT
struct audit_buffer;
struct audit_context;
#endif
#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,
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_getname(const char *name);
extern void audit_putname(const char *name);
extern void audit_inode(const char *name, unsigned long ino, dev_t rdev);
/* Private API (for audit.c only) */
extern int audit_receive_filter(int type, int pid, int uid, int seq,
void *data);
extern void audit_get_stamp(struct audit_context *ctx,
struct timespec *t, int *serial);
extern int audit_set_loginuid(struct audit_context *ctx, uid_t loginuid);
#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_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i,d) do { ; } while (0)
#endif
#ifdef CONFIG_AUDIT
/* These are defined in audit.c */
/* Public API */
extern void audit_log(struct audit_context *ctx,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx);
extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...)
__attribute__((format(printf,2,3)));
extern void audit_log_end(struct audit_buffer *ab);
extern void audit_log_end_fast(struct audit_buffer *ab);
extern void audit_log_end_irq(struct audit_buffer *ab);
extern void audit_log_d_path(struct audit_buffer *ab,
const char *prefix,
struct dentry *dentry,
struct vfsmount *vfsmnt);
extern int audit_set_rate_limit(int limit);
extern int audit_set_backlog_limit(int limit);
extern int audit_set_enabled(int state);
extern int audit_set_failure(int state);
/* Private API (for auditsc.c only) */
extern void audit_send_reply(int pid, int seq, int type,
int done, int multi,
void *payload, int size);
extern void audit_log_lost(const char *message);
#else
#define audit_log(t,f,...) do { ; } while (0)
#define audit_log_start(t) ({ NULL; })
#define audit_log_vformat(b,f,a) do { ; } while (0)
#define audit_log_format(b,f,...) do { ; } while (0)
#define audit_log_end(b) do { ; } while (0)
#define audit_log_end_fast(b) do { ; } while (0)
#define audit_log_end_irq(b) do { ; } while (0)
#define audit_log_d_path(b,p,d,v) do { ; } while (0)
#define audit_set_rate_limit(l) do { ; } while (0)
#define audit_set_backlog_limit(l) do { ; } while (0)
#define audit_set_enabled(s) do { ; } while (0)
#define audit_set_failure(s) do { ; } while (0)
#endif
#endif
#endif
......@@ -20,6 +20,7 @@
#include <linux/radix-tree.h>
#include <linux/kobject.h>
#include <asm/atomic.h>
#include <linux/audit.h>
struct iovec;
struct nameidata;
......@@ -1159,7 +1160,18 @@ extern char * getname(const char __user *);
extern void vfs_caches_init(unsigned long);
#define __getname() kmem_cache_alloc(names_cachep, SLAB_KERNEL)
#define putname(name) kmem_cache_free(names_cachep, (void *)(name))
#define __putname(name) kmem_cache_free(names_cachep, (void *)(name))
#ifndef CONFIG_AUDITSYSCALL
#define putname(name) __putname(name)
#else
#define putname(name) \
do { \
if (unlikely(current->audit_context)) \
audit_putname(name); \
else \
__putname(name); \
} while (0)
#endif
extern int register_blkdev(unsigned int, const char *);
extern int unregister_blkdev(unsigned int, const char *);
......
......@@ -13,6 +13,7 @@
#define NETLINK_XFRM 6 /* ipsec */
#define NETLINK_SELINUX 7 /* SELinux event notifications */
#define NETLINK_ARPD 8
#define NETLINK_AUDIT 9 /* auditing */
#define NETLINK_ROUTE6 11 /* af_inet6 route comm channel */
#define NETLINK_IP6_FW 13
#define NETLINK_DNRTMSG 14 /* DECnet routing messages */
......
......@@ -371,6 +371,8 @@ int set_current_groups(struct group_info *group_info);
((gi)->blocks[(i)/NGROUPS_PER_BLOCK][(i)%NGROUPS_PER_BLOCK])
struct audit_context; /* See audit.c */
struct task_struct {
volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
struct thread_info *thread_info;
......@@ -474,6 +476,7 @@ struct task_struct {
sigset_t *notifier_mask;
void *security;
struct audit_context *audit_context;
/* Thread group tracking */
u32 parent_exec_id;
......
......@@ -137,6 +137,26 @@ config SYSCTL
building a kernel for install/rescue disks or your system is very
limited in memory.
config AUDIT
bool "Auditing support"
default y if SECURITY_SELINUX
default n
help
Enable auditing infrastructure that can be used with another
kernel subsystem, such as SELinux (which requires this for
logging of avc messages output). Does not do system-call
auditing without CONFIG_AUDITSYSCALL.
config AUDITSYSCALL
bool "Enable system-call auditing support"
depends on AUDIT && (X86 || PPC64)
default y if SECURITY_SELINUX
default n
help
Enable low-overhead system-call auditing infrastructure that
can be used independently or with another kernel subsystem,
such as SELinux.
config LOG_BUF_SHIFT
int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
range 12 20
......
......@@ -21,6 +21,8 @@ obj-$(CONFIG_COMPAT) += compat.o
obj-$(CONFIG_IKCONFIG) += configs.o
obj-$(CONFIG_IKCONFIG_PROC) += configs.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_AUDIT) += audit.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
ifneq ($(CONFIG_IA64),y)
# According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is
......
This diff is collapsed.
This diff is collapsed.
......@@ -32,6 +32,7 @@
#include <linux/futex.h>
#include <linux/ptrace.h>
#include <linux/mount.h>
#include <linux/audit.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
......@@ -83,6 +84,8 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current);
if (unlikely(tsk->audit_context))
audit_free(tsk);
security_task_free(tsk);
free_uid(tsk->user);
put_group_info(tsk->group_info);
......@@ -949,13 +952,16 @@ struct task_struct *copy_process(unsigned long clone_flags,
p->start_time = get_jiffies_64();
p->security = NULL;
p->io_context = NULL;
p->audit_context = NULL;
retval = -ENOMEM;
if ((retval = security_task_alloc(p)))
goto bad_fork_cleanup;
if ((retval = audit_alloc(p)))
goto bad_fork_cleanup_security;
/* copy all the process information */
if ((retval = copy_semundo(clone_flags, p)))
goto bad_fork_cleanup_security;
goto bad_fork_cleanup_audit;
if ((retval = copy_files(clone_flags, p)))
goto bad_fork_cleanup_semundo;
if ((retval = copy_fs(clone_flags, p)))
......@@ -1090,6 +1096,8 @@ struct task_struct *copy_process(unsigned long clone_flags,
exit_files(p); /* blocking */
bad_fork_cleanup_semundo:
exit_sem(p);
bad_fork_cleanup_audit:
audit_free(p);
bad_fork_cleanup_security:
security_task_free(p);
bad_fork_cleanup:
......
......@@ -22,11 +22,14 @@
#include <linux/un.h>
#include <net/af_unix.h>
#include <linux/ip.h>
#include <linux/audit.h>
#include <linux/ipv6.h>
#include <net/ipv6.h>
#include "avc.h"
#include "avc_ss.h"
#ifdef CONFIG_AUDIT
#include "class_to_string.h"
#endif
#include "common_perm_to_string.h"
#include "av_inherit.h"
#include "av_perm_to_string.h"
......@@ -68,14 +71,10 @@ struct avc_callback_node {
};
static spinlock_t avc_lock = SPIN_LOCK_UNLOCKED;
static spinlock_t avc_log_lock = SPIN_LOCK_UNLOCKED;
static struct avc_node *avc_node_freelist = NULL;
static struct avc_cache avc_cache;
static char *avc_audit_buffer = NULL;
static unsigned avc_cache_stats[AVC_NSTATS];
static struct avc_callback_node *avc_callbacks = NULL;
static unsigned int avc_log_level = 4; /* default: KERN_WARNING */
static char avc_level_string[4] = "< >";
static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
{
......@@ -87,14 +86,14 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
* @tclass: target security class
* @av: access vector
*/
void avc_dump_av(u16 tclass, u32 av)
void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
{
char **common_pts = 0;
u32 common_base = 0;
int i, i2, perm;
if (av == 0) {
printk(" null");
audit_log_format(ab, " null");
return;
}
......@@ -106,12 +105,12 @@ void avc_dump_av(u16 tclass, u32 av)
}
}
printk(" {");
audit_log_format(ab, " {");
i = 0;
perm = 1;
while (perm < common_base) {
if (perm & av)
printk(" %s", common_pts[i]);
audit_log_format(ab, " %s", common_pts[i]);
i++;
perm <<= 1;
}
......@@ -124,13 +123,14 @@ void avc_dump_av(u16 tclass, u32 av)
break;
}
if (i2 < ARRAY_SIZE(av_perm_to_string))
printk(" %s", av_perm_to_string[i2].name);
audit_log_format(ab, " %s",
av_perm_to_string[i2].name);
}
i++;
perm <<= 1;
}
printk(" }");
audit_log_format(ab, " }");
}
/**
......@@ -139,7 +139,7 @@ void avc_dump_av(u16 tclass, u32 av)
* @tsid: target security identifier
* @tclass: target security class
*/
void avc_dump_query(u32 ssid, u32 tsid, u16 tclass)
void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass)
{
int rc;
char *scontext;
......@@ -147,20 +147,20 @@ void avc_dump_query(u32 ssid, u32 tsid, u16 tclass)
rc = security_sid_to_context(ssid, &scontext, &scontext_len);
if (rc)
printk("ssid=%d", ssid);
audit_log_format(ab, "ssid=%d", ssid);
else {
printk("scontext=%s", scontext);
audit_log_format(ab, "scontext=%s", scontext);
kfree(scontext);
}
rc = security_sid_to_context(tsid, &scontext, &scontext_len);
if (rc)
printk(" tsid=%d", tsid);
audit_log_format(ab, " tsid=%d", tsid);
else {
printk(" tcontext=%s", scontext);
audit_log_format(ab, " tcontext=%s", scontext);
kfree(scontext);
}
printk(" tclass=%s", class_to_string[tclass]);
audit_log_format(ab, " tclass=%s", class_to_string[tclass]);
}
/**
......@@ -194,11 +194,7 @@ void __init avc_init(void)
avc_node_freelist = new;
}
avc_audit_buffer = (char *)__get_free_page(GFP_ATOMIC);
if (!avc_audit_buffer)
panic("AVC: unable to allocate audit buffer\n");
avc_level_string[1] = '0' + avc_log_level;
audit_log(current->audit_context, "AVC INITIALIZED\n");
}
#if 0
......@@ -430,12 +426,13 @@ static inline void avc_print_ipv6_addr(struct in6_addr *addr, u16 port,
printk(" %s=%d", name2, ntohs(port));
}
static inline void avc_print_ipv4_addr(u32 addr, u16 port, char *name1, char *name2)
static inline void avc_print_ipv4_addr(struct audit_buffer *ab, u32 addr,
u16 port, char *name1, char *name2)
{
if (addr)
printk(" %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
audit_log_format(ab, " %s=%d.%d.%d.%d", name1, NIPQUAD(addr));
if (port)
printk(" %s=%d", name2, ntohs(port));
audit_log_format(ab, " %s=%d", name2, ntohs(port));
}
/*
......@@ -515,9 +512,8 @@ void avc_audit(u32 ssid, u32 tsid,
{
struct task_struct *tsk = current;
struct inode *inode = NULL;
char *p;
u32 denied, audited;
unsigned long flags;
struct audit_buffer *ab;
denied = requested & ~avd->allowed;
if (denied) {
......@@ -535,19 +531,18 @@ void avc_audit(u32 ssid, u32 tsid,
if (!check_avc_ratelimit())
return;
/* prevent overlapping printks */
spin_lock_irqsave(&avc_log_lock,flags);
printk("%s\n", avc_level_string);
printk("%savc: %s ", avc_level_string, denied ? "denied" : "granted");
avc_dump_av(tclass,audited);
printk(" for ");
ab = audit_log_start(current->audit_context);
if (!ab)
return; /* audit_panic has been called */
audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
avc_dump_av(ab, tclass,audited);
audit_log_format(ab, " for ");
if (a && a->tsk)
tsk = a->tsk;
if (tsk && tsk->pid) {
struct mm_struct *mm;
struct vm_area_struct *vma;
printk(" pid=%d", tsk->pid);
audit_log_format(ab, " pid=%d", tsk->pid);
if (tsk == current)
mm = current->mm;
else
......@@ -558,11 +553,9 @@ void avc_audit(u32 ssid, u32 tsid,
while (vma) {
if ((vma->vm_flags & VM_EXECUTABLE) &&
vma->vm_file) {
p = d_path(vma->vm_file->f_dentry,
vma->vm_file->f_vfsmnt,
avc_audit_buffer,
PAGE_SIZE);
printk(" exe=%s", p);
audit_log_d_path(ab, "exe=",
vma->vm_file->f_dentry,
vma->vm_file->f_vfsmnt);
break;
}
vma = vma->vm_next;
......@@ -572,29 +565,26 @@ void avc_audit(u32 ssid, u32 tsid,
if (tsk != current)
mmput(mm);
} else {
printk(" comm=%s", tsk->comm);
audit_log_format(ab, " comm=%s", tsk->comm);
}
}
if (a) {
switch (a->type) {
case AVC_AUDIT_DATA_IPC:
printk(" key=%d", a->u.ipc_id);
audit_log_format(ab, " key=%d", a->u.ipc_id);
break;
case AVC_AUDIT_DATA_CAP:
printk(" capability=%d", a->u.cap);
audit_log_format(ab, " capability=%d", a->u.cap);
break;
case AVC_AUDIT_DATA_FS:
if (a->u.fs.dentry) {
struct dentry *dentry = a->u.fs.dentry;
if (a->u.fs.mnt) {
p = d_path(dentry,
a->u.fs.mnt,
avc_audit_buffer,
PAGE_SIZE);
if (p)
printk(" path=%s", p);
audit_log_d_path(ab, "path=", dentry,
a->u.fs.mnt);
} else {
printk(" name=%s", dentry->d_name.name);
audit_log_format(ab, " name=%s",
dentry->d_name.name);
}
inode = dentry->d_inode;
} else if (a->u.fs.inode) {
......@@ -602,27 +592,31 @@ void avc_audit(u32 ssid, u32 tsid,
inode = a->u.fs.inode;
dentry = d_find_alias(inode);
if (dentry) {
printk(" name=%s", dentry->d_name.name);
audit_log_format(ab, " name=%s",
dentry->d_name.name);
dput(dentry);
}
}
if (inode)
printk(" dev=%s ino=%ld",
inode->i_sb->s_id, inode->i_ino);
audit_log_format(ab, " dev=%s ino=%ld",
inode->i_sb->s_id,
inode->i_ino);
break;
case AVC_AUDIT_DATA_NET:
if (a->u.net.sk) {
struct sock *sk = a->u.net.sk;
struct unix_sock *u;
int len = 0;
char *p = NULL;
switch (sk->sk_family) {
case AF_INET: {
struct inet_opt *inet = inet_sk(sk);
avc_print_ipv4_addr(inet->rcv_saddr,
avc_print_ipv4_addr(ab, inet->rcv_saddr,
inet->sport,
"laddr", "lport");
avc_print_ipv4_addr(inet->daddr,
avc_print_ipv4_addr(ab, inet->daddr,
inet->dport,
"faddr", "fport");
break;
......@@ -642,34 +636,32 @@ void avc_audit(u32 ssid, u32 tsid,
case AF_UNIX:
u = unix_sk(sk);
if (u->dentry) {
p = d_path(u->dentry,
u->mnt,
avc_audit_buffer,
PAGE_SIZE);
printk(" path=%s", p);
} else if (u->addr) {
p = avc_audit_buffer;
memcpy(p,
u->addr->name->sun_path,
u->addr->len-sizeof(short));
if (*p == 0) {
*p = '@';
p += u->addr->len-sizeof(short);
*p = 0;
}
printk(" path=%s",
avc_audit_buffer);
audit_log_d_path(ab, "path=",
u->dentry, u->mnt);
break;
}
if (!u->addr)
break;
len = u->addr->len-sizeof(short);
p = &u->addr->name->sun_path[0];
if (*p)
audit_log_format(ab,
"path=%*.*s", len,
len, p);
else
audit_log_format(ab,
"path=@%*.*s", len-1,
len-1, p+1);
break;
}
}
switch (a->u.net.family) {
case AF_INET:
avc_print_ipv4_addr(a->u.net.v4info.saddr,
avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
a->u.net.sport,
"saddr", "src");
avc_print_ipv4_addr(a->u.net.v4info.daddr,
avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
a->u.net.dport,
"daddr", "dest");
break;
......@@ -683,15 +675,14 @@ void avc_audit(u32 ssid, u32 tsid,
break;
}
if (a->u.net.netif)
printk(" netif=%s", a->u.net.netif);
audit_log_format(ab, " netif=%s",
a->u.net.netif);
break;
}
}
printk(" ");
avc_dump_query(ssid, tsid, tclass);
printk("\n");
spin_unlock_irqrestore(&avc_log_lock,flags);
audit_log_format(ab, " ");
avc_dump_query(ab, ssid, tsid, tclass);
audit_log_end(ab);
}
/**
......@@ -1120,14 +1111,3 @@ int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
avc_audit(ssid, tsid, tclass, requested, &avd, rc, auditdata);
return rc;
}
static int __init avc_log_level_setup(char *str)
{
avc_log_level = simple_strtol(str, NULL, 0);
if (avc_log_level > 7)
avc_log_level = 7;
return 1;
}
__setup("avc_log_level=", avc_log_level_setup);
......@@ -127,9 +127,10 @@ static inline void avc_cache_stats_add(int type, unsigned val)
/*
* AVC display support
*/
void avc_dump_av(u16 tclass, u32 av);
void avc_dump_query(u32 ssid, u32 tsid, u16 tclass);
void avc_dump_cache(char *tag);
struct audit_buffer;
void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
void avc_dump_query(struct audit_buffer *ab, u32 ssid, u32 tsid, u16 tclass);
void avc_dump_cache(struct audit_buffer *ab, char *tag);
/*
* AVC operations
......
......@@ -399,7 +399,7 @@ int security_sid_to_context(u32 sid, char **scontext, u32 *scontext_len)
char *scontextp;
*scontext_len = strlen(initial_sid_to_string[sid]) + 1;
scontextp = kmalloc(*scontext_len,GFP_KERNEL);
scontextp = kmalloc(*scontext_len,GFP_ATOMIC);
strcpy(scontextp, initial_sid_to_string[sid]);
*scontext = scontextp;
goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment