Commit 19fa95e9 authored by Linus Torvalds's avatar Linus Torvalds

Merge master.kernel.org:/pub/scm/linux/kernel/git/dwmw2/audit-2.6

parents ba483d57 0107b3cf
...@@ -1083,6 +1083,23 @@ source "drivers/zorro/Kconfig" ...@@ -1083,6 +1083,23 @@ source "drivers/zorro/Kconfig"
source kernel/power/Kconfig source kernel/power/Kconfig
config SECCOMP
bool "Enable seccomp to safely compute untrusted bytecode"
depends on PROC_FS
default y
help
This kernel feature is useful for number crunching applications
that may need to compute untrusted bytecode during their
execution. By using pipes or other transports made available to
the process as file descriptors supporting the read/write
syscalls, it's possible to isolate those applications in
their own address space using seccomp. Once seccomp is
enabled via /proc/<pid>/seccomp, it cannot be disabled
and the task is only allowed to execute a few safe syscalls
defined by each seccomp mode.
If unsure, say Y. Only embedded should say N here.
endmenu endmenu
config ISA_DMA_API config ISA_DMA_API
......
...@@ -202,7 +202,7 @@ _GLOBAL(DoSyscall) ...@@ -202,7 +202,7 @@ _GLOBAL(DoSyscall)
rlwinm r11,r11,0,~_TIFL_FORCE_NOERROR rlwinm r11,r11,0,~_TIFL_FORCE_NOERROR
stw r11,TI_LOCAL_FLAGS(r10) stw r11,TI_LOCAL_FLAGS(r10)
lwz r11,TI_FLAGS(r10) lwz r11,TI_FLAGS(r10)
andi. r11,r11,_TIF_SYSCALL_TRACE andi. r11,r11,_TIF_SYSCALL_T_OR_A
bne- syscall_dotrace bne- syscall_dotrace
syscall_dotrace_cont: syscall_dotrace_cont:
cmplwi 0,r0,NR_syscalls cmplwi 0,r0,NR_syscalls
...@@ -237,7 +237,7 @@ ret_from_syscall: ...@@ -237,7 +237,7 @@ ret_from_syscall:
SYNC SYNC
MTMSRD(r10) MTMSRD(r10)
lwz r9,TI_FLAGS(r12) lwz r9,TI_FLAGS(r12)
andi. r0,r9,(_TIF_SYSCALL_TRACE|_TIF_SIGPENDING|_TIF_NEED_RESCHED) andi. r0,r9,(_TIF_SYSCALL_T_OR_A|_TIF_SIGPENDING|_TIF_NEED_RESCHED)
bne- syscall_exit_work bne- syscall_exit_work
syscall_exit_cont: syscall_exit_cont:
#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE) #if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
...@@ -277,7 +277,8 @@ syscall_dotrace: ...@@ -277,7 +277,8 @@ syscall_dotrace:
SAVE_NVGPRS(r1) SAVE_NVGPRS(r1)
li r0,0xc00 li r0,0xc00
stw r0,TRAP(r1) stw r0,TRAP(r1)
bl do_syscall_trace addi r3,r1,STACK_FRAME_OVERHEAD
bl do_syscall_trace_enter
lwz r0,GPR0(r1) /* Restore original registers */ lwz r0,GPR0(r1) /* Restore original registers */
lwz r3,GPR3(r1) lwz r3,GPR3(r1)
lwz r4,GPR4(r1) lwz r4,GPR4(r1)
...@@ -291,7 +292,7 @@ syscall_dotrace: ...@@ -291,7 +292,7 @@ syscall_dotrace:
syscall_exit_work: syscall_exit_work:
stw r6,RESULT(r1) /* Save result */ stw r6,RESULT(r1) /* Save result */
stw r3,GPR3(r1) /* Update return value */ stw r3,GPR3(r1) /* Update return value */
andi. r0,r9,_TIF_SYSCALL_TRACE andi. r0,r9,_TIF_SYSCALL_T_OR_A
beq 5f beq 5f
ori r10,r10,MSR_EE ori r10,r10,MSR_EE
SYNC SYNC
...@@ -303,7 +304,8 @@ syscall_exit_work: ...@@ -303,7 +304,8 @@ syscall_exit_work:
li r4,0xc00 li r4,0xc00
stw r4,TRAP(r1) stw r4,TRAP(r1)
4: 4:
bl do_syscall_trace addi r3,r1,STACK_FRAME_OVERHEAD
bl do_syscall_trace_leave
REST_NVGPRS(r1) REST_NVGPRS(r1)
2: 2:
lwz r3,GPR3(r1) lwz r3,GPR3(r1)
...@@ -627,8 +629,8 @@ sigreturn_exit: ...@@ -627,8 +629,8 @@ sigreturn_exit:
subi r1,r3,STACK_FRAME_OVERHEAD subi r1,r3,STACK_FRAME_OVERHEAD
rlwinm r12,r1,0,0,18 /* current_thread_info() */ rlwinm r12,r1,0,0,18 /* current_thread_info() */
lwz r9,TI_FLAGS(r12) lwz r9,TI_FLAGS(r12)
andi. r0,r9,_TIF_SYSCALL_TRACE andi. r0,r9,_TIF_SYSCALL_T_OR_A
bnel- do_syscall_trace bnel- do_syscall_trace_leave
/* fall through */ /* fall through */
.globl ret_from_except_full .globl ret_from_except_full
......
...@@ -55,7 +55,6 @@ ...@@ -55,7 +55,6 @@
#define EXPORT_SYMTAB_STROPS #define EXPORT_SYMTAB_STROPS
extern void transfer_to_handler(void); extern void transfer_to_handler(void);
extern void do_syscall_trace(void);
extern void do_IRQ(struct pt_regs *regs); extern void do_IRQ(struct pt_regs *regs);
extern void MachineCheckException(struct pt_regs *regs); extern void MachineCheckException(struct pt_regs *regs);
extern void AlignmentException(struct pt_regs *regs); extern void AlignmentException(struct pt_regs *regs);
...@@ -74,7 +73,6 @@ extern unsigned long mm_ptov (unsigned long paddr); ...@@ -74,7 +73,6 @@ extern unsigned long mm_ptov (unsigned long paddr);
EXPORT_SYMBOL(clear_pages); EXPORT_SYMBOL(clear_pages);
EXPORT_SYMBOL(clear_user_page); EXPORT_SYMBOL(clear_user_page);
EXPORT_SYMBOL(do_signal); EXPORT_SYMBOL(do_signal);
EXPORT_SYMBOL(do_syscall_trace);
EXPORT_SYMBOL(transfer_to_handler); EXPORT_SYMBOL(transfer_to_handler);
EXPORT_SYMBOL(do_IRQ); EXPORT_SYMBOL(do_IRQ);
EXPORT_SYMBOL(MachineCheckException); EXPORT_SYMBOL(MachineCheckException);
......
...@@ -27,6 +27,9 @@ ...@@ -27,6 +27,9 @@
#include <linux/user.h> #include <linux/user.h>
#include <linux/security.h> #include <linux/security.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/seccomp.h>
#include <linux/audit.h>
#include <linux/module.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -455,11 +458,10 @@ out: ...@@ -455,11 +458,10 @@ out:
return ret; return ret;
} }
void do_syscall_trace(void) static void do_syscall_trace(void)
{ {
if (!test_thread_flag(TIF_SYSCALL_TRACE) /* the 0x80 provides a way for the tracing parent to distinguish
|| !(current->ptrace & PT_PTRACED)) between a syscall stop and SIGTRAP delivery */
return;
ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
? 0x80 : 0)); ? 0x80 : 0));
...@@ -473,3 +475,33 @@ void do_syscall_trace(void) ...@@ -473,3 +475,33 @@ void do_syscall_trace(void)
current->exit_code = 0; current->exit_code = 0;
} }
} }
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, AUDIT_ARCH_PPC,
regs->gpr[0],
regs->gpr[3], regs->gpr[4],
regs->gpr[5], regs->gpr[6]);
}
void do_syscall_trace_leave(struct pt_regs *regs)
{
secure_computing(regs->gpr[0]);
if (unlikely(current->audit_context))
audit_syscall_exit(current,
(regs->ccr&0x1000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
if ((test_thread_flag(TIF_SYSCALL_TRACE))
&& (current->ptrace & PT_PTRACED))
do_syscall_trace();
}
EXPORT_SYMBOL(do_syscall_trace_enter);
EXPORT_SYMBOL(do_syscall_trace_leave);
#ifndef _ASM_SECCOMP_H
#include <linux/unistd.h>
#define __NR_seccomp_read __NR_read
#define __NR_seccomp_write __NR_write
#define __NR_seccomp_exit __NR_exit
#define __NR_seccomp_sigreturn __NR_rt_sigreturn
#endif /* _ASM_SECCOMP_H */
...@@ -77,12 +77,19 @@ static inline struct thread_info *current_thread_info(void) ...@@ -77,12 +77,19 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling #define TIF_POLLING_NRFLAG 4 /* true if poll_idle() is polling
TIF_NEED_RESCHED */ TIF_NEED_RESCHED */
#define TIF_MEMDIE 5 #define TIF_MEMDIE 5
#define TIF_SYSCALL_AUDIT 6 /* syscall auditing active */
#define TIF_SECCOMP 7 /* secure computing */
/* as above, but as bit values */ /* as above, but as bit values */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME) #define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_SIGPENDING (1<<TIF_SIGPENDING) #define _TIF_SIGPENDING (1<<TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_SYSCALL_T_OR_A (_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP)
/* /*
* Non racy (local) flags bit numbers * Non racy (local) flags bit numbers
......
...@@ -27,15 +27,52 @@ ...@@ -27,15 +27,52 @@
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/elf.h> #include <linux/elf.h>
/* Request and reply types */ /* The netlink messages for the audit system is divided into blocks:
#define AUDIT_GET 1000 /* Get status */ * 1000 - 1099 are for commanding the audit system
#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */ * 1100 - 1199 user space trusted application messages
#define AUDIT_LIST 1002 /* List filtering rules */ * 1200 - 1299 messages internal to the audit daemon
#define AUDIT_ADD 1003 /* Add filtering rule */ * 1300 - 1399 audit event messages
#define AUDIT_DEL 1004 /* Delete filtering rule */ * 1400 - 1499 SE Linux use
#define AUDIT_USER 1005 /* Send a message from user-space */ * 1500 - 1999 future use
#define AUDIT_LOGIN 1006 /* Define the login id and informaiton */ * 2000 is for otherwise unclassified kernel audit messages
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */ *
* Messages from 1000-1199 are bi-directional. 1200-1299 are exclusively user
* space. Anything over that is kernel --> user space communication.
*/
#define AUDIT_GET 1000 /* Get status */
#define AUDIT_SET 1001 /* Set status (enable/disable/auditd) */
#define AUDIT_LIST 1002 /* List syscall filtering rules */
#define AUDIT_ADD 1003 /* Add syscall filtering rule */
#define AUDIT_DEL 1004 /* Delete syscall filtering rule */
#define AUDIT_USER 1005 /* Message from userspace -- deprecated */
#define AUDIT_LOGIN 1006 /* Define the login id and information */
#define AUDIT_WATCH_INS 1007 /* Insert file/dir watch entry */
#define AUDIT_WATCH_REM 1008 /* Remove file/dir watch entry */
#define AUDIT_WATCH_LIST 1009 /* List all file/dir watches */
#define AUDIT_SIGNAL_INFO 1010 /* Get info about sender of signal to auditd */
#define AUDIT_FIRST_USER_MSG 1100 /* Userspace messages uninteresting to kernel */
#define AUDIT_LAST_USER_MSG 1199
#define AUDIT_DAEMON_START 1200 /* Daemon startup record */
#define AUDIT_DAEMON_END 1201 /* Daemon normal stop record */
#define AUDIT_DAEMON_ABORT 1202 /* Daemon error stop record */
#define AUDIT_DAEMON_CONFIG 1203 /* Daemon config change */
#define AUDIT_SYSCALL 1300 /* Syscall event */
#define AUDIT_FS_WATCH 1301 /* Filesystem watch event */
#define AUDIT_PATH 1302 /* Filename path information */
#define AUDIT_IPC 1303 /* IPC record */
#define AUDIT_SOCKETCALL 1304 /* sys_socketcall arguments */
#define AUDIT_CONFIG_CHANGE 1305 /* Audit system configuration change */
#define AUDIT_SOCKADDR 1306 /* sockaddr copied as syscall arg */
#define AUDIT_CWD 1307 /* Current working directory */
#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
#define AUDIT_AVC_PATH 1402 /* dentry, vfsmount pair from avc */
#define AUDIT_KERNEL 2000 /* Asynchronous audit record. NOT A REQUEST. */
/* Rule flags */ /* Rule flags */
#define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */ #define AUDIT_PER_TASK 0x01 /* Apply rule at task creation (not syscall) */
...@@ -132,16 +169,9 @@ ...@@ -132,16 +169,9 @@
#define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE) #define AUDIT_ARCH_V850 (EM_V850|__AUDIT_ARCH_LE)
#define AUDIT_ARCH_X86_64 (EM_X86_64|__AUDIT_ARCH_64BIT|__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;
char data[1200];
};
#endif
struct audit_status { struct audit_status {
__u32 mask; /* Bit mask for valid entries */ __u32 mask; /* Bit mask for valid entries */
__u32 enabled; /* 1 = enabled, 0 = disbaled */ __u32 enabled; /* 1 = enabled, 0 = disabled */
__u32 failure; /* Failure-to-log action */ __u32 failure; /* Failure-to-log action */
__u32 pid; /* pid of auditd process */ __u32 pid; /* pid of auditd process */
__u32 rate_limit; /* messages rate limit (per second) */ __u32 rate_limit; /* messages rate limit (per second) */
...@@ -161,6 +191,11 @@ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */ ...@@ -161,6 +191,11 @@ struct audit_rule { /* for AUDIT_LIST, AUDIT_ADD, and AUDIT_DEL */
#ifdef __KERNEL__ #ifdef __KERNEL__
struct audit_sig_info {
uid_t uid;
pid_t pid;
};
struct audit_buffer; struct audit_buffer;
struct audit_context; struct audit_context;
struct inode; struct inode;
...@@ -185,11 +220,16 @@ extern void audit_inode(const char *name, const struct inode *inode); ...@@ -185,11 +220,16 @@ extern void audit_inode(const char *name, const struct inode *inode);
/* Private API (for audit.c only) */ /* Private API (for audit.c only) */
extern int audit_receive_filter(int type, int pid, int uid, int seq, extern int audit_receive_filter(int type, int pid, int uid, int seq,
void *data, uid_t loginuid); void *data, uid_t loginuid);
extern void audit_get_stamp(struct audit_context *ctx, extern unsigned int audit_serial(void);
struct timespec *t, unsigned int *serial); extern void auditsc_get_stamp(struct audit_context *ctx,
struct timespec *t, unsigned int *serial);
extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid); extern int audit_set_loginuid(struct task_struct *task, uid_t loginuid);
extern uid_t audit_get_loginuid(struct audit_context *ctx); extern uid_t audit_get_loginuid(struct audit_context *ctx);
extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode); extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode);
extern int audit_socketcall(int nargs, unsigned long *args);
extern int audit_sockaddr(int len, void *addr);
extern int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt);
extern void audit_signal_info(int sig, struct task_struct *t);
#else #else
#define audit_alloc(t) ({ 0; }) #define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0) #define audit_free(t) do { ; } while (0)
...@@ -198,18 +238,24 @@ extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mo ...@@ -198,18 +238,24 @@ extern int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mo
#define audit_getname(n) do { ; } while (0) #define audit_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0) #define audit_putname(n) do { ; } while (0)
#define audit_inode(n,i) do { ; } while (0) #define audit_inode(n,i) do { ; } while (0)
#define audit_receive_filter(t,p,u,s,d,l) ({ -EOPNOTSUPP; })
#define auditsc_get_stamp(c,t,s) do { BUG(); } while (0)
#define audit_get_loginuid(c) ({ -1; }) #define audit_get_loginuid(c) ({ -1; })
#define audit_ipc_perms(q,u,g,m) ({ 0; }) #define audit_ipc_perms(q,u,g,m) ({ 0; })
#define audit_socketcall(n,a) ({ 0; })
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_avc_path(dentry, mnt) ({ 0; })
#define audit_signal_info(s,t) do { ; } while (0)
#endif #endif
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
/* These are defined in audit.c */ /* These are defined in audit.c */
/* Public API */ /* Public API */
extern void audit_log(struct audit_context *ctx, extern void audit_log(struct audit_context *ctx, int type,
const char *fmt, ...) const char *fmt, ...)
__attribute__((format(printf,2,3))); __attribute__((format(printf,3,4)));
extern struct audit_buffer *audit_log_start(struct audit_context *ctx); extern struct audit_buffer *audit_log_start(struct audit_context *ctx,int type);
extern void audit_log_format(struct audit_buffer *ab, extern void audit_log_format(struct audit_buffer *ab,
const char *fmt, ...) const char *fmt, ...)
__attribute__((format(printf,2,3))); __attribute__((format(printf,2,3)));
...@@ -229,8 +275,8 @@ extern void audit_send_reply(int pid, int seq, int type, ...@@ -229,8 +275,8 @@ extern void audit_send_reply(int pid, int seq, int type,
void *payload, int size); void *payload, int size);
extern void audit_log_lost(const char *message); extern void audit_log_lost(const char *message);
#else #else
#define audit_log(t,f,...) do { ; } while (0) #define audit_log(c,t,f,...) do { ; } while (0)
#define audit_log_start(t) ({ NULL; }) #define audit_log_start(c,t) ({ NULL; })
#define audit_log_vformat(b,f,a) do { ; } while (0) #define audit_log_vformat(b,f,a) do { ; } while (0)
#define audit_log_format(b,f,...) do { ; } while (0) #define audit_log_format(b,f,...) do { ; } while (0)
#define audit_log_end(b) do { ; } while (0) #define audit_log_end(b) do { ; } while (0)
......
...@@ -164,6 +164,7 @@ config SYSCTL ...@@ -164,6 +164,7 @@ config SYSCTL
config AUDIT config AUDIT
bool "Auditing support" bool "Auditing support"
depends on NET
default y if SECURITY_SELINUX default y if SECURITY_SELINUX
help help
Enable auditing infrastructure that can be used with another Enable auditing infrastructure that can be used with another
...@@ -173,7 +174,7 @@ config AUDIT ...@@ -173,7 +174,7 @@ config AUDIT
config AUDITSYSCALL config AUDITSYSCALL
bool "Enable system-call auditing support" bool "Enable system-call auditing support"
depends on AUDIT && (X86 || PPC64 || ARCH_S390 || IA64 || UML) depends on AUDIT && (X86 || PPC || PPC64 || ARCH_S390 || IA64 || UML)
default y if SECURITY_SELINUX default y if SECURITY_SELINUX
help help
Enable low-overhead system-call auditing infrastructure that Enable low-overhead system-call auditing infrastructure that
......
...@@ -46,6 +46,8 @@ ...@@ -46,6 +46,8 @@
#include <asm/types.h> #include <asm/types.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/err.h>
#include <linux/kthread.h>
#include <linux/audit.h> #include <linux/audit.h>
...@@ -68,7 +70,7 @@ static int audit_failure = AUDIT_FAIL_PRINTK; ...@@ -68,7 +70,7 @@ static int audit_failure = AUDIT_FAIL_PRINTK;
/* If audit records are to be written to the netlink socket, audit_pid /* If audit records are to be written to the netlink socket, audit_pid
* contains the (non-zero) pid. */ * contains the (non-zero) pid. */
static int audit_pid; int audit_pid;
/* If audit_limit is non-zero, limit the rate of sending audit records /* If audit_limit is non-zero, limit the rate of sending audit records
* to that number per second. This prevents DoS attacks, but results in * to that number per second. This prevents DoS attacks, but results in
...@@ -77,7 +79,10 @@ static int audit_rate_limit; ...@@ -77,7 +79,10 @@ static int audit_rate_limit;
/* Number of outstanding audit_buffers allowed. */ /* Number of outstanding audit_buffers allowed. */
static int audit_backlog_limit = 64; static int audit_backlog_limit = 64;
static atomic_t audit_backlog = ATOMIC_INIT(0);
/* The identity of the user shutting down the audit system. */
uid_t audit_sig_uid = -1;
pid_t audit_sig_pid = -1;
/* Records can be lost in several ways: /* Records can be lost in several ways:
0) [suppressed in audit_alloc] 0) [suppressed in audit_alloc]
...@@ -91,19 +96,17 @@ static atomic_t audit_lost = ATOMIC_INIT(0); ...@@ -91,19 +96,17 @@ static atomic_t audit_lost = ATOMIC_INIT(0);
/* The netlink socket. */ /* The netlink socket. */
static struct sock *audit_sock; static struct sock *audit_sock;
/* There are two lists of audit buffers. The txlist contains audit /* The audit_freelist is a list of pre-allocated audit buffers (if more
* buffers that cannot be sent immediately to the netlink device because
* we are in an irq context (these are sent later in a tasklet).
*
* The second list is a list of pre-allocated audit buffers (if more
* than AUDIT_MAXFREE are in use, the audit buffer is freed instead of * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of
* being placed on the freelist). */ * being placed on the freelist). */
static DEFINE_SPINLOCK(audit_txlist_lock);
static DEFINE_SPINLOCK(audit_freelist_lock); static DEFINE_SPINLOCK(audit_freelist_lock);
static int audit_freelist_count = 0; static int audit_freelist_count = 0;
static LIST_HEAD(audit_txlist);
static LIST_HEAD(audit_freelist); static LIST_HEAD(audit_freelist);
static struct sk_buff_head audit_skb_queue;
static struct task_struct *kauditd_task;
static DECLARE_WAIT_QUEUE_HEAD(kauditd_wait);
/* There are three lists of rules -- one to search at task creation /* There are three lists of rules -- one to search at task creation
* time, one to search at syscall entry time, and another to search at * time, one to search at syscall entry time, and another to search at
* syscall exit time. */ * syscall exit time. */
...@@ -112,7 +115,7 @@ static LIST_HEAD(audit_entlist); ...@@ -112,7 +115,7 @@ static LIST_HEAD(audit_entlist);
static LIST_HEAD(audit_extlist); static LIST_HEAD(audit_extlist);
/* The netlink socket is only to be read by 1 CPU, which lets us assume /* The netlink socket is only to be read by 1 CPU, which lets us assume
* that list additions and deletions never happen simultaneiously in * that list additions and deletions never happen simultaneously in
* auditsc.c */ * auditsc.c */
static DECLARE_MUTEX(audit_netlink_sem); static DECLARE_MUTEX(audit_netlink_sem);
...@@ -132,21 +135,14 @@ static DECLARE_MUTEX(audit_netlink_sem); ...@@ -132,21 +135,14 @@ static DECLARE_MUTEX(audit_netlink_sem);
* use simultaneously. */ * use simultaneously. */
struct audit_buffer { struct audit_buffer {
struct list_head list; struct list_head list;
struct sk_buff_head sklist; /* formatted skbs ready to send */ struct sk_buff *skb; /* formatted skb ready to send */
struct audit_context *ctx; /* NULL or associated context */ struct audit_context *ctx; /* NULL or associated context */
int len; /* used area of tmp */
char tmp[AUDIT_BUFSIZ];
/* Pointer to header and contents */
struct nlmsghdr *nlh;
int total;
int type;
int pid;
}; };
void audit_set_type(struct audit_buffer *ab, int type) static void audit_set_pid(struct audit_buffer *ab, pid_t pid)
{ {
ab->type = type; struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
nlh->nlmsg_pid = pid;
} }
struct audit_entry { struct audit_entry {
...@@ -154,9 +150,6 @@ struct audit_entry { ...@@ -154,9 +150,6 @@ struct audit_entry {
struct audit_rule rule; struct audit_rule rule;
}; };
static void audit_log_end_irq(struct audit_buffer *ab);
static void audit_log_end_fast(struct audit_buffer *ab);
static void audit_panic(const char *message) static void audit_panic(const char *message)
{ {
switch (audit_failure) switch (audit_failure)
...@@ -227,10 +220,8 @@ void audit_log_lost(const char *message) ...@@ -227,10 +220,8 @@ void audit_log_lost(const char *message)
if (print) { if (print) {
printk(KERN_WARNING printk(KERN_WARNING
"audit: audit_lost=%d audit_backlog=%d" "audit: audit_lost=%d audit_rate_limit=%d audit_backlog_limit=%d\n",
" audit_rate_limit=%d audit_backlog_limit=%d\n",
atomic_read(&audit_lost), atomic_read(&audit_lost),
atomic_read(&audit_backlog),
audit_rate_limit, audit_rate_limit,
audit_backlog_limit); audit_backlog_limit);
audit_panic(message); audit_panic(message);
...@@ -242,7 +233,8 @@ static int audit_set_rate_limit(int limit, uid_t loginuid) ...@@ -242,7 +233,8 @@ static int audit_set_rate_limit(int limit, uid_t loginuid)
{ {
int old = audit_rate_limit; int old = audit_rate_limit;
audit_rate_limit = limit; audit_rate_limit = limit;
audit_log(NULL, "audit_rate_limit=%d old=%d by auid %u", audit_log(NULL, AUDIT_CONFIG_CHANGE,
"audit_rate_limit=%d old=%d by auid=%u",
audit_rate_limit, old, loginuid); audit_rate_limit, old, loginuid);
return old; return old;
} }
...@@ -251,7 +243,8 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid) ...@@ -251,7 +243,8 @@ static int audit_set_backlog_limit(int limit, uid_t loginuid)
{ {
int old = audit_backlog_limit; int old = audit_backlog_limit;
audit_backlog_limit = limit; audit_backlog_limit = limit;
audit_log(NULL, "audit_backlog_limit=%d old=%d by auid %u", audit_log(NULL, AUDIT_CONFIG_CHANGE,
"audit_backlog_limit=%d old=%d by auid=%u",
audit_backlog_limit, old, loginuid); audit_backlog_limit, old, loginuid);
return old; return old;
} }
...@@ -262,8 +255,9 @@ static int audit_set_enabled(int state, uid_t loginuid) ...@@ -262,8 +255,9 @@ static int audit_set_enabled(int state, uid_t loginuid)
if (state != 0 && state != 1) if (state != 0 && state != 1)
return -EINVAL; return -EINVAL;
audit_enabled = state; audit_enabled = state;
audit_log(NULL, "audit_enabled=%d old=%d by auid %u", audit_log(NULL, AUDIT_CONFIG_CHANGE,
audit_enabled, old, loginuid); "audit_enabled=%d old=%d by auid=%u",
audit_enabled, old, loginuid);
return old; return old;
} }
...@@ -275,12 +269,44 @@ static int audit_set_failure(int state, uid_t loginuid) ...@@ -275,12 +269,44 @@ static int audit_set_failure(int state, uid_t loginuid)
&& state != AUDIT_FAIL_PANIC) && state != AUDIT_FAIL_PANIC)
return -EINVAL; return -EINVAL;
audit_failure = state; audit_failure = state;
audit_log(NULL, "audit_failure=%d old=%d by auid %u", audit_log(NULL, AUDIT_CONFIG_CHANGE,
audit_failure, old, loginuid); "audit_failure=%d old=%d by auid=%u",
audit_failure, old, loginuid);
return old; return old;
} }
#ifdef CONFIG_NET int kauditd_thread(void *dummy)
{
struct sk_buff *skb;
while (1) {
skb = skb_dequeue(&audit_skb_queue);
if (skb) {
if (audit_pid) {
int err = netlink_unicast(audit_sock, skb, audit_pid, 0);
if (err < 0) {
BUG_ON(err != -ECONNREFUSED); /* Shoudn't happen */
printk(KERN_ERR "audit: *NO* daemon at audit_pid=%d\n", audit_pid);
audit_pid = 0;
}
} else {
printk(KERN_ERR "%s\n", skb->data + NLMSG_SPACE(0));
kfree_skb(skb);
}
} else {
DECLARE_WAITQUEUE(wait, current);
set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue(&kauditd_wait, &wait);
if (!skb_queue_len(&audit_skb_queue))
schedule();
__set_current_state(TASK_RUNNING);
remove_wait_queue(&kauditd_wait, &wait);
}
}
}
void audit_send_reply(int pid, int seq, int type, int done, int multi, void audit_send_reply(int pid, int seq, int type, int done, int multi,
void *payload, int size) void *payload, int size)
{ {
...@@ -293,13 +319,16 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi, ...@@ -293,13 +319,16 @@ void audit_send_reply(int pid, int seq, int type, int done, int multi,
skb = alloc_skb(len, GFP_KERNEL); skb = alloc_skb(len, GFP_KERNEL);
if (!skb) if (!skb)
goto nlmsg_failure; return;
nlh = NLMSG_PUT(skb, pid, seq, t, len - sizeof(*nlh)); nlh = NLMSG_PUT(skb, pid, seq, t, size);
nlh->nlmsg_flags = flags; nlh->nlmsg_flags = flags;
data = NLMSG_DATA(nlh); data = NLMSG_DATA(nlh);
memcpy(data, payload, size); memcpy(data, payload, size);
netlink_unicast(audit_sock, skb, pid, MSG_DONTWAIT);
/* Ignore failure. It'll only happen if the sender goes away,
because our timeout is set to infinite. */
netlink_unicast(audit_sock, skb, pid, 0);
return; return;
nlmsg_failure: /* Used by NLMSG_PUT */ nlmsg_failure: /* Used by NLMSG_PUT */
...@@ -321,10 +350,12 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type) ...@@ -321,10 +350,12 @@ static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
case AUDIT_SET: case AUDIT_SET:
case AUDIT_ADD: case AUDIT_ADD:
case AUDIT_DEL: case AUDIT_DEL:
case AUDIT_SIGNAL_INFO:
if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL)) if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
err = -EPERM; err = -EPERM;
break; break;
case AUDIT_USER: case AUDIT_USER:
case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
if (!cap_raised(eff_cap, CAP_AUDIT_WRITE)) if (!cap_raised(eff_cap, CAP_AUDIT_WRITE))
err = -EPERM; err = -EPERM;
break; break;
...@@ -344,11 +375,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -344,11 +375,21 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
struct audit_buffer *ab; struct audit_buffer *ab;
u16 msg_type = nlh->nlmsg_type; u16 msg_type = nlh->nlmsg_type;
uid_t loginuid; /* loginuid of sender */ uid_t loginuid; /* loginuid of sender */
struct audit_sig_info sig_data;
err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type); err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
if (err) if (err)
return err; return err;
/* As soon as there's any sign of userspace auditd, start kauditd to talk to it */
if (!kauditd_task)
kauditd_task = kthread_run(kauditd_thread, NULL, "kauditd");
if (IS_ERR(kauditd_task)) {
err = PTR_ERR(kauditd_task);
kauditd_task = NULL;
return err;
}
pid = NETLINK_CREDS(skb)->pid; pid = NETLINK_CREDS(skb)->pid;
uid = NETLINK_CREDS(skb)->uid; uid = NETLINK_CREDS(skb)->uid;
loginuid = NETLINK_CB(skb).loginuid; loginuid = NETLINK_CB(skb).loginuid;
...@@ -363,7 +404,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -363,7 +404,7 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
status_set.rate_limit = audit_rate_limit; status_set.rate_limit = audit_rate_limit;
status_set.backlog_limit = audit_backlog_limit; status_set.backlog_limit = audit_backlog_limit;
status_set.lost = atomic_read(&audit_lost); status_set.lost = atomic_read(&audit_lost);
status_set.backlog = atomic_read(&audit_backlog); status_set.backlog = skb_queue_len(&audit_skb_queue);
audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0, audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_GET, 0, 0,
&status_set, sizeof(status_set)); &status_set, sizeof(status_set));
break; break;
...@@ -382,7 +423,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -382,7 +423,8 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
if (status_get->mask & AUDIT_STATUS_PID) { if (status_get->mask & AUDIT_STATUS_PID) {
int old = audit_pid; int old = audit_pid;
audit_pid = status_get->pid; audit_pid = status_get->pid;
audit_log(NULL, "audit_pid=%d old=%d by auid %u", audit_log(NULL, AUDIT_CONFIG_CHANGE,
"audit_pid=%d old=%d by auid=%u",
audit_pid, old, loginuid); audit_pid, old, loginuid);
} }
if (status_get->mask & AUDIT_STATUS_RATE_LIMIT) if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
...@@ -392,18 +434,15 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -392,18 +434,15 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
loginuid); loginuid);
break; break;
case AUDIT_USER: case AUDIT_USER:
ab = audit_log_start(NULL); case AUDIT_FIRST_USER_MSG...AUDIT_LAST_USER_MSG:
ab = audit_log_start(NULL, msg_type);
if (!ab) if (!ab)
break; /* audit_panic has been called */ break; /* audit_panic has been called */
audit_log_format(ab, audit_log_format(ab,
"user pid=%d uid=%d length=%d loginuid=%u" "user pid=%d uid=%u auid=%u"
" msg='%.1024s'", " msg='%.1024s'",
pid, uid, pid, uid, loginuid, (char *)data);
(int)(nlh->nlmsg_len audit_set_pid(ab, pid);
- ((char *)data - (char *)nlh)),
loginuid, (char *)data);
ab->type = AUDIT_USER;
ab->pid = pid;
audit_log_end(ab); audit_log_end(ab);
break; break;
case AUDIT_ADD: case AUDIT_ADD:
...@@ -412,12 +451,14 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) ...@@ -412,12 +451,14 @@ static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
return -EINVAL; return -EINVAL;
/* fallthrough */ /* fallthrough */
case AUDIT_LIST: case AUDIT_LIST:
#ifdef CONFIG_AUDITSYSCALL
err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid, err = audit_receive_filter(nlh->nlmsg_type, NETLINK_CB(skb).pid,
uid, seq, data, loginuid); uid, seq, data, loginuid);
#else break;
err = -EOPNOTSUPP; case AUDIT_SIGNAL_INFO:
#endif sig_data.uid = audit_sig_uid;
sig_data.pid = audit_sig_pid;
audit_send_reply(NETLINK_CB(skb).pid, seq, AUDIT_SIGNAL_INFO,
0, 0, &sig_data, sizeof(sig_data));
break; break;
default: default:
err = -EINVAL; err = -EINVAL;
...@@ -467,87 +508,6 @@ static void audit_receive(struct sock *sk, int length) ...@@ -467,87 +508,6 @@ static void audit_receive(struct sock *sk, int length)
up(&audit_netlink_sem); up(&audit_netlink_sem);
} }
/* Move data from tmp buffer into an skb. This is an extra copy, and
* that is unfortunate. However, the copy will only occur when a record
* is being written to user space, which is already a high-overhead
* operation. (Elimination of the copy is possible, for example, by
* writing directly into a pre-allocated skb, at the cost of wasting
* memory. */
static void audit_log_move(struct audit_buffer *ab)
{
struct sk_buff *skb;
char *start;
int extra = ab->nlh ? 0 : NLMSG_SPACE(0);
/* possible resubmission */
if (ab->len == 0)
return;
skb = skb_peek_tail(&ab->sklist);
if (!skb || skb_tailroom(skb) <= ab->len + extra) {
skb = alloc_skb(2 * ab->len + extra, GFP_ATOMIC);
if (!skb) {
ab->len = 0; /* Lose information in ab->tmp */
audit_log_lost("out of memory in audit_log_move");
return;
}
__skb_queue_tail(&ab->sklist, skb);
if (!ab->nlh)
ab->nlh = (struct nlmsghdr *)skb_put(skb,
NLMSG_SPACE(0));
}
start = skb_put(skb, ab->len);
memcpy(start, ab->tmp, ab->len);
ab->len = 0;
}
/* Iterate over the skbuff in the audit_buffer, sending their contents
* to user space. */
static inline int audit_log_drain(struct audit_buffer *ab)
{
struct sk_buff *skb;
while ((skb = skb_dequeue(&ab->sklist))) {
int retval = 0;
if (audit_pid) {
if (ab->nlh) {
ab->nlh->nlmsg_len = ab->total;
ab->nlh->nlmsg_type = ab->type;
ab->nlh->nlmsg_flags = 0;
ab->nlh->nlmsg_seq = 0;
ab->nlh->nlmsg_pid = ab->pid;
}
skb_get(skb); /* because netlink_* frees */
retval = netlink_unicast(audit_sock, skb, audit_pid,
MSG_DONTWAIT);
}
if (retval == -EAGAIN &&
(atomic_read(&audit_backlog)) < audit_backlog_limit) {
skb_queue_head(&ab->sklist, skb);
audit_log_end_irq(ab);
return 1;
}
if (retval < 0) {
if (retval == -ECONNREFUSED) {
printk(KERN_ERR
"audit: *NO* daemon at audit_pid=%d\n",
audit_pid);
audit_pid = 0;
} else
audit_log_lost("netlink socket too busy");
}
if (!audit_pid) { /* No daemon */
int offset = ab->nlh ? NLMSG_SPACE(0) : 0;
int len = skb->len - offset;
skb->data[offset + len] = '\0';
printk(KERN_ERR "%s\n", skb->data + offset);
}
kfree_skb(skb);
ab->nlh = NULL;
}
return 0;
}
/* Initialize audit support at boot time. */ /* Initialize audit support at boot time. */
static int __init audit_init(void) static int __init audit_init(void)
...@@ -558,40 +518,13 @@ static int __init audit_init(void) ...@@ -558,40 +518,13 @@ static int __init audit_init(void)
if (!audit_sock) if (!audit_sock)
audit_panic("cannot initialize netlink socket"); audit_panic("cannot initialize netlink socket");
audit_sock->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT;
skb_queue_head_init(&audit_skb_queue);
audit_initialized = 1; audit_initialized = 1;
audit_enabled = audit_default; audit_enabled = audit_default;
audit_log(NULL, "initialized"); audit_log(NULL, AUDIT_KERNEL, "initialized");
return 0;
}
#else
/* Without CONFIG_NET, we have no skbuffs. For now, print what we have
* in the buffer. */
static void audit_log_move(struct audit_buffer *ab)
{
printk(KERN_ERR "%*.*s\n", ab->len, ab->len, ab->tmp);
ab->len = 0;
}
static inline int audit_log_drain(struct audit_buffer *ab)
{
return 0;
}
/* Initialize audit support at boot time. */
int __init audit_init(void)
{
printk(KERN_INFO "audit: initializing WITHOUT netlink support\n");
audit_sock = NULL;
audit_pid = 0;
audit_initialized = 1;
audit_enabled = audit_default;
audit_log(NULL, "initialized");
return 0; return 0;
} }
#endif
__initcall(audit_init); __initcall(audit_init);
/* Process kernel command-line parameter at boot time. audit=0 or audit=1. */ /* Process kernel command-line parameter at boot time. audit=0 or audit=1. */
...@@ -608,6 +541,102 @@ static int __init audit_enable(char *str) ...@@ -608,6 +541,102 @@ static int __init audit_enable(char *str)
__setup("audit=", audit_enable); __setup("audit=", audit_enable);
static void audit_buffer_free(struct audit_buffer *ab)
{
unsigned long flags;
if (!ab)
return;
if (ab->skb)
kfree_skb(ab->skb);
spin_lock_irqsave(&audit_freelist_lock, flags);
if (++audit_freelist_count > AUDIT_MAXFREE)
kfree(ab);
else
list_add(&ab->list, &audit_freelist);
spin_unlock_irqrestore(&audit_freelist_lock, flags);
}
static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx,
int gfp_mask, int type)
{
unsigned long flags;
struct audit_buffer *ab = NULL;
struct nlmsghdr *nlh;
spin_lock_irqsave(&audit_freelist_lock, flags);
if (!list_empty(&audit_freelist)) {
ab = list_entry(audit_freelist.next,
struct audit_buffer, list);
list_del(&ab->list);
--audit_freelist_count;
}
spin_unlock_irqrestore(&audit_freelist_lock, flags);
if (!ab) {
ab = kmalloc(sizeof(*ab), gfp_mask);
if (!ab)
goto err;
}
ab->skb = alloc_skb(AUDIT_BUFSIZ, gfp_mask);
if (!ab->skb)
goto err;
ab->ctx = ctx;
nlh = (struct nlmsghdr *)skb_put(ab->skb, NLMSG_SPACE(0));
nlh->nlmsg_type = type;
nlh->nlmsg_flags = 0;
nlh->nlmsg_pid = 0;
nlh->nlmsg_seq = 0;
return ab;
err:
audit_buffer_free(ab);
return NULL;
}
/* Compute a serial number for the audit record. Audit records are
* written to user-space as soon as they are generated, so a complete
* audit record may be written in several pieces. The timestamp of the
* record and this serial number are used by the user-space tools to
* determine which pieces belong to the same audit record. The
* (timestamp,serial) tuple is unique for each syscall and is live from
* syscall entry to syscall exit.
*
* Atomic values are only guaranteed to be 24-bit, so we count down.
*
* NOTE: Another possibility is to store the formatted records off the
* audit context (for those records that have a context), and emit them
* all at syscall exit. However, this could delay the reporting of
* significant errors until syscall exit (or never, if the system
* halts). */
unsigned int audit_serial(void)
{
static atomic_t serial = ATOMIC_INIT(0xffffff);
unsigned int a, b;
do {
a = atomic_read(&serial);
if (atomic_dec_and_test(&serial))
atomic_set(&serial, 0xffffff);
b = atomic_read(&serial);
} while (b != a - 1);
return 0xffffff - b;
}
static inline void audit_get_stamp(struct audit_context *ctx,
struct timespec *t, unsigned int *serial)
{
if (ctx)
auditsc_get_stamp(ctx, t, serial);
else {
*t = CURRENT_TIME;
*serial = audit_serial();
}
}
/* Obtain an audit buffer. This routine does locking to obtain the /* Obtain an audit buffer. This routine does locking to obtain the
* audit buffer, but then no locking is required for calls to * audit buffer, but then no locking is required for calls to
...@@ -615,10 +644,9 @@ __setup("audit=", audit_enable); ...@@ -615,10 +644,9 @@ __setup("audit=", audit_enable);
* syscall, then the syscall is marked as auditable and an audit record * syscall, then the syscall is marked as auditable and an audit record
* will be written at syscall exit. If there is no associated task, tsk * will be written at syscall exit. If there is no associated task, tsk
* should be NULL. */ * should be NULL. */
struct audit_buffer *audit_log_start(struct audit_context *ctx) struct audit_buffer *audit_log_start(struct audit_context *ctx, int type)
{ {
struct audit_buffer *ab = NULL; struct audit_buffer *ab = NULL;
unsigned long flags;
struct timespec t; struct timespec t;
unsigned int serial; unsigned int serial;
...@@ -626,57 +654,48 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx) ...@@ -626,57 +654,48 @@ struct audit_buffer *audit_log_start(struct audit_context *ctx)
return NULL; return NULL;
if (audit_backlog_limit if (audit_backlog_limit
&& atomic_read(&audit_backlog) > audit_backlog_limit) { && skb_queue_len(&audit_skb_queue) > audit_backlog_limit) {
if (audit_rate_check()) if (audit_rate_check())
printk(KERN_WARNING printk(KERN_WARNING
"audit: audit_backlog=%d > " "audit: audit_backlog=%d > "
"audit_backlog_limit=%d\n", "audit_backlog_limit=%d\n",
atomic_read(&audit_backlog), skb_queue_len(&audit_skb_queue),
audit_backlog_limit); audit_backlog_limit);
audit_log_lost("backlog limit exceeded"); audit_log_lost("backlog limit exceeded");
return NULL; return NULL;
} }
spin_lock_irqsave(&audit_freelist_lock, flags); ab = audit_buffer_alloc(ctx, GFP_ATOMIC, type);
if (!list_empty(&audit_freelist)) {
ab = list_entry(audit_freelist.next,
struct audit_buffer, list);
list_del(&ab->list);
--audit_freelist_count;
}
spin_unlock_irqrestore(&audit_freelist_lock, flags);
if (!ab)
ab = kmalloc(sizeof(*ab), GFP_ATOMIC);
if (!ab) { if (!ab) {
audit_log_lost("out of memory in audit_log_start"); audit_log_lost("out of memory in audit_log_start");
return NULL; return NULL;
} }
atomic_inc(&audit_backlog); audit_get_stamp(ab->ctx, &t, &serial);
skb_queue_head_init(&ab->sklist);
ab->ctx = ctx;
ab->len = 0;
ab->nlh = NULL;
ab->total = 0;
ab->type = AUDIT_KERNEL;
ab->pid = 0;
#ifdef CONFIG_AUDITSYSCALL
if (ab->ctx)
audit_get_stamp(ab->ctx, &t, &serial);
else
#endif
{
t = CURRENT_TIME;
serial = 0;
}
audit_log_format(ab, "audit(%lu.%03lu:%u): ", audit_log_format(ab, "audit(%lu.%03lu:%u): ",
t.tv_sec, t.tv_nsec/1000000, serial); t.tv_sec, t.tv_nsec/1000000, serial);
return ab; return ab;
} }
/**
* audit_expand - expand skb in the audit buffer
* @ab: audit_buffer
*
* Returns 0 (no space) on failed expansion, or available space if
* successful.
*/
static inline int audit_expand(struct audit_buffer *ab, int extra)
{
struct sk_buff *skb = ab->skb;
int ret = pskb_expand_head(skb, skb_headroom(skb), extra,
GFP_ATOMIC);
if (ret < 0) {
audit_log_lost("out of memory in audit_expand");
return 0;
}
return skb_tailroom(skb);
}
/* Format an audit message into the audit buffer. If there isn't enough /* Format an audit message into the audit buffer. If there isn't enough
* room in the audit buffer, more room will be allocated and vsnprint * room in the audit buffer, more room will be allocated and vsnprint
...@@ -686,26 +705,35 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt, ...@@ -686,26 +705,35 @@ static void audit_log_vformat(struct audit_buffer *ab, const char *fmt,
va_list args) va_list args)
{ {
int len, avail; int len, avail;
struct sk_buff *skb;
va_list args2;
if (!ab) if (!ab)
return; return;
avail = sizeof(ab->tmp) - ab->len; BUG_ON(!ab->skb);
if (avail <= 0) { skb = ab->skb;
audit_log_move(ab); avail = skb_tailroom(skb);
avail = sizeof(ab->tmp) - ab->len; if (avail == 0) {
avail = audit_expand(ab, AUDIT_BUFSIZ);
if (!avail)
goto out;
} }
len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); va_copy(args2, args);
len = vsnprintf(skb->tail, avail, fmt, args);
if (len >= avail) { if (len >= avail) {
/* The printk buffer is 1024 bytes long, so if we get /* The printk buffer is 1024 bytes long, so if we get
* here and AUDIT_BUFSIZ is at least 1024, then we can * here and AUDIT_BUFSIZ is at least 1024, then we can
* log everything that printk could have logged. */ * log everything that printk could have logged. */
audit_log_move(ab); avail = audit_expand(ab, max_t(unsigned, AUDIT_BUFSIZ, 1+len-avail));
avail = sizeof(ab->tmp) - ab->len; if (!avail)
len = vsnprintf(ab->tmp + ab->len, avail, fmt, args); goto out;
len = vsnprintf(skb->tail, avail, fmt, args2);
} }
ab->len += (len < avail) ? len : avail; if (len > 0)
ab->total += (len < avail) ? len : avail; skb_put(skb, len);
out:
return;
} }
/* Format a message into the audit buffer. All the work is done in /* Format a message into the audit buffer. All the work is done in
...@@ -721,20 +749,47 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...) ...@@ -721,20 +749,47 @@ void audit_log_format(struct audit_buffer *ab, const char *fmt, ...)
va_end(args); va_end(args);
} }
void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf, size_t len) /* This function will take the passed buf and convert it into a string of
* ascii hex digits. The new string is placed onto the skb. */
void audit_log_hex(struct audit_buffer *ab, const unsigned char *buf,
size_t len)
{ {
int i; int i, avail, new_len;
unsigned char *ptr;
struct sk_buff *skb;
static const unsigned char *hex = "0123456789ABCDEF";
BUG_ON(!ab->skb);
skb = ab->skb;
avail = skb_tailroom(skb);
new_len = len<<1;
if (new_len >= avail) {
/* Round the buffer request up to the next multiple */
new_len = AUDIT_BUFSIZ*(((new_len-avail)/AUDIT_BUFSIZ) + 1);
avail = audit_expand(ab, new_len);
if (!avail)
return;
}
for (i=0; i<len; i++) ptr = skb->tail;
audit_log_format(ab, "%02x", buf[i]); for (i=0; i<len; i++) {
*ptr++ = hex[(buf[i] & 0xF0)>>4]; /* Upper nibble */
*ptr++ = hex[buf[i] & 0x0F]; /* Lower nibble */
}
*ptr = 0;
skb_put(skb, len << 1); /* new string is twice the old string */
} }
/* This code will escape a string that is passed to it if the string
* contains a control character, unprintable character, double quote mark,
* or a space. Unescaped strings will start and end with a double quote mark.
* Strings that are escaped are printed in hex (2 digits per char). */
void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
{ {
const unsigned char *p = string; const unsigned char *p = string;
while (*p) { while (*p) {
if (*p == '"' || *p == ' ' || *p < 0x20 || *p > 0x7f) { if (*p == '"' || *p < 0x21 || *p > 0x7f) {
audit_log_hex(ab, string, strlen(string)); audit_log_hex(ab, string, strlen(string));
return; return;
} }
...@@ -743,117 +798,63 @@ void audit_log_untrustedstring(struct audit_buffer *ab, const char *string) ...@@ -743,117 +798,63 @@ void audit_log_untrustedstring(struct audit_buffer *ab, const char *string)
audit_log_format(ab, "\"%s\"", string); audit_log_format(ab, "\"%s\"", string);
} }
/* This is a helper-function to print the escaped d_path */
/* This is a helper-function to print the d_path without using a static
* buffer or allocating another buffer in addition to the one in
* audit_buffer. */
void audit_log_d_path(struct audit_buffer *ab, const char *prefix, void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
struct dentry *dentry, struct vfsmount *vfsmnt) struct dentry *dentry, struct vfsmount *vfsmnt)
{ {
char *p; char *p, *path;
int len, avail;
if (prefix) audit_log_format(ab, " %s", prefix); if (prefix)
audit_log_format(ab, " %s", prefix);
if (ab->len > 128)
audit_log_move(ab);
avail = sizeof(ab->tmp) - ab->len;
p = d_path(dentry, vfsmnt, ab->tmp + ab->len, avail);
if (IS_ERR(p)) {
/* FIXME: can we save some information here? */
audit_log_format(ab, "<toolong>");
} else {
/* path isn't at start of buffer */
len = (ab->tmp + sizeof(ab->tmp) - 1) - p;
memmove(ab->tmp + ab->len, p, len);
ab->len += len;
ab->total += len;
}
}
/* Remove queued messages from the audit_txlist and send them to userspace. */
static void audit_tasklet_handler(unsigned long arg)
{
LIST_HEAD(list);
struct audit_buffer *ab;
unsigned long flags;
spin_lock_irqsave(&audit_txlist_lock, flags); /* We will allow 11 spaces for ' (deleted)' to be appended */
list_splice_init(&audit_txlist, &list); path = kmalloc(PATH_MAX+11, GFP_KERNEL);
spin_unlock_irqrestore(&audit_txlist_lock, flags); if (!path) {
audit_log_format(ab, "<no memory>");
while (!list_empty(&list)) { return;
ab = list_entry(list.next, struct audit_buffer, list);
list_del(&ab->list);
audit_log_end_fast(ab);
} }
p = d_path(dentry, vfsmnt, path, PATH_MAX+11);
if (IS_ERR(p)) { /* Should never happen since we send PATH_MAX */
/* FIXME: can we save some information here? */
audit_log_format(ab, "<too long>");
} else
audit_log_untrustedstring(ab, p);
kfree(path);
} }
static DECLARE_TASKLET(audit_tasklet, audit_tasklet_handler, 0);
/* The netlink_* functions cannot be called inside an irq context, so /* The netlink_* functions cannot be called inside an irq context, so
* the audit buffer is places on a queue and a tasklet is scheduled to * the audit buffer is places on a queue and a tasklet is scheduled to
* remove them from the queue outside the irq context. May be called in * remove them from the queue outside the irq context. May be called in
* any context. */ * any context. */
static void audit_log_end_irq(struct audit_buffer *ab) void audit_log_end(struct audit_buffer *ab)
{
unsigned long flags;
if (!ab)
return;
spin_lock_irqsave(&audit_txlist_lock, flags);
list_add_tail(&ab->list, &audit_txlist);
spin_unlock_irqrestore(&audit_txlist_lock, flags);
tasklet_schedule(&audit_tasklet);
}
/* Send the message in the audit buffer directly to user space. May not
* be called in an irq context. */
static void audit_log_end_fast(struct audit_buffer *ab)
{ {
unsigned long flags;
BUG_ON(in_irq());
if (!ab) if (!ab)
return; return;
if (!audit_rate_check()) { if (!audit_rate_check()) {
audit_log_lost("rate limit exceeded"); audit_log_lost("rate limit exceeded");
} else { } else {
audit_log_move(ab); if (audit_pid) {
if (audit_log_drain(ab)) struct nlmsghdr *nlh = (struct nlmsghdr *)ab->skb->data;
return; nlh->nlmsg_len = ab->skb->len - NLMSG_SPACE(0);
skb_queue_tail(&audit_skb_queue, ab->skb);
ab->skb = NULL;
wake_up_interruptible(&kauditd_wait);
} else {
printk("%s\n", ab->skb->data + NLMSG_SPACE(0));
}
} }
audit_buffer_free(ab);
atomic_dec(&audit_backlog);
spin_lock_irqsave(&audit_freelist_lock, flags);
if (++audit_freelist_count > AUDIT_MAXFREE)
kfree(ab);
else
list_add(&ab->list, &audit_freelist);
spin_unlock_irqrestore(&audit_freelist_lock, flags);
}
/* Send or queue the message in the audit buffer, depending on the
* current context. (A convenience function that may be called in any
* context.) */
void audit_log_end(struct audit_buffer *ab)
{
if (in_irq())
audit_log_end_irq(ab);
else
audit_log_end_fast(ab);
} }
/* Log an audit record. This is a convenience function that calls /* Log an audit record. This is a convenience function that calls
* audit_log_start, audit_log_vformat, and audit_log_end. It may be * audit_log_start, audit_log_vformat, and audit_log_end. It may be
* called in any context. */ * called in any context. */
void audit_log(struct audit_context *ctx, const char *fmt, ...) void audit_log(struct audit_context *ctx, int type, const char *fmt, ...)
{ {
struct audit_buffer *ab; struct audit_buffer *ab;
va_list args; va_list args;
ab = audit_log_start(ctx); ab = audit_log_start(ctx, type);
if (ab) { if (ab) {
va_start(args, fmt); va_start(args, fmt);
audit_log_vformat(ab, fmt, args); audit_log_vformat(ab, fmt, args);
......
...@@ -34,7 +34,8 @@ ...@@ -34,7 +34,8 @@
#include <asm/types.h> #include <asm/types.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mount.h>
#include <linux/socket.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/time.h> #include <linux/time.h>
...@@ -112,6 +113,23 @@ struct audit_aux_data_ipcctl { ...@@ -112,6 +113,23 @@ struct audit_aux_data_ipcctl {
mode_t mode; mode_t mode;
}; };
struct audit_aux_data_socketcall {
struct audit_aux_data d;
int nargs;
unsigned long args[0];
};
struct audit_aux_data_sockaddr {
struct audit_aux_data d;
int len;
char a[0];
};
struct audit_aux_data_path {
struct audit_aux_data d;
struct dentry *dentry;
struct vfsmount *mnt;
};
/* The per-task audit context. */ /* The per-task audit context. */
struct audit_context { struct audit_context {
...@@ -127,6 +145,8 @@ struct audit_context { ...@@ -127,6 +145,8 @@ struct audit_context {
int auditable; /* 1 if record should be written */ int auditable; /* 1 if record should be written */
int name_count; int name_count;
struct audit_names names[AUDIT_NAMES]; struct audit_names names[AUDIT_NAMES];
struct dentry * pwd;
struct vfsmount * pwdmnt;
struct audit_context *previous; /* For nested syscalls */ struct audit_context *previous; /* For nested syscalls */
struct audit_aux_data *aux; struct audit_aux_data *aux;
...@@ -157,6 +177,8 @@ struct audit_entry { ...@@ -157,6 +177,8 @@ struct audit_entry {
struct audit_rule rule; struct audit_rule rule;
}; };
extern int audit_pid;
/* Check to see if two rules are identical. It is called from /* Check to see if two rules are identical. It is called from
* audit_del_rule during AUDIT_DEL. */ * audit_del_rule during AUDIT_DEL. */
static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b) static int audit_compare_rule(struct audit_rule *a, struct audit_rule *b)
...@@ -226,7 +248,6 @@ static inline int audit_del_rule(struct audit_rule *rule, ...@@ -226,7 +248,6 @@ static inline int audit_del_rule(struct audit_rule *rule,
return -EFAULT; /* No matching rule */ return -EFAULT; /* No matching rule */
} }
#ifdef CONFIG_NET
/* Copy rule from user-space to kernel-space. Called during /* Copy rule from user-space to kernel-space. Called during
* AUDIT_ADD. */ * AUDIT_ADD. */
static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s) static int audit_copy_rule(struct audit_rule *d, struct audit_rule *s)
...@@ -287,7 +308,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, ...@@ -287,7 +308,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
err = audit_add_rule(entry, &audit_entlist); err = audit_add_rule(entry, &audit_entlist);
if (!err && (flags & AUDIT_AT_EXIT)) if (!err && (flags & AUDIT_AT_EXIT))
err = audit_add_rule(entry, &audit_extlist); err = audit_add_rule(entry, &audit_extlist);
audit_log(NULL, "auid %u added an audit rule\n", loginuid); audit_log(NULL, AUDIT_CONFIG_CHANGE,
"auid=%u added an audit rule\n", loginuid);
break; break;
case AUDIT_DEL: case AUDIT_DEL:
flags =((struct audit_rule *)data)->flags; flags =((struct audit_rule *)data)->flags;
...@@ -297,7 +319,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, ...@@ -297,7 +319,8 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
err = audit_del_rule(data, &audit_entlist); err = audit_del_rule(data, &audit_entlist);
if (!err && (flags & AUDIT_AT_EXIT)) if (!err && (flags & AUDIT_AT_EXIT))
err = audit_del_rule(data, &audit_extlist); err = audit_del_rule(data, &audit_extlist);
audit_log(NULL, "auid %u removed an audit rule\n", loginuid); audit_log(NULL, AUDIT_CONFIG_CHANGE,
"auid=%u removed an audit rule\n", loginuid);
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -305,7 +328,6 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data, ...@@ -305,7 +328,6 @@ int audit_receive_filter(int type, int pid, int uid, int seq, void *data,
return err; return err;
} }
#endif
/* Compare a task_struct with an audit_rule. Return 1 on match, 0 /* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise. */ * otherwise. */
...@@ -444,7 +466,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk) ...@@ -444,7 +466,7 @@ static enum audit_state audit_filter_task(struct task_struct *tsk)
/* At syscall entry and exit time, this filter is called if the /* At syscall entry and exit time, this filter is called if the
* audit_state is not low enough that auditing cannot take place, but is * audit_state is not low enough that auditing cannot take place, but is
* also not high enough that we already know we have to write and audit * also not high enough that we already know we have to write an audit
* record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT). * record (i.e., the state is AUDIT_SETUP_CONTEXT or AUDIT_BUILD_CONTEXT).
*/ */
static enum audit_state audit_filter_syscall(struct task_struct *tsk, static enum audit_state audit_filter_syscall(struct task_struct *tsk,
...@@ -532,6 +554,12 @@ static inline void audit_free_names(struct audit_context *context) ...@@ -532,6 +554,12 @@ static inline void audit_free_names(struct audit_context *context)
if (context->names[i].name) if (context->names[i].name)
__putname(context->names[i].name); __putname(context->names[i].name);
context->name_count = 0; context->name_count = 0;
if (context->pwd)
dput(context->pwd);
if (context->pwdmnt)
mntput(context->pwdmnt);
context->pwd = NULL;
context->pwdmnt = NULL;
} }
static inline void audit_free_aux(struct audit_context *context) static inline void audit_free_aux(struct audit_context *context)
...@@ -539,6 +567,11 @@ static inline void audit_free_aux(struct audit_context *context) ...@@ -539,6 +567,11 @@ static inline void audit_free_aux(struct audit_context *context)
struct audit_aux_data *aux; struct audit_aux_data *aux;
while ((aux = context->aux)) { while ((aux = context->aux)) {
if (aux->type == AUDIT_AVC_PATH) {
struct audit_aux_data_path *axi = (void *)aux;
dput(axi->dentry);
mntput(axi->mnt);
}
context->aux = aux->next; context->aux = aux->next;
kfree(aux); kfree(aux);
} }
...@@ -625,7 +658,8 @@ static void audit_log_task_info(struct audit_buffer *ab) ...@@ -625,7 +658,8 @@ static void audit_log_task_info(struct audit_buffer *ab)
struct vm_area_struct *vma; struct vm_area_struct *vma;
get_task_comm(name, current); get_task_comm(name, current);
audit_log_format(ab, " comm=%s", name); audit_log_format(ab, " comm=");
audit_log_untrustedstring(ab, name);
if (!mm) if (!mm)
return; return;
...@@ -649,23 +683,24 @@ static void audit_log_exit(struct audit_context *context) ...@@ -649,23 +683,24 @@ static void audit_log_exit(struct audit_context *context)
{ {
int i; int i;
struct audit_buffer *ab; struct audit_buffer *ab;
struct audit_aux_data *aux;
ab = audit_log_start(context); ab = audit_log_start(context, AUDIT_SYSCALL);
if (!ab) if (!ab)
return; /* audit_panic has been called */ return; /* audit_panic has been called */
audit_log_format(ab, "syscall=%d", context->major); audit_log_format(ab, "arch=%x syscall=%d",
context->arch, context->major);
if (context->personality != PER_LINUX) if (context->personality != PER_LINUX)
audit_log_format(ab, " per=%lx", context->personality); audit_log_format(ab, " per=%lx", context->personality);
audit_log_format(ab, " arch=%x", context->arch);
if (context->return_valid) if (context->return_valid)
audit_log_format(ab, " success=%s exit=%ld", audit_log_format(ab, " success=%s exit=%ld",
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no", (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
context->return_code); context->return_code);
audit_log_format(ab, audit_log_format(ab,
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d" " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
" pid=%d loginuid=%d uid=%d gid=%d" " pid=%d auid=%u uid=%u gid=%u"
" euid=%d suid=%d fsuid=%d" " euid=%u suid=%u fsuid=%u"
" egid=%d sgid=%d fsgid=%d", " egid=%u sgid=%u fsgid=%u",
context->argv[0], context->argv[0],
context->argv[1], context->argv[1],
context->argv[2], context->argv[2],
...@@ -679,33 +714,57 @@ static void audit_log_exit(struct audit_context *context) ...@@ -679,33 +714,57 @@ static void audit_log_exit(struct audit_context *context)
context->egid, context->sgid, context->fsgid); context->egid, context->sgid, context->fsgid);
audit_log_task_info(ab); audit_log_task_info(ab);
audit_log_end(ab); audit_log_end(ab);
while (context->aux) {
struct audit_aux_data *aux;
ab = audit_log_start(context); for (aux = context->aux; aux; aux = aux->next) {
ab = audit_log_start(context, aux->type);
if (!ab) if (!ab)
continue; /* audit_panic has been called */ continue; /* audit_panic has been called */
aux = context->aux;
context->aux = aux->next;
audit_log_format(ab, "auxitem=%d", aux->type);
switch (aux->type) { switch (aux->type) {
case AUDIT_AUX_IPCPERM: { case AUDIT_IPC: {
struct audit_aux_data_ipcctl *axi = (void *)aux; struct audit_aux_data_ipcctl *axi = (void *)aux;
audit_log_format(ab, audit_log_format(ab,
" qbytes=%lx uid=%d gid=%d mode=%x", " qbytes=%lx iuid=%u igid=%u mode=%x",
axi->qbytes, axi->uid, axi->gid, axi->mode); axi->qbytes, axi->uid, axi->gid, axi->mode);
} break; }
case AUDIT_SOCKETCALL: {
int i;
struct audit_aux_data_socketcall *axs = (void *)aux;
audit_log_format(ab, "nargs=%d", axs->nargs);
for (i=0; i<axs->nargs; i++)
audit_log_format(ab, " a%d=%lx", i, axs->args[i]);
break; }
case AUDIT_SOCKADDR: {
struct audit_aux_data_sockaddr *axs = (void *)aux;
audit_log_format(ab, "saddr=");
audit_log_hex(ab, axs->a, axs->len);
break; }
case AUDIT_AVC_PATH: {
struct audit_aux_data_path *axi = (void *)aux;
audit_log_d_path(ab, "path=", axi->dentry, axi->mnt);
break; }
} }
audit_log_end(ab); audit_log_end(ab);
kfree(aux);
} }
if (context->pwd && context->pwdmnt) {
ab = audit_log_start(context, AUDIT_CWD);
if (ab) {
audit_log_d_path(ab, "cwd=", context->pwd, context->pwdmnt);
audit_log_end(ab);
}
}
for (i = 0; i < context->name_count; i++) { for (i = 0; i < context->name_count; i++) {
ab = audit_log_start(context); ab = audit_log_start(context, AUDIT_PATH);
if (!ab) if (!ab)
continue; /* audit_panic has been called */ continue; /* audit_panic has been called */
audit_log_format(ab, "item=%d", i); audit_log_format(ab, "item=%d", i);
if (context->names[i].name) { if (context->names[i].name) {
audit_log_format(ab, " name="); audit_log_format(ab, " name=");
...@@ -713,7 +772,7 @@ static void audit_log_exit(struct audit_context *context) ...@@ -713,7 +772,7 @@ static void audit_log_exit(struct audit_context *context)
} }
if (context->names[i].ino != (unsigned long)-1) if (context->names[i].ino != (unsigned long)-1)
audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o" audit_log_format(ab, " inode=%lu dev=%02x:%02x mode=%#o"
" uid=%d gid=%d rdev=%02x:%02x", " ouid=%u ogid=%u rdev=%02x:%02x",
context->names[i].ino, context->names[i].ino,
MAJOR(context->names[i].dev), MAJOR(context->names[i].dev),
MINOR(context->names[i].dev), MINOR(context->names[i].dev),
...@@ -741,42 +800,12 @@ void audit_free(struct task_struct *tsk) ...@@ -741,42 +800,12 @@ void audit_free(struct task_struct *tsk)
/* Check for system calls that do not go through the exit /* Check for system calls that do not go through the exit
* function (e.g., exit_group), then free context block. */ * function (e.g., exit_group), then free context block. */
if (context->in_syscall && context->auditable) if (context->in_syscall && context->auditable && context->pid != audit_pid)
audit_log_exit(context); audit_log_exit(context);
audit_free_context(context); audit_free_context(context);
} }
/* Compute a serial number for the audit record. Audit records are
* written to user-space as soon as they are generated, so a complete
* audit record may be written in several pieces. The timestamp of the
* record and this serial number are used by the user-space daemon to
* determine which pieces belong to the same audit record. The
* (timestamp,serial) tuple is unique for each syscall and is live from
* syscall entry to syscall exit.
*
* Atomic values are only guaranteed to be 24-bit, so we count down.
*
* NOTE: Another possibility is to store the formatted records off the
* audit context (for those records that have a context), and emit them
* all at syscall exit. However, this could delay the reporting of
* significant errors until syscall exit (or never, if the system
* halts). */
static inline unsigned int audit_serial(void)
{
static atomic_t serial = ATOMIC_INIT(0xffffff);
unsigned int a, b;
do {
a = atomic_read(&serial);
if (atomic_dec_and_test(&serial))
atomic_set(&serial, 0xffffff);
b = atomic_read(&serial);
} while (b != a - 1);
return 0xffffff - b;
}
/* Fill in audit context at syscall entry. This only happens if the /* Fill in audit context at syscall entry. This only happens if the
* audit context was created when the task was created and the state or * audit context was created when the task was created and the state or
* filters demand the audit context be built. If the state from the * filters demand the audit context be built. If the state from the
...@@ -876,7 +905,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code) ...@@ -876,7 +905,7 @@ void audit_syscall_exit(struct task_struct *tsk, int valid, long return_code)
if (likely(!context)) if (likely(!context))
return; return;
if (context->in_syscall && context->auditable) if (context->in_syscall && context->auditable && context->pid != audit_pid)
audit_log_exit(context); audit_log_exit(context);
context->in_syscall = 0; context->in_syscall = 0;
...@@ -916,6 +945,13 @@ void audit_getname(const char *name) ...@@ -916,6 +945,13 @@ void audit_getname(const char *name)
context->names[context->name_count].name = name; context->names[context->name_count].name = name;
context->names[context->name_count].ino = (unsigned long)-1; context->names[context->name_count].ino = (unsigned long)-1;
++context->name_count; ++context->name_count;
if (!context->pwd) {
read_lock(&current->fs->lock);
context->pwd = dget(current->fs->pwd);
context->pwdmnt = mntget(current->fs->pwdmnt);
read_unlock(&current->fs->lock);
}
} }
/* Intercept a putname request. Called from /* Intercept a putname request. Called from
...@@ -994,34 +1030,26 @@ void audit_inode(const char *name, const struct inode *inode) ...@@ -994,34 +1030,26 @@ void audit_inode(const char *name, const struct inode *inode)
context->names[idx].rdev = inode->i_rdev; context->names[idx].rdev = inode->i_rdev;
} }
void audit_get_stamp(struct audit_context *ctx, void auditsc_get_stamp(struct audit_context *ctx,
struct timespec *t, unsigned int *serial) struct timespec *t, unsigned int *serial)
{ {
if (ctx) { t->tv_sec = ctx->ctime.tv_sec;
t->tv_sec = ctx->ctime.tv_sec; t->tv_nsec = ctx->ctime.tv_nsec;
t->tv_nsec = ctx->ctime.tv_nsec; *serial = ctx->serial;
*serial = ctx->serial; ctx->auditable = 1;
ctx->auditable = 1;
} else {
*t = CURRENT_TIME;
*serial = 0;
}
} }
extern int audit_set_type(struct audit_buffer *ab, int type);
int audit_set_loginuid(struct task_struct *task, uid_t loginuid) int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
{ {
if (task->audit_context) { if (task->audit_context) {
struct audit_buffer *ab; struct audit_buffer *ab;
ab = audit_log_start(NULL); ab = audit_log_start(NULL, AUDIT_LOGIN);
if (ab) { if (ab) {
audit_log_format(ab, "login pid=%d uid=%u " audit_log_format(ab, "login pid=%d uid=%u "
"old loginuid=%u new loginuid=%u", "old auid=%u new auid=%u",
task->pid, task->uid, task->pid, task->uid,
task->audit_context->loginuid, loginuid); task->audit_context->loginuid, loginuid);
audit_set_type(ab, AUDIT_LOGIN);
audit_log_end(ab); audit_log_end(ab);
} }
task->audit_context->loginuid = loginuid; task->audit_context->loginuid = loginuid;
...@@ -1051,8 +1079,89 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode) ...@@ -1051,8 +1079,89 @@ int audit_ipc_perms(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
ax->gid = gid; ax->gid = gid;
ax->mode = mode; ax->mode = mode;
ax->d.type = AUDIT_AUX_IPCPERM; ax->d.type = AUDIT_IPC;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
int audit_socketcall(int nargs, unsigned long *args)
{
struct audit_aux_data_socketcall *ax;
struct audit_context *context = current->audit_context;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax) + nargs * sizeof(unsigned long), GFP_KERNEL);
if (!ax)
return -ENOMEM;
ax->nargs = nargs;
memcpy(ax->args, args, nargs * sizeof(unsigned long));
ax->d.type = AUDIT_SOCKETCALL;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
int audit_sockaddr(int len, void *a)
{
struct audit_aux_data_sockaddr *ax;
struct audit_context *context = current->audit_context;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax) + len, GFP_KERNEL);
if (!ax)
return -ENOMEM;
ax->len = len;
memcpy(ax->a, a, len);
ax->d.type = AUDIT_SOCKADDR;
ax->d.next = context->aux; ax->d.next = context->aux;
context->aux = (void *)ax; context->aux = (void *)ax;
return 0; return 0;
} }
int audit_avc_path(struct dentry *dentry, struct vfsmount *mnt)
{
struct audit_aux_data_path *ax;
struct audit_context *context = current->audit_context;
if (likely(!context))
return 0;
ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
if (!ax)
return -ENOMEM;
ax->dentry = dget(dentry);
ax->mnt = mntget(mnt);
ax->d.type = AUDIT_AVC_PATH;
ax->d.next = context->aux;
context->aux = (void *)ax;
return 0;
}
void audit_signal_info(int sig, struct task_struct *t)
{
extern pid_t audit_sig_pid;
extern uid_t audit_sig_uid;
if (unlikely(audit_pid && t->pid == audit_pid)) {
if (sig == SIGTERM || sig == SIGHUP) {
struct audit_context *ctx = current->audit_context;
audit_sig_pid = current->pid;
if (ctx)
audit_sig_uid = ctx->loginuid;
else
audit_sig_uid = current->uid;
}
}
}
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/posix-timers.h> #include <linux/posix-timers.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/audit.h>
#include <asm/param.h> #include <asm/param.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/unistd.h> #include <asm/unistd.h>
...@@ -667,7 +668,11 @@ static int check_kill_permission(int sig, struct siginfo *info, ...@@ -667,7 +668,11 @@ static int check_kill_permission(int sig, struct siginfo *info,
&& (current->uid ^ t->suid) && (current->uid ^ t->uid) && (current->uid ^ t->suid) && (current->uid ^ t->uid)
&& !capable(CAP_KILL)) && !capable(CAP_KILL))
return error; return error;
return security_task_kill(t, info, sig);
error = security_task_kill(t, info, sig);
if (!error)
audit_signal_info(sig, t); /* Let audit system see the signal */
return error;
} }
/* forward decl */ /* forward decl */
......
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/kmod.h> #include <linux/kmod.h>
#include <linux/audit.h>
#ifdef CONFIG_NET_RADIO #ifdef CONFIG_NET_RADIO
#include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ #include <linux/wireless.h> /* Note : will define WIRELESS_EXT */
...@@ -226,7 +227,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr) ...@@ -226,7 +227,7 @@ int move_addr_to_kernel(void __user *uaddr, int ulen, void *kaddr)
return 0; return 0;
if(copy_from_user(kaddr,uaddr,ulen)) if(copy_from_user(kaddr,uaddr,ulen))
return -EFAULT; return -EFAULT;
return 0; return audit_sockaddr(ulen, kaddr);
} }
/** /**
...@@ -1906,7 +1907,11 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args) ...@@ -1906,7 +1907,11 @@ asmlinkage long sys_socketcall(int call, unsigned long __user *args)
/* copy_from_user should be SMP safe. */ /* copy_from_user should be SMP safe. */
if (copy_from_user(a, args, nargs[call])) if (copy_from_user(a, args, nargs[call]))
return -EFAULT; return -EFAULT;
err = audit_socketcall(nargs[call]/sizeof(unsigned long), a);
if (err)
return err;
a0=a[0]; a0=a[0];
a1=a[1]; a1=a[1];
......
...@@ -242,7 +242,7 @@ void __init avc_init(void) ...@@ -242,7 +242,7 @@ void __init avc_init(void)
avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node), avc_node_cachep = kmem_cache_create("avc_node", sizeof(struct avc_node),
0, SLAB_PANIC, NULL, NULL); 0, SLAB_PANIC, NULL, NULL);
audit_log(current->audit_context, "AVC INITIALIZED\n"); audit_log(current->audit_context, AUDIT_KERNEL, "AVC INITIALIZED\n");
} }
int avc_get_hash_stats(char *page) int avc_get_hash_stats(char *page)
...@@ -532,6 +532,7 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -532,6 +532,7 @@ void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested, u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *a) struct av_decision *avd, int result, struct avc_audit_data *a)
{ {
struct task_struct *tsk = current;
struct inode *inode = NULL; struct inode *inode = NULL;
u32 denied, audited; u32 denied, audited;
struct audit_buffer *ab; struct audit_buffer *ab;
...@@ -549,12 +550,18 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -549,12 +550,18 @@ void avc_audit(u32 ssid, u32 tsid,
return; return;
} }
ab = audit_log_start(current->audit_context); ab = audit_log_start(current->audit_context, AUDIT_AVC);
if (!ab) if (!ab)
return; /* audit_panic has been called */ return; /* audit_panic has been called */
audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted"); audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
avc_dump_av(ab, tclass,audited); avc_dump_av(ab, tclass,audited);
audit_log_format(ab, " for "); audit_log_format(ab, " for ");
if (a && a->tsk)
tsk = a->tsk;
if (tsk && tsk->pid) {
audit_log_format(ab, " pid=%d comm=", tsk->pid);
audit_log_untrustedstring(ab, tsk->comm);
}
if (a) { if (a) {
switch (a->type) { switch (a->type) {
case AVC_AUDIT_DATA_IPC: case AVC_AUDIT_DATA_IPC:
...@@ -566,21 +573,18 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -566,21 +573,18 @@ void avc_audit(u32 ssid, u32 tsid,
case AVC_AUDIT_DATA_FS: case AVC_AUDIT_DATA_FS:
if (a->u.fs.dentry) { if (a->u.fs.dentry) {
struct dentry *dentry = a->u.fs.dentry; struct dentry *dentry = a->u.fs.dentry;
if (a->u.fs.mnt) { if (a->u.fs.mnt)
audit_log_d_path(ab, "path=", dentry, audit_avc_path(dentry, a->u.fs.mnt);
a->u.fs.mnt); audit_log_format(ab, " name=");
} else { audit_log_untrustedstring(ab, dentry->d_name.name);
audit_log_format(ab, " name=%s",
dentry->d_name.name);
}
inode = dentry->d_inode; inode = dentry->d_inode;
} else if (a->u.fs.inode) { } else if (a->u.fs.inode) {
struct dentry *dentry; struct dentry *dentry;
inode = a->u.fs.inode; inode = a->u.fs.inode;
dentry = d_find_alias(inode); dentry = d_find_alias(inode);
if (dentry) { if (dentry) {
audit_log_format(ab, " name=%s", audit_log_format(ab, " name=");
dentry->d_name.name); audit_log_untrustedstring(ab, dentry->d_name.name);
dput(dentry); dput(dentry);
} }
} }
...@@ -623,22 +627,20 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -623,22 +627,20 @@ void avc_audit(u32 ssid, u32 tsid,
case AF_UNIX: case AF_UNIX:
u = unix_sk(sk); u = unix_sk(sk);
if (u->dentry) { if (u->dentry) {
audit_log_d_path(ab, "path=", audit_avc_path(u->dentry, u->mnt);
u->dentry, u->mnt); audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, u->dentry->d_name.name);
break; break;
} }
if (!u->addr) if (!u->addr)
break; break;
len = u->addr->len-sizeof(short); len = u->addr->len-sizeof(short);
p = &u->addr->name->sun_path[0]; p = &u->addr->name->sun_path[0];
audit_log_format(ab, " path=");
if (*p) if (*p)
audit_log_format(ab, audit_log_untrustedstring(ab, p);
"path=%*.*s", len,
len, p);
else else
audit_log_format(ab, audit_log_hex(ab, p, len);
"path=@%*.*s", len-1,
len-1, p+1);
break; break;
} }
} }
......
...@@ -3419,7 +3419,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) ...@@ -3419,7 +3419,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
if (err) { if (err) {
if (err == -EINVAL) { if (err == -EINVAL) {
audit_log(current->audit_context, audit_log(current->audit_context, AUDIT_SELINUX_ERR,
"SELinux: unrecognized netlink message" "SELinux: unrecognized netlink message"
" type=%hu for sclass=%hu\n", " type=%hu for sclass=%hu\n",
nlh->nlmsg_type, isec->sclass); nlh->nlmsg_type, isec->sclass);
......
...@@ -97,6 +97,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] = ...@@ -97,6 +97,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] =
{ AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_ADD, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, { AUDIT_DEL, NETLINK_AUDIT_SOCKET__NLMSG_WRITE },
{ AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY }, { AUDIT_USER, NETLINK_AUDIT_SOCKET__NLMSG_RELAY },
{ AUDIT_SIGNAL_INFO, NETLINK_AUDIT_SOCKET__NLMSG_READ },
}; };
...@@ -141,8 +142,13 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm) ...@@ -141,8 +142,13 @@ int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm)
break; break;
case SECCLASS_NETLINK_AUDIT_SOCKET: case SECCLASS_NETLINK_AUDIT_SOCKET:
err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms, if (nlmsg_type >= AUDIT_FIRST_USER_MSG &&
sizeof(nlmsg_audit_perms)); nlmsg_type <= AUDIT_LAST_USER_MSG) {
*perm = NETLINK_AUDIT_SOCKET__NLMSG_RELAY;
} else {
err = nlmsg_perm(nlmsg_type, perm, nlmsg_audit_perms,
sizeof(nlmsg_audit_perms));
}
break; break;
/* No messaging from userspace, or class unknown/unhandled */ /* No messaging from userspace, or class unknown/unhandled */
......
...@@ -365,7 +365,7 @@ static int security_validtrans_handle_fail(struct context *ocontext, ...@@ -365,7 +365,7 @@ static int security_validtrans_handle_fail(struct context *ocontext,
goto out; goto out;
if (context_struct_to_string(tcontext, &t, &tlen) < 0) if (context_struct_to_string(tcontext, &t, &tlen) < 0)
goto out; goto out;
audit_log(current->audit_context, audit_log(current->audit_context, AUDIT_SELINUX_ERR,
"security_validate_transition: denied for" "security_validate_transition: denied for"
" oldcontext=%s newcontext=%s taskcontext=%s tclass=%s", " oldcontext=%s newcontext=%s taskcontext=%s tclass=%s",
o, n, t, policydb.p_class_val_to_name[tclass-1]); o, n, t, policydb.p_class_val_to_name[tclass-1]);
...@@ -742,7 +742,7 @@ static int compute_sid_handle_invalid_context( ...@@ -742,7 +742,7 @@ static int compute_sid_handle_invalid_context(
goto out; goto out;
if (context_struct_to_string(newcontext, &n, &nlen) < 0) if (context_struct_to_string(newcontext, &n, &nlen) < 0)
goto out; goto out;
audit_log(current->audit_context, audit_log(current->audit_context, AUDIT_SELINUX_ERR,
"security_compute_sid: invalid context %s" "security_compute_sid: invalid context %s"
" for scontext=%s" " for scontext=%s"
" tcontext=%s" " tcontext=%s"
......
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