Commit f429ee3b authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit: (29 commits)
  audit: no leading space in audit_log_d_path prefix
  audit: treat s_id as an untrusted string
  audit: fix signedness bug in audit_log_execve_info()
  audit: comparison on interprocess fields
  audit: implement all object interfield comparisons
  audit: allow interfield comparison between gid and ogid
  audit: complex interfield comparison helper
  audit: allow interfield comparison in audit rules
  Kernel: Audit Support For The ARM Platform
  audit: do not call audit_getname on error
  audit: only allow tasks to set their loginuid if it is -1
  audit: remove task argument to audit_set_loginuid
  audit: allow audit matching on inode gid
  audit: allow matching on obj_uid
  audit: remove audit_finish_fork as it can't be called
  audit: reject entry,always rules
  audit: inline audit_free to simplify the look of generic code
  audit: drop audit_set_macxattr as it doesn't do anything
  audit: inline checks for not needing to collect aux records
  audit: drop some potentially inadvisable likely notations
  ...

Use evil merge to fix up grammar mistakes in Kconfig file.

Bad speling and horrible grammar (and copious swearing) is to be
expected, but let's keep it to commit messages and comments, rather than
expose it to users in config help texts or printouts.
parents 22b4eb5e c158a35c
......@@ -24,7 +24,6 @@
#define MAX_INSN_SIZE 2
#define MAX_STACK_SIZE 64 /* 32 would probably be OK */
#define regs_return_value(regs) ((regs)->ARM_r0)
#define flush_insn_slot(p) do { } while (0)
#define kretprobe_blacklist_size 0
......
......@@ -189,6 +189,11 @@ static inline int valid_user_regs(struct pt_regs *regs)
return 0;
}
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->ARM_r0;
}
#define instruction_pointer(regs) (regs)->ARM_pc
#ifdef CONFIG_SMP
......
......@@ -129,6 +129,7 @@ extern void vfp_flush_hwstate(struct thread_info *);
/*
* thread information flags:
* TIF_SYSCALL_TRACE - syscall trace active
* TIF_SYSCAL_AUDIT - syscall auditing active
* TIF_SIGPENDING - signal pending
* TIF_NEED_RESCHED - rescheduling necessary
* TIF_NOTIFY_RESUME - callback before returning to user
......@@ -139,6 +140,7 @@ extern void vfp_flush_hwstate(struct thread_info *);
#define TIF_NEED_RESCHED 1
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
#define TIF_SYSCALL_TRACE 8
#define TIF_SYSCALL_AUDIT 9
#define TIF_POLLING_NRFLAG 16
#define TIF_USING_IWMMXT 17
#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
......@@ -149,11 +151,15 @@ extern void vfp_flush_hwstate(struct thread_info *);
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT)
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
#define _TIF_SECCOMP (1 << TIF_SECCOMP)
/* Checks for any syscall work in entry-common.S */
#define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT)
/*
* Change these and you break ASM code in entry-common.S
*/
......
......@@ -87,7 +87,7 @@ ENTRY(ret_from_fork)
get_thread_info tsk
ldr r1, [tsk, #TI_FLAGS] @ check for syscall tracing
mov why, #1
tst r1, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
tst r1, #_TIF_SYSCALL_WORK @ are we tracing syscalls?
beq ret_slow_syscall
mov r1, sp
mov r0, #1 @ trace exit [IP = 1]
......@@ -443,7 +443,7 @@ ENTRY(vector_swi)
1:
#endif
tst r10, #_TIF_SYSCALL_TRACE @ are we tracing syscalls?
tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls?
bne __sys_trace
cmp scno, #NR_syscalls @ check upper syscall limit
......
......@@ -906,11 +906,6 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
{
unsigned long ip;
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return scno;
if (!(current->ptrace & PT_PTRACED))
return scno;
/*
* Save IP. IP is used to denote syscall entry/exit:
* IP = 0 -> entry, = 1 -> exit
......@@ -918,6 +913,17 @@ asmlinkage int syscall_trace(int why, struct pt_regs *regs, int scno)
ip = regs->ARM_ip;
regs->ARM_ip = why;
if (!ip)
audit_syscall_exit(regs);
else
audit_syscall_entry(AUDIT_ARCH_ARMEB, scno, regs->ARM_r0,
regs->ARM_r1, regs->ARM_r2, regs->ARM_r3);
if (!test_thread_flag(TIF_SYSCALL_TRACE))
return scno;
if (!(current->ptrace & PT_PTRACED))
return scno;
current_thread_info()->syscall = scno;
/* the 0x80 provides a way for the tracing parent to distinguish
......
......@@ -246,7 +246,18 @@ static inline unsigned long user_stack_pointer(struct pt_regs *regs)
return regs->ar_bspstore;
}
#define regs_return_value(regs) ((regs)->r8)
static inline int is_syscall_success(struct pt_regs *regs)
{
return regs->r10 != -1;
}
static inline long regs_return_value(struct pt_regs *regs)
{
if (is_syscall_success(regs))
return regs->r8;
else
return -regs->r8;
}
/* Conserve space in histogram by encoding slot bits in address
* bits 2 and 3 rather than bits 0 and 1.
......
......@@ -1246,15 +1246,8 @@ syscall_trace_enter (long arg0, long arg1, long arg2, long arg3,
if (test_thread_flag(TIF_RESTORE_RSE))
ia64_sync_krbs();
if (unlikely(current->audit_context)) {
long syscall;
int arch;
syscall = regs.r15;
arch = AUDIT_ARCH_IA64;
audit_syscall_entry(arch, syscall, arg0, arg1, arg2, arg3);
}
audit_syscall_entry(AUDIT_ARCH_IA64, regs.r15, arg0, arg1, arg2, arg3);
return 0;
}
......@@ -1268,14 +1261,7 @@ syscall_trace_leave (long arg0, long arg1, long arg2, long arg3,
{
int step;
if (unlikely(current->audit_context)) {
int success = AUDITSC_RESULT(regs.r10);
long result = regs.r8;
if (success != AUDITSC_SUCCESS)
result = -result;
audit_syscall_exit(success, result);
}
audit_syscall_exit(&regs);
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
......
......@@ -61,6 +61,11 @@ struct pt_regs {
#define instruction_pointer(regs) ((regs)->pc)
#define profile_pc(regs) instruction_pointer(regs)
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->r3;
}
#else /* __KERNEL__ */
/* pt_regs offsets used by gdbserver etc in ptrace syscalls */
......
......@@ -147,10 +147,8 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
*/
ret = -1L;
if (unlikely(current->audit_context))
audit_syscall_entry(EM_MICROBLAZE, regs->r12,
regs->r5, regs->r6,
regs->r7, regs->r8);
audit_syscall_entry(EM_MICROBLAZE, regs->r12, regs->r5, regs->r6,
regs->r7, regs->r8);
return ret ?: regs->r12;
}
......@@ -159,8 +157,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
{
int step;
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->r3), regs->r3);
audit_syscall_exit(regs);
step = test_thread_flag(TIF_SINGLESTEP);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
......
......@@ -137,7 +137,19 @@ extern int ptrace_set_watch_regs(struct task_struct *child,
*/
#define user_mode(regs) (((regs)->cp0_status & KU_MASK) == KU_USER)
#define regs_return_value(_regs) ((_regs)->regs[2])
static inline int is_syscall_success(struct pt_regs *regs)
{
return !regs->regs[7];
}
static inline long regs_return_value(struct pt_regs *regs)
{
if (is_syscall_success(regs))
return regs->regs[2];
else
return -regs->regs[2];
}
#define instruction_pointer(regs) ((regs)->cp0_epc)
#define profile_pc(regs) instruction_pointer(regs)
......
......@@ -560,10 +560,9 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs)
}
out:
if (unlikely(current->audit_context))
audit_syscall_entry(audit_arch(), regs->regs[2],
regs->regs[4], regs->regs[5],
regs->regs[6], regs->regs[7]);
audit_syscall_entry(audit_arch(), regs->regs[2],
regs->regs[4], regs->regs[5],
regs->regs[6], regs->regs[7]);
}
/*
......@@ -572,9 +571,7 @@ asmlinkage void syscall_trace_enter(struct pt_regs *regs)
*/
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
{
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->regs[7]),
-regs->regs[2]);
audit_syscall_exit(regs);
if (!(current->ptrace & PT_PTRACED))
return;
......
......@@ -86,7 +86,18 @@ struct pt_regs {
#define instruction_pointer(regs) ((regs)->nip)
#define user_stack_pointer(regs) ((regs)->gpr[1])
#define kernel_stack_pointer(regs) ((regs)->gpr[1])
#define regs_return_value(regs) ((regs)->gpr[3])
static inline int is_syscall_success(struct pt_regs *regs)
{
return !(regs->ccr & 0x10000000);
}
static inline long regs_return_value(struct pt_regs *regs)
{
if (is_syscall_success(regs))
return regs->gpr[3];
else
return -regs->gpr[3];
}
#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *regs);
......
......@@ -1724,22 +1724,20 @@ long do_syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->gpr[0]);
if (unlikely(current->audit_context)) {
#ifdef CONFIG_PPC64
if (!is_32bit_task())
audit_syscall_entry(AUDIT_ARCH_PPC64,
regs->gpr[0],
regs->gpr[3], regs->gpr[4],
regs->gpr[5], regs->gpr[6]);
else
if (!is_32bit_task())
audit_syscall_entry(AUDIT_ARCH_PPC64,
regs->gpr[0],
regs->gpr[3], regs->gpr[4],
regs->gpr[5], regs->gpr[6]);
else
#endif
audit_syscall_entry(AUDIT_ARCH_PPC,
regs->gpr[0],
regs->gpr[3] & 0xffffffff,
regs->gpr[4] & 0xffffffff,
regs->gpr[5] & 0xffffffff,
regs->gpr[6] & 0xffffffff);
}
audit_syscall_entry(AUDIT_ARCH_PPC,
regs->gpr[0],
regs->gpr[3] & 0xffffffff,
regs->gpr[4] & 0xffffffff,
regs->gpr[5] & 0xffffffff,
regs->gpr[6] & 0xffffffff);
return ret ?: regs->gpr[0];
}
......@@ -1748,9 +1746,7 @@ void do_syscall_trace_leave(struct pt_regs *regs)
{
int step;
if (unlikely(current->audit_context))
audit_syscall_exit((regs->ccr&0x10000000)?AUDITSC_FAILURE:AUDITSC_SUCCESS,
regs->result);
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->result);
......
......@@ -541,9 +541,13 @@ struct user_regs_struct
#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
#define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN)
#define user_stack_pointer(regs)((regs)->gprs[15])
#define regs_return_value(regs)((regs)->gprs[2])
#define profile_pc(regs) instruction_pointer(regs)
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->gprs[2];
}
int regs_query_register_offset(const char *name);
const char *regs_query_register_name(unsigned int offset);
unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset);
......
......@@ -740,20 +740,17 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->gprs[2]);
if (unlikely(current->audit_context))
audit_syscall_entry(is_compat_task() ?
AUDIT_ARCH_S390 : AUDIT_ARCH_S390X,
regs->gprs[2], regs->orig_gpr2,
regs->gprs[3], regs->gprs[4],
regs->gprs[5]);
audit_syscall_entry(is_compat_task() ?
AUDIT_ARCH_S390 : AUDIT_ARCH_S390X,
regs->gprs[2], regs->orig_gpr2,
regs->gprs[3], regs->gprs[4],
regs->gprs[5]);
return ret ?: regs->gprs[2];
}
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
{
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->gprs[2]),
regs->gprs[2]);
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->gprs[2]);
......
......@@ -76,7 +76,10 @@ struct pt_dspregs {
#ifdef __KERNEL__
#define MAX_REG_OFFSET offsetof(struct pt_regs, tra)
#define regs_return_value(_regs) ((_regs)->regs[0])
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->regs[0];
}
#endif /* __KERNEL__ */
......
......@@ -13,7 +13,10 @@ struct pt_regs {
#ifdef __KERNEL__
#define MAX_REG_OFFSET offsetof(struct pt_regs, tregs[7])
#define regs_return_value(_regs) ((_regs)->regs[3])
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->regs[3];
}
#endif /* __KERNEL__ */
......
......@@ -518,10 +518,9 @@ asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->regs[0]);
if (unlikely(current->audit_context))
audit_syscall_entry(audit_arch(), regs->regs[3],
regs->regs[4], regs->regs[5],
regs->regs[6], regs->regs[7]);
audit_syscall_entry(audit_arch(), regs->regs[3],
regs->regs[4], regs->regs[5],
regs->regs[6], regs->regs[7]);
return ret ?: regs->regs[0];
}
......@@ -530,9 +529,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
{
int step;
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->regs[0]),
regs->regs[0]);
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->regs[0]);
......
......@@ -536,10 +536,9 @@ asmlinkage long long do_syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->regs[9]);
if (unlikely(current->audit_context))
audit_syscall_entry(audit_arch(), regs->regs[1],
regs->regs[2], regs->regs[3],
regs->regs[4], regs->regs[5]);
audit_syscall_entry(audit_arch(), regs->regs[1],
regs->regs[2], regs->regs[3],
regs->regs[4], regs->regs[5]);
return ret ?: regs->regs[9];
}
......@@ -548,9 +547,7 @@ asmlinkage void do_syscall_trace_leave(struct pt_regs *regs)
{
int step;
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->regs[9]),
regs->regs[9]);
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->regs[9]);
......
......@@ -207,7 +207,15 @@ do { current_thread_info()->syscall_noerror = 1; \
#define instruction_pointer(regs) ((regs)->tpc)
#define instruction_pointer_set(regs, val) ((regs)->tpc = (val))
#define user_stack_pointer(regs) ((regs)->u_regs[UREG_FP])
#define regs_return_value(regs) ((regs)->u_regs[UREG_I0])
static inline int is_syscall_success(struct pt_regs *regs)
{
return !(regs->tstate & (TSTATE_XCARRY | TSTATE_ICARRY));
}
static inline long regs_return_value(struct pt_regs *regs)
{
return regs->u_regs[UREG_I0];
}
#ifdef CONFIG_SMP
extern unsigned long profile_pc(struct pt_regs *);
#else
......
......@@ -1071,32 +1071,22 @@ asmlinkage int syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->u_regs[UREG_G1]);
if (unlikely(current->audit_context) && !ret)
audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
AUDIT_ARCH_SPARC :
AUDIT_ARCH_SPARC64),
regs->u_regs[UREG_G1],
regs->u_regs[UREG_I0],
regs->u_regs[UREG_I1],
regs->u_regs[UREG_I2],
regs->u_regs[UREG_I3]);
audit_syscall_entry((test_thread_flag(TIF_32BIT) ?
AUDIT_ARCH_SPARC :
AUDIT_ARCH_SPARC64),
regs->u_regs[UREG_G1],
regs->u_regs[UREG_I0],
regs->u_regs[UREG_I1],
regs->u_regs[UREG_I2],
regs->u_regs[UREG_I3]);
return ret;
}
asmlinkage void syscall_trace_leave(struct pt_regs *regs)
{
#ifdef CONFIG_AUDITSYSCALL
if (unlikely(current->audit_context)) {
unsigned long tstate = regs->tstate;
int result = AUDITSC_SUCCESS;
audit_syscall_exit(regs);
if (unlikely(tstate & (TSTATE_XCARRY | TSTATE_ICARRY)))
result = AUDITSC_FAILURE;
audit_syscall_exit(result, regs->u_regs[UREG_I0]);
}
#endif
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->u_regs[UREG_G1]);
......
......@@ -167,17 +167,15 @@ void syscall_trace(struct uml_pt_regs *regs, int entryexit)
int is_singlestep = (current->ptrace & PT_DTRACE) && entryexit;
int tracesysgood;
if (unlikely(current->audit_context)) {
if (!entryexit)
audit_syscall_entry(HOST_AUDIT_ARCH,
UPT_SYSCALL_NR(regs),
UPT_SYSCALL_ARG1(regs),
UPT_SYSCALL_ARG2(regs),
UPT_SYSCALL_ARG3(regs),
UPT_SYSCALL_ARG4(regs));
else audit_syscall_exit(AUDITSC_RESULT(UPT_SYSCALL_RET(regs)),
UPT_SYSCALL_RET(regs));
}
if (!entryexit)
audit_syscall_entry(HOST_AUDIT_ARCH,
UPT_SYSCALL_NR(regs),
UPT_SYSCALL_ARG1(regs),
UPT_SYSCALL_ARG2(regs),
UPT_SYSCALL_ARG3(regs),
UPT_SYSCALL_ARG4(regs));
else
audit_syscall_exit(regs);
/* Fake a debug trap */
if (is_singlestep)
......
......@@ -14,6 +14,7 @@
#include <asm/segment.h>
#include <asm/irqflags.h>
#include <linux/linkage.h>
#include <linux/err.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
#include <linux/elf-em.h>
......@@ -189,7 +190,7 @@ sysexit_from_sys_call:
movl %ebx,%edx /* 3rd arg: 1st syscall arg */
movl %eax,%esi /* 2nd arg: syscall number */
movl $AUDIT_ARCH_I386,%edi /* 1st arg: audit arch */
call audit_syscall_entry
call __audit_syscall_entry
movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall number */
cmpq $(IA32_NR_syscalls-1),%rax
ja ia32_badsys
......@@ -206,12 +207,13 @@ sysexit_from_sys_call:
TRACE_IRQS_ON
sti
movl %eax,%esi /* second arg, syscall return value */
cmpl $0,%eax /* is it < 0? */
setl %al /* 1 if so, 0 if not */
cmpl $-MAX_ERRNO,%eax /* is it an error ? */
jbe 1f
movslq %eax, %rsi /* if error sign extend to 64 bits */
1: setbe %al /* 1 if error, 0 if not */
movzbl %al,%edi /* zero-extend that into %edi */
inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */
call audit_syscall_exit
movl RAX-ARGOFFSET(%rsp),%eax /* reload syscall return value */
call __audit_syscall_exit
movq RAX-ARGOFFSET(%rsp),%rax /* reload syscall return value */
movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi
cli
TRACE_IRQS_OFF
......
......@@ -42,6 +42,7 @@
*/
#include <linux/linkage.h>
#include <linux/err.h>
#include <asm/thread_info.h>
#include <asm/irqflags.h>
#include <asm/errno.h>
......@@ -453,7 +454,7 @@ sysenter_audit:
movl %ebx,%ecx /* 3rd arg: 1st syscall arg */
movl %eax,%edx /* 2nd arg: syscall number */
movl $AUDIT_ARCH_I386,%eax /* 1st arg: audit arch */
call audit_syscall_entry
call __audit_syscall_entry
pushl_cfi %ebx
movl PT_EAX(%esp),%eax /* reload syscall number */
jmp sysenter_do_call
......@@ -464,11 +465,10 @@ sysexit_audit:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_ANY)
movl %eax,%edx /* second arg, syscall return value */
cmpl $0,%eax /* is it < 0? */
setl %al /* 1 if so, 0 if not */
cmpl $-MAX_ERRNO,%eax /* is it an error ? */
setbe %al /* 1 if so, 0 if not */
movzbl %al,%eax /* zero-extend that */
inc %eax /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */
call audit_syscall_exit
call __audit_syscall_exit
DISABLE_INTERRUPTS(CLBR_ANY)
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
......
......@@ -55,6 +55,7 @@
#include <asm/paravirt.h>
#include <asm/ftrace.h>
#include <asm/percpu.h>
#include <linux/err.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
#include <linux/elf-em.h>
......@@ -548,7 +549,7 @@ badsys:
#ifdef CONFIG_AUDITSYSCALL
/*
* Fast path for syscall audit without full syscall trace.
* We just call audit_syscall_entry() directly, and then
* We just call __audit_syscall_entry() directly, and then
* jump back to the normal fast path.
*/
auditsys:
......@@ -558,22 +559,21 @@ auditsys:
movq %rdi,%rdx /* 3rd arg: 1st syscall arg */
movq %rax,%rsi /* 2nd arg: syscall number */
movl $AUDIT_ARCH_X86_64,%edi /* 1st arg: audit arch */
call audit_syscall_entry
call __audit_syscall_entry
LOAD_ARGS 0 /* reload call-clobbered registers */
jmp system_call_fastpath
/*
* Return fast path for syscall audit. Call audit_syscall_exit()
* Return fast path for syscall audit. Call __audit_syscall_exit()
* directly and then jump back to the fast path with TIF_SYSCALL_AUDIT
* masked off.
*/
sysret_audit:
movq RAX-ARGOFFSET(%rsp),%rsi /* second arg, syscall return value */
cmpq $0,%rsi /* is it < 0? */
setl %al /* 1 if so, 0 if not */
cmpq $-MAX_ERRNO,%rsi /* is it < -MAX_ERRNO? */
setbe %al /* 1 if so, 0 if not */
movzbl %al,%edi /* zero-extend that into %edi */
inc %edi /* first arg, 0->1(AUDITSC_SUCCESS), 1->2(AUDITSC_FAILURE) */
call audit_syscall_exit
call __audit_syscall_exit
movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT),%edi
jmp sysret_check
#endif /* CONFIG_AUDITSYSCALL */
......
......@@ -1392,20 +1392,18 @@ long syscall_trace_enter(struct pt_regs *regs)
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->orig_ax);
if (unlikely(current->audit_context)) {
if (IS_IA32)
audit_syscall_entry(AUDIT_ARCH_I386,
regs->orig_ax,
regs->bx, regs->cx,
regs->dx, regs->si);
if (IS_IA32)
audit_syscall_entry(AUDIT_ARCH_I386,
regs->orig_ax,
regs->bx, regs->cx,
regs->dx, regs->si);
#ifdef CONFIG_X86_64
else
audit_syscall_entry(AUDIT_ARCH_X86_64,
regs->orig_ax,
regs->di, regs->si,
regs->dx, regs->r10);
else
audit_syscall_entry(AUDIT_ARCH_X86_64,
regs->orig_ax,
regs->di, regs->si,
regs->dx, regs->r10);
#endif
}
return ret ?: regs->orig_ax;
}
......@@ -1414,8 +1412,7 @@ void syscall_trace_leave(struct pt_regs *regs)
{
bool step;
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->ax);
......
......@@ -335,9 +335,9 @@ static void do_sys_vm86(struct kernel_vm86_struct *info, struct task_struct *tsk
if (info->flags & VM86_SCREEN_BITMAP)
mark_screen_rdonly(tsk->mm);
/*call audit_syscall_exit since we do not exit via the normal paths */
/*call __audit_syscall_exit since we do not exit via the normal paths */
if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(0), 0);
__audit_syscall_exit(1, 0);
__asm__ __volatile__(
"movl %0,%%esp\n\t"
......
......@@ -3,3 +3,8 @@
#else
#include "ptrace_64.h"
#endif
static inline long regs_return_value(struct uml_pt_regs *regs)
{
return UPT_SYSCALL_RET(regs);
}
......@@ -334,8 +334,7 @@ void do_syscall_trace_enter(struct pt_regs *regs)
do_syscall_trace();
#if 0
if (unlikely(current->audit_context))
audit_syscall_entry(current, AUDIT_ARCH_XTENSA..);
audit_syscall_entry(current, AUDIT_ARCH_XTENSA..);
#endif
}
......
......@@ -140,21 +140,19 @@ static int do_getname(const char __user *filename, char *page)
static char *getname_flags(const char __user *filename, int flags, int *empty)
{
char *tmp, *result;
result = ERR_PTR(-ENOMEM);
tmp = __getname();
if (tmp) {
int retval = do_getname(filename, tmp);
result = tmp;
if (retval < 0) {
if (retval == -ENOENT && empty)
*empty = 1;
if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
__putname(tmp);
result = ERR_PTR(retval);
}
char *result = __getname();
int retval;
if (!result)
return ERR_PTR(-ENOMEM);
retval = do_getname(filename, result);
if (retval < 0) {
if (retval == -ENOENT && empty)
*empty = 1;
if (retval != -ENOENT || !(flags & LOOKUP_EMPTY)) {
__putname(result);
return ERR_PTR(retval);
}
}
audit_getname(result);
......
......@@ -1132,9 +1132,6 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
ssize_t length;
uid_t loginuid;
if (!capable(CAP_AUDIT_CONTROL))
return -EPERM;
rcu_read_lock();
if (current != pid_task(proc_pid(inode), PIDTYPE_PID)) {
rcu_read_unlock();
......@@ -1163,7 +1160,7 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf,
goto out_free_page;
}
length = audit_set_loginuid(current, loginuid);
length = audit_set_loginuid(loginuid);
if (likely(length == 0))
length = count;
......
......@@ -26,6 +26,7 @@
#include <linux/types.h>
#include <linux/elf-em.h>
#include <linux/ptrace.h>
/* The netlink messages for the audit system is divided into blocks:
* 1000 - 1099 are for commanding the audit system
......@@ -181,6 +182,40 @@
* AUDIT_UNUSED_BITS is updated if need be. */
#define AUDIT_UNUSED_BITS 0x07FFFC00
/* AUDIT_FIELD_COMPARE rule list */
#define AUDIT_COMPARE_UID_TO_OBJ_UID 1
#define AUDIT_COMPARE_GID_TO_OBJ_GID 2
#define AUDIT_COMPARE_EUID_TO_OBJ_UID 3
#define AUDIT_COMPARE_EGID_TO_OBJ_GID 4
#define AUDIT_COMPARE_AUID_TO_OBJ_UID 5
#define AUDIT_COMPARE_SUID_TO_OBJ_UID 6
#define AUDIT_COMPARE_SGID_TO_OBJ_GID 7
#define AUDIT_COMPARE_FSUID_TO_OBJ_UID 8
#define AUDIT_COMPARE_FSGID_TO_OBJ_GID 9
#define AUDIT_COMPARE_UID_TO_AUID 10
#define AUDIT_COMPARE_UID_TO_EUID 11
#define AUDIT_COMPARE_UID_TO_FSUID 12
#define AUDIT_COMPARE_UID_TO_SUID 13
#define AUDIT_COMPARE_AUID_TO_FSUID 14
#define AUDIT_COMPARE_AUID_TO_SUID 15
#define AUDIT_COMPARE_AUID_TO_EUID 16
#define AUDIT_COMPARE_EUID_TO_SUID 17
#define AUDIT_COMPARE_EUID_TO_FSUID 18
#define AUDIT_COMPARE_SUID_TO_FSUID 19
#define AUDIT_COMPARE_GID_TO_EGID 20
#define AUDIT_COMPARE_GID_TO_FSGID 21
#define AUDIT_COMPARE_GID_TO_SGID 22
#define AUDIT_COMPARE_EGID_TO_FSGID 23
#define AUDIT_COMPARE_EGID_TO_SGID 24
#define AUDIT_COMPARE_SGID_TO_FSGID 25
#define AUDIT_MAX_FIELD_COMPARE AUDIT_COMPARE_SGID_TO_FSGID
/* Rule fields */
/* These are useful when checking the
......@@ -222,6 +257,9 @@
#define AUDIT_PERM 106
#define AUDIT_DIR 107
#define AUDIT_FILETYPE 108
#define AUDIT_OBJ_UID 109
#define AUDIT_OBJ_GID 110
#define AUDIT_FIELD_COMPARE 111
#define AUDIT_ARG0 200
#define AUDIT_ARG1 (AUDIT_ARG0+1)
......@@ -408,28 +446,24 @@ struct audit_field {
void *lsm_rule;
};
#define AUDITSC_INVALID 0
#define AUDITSC_SUCCESS 1
#define AUDITSC_FAILURE 2
#define AUDITSC_RESULT(x) ( ((long)(x))<0?AUDITSC_FAILURE:AUDITSC_SUCCESS )
extern int __init audit_register_class(int class, unsigned *list);
extern int audit_classify_syscall(int abi, unsigned syscall);
extern int audit_classify_arch(int arch);
#ifdef CONFIG_AUDITSYSCALL
/* These are defined in auditsc.c */
/* Public API */
extern void audit_finish_fork(struct task_struct *child);
extern int audit_alloc(struct task_struct *task);
extern void audit_free(struct task_struct *task);
extern void audit_syscall_entry(int arch,
int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void audit_syscall_exit(int failed, long return_code);
extern void __audit_free(struct task_struct *task);
extern void __audit_syscall_entry(int arch,
int major, unsigned long a0, unsigned long a1,
unsigned long a2, unsigned long a3);
extern void __audit_syscall_exit(int ret_success, long ret_value);
extern void __audit_getname(const char *name);
extern void audit_putname(const char *name);
extern void __audit_inode(const char *name, const struct dentry *dentry);
extern void __audit_inode_child(const struct dentry *dentry,
const struct inode *parent);
extern void __audit_seccomp(unsigned long syscall);
extern void __audit_ptrace(struct task_struct *t);
static inline int audit_dummy_context(void)
......@@ -437,6 +471,27 @@ static inline int audit_dummy_context(void)
void *p = current->audit_context;
return !p || *(int *)p;
}
static inline void audit_free(struct task_struct *task)
{
if (unlikely(task->audit_context))
__audit_free(task);
}
static inline void audit_syscall_entry(int arch, int major, unsigned long a0,
unsigned long a1, unsigned long a2,
unsigned long a3)
{
if (unlikely(!audit_dummy_context()))
__audit_syscall_entry(arch, major, a0, a1, a2, a3);
}
static inline void audit_syscall_exit(void *pt_regs)
{
if (unlikely(current->audit_context)) {
int success = is_syscall_success(pt_regs);
int return_code = regs_return_value(pt_regs);
__audit_syscall_exit(success, return_code);
}
}
static inline void audit_getname(const char *name)
{
if (unlikely(!audit_dummy_context()))
......@@ -453,6 +508,12 @@ static inline void audit_inode_child(const struct dentry *dentry,
}
void audit_core_dumps(long signr);
static inline void audit_seccomp(unsigned long syscall)
{
if (unlikely(!audit_dummy_context()))
__audit_seccomp(syscall);
}
static inline void audit_ptrace(struct task_struct *t)
{
if (unlikely(!audit_dummy_context()))
......@@ -463,17 +524,16 @@ static inline void audit_ptrace(struct task_struct *t)
extern unsigned int audit_serial(void);
extern int 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(uid_t loginuid);
#define audit_get_loginuid(t) ((t)->loginuid)
#define audit_get_sessionid(t) ((t)->sessionid)
extern void audit_log_task_context(struct audit_buffer *ab);
extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
extern int audit_bprm(struct linux_binprm *bprm);
extern void audit_socketcall(int nargs, unsigned long *args);
extern int audit_sockaddr(int len, void *addr);
extern int __audit_bprm(struct linux_binprm *bprm);
extern void __audit_socketcall(int nargs, unsigned long *args);
extern int __audit_sockaddr(int len, void *addr);
extern void __audit_fd_pair(int fd1, int fd2);
extern int audit_set_macxattr(const char *name);
extern void __audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr);
extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification);
......@@ -499,6 +559,23 @@ static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid
if (unlikely(!audit_dummy_context()))
__audit_ipc_set_perm(qbytes, uid, gid, mode);
}
static inline int audit_bprm(struct linux_binprm *bprm)
{
if (unlikely(!audit_dummy_context()))
return __audit_bprm(bprm);
return 0;
}
static inline void audit_socketcall(int nargs, unsigned long *args)
{
if (unlikely(!audit_dummy_context()))
__audit_socketcall(nargs, args);
}
static inline int audit_sockaddr(int len, void *addr)
{
if (unlikely(!audit_dummy_context()))
return __audit_sockaddr(len, addr);
return 0;
}
static inline void audit_mq_open(int oflag, umode_t mode, struct mq_attr *attr)
{
if (unlikely(!audit_dummy_context()))
......@@ -544,12 +621,11 @@ static inline void audit_mmap_fd(int fd, int flags)
extern int audit_n_rules;
extern int audit_signals;
#else
#define audit_finish_fork(t)
#else /* CONFIG_AUDITSYSCALL */
#define audit_alloc(t) ({ 0; })
#define audit_free(t) do { ; } while (0)
#define audit_syscall_entry(ta,a,b,c,d,e) do { ; } while (0)
#define audit_syscall_exit(f,r) do { ; } while (0)
#define audit_syscall_exit(r) do { ; } while (0)
#define audit_dummy_context() 1
#define audit_getname(n) do { ; } while (0)
#define audit_putname(n) do { ; } while (0)
......@@ -558,6 +634,7 @@ extern int audit_signals;
#define audit_inode(n,d) do { (void)(d); } while (0)
#define audit_inode_child(i,p) do { ; } while (0)
#define audit_core_dumps(i) do { ; } while (0)
#define audit_seccomp(i) do { ; } while (0)
#define auditsc_get_stamp(c,t,s) (0)
#define audit_get_loginuid(t) (-1)
#define audit_get_sessionid(t) (-1)
......@@ -568,7 +645,6 @@ extern int audit_signals;
#define audit_socketcall(n,a) ((void)0)
#define audit_fd_pair(n,a) ((void)0)
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_set_macxattr(n) do { ; } while (0)
#define audit_mq_open(o,m,a) ((void)0)
#define audit_mq_sendrecv(d,l,p,t) ((void)0)
#define audit_mq_notify(d,n) ((void)0)
......@@ -579,7 +655,7 @@ extern int audit_signals;
#define audit_ptrace(t) ((void)0)
#define audit_n_rules 0
#define audit_signals 0
#endif
#endif /* CONFIG_AUDITSYSCALL */
#ifdef CONFIG_AUDIT
/* These are defined in audit.c */
......
......@@ -112,6 +112,7 @@
#include <linux/compiler.h> /* For unlikely. */
#include <linux/sched.h> /* For struct task_struct. */
#include <linux/err.h> /* for IS_ERR_VALUE */
extern long arch_ptrace(struct task_struct *child, long request,
......@@ -266,6 +267,15 @@ static inline void ptrace_release_task(struct task_struct *task)
#define force_successful_syscall_return() do { } while (0)
#endif
#ifndef is_syscall_success
/*
* On most systems we can tell if a syscall is a success based on if the retval
* is an error value. On some systems like ia64 and powerpc they have different
* indicators of success/failure and must define their own.
*/
#define is_syscall_success(regs) (!IS_ERR_VALUE((unsigned long)(regs_return_value(regs))))
#endif
/*
* <asm/ptrace.h> should define the following things inside #ifdef __KERNEL__.
*
......
......@@ -355,7 +355,7 @@ config AUDIT
config AUDITSYSCALL
bool "Enable system-call auditing support"
depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH)
depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH || ARM)
default y if SECURITY_SELINUX
help
Enable low-overhead system-call auditing infrastructure that
......@@ -372,6 +372,20 @@ config AUDIT_TREE
depends on AUDITSYSCALL
select FSNOTIFY
config AUDIT_LOGINUID_IMMUTABLE
bool "Make audit loginuid immutable"
depends on AUDIT
help
The config option toggles if a task setting its loginuid requires
CAP_SYS_AUDITCONTROL or if that task should require no special permissions
but should instead only allow setting its loginuid if it was never
previously set. On systems which use systemd or a similar central
process to restart login services this should be set to true. On older
systems in which an admin would typically have to directly stop and
start processes this should be set to false. Setting this to true allows
one to drop potentially dangerous capabilites from the login tasks,
but may not be backwards compatible with older init systems.
source "kernel/irq/Kconfig"
menu "RCU Subsystem"
......
......@@ -631,7 +631,7 @@ static int audit_log_common_recv_msg(struct audit_buffer **ab, u16 msg_type,
}
*ab = audit_log_start(NULL, GFP_KERNEL, msg_type);
audit_log_format(*ab, "user pid=%d uid=%u auid=%u ses=%u",
audit_log_format(*ab, "pid=%d uid=%u auid=%u ses=%u",
pid, uid, auid, ses);
if (sid) {
rc = security_secid_to_secctx(sid, &ctx, &len);
......@@ -1423,7 +1423,7 @@ void audit_log_d_path(struct audit_buffer *ab, const char *prefix,
char *p, *pathname;
if (prefix)
audit_log_format(ab, " %s", prefix);
audit_log_format(ab, "%s", prefix);
/* We will allow 11 spaces for ' (deleted)' to be appended */
pathname = kmalloc(PATH_MAX+11, ab->gfp_mask);
......
......@@ -36,12 +36,8 @@ enum audit_state {
AUDIT_DISABLED, /* Do not create per-task audit_context.
* No syscall-specific audit records can
* be generated. */
AUDIT_SETUP_CONTEXT, /* Create the per-task audit_context,
* but don't necessarily fill it in at
* syscall entry time (i.e., filter
* instead). */
AUDIT_BUILD_CONTEXT, /* Create the per-task audit_context,
* and always fill it in at syscall
* and fill it in at syscall
* entry time. This makes a full
* syscall record available if some
* other part of the kernel decides it
......
......@@ -235,13 +235,15 @@ static inline struct audit_entry *audit_to_entry_common(struct audit_rule *rule)
switch(listnr) {
default:
goto exit_err;
case AUDIT_FILTER_USER:
case AUDIT_FILTER_TYPE:
#ifdef CONFIG_AUDITSYSCALL
case AUDIT_FILTER_ENTRY:
if (rule->action == AUDIT_ALWAYS)
goto exit_err;
case AUDIT_FILTER_EXIT:
case AUDIT_FILTER_TASK:
#endif
case AUDIT_FILTER_USER:
case AUDIT_FILTER_TYPE:
;
}
if (unlikely(rule->action == AUDIT_POSSIBLE)) {
......@@ -385,7 +387,7 @@ static struct audit_entry *audit_rule_to_entry(struct audit_rule *rule)
goto exit_free;
break;
case AUDIT_FILETYPE:
if ((f->val & ~S_IFMT) > S_IFMT)
if (f->val & ~S_IFMT)
goto exit_free;
break;
case AUDIT_INODE:
......@@ -459,6 +461,8 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
case AUDIT_ARG1:
case AUDIT_ARG2:
case AUDIT_ARG3:
case AUDIT_OBJ_UID:
case AUDIT_OBJ_GID:
break;
case AUDIT_ARCH:
entry->rule.arch_f = f;
......@@ -522,7 +526,6 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
goto exit_free;
break;
case AUDIT_FILTERKEY:
err = -EINVAL;
if (entry->rule.filterkey || f->val > AUDIT_MAX_KEY_LEN)
goto exit_free;
str = audit_unpack_string(&bufp, &remain, f->val);
......@@ -536,7 +539,11 @@ static struct audit_entry *audit_data_to_entry(struct audit_rule_data *data,
goto exit_free;
break;
case AUDIT_FILETYPE:
if ((f->val & ~S_IFMT) > S_IFMT)
if (f->val & ~S_IFMT)
goto exit_free;
break;
case AUDIT_FIELD_COMPARE:
if (f->val > AUDIT_MAX_FIELD_COMPARE)
goto exit_free;
break;
default:
......
......@@ -70,9 +70,15 @@
#include "audit.h"
/* flags stating the success for a syscall */
#define AUDITSC_INVALID 0
#define AUDITSC_SUCCESS 1
#define AUDITSC_FAILURE 2
/* AUDIT_NAMES is the number of slots we reserve in the audit_context
* for saving names from getname(). */
#define AUDIT_NAMES 20
* for saving names from getname(). If we get more names we will allocate
* a name dynamically and also add those to the list anchored by names_list. */
#define AUDIT_NAMES 5
/* Indicates that audit should log the full pathname. */
#define AUDIT_NAME_FULL -1
......@@ -101,9 +107,8 @@ struct audit_cap_data {
*
* Further, in fs/namei.c:path_lookup() we store the inode and device. */
struct audit_names {
struct list_head list; /* audit_context->names_list */
const char *name;
int name_len; /* number of name's characters to log */
unsigned name_put; /* call __putname() for this name */
unsigned long ino;
dev_t dev;
umode_t mode;
......@@ -113,6 +118,14 @@ struct audit_names {
u32 osid;
struct audit_cap_data fcap;
unsigned int fcap_ver;
int name_len; /* number of name's characters to log */
bool name_put; /* call __putname() for this name */
/*
* This was an allocated audit_names and not from the array of
* names allocated in the task audit context. Thus this name
* should be freed on syscall exit
*/
bool should_free;
};
struct audit_aux_data {
......@@ -174,8 +187,17 @@ struct audit_context {
long return_code;/* syscall return code */
u64 prio;
int return_valid; /* return code is valid */
int name_count;
struct audit_names names[AUDIT_NAMES];
/*
* The names_list is the list of all audit_names collected during this
* syscall. The first AUDIT_NAMES entries in the names_list will
* actually be from the preallocated_names array for performance
* reasons. Except during allocation they should never be referenced
* through the preallocated_names array and should only be found/used
* by running the names_list.
*/
struct audit_names preallocated_names[AUDIT_NAMES];
int name_count; /* total records in names_list */
struct list_head names_list; /* anchor for struct audit_names->list */
char * filterkey; /* key for rule that triggered record */
struct path pwd;
struct audit_context *previous; /* For nested syscalls */
......@@ -305,21 +327,21 @@ static int audit_match_perm(struct audit_context *ctx, int mask)
}
}
static int audit_match_filetype(struct audit_context *ctx, int which)
static int audit_match_filetype(struct audit_context *ctx, int val)
{
unsigned index = which & ~S_IFMT;
umode_t mode = which & S_IFMT;
struct audit_names *n;
umode_t mode = (umode_t)val;
if (unlikely(!ctx))
return 0;
if (index >= ctx->name_count)
return 0;
if (ctx->names[index].ino == -1)
return 0;
if ((ctx->names[index].mode ^ mode) & S_IFMT)
return 0;
return 1;
list_for_each_entry(n, &ctx->names_list, list) {
if ((n->ino != -1) &&
((n->mode & S_IFMT) == mode))
return 1;
}
return 0;
}
/*
......@@ -441,6 +463,134 @@ static int match_tree_refs(struct audit_context *ctx, struct audit_tree *tree)
return 0;
}
static int audit_compare_id(uid_t uid1,
struct audit_names *name,
unsigned long name_offset,
struct audit_field *f,
struct audit_context *ctx)
{
struct audit_names *n;
unsigned long addr;
uid_t uid2;
int rc;
BUILD_BUG_ON(sizeof(uid_t) != sizeof(gid_t));
if (name) {
addr = (unsigned long)name;
addr += name_offset;
uid2 = *(uid_t *)addr;
rc = audit_comparator(uid1, f->op, uid2);
if (rc)
return rc;
}
if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
addr = (unsigned long)n;
addr += name_offset;
uid2 = *(uid_t *)addr;
rc = audit_comparator(uid1, f->op, uid2);
if (rc)
return rc;
}
}
return 0;
}
static int audit_field_compare(struct task_struct *tsk,
const struct cred *cred,
struct audit_field *f,
struct audit_context *ctx,
struct audit_names *name)
{
switch (f->val) {
/* process to file object comparisons */
case AUDIT_COMPARE_UID_TO_OBJ_UID:
return audit_compare_id(cred->uid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_GID_TO_OBJ_GID:
return audit_compare_id(cred->gid,
name, offsetof(struct audit_names, gid),
f, ctx);
case AUDIT_COMPARE_EUID_TO_OBJ_UID:
return audit_compare_id(cred->euid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_EGID_TO_OBJ_GID:
return audit_compare_id(cred->egid,
name, offsetof(struct audit_names, gid),
f, ctx);
case AUDIT_COMPARE_AUID_TO_OBJ_UID:
return audit_compare_id(tsk->loginuid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_SUID_TO_OBJ_UID:
return audit_compare_id(cred->suid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_SGID_TO_OBJ_GID:
return audit_compare_id(cred->sgid,
name, offsetof(struct audit_names, gid),
f, ctx);
case AUDIT_COMPARE_FSUID_TO_OBJ_UID:
return audit_compare_id(cred->fsuid,
name, offsetof(struct audit_names, uid),
f, ctx);
case AUDIT_COMPARE_FSGID_TO_OBJ_GID:
return audit_compare_id(cred->fsgid,
name, offsetof(struct audit_names, gid),
f, ctx);
/* uid comparisons */
case AUDIT_COMPARE_UID_TO_AUID:
return audit_comparator(cred->uid, f->op, tsk->loginuid);
case AUDIT_COMPARE_UID_TO_EUID:
return audit_comparator(cred->uid, f->op, cred->euid);
case AUDIT_COMPARE_UID_TO_SUID:
return audit_comparator(cred->uid, f->op, cred->suid);
case AUDIT_COMPARE_UID_TO_FSUID:
return audit_comparator(cred->uid, f->op, cred->fsuid);
/* auid comparisons */
case AUDIT_COMPARE_AUID_TO_EUID:
return audit_comparator(tsk->loginuid, f->op, cred->euid);
case AUDIT_COMPARE_AUID_TO_SUID:
return audit_comparator(tsk->loginuid, f->op, cred->suid);
case AUDIT_COMPARE_AUID_TO_FSUID:
return audit_comparator(tsk->loginuid, f->op, cred->fsuid);
/* euid comparisons */
case AUDIT_COMPARE_EUID_TO_SUID:
return audit_comparator(cred->euid, f->op, cred->suid);
case AUDIT_COMPARE_EUID_TO_FSUID:
return audit_comparator(cred->euid, f->op, cred->fsuid);
/* suid comparisons */
case AUDIT_COMPARE_SUID_TO_FSUID:
return audit_comparator(cred->suid, f->op, cred->fsuid);
/* gid comparisons */
case AUDIT_COMPARE_GID_TO_EGID:
return audit_comparator(cred->gid, f->op, cred->egid);
case AUDIT_COMPARE_GID_TO_SGID:
return audit_comparator(cred->gid, f->op, cred->sgid);
case AUDIT_COMPARE_GID_TO_FSGID:
return audit_comparator(cred->gid, f->op, cred->fsgid);
/* egid comparisons */
case AUDIT_COMPARE_EGID_TO_SGID:
return audit_comparator(cred->egid, f->op, cred->sgid);
case AUDIT_COMPARE_EGID_TO_FSGID:
return audit_comparator(cred->egid, f->op, cred->fsgid);
/* sgid comparison */
case AUDIT_COMPARE_SGID_TO_FSGID:
return audit_comparator(cred->sgid, f->op, cred->fsgid);
default:
WARN(1, "Missing AUDIT_COMPARE define. Report as a bug\n");
return 0;
}
return 0;
}
/* Determine if any context name data matches a rule's watch data */
/* Compare a task_struct with an audit_rule. Return 1 on match, 0
* otherwise.
......@@ -457,13 +607,14 @@ static int audit_filter_rules(struct task_struct *tsk,
bool task_creation)
{
const struct cred *cred;
int i, j, need_sid = 1;
int i, need_sid = 1;
u32 sid;
cred = rcu_dereference_check(tsk->cred, tsk == current || task_creation);
for (i = 0; i < rule->field_count; i++) {
struct audit_field *f = &rule->fields[i];
struct audit_names *n;
int result = 0;
switch (f->type) {
......@@ -522,12 +673,14 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_DEVMAJOR:
if (name)
result = audit_comparator(MAJOR(name->dev),
f->op, f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MAJOR(ctx->names[j].dev), f->op, f->val)) {
if (name) {
if (audit_comparator(MAJOR(name->dev), f->op, f->val) ||
audit_comparator(MAJOR(name->rdev), f->op, f->val))
++result;
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(MAJOR(n->dev), f->op, f->val) ||
audit_comparator(MAJOR(n->rdev), f->op, f->val)) {
++result;
break;
}
......@@ -535,12 +688,14 @@ static int audit_filter_rules(struct task_struct *tsk,
}
break;
case AUDIT_DEVMINOR:
if (name)
result = audit_comparator(MINOR(name->dev),
f->op, f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(MINOR(ctx->names[j].dev), f->op, f->val)) {
if (name) {
if (audit_comparator(MINOR(name->dev), f->op, f->val) ||
audit_comparator(MINOR(name->rdev), f->op, f->val))
++result;
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(MINOR(n->dev), f->op, f->val) ||
audit_comparator(MINOR(n->rdev), f->op, f->val)) {
++result;
break;
}
......@@ -551,8 +706,32 @@ static int audit_filter_rules(struct task_struct *tsk,
if (name)
result = (name->ino == f->val);
else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (audit_comparator(ctx->names[j].ino, f->op, f->val)) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->ino, f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_OBJ_UID:
if (name) {
result = audit_comparator(name->uid, f->op, f->val);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->uid, f->op, f->val)) {
++result;
break;
}
}
}
break;
case AUDIT_OBJ_GID:
if (name) {
result = audit_comparator(name->gid, f->op, f->val);
} else if (ctx) {
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_comparator(n->gid, f->op, f->val)) {
++result;
break;
}
......@@ -607,11 +786,10 @@ static int audit_filter_rules(struct task_struct *tsk,
name->osid, f->type, f->op,
f->lsm_rule, ctx);
} else if (ctx) {
for (j = 0; j < ctx->name_count; j++) {
if (security_audit_rule_match(
ctx->names[j].osid,
f->type, f->op,
f->lsm_rule, ctx)) {
list_for_each_entry(n, &ctx->names_list, list) {
if (security_audit_rule_match(n->osid, f->type,
f->op, f->lsm_rule,
ctx)) {
++result;
break;
}
......@@ -643,8 +821,10 @@ static int audit_filter_rules(struct task_struct *tsk,
case AUDIT_FILETYPE:
result = audit_match_filetype(ctx, f->val);
break;
case AUDIT_FIELD_COMPARE:
result = audit_field_compare(tsk, cred, f, ctx, name);
break;
}
if (!result)
return 0;
}
......@@ -722,40 +902,53 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
return AUDIT_BUILD_CONTEXT;
}
/* At syscall exit time, this filter is called if any audit_names[] have been
/*
* Given an audit_name check the inode hash table to see if they match.
* Called holding the rcu read lock to protect the use of audit_inode_hash
*/
static int audit_filter_inode_name(struct task_struct *tsk,
struct audit_names *n,
struct audit_context *ctx) {
int word, bit;
int h = audit_hash_ino((u32)n->ino);
struct list_head *list = &audit_inode_hash[h];
struct audit_entry *e;
enum audit_state state;
word = AUDIT_WORD(ctx->major);
bit = AUDIT_BIT(ctx->major);
if (list_empty(list))
return 0;
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n, &state, false)) {
ctx->current_state = state;
return 1;
}
}
return 0;
}
/* At syscall exit time, this filter is called if any audit_names have been
* collected during syscall processing. We only check rules in sublists at hash
* buckets applicable to the inode numbers in audit_names[].
* buckets applicable to the inode numbers in audit_names.
* Regarding audit_state, same rules apply as for audit_filter_syscall().
*/
void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
{
int i;
struct audit_entry *e;
enum audit_state state;
struct audit_names *n;
if (audit_pid && tsk->tgid == audit_pid)
return;
rcu_read_lock();
for (i = 0; i < ctx->name_count; i++) {
int word = AUDIT_WORD(ctx->major);
int bit = AUDIT_BIT(ctx->major);
struct audit_names *n = &ctx->names[i];
int h = audit_hash_ino((u32)n->ino);
struct list_head *list = &audit_inode_hash[h];
if (list_empty(list))
continue;
list_for_each_entry_rcu(e, list, list) {
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n,
&state, false)) {
rcu_read_unlock();
ctx->current_state = state;
return;
}
}
list_for_each_entry(n, &ctx->names_list, list) {
if (audit_filter_inode_name(tsk, n, ctx))
break;
}
rcu_read_unlock();
}
......@@ -766,7 +959,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
{
struct audit_context *context = tsk->audit_context;
if (likely(!context))
if (!context)
return NULL;
context->return_valid = return_valid;
......@@ -799,7 +992,7 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
static inline void audit_free_names(struct audit_context *context)
{
int i;
struct audit_names *n, *next;
#if AUDIT_DEBUG == 2
if (context->put_count + context->ino_count != context->name_count) {
......@@ -810,10 +1003,9 @@ static inline void audit_free_names(struct audit_context *context)
context->serial, context->major, context->in_syscall,
context->name_count, context->put_count,
context->ino_count);
for (i = 0; i < context->name_count; i++) {
list_for_each_entry(n, &context->names_list, list) {
printk(KERN_ERR "names[%d] = %p = %s\n", i,
context->names[i].name,
context->names[i].name ?: "(null)");
n->name, n->name ?: "(null)");
}
dump_stack();
return;
......@@ -824,9 +1016,12 @@ static inline void audit_free_names(struct audit_context *context)
context->ino_count = 0;
#endif
for (i = 0; i < context->name_count; i++) {
if (context->names[i].name && context->names[i].name_put)
__putname(context->names[i].name);
list_for_each_entry_safe(n, next, &context->names_list, list) {
list_del(&n->list);
if (n->name && n->name_put)
__putname(n->name);
if (n->should_free)
kfree(n);
}
context->name_count = 0;
path_put(&context->pwd);
......@@ -864,6 +1059,7 @@ static inline struct audit_context *audit_alloc_context(enum audit_state state)
return NULL;
audit_zero_context(context, state);
INIT_LIST_HEAD(&context->killed_trees);
INIT_LIST_HEAD(&context->names_list);
return context;
}
......@@ -886,7 +1082,7 @@ int audit_alloc(struct task_struct *tsk)
return 0; /* Return if not auditing. */
state = audit_filter_task(tsk, &key);
if (likely(state == AUDIT_DISABLED))
if (state == AUDIT_DISABLED)
return 0;
if (!(context = audit_alloc_context(state))) {
......@@ -975,7 +1171,7 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk
while (vma) {
if ((vma->vm_flags & VM_EXECUTABLE) &&
vma->vm_file) {
audit_log_d_path(ab, "exe=",
audit_log_d_path(ab, " exe=",
&vma->vm_file->f_path);
break;
}
......@@ -1166,8 +1362,8 @@ static void audit_log_execve_info(struct audit_context *context,
struct audit_buffer **ab,
struct audit_aux_data_execve *axi)
{
int i;
size_t len, len_sent = 0;
int i, len;
size_t len_sent = 0;
const char __user *p;
char *buf;
......@@ -1324,6 +1520,68 @@ static void show_special(struct audit_context *context, int *call_panic)
audit_log_end(ab);
}
static void audit_log_name(struct audit_context *context, struct audit_names *n,
int record_num, int *call_panic)
{
struct audit_buffer *ab;
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
if (!ab)
return; /* audit_panic has been called */
audit_log_format(ab, "item=%d", record_num);
if (n->name) {
switch (n->name_len) {
case AUDIT_NAME_FULL:
/* log the full path */
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, n->name);
break;
case 0:
/* name was specified as a relative path and the
* directory component is the cwd */
audit_log_d_path(ab, " name=", &context->pwd);
break;
default:
/* log the name's directory component */
audit_log_format(ab, " name=");
audit_log_n_untrustedstring(ab, n->name,
n->name_len);
}
} else
audit_log_format(ab, " name=(null)");
if (n->ino != (unsigned long)-1) {
audit_log_format(ab, " inode=%lu"
" dev=%02x:%02x mode=%#ho"
" ouid=%u ogid=%u rdev=%02x:%02x",
n->ino,
MAJOR(n->dev),
MINOR(n->dev),
n->mode,
n->uid,
n->gid,
MAJOR(n->rdev),
MINOR(n->rdev));
}
if (n->osid != 0) {
char *ctx = NULL;
u32 len;
if (security_secid_to_secctx(
n->osid, &ctx, &len)) {
audit_log_format(ab, " osid=%u", n->osid);
*call_panic = 2;
} else {
audit_log_format(ab, " obj=%s", ctx);
security_release_secctx(ctx, len);
}
}
audit_log_fcaps(ab, n);
audit_log_end(ab);
}
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{
const struct cred *cred;
......@@ -1331,6 +1589,7 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
struct audit_buffer *ab;
struct audit_aux_data *aux;
const char *tty;
struct audit_names *n;
/* tsk == current */
context->pid = tsk->pid;
......@@ -1466,70 +1725,14 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
if (context->pwd.dentry && context->pwd.mnt) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_CWD);
if (ab) {
audit_log_d_path(ab, "cwd=", &context->pwd);
audit_log_d_path(ab, " cwd=", &context->pwd);
audit_log_end(ab);
}
}
for (i = 0; i < context->name_count; i++) {
struct audit_names *n = &context->names[i];
ab = audit_log_start(context, GFP_KERNEL, AUDIT_PATH);
if (!ab)
continue; /* audit_panic has been called */
audit_log_format(ab, "item=%d", i);
if (n->name) {
switch(n->name_len) {
case AUDIT_NAME_FULL:
/* log the full path */
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, n->name);
break;
case 0:
/* name was specified as a relative path and the
* directory component is the cwd */
audit_log_d_path(ab, "name=", &context->pwd);
break;
default:
/* log the name's directory component */
audit_log_format(ab, " name=");
audit_log_n_untrustedstring(ab, n->name,
n->name_len);
}
} else
audit_log_format(ab, " name=(null)");
if (n->ino != (unsigned long)-1) {
audit_log_format(ab, " inode=%lu"
" dev=%02x:%02x mode=%#ho"
" ouid=%u ogid=%u rdev=%02x:%02x",
n->ino,
MAJOR(n->dev),
MINOR(n->dev),
n->mode,
n->uid,
n->gid,
MAJOR(n->rdev),
MINOR(n->rdev));
}
if (n->osid != 0) {
char *ctx = NULL;
u32 len;
if (security_secid_to_secctx(
n->osid, &ctx, &len)) {
audit_log_format(ab, " osid=%u", n->osid);
call_panic = 2;
} else {
audit_log_format(ab, " obj=%s", ctx);
security_release_secctx(ctx, len);
}
}
audit_log_fcaps(ab, n);
audit_log_end(ab);
}
i = 0;
list_for_each_entry(n, &context->names_list, list)
audit_log_name(context, n, i++, &call_panic);
/* Send end of event record to help user space know we are finished */
ab = audit_log_start(context, GFP_KERNEL, AUDIT_EOE);
......@@ -1545,12 +1748,12 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
*
* Called from copy_process and do_exit
*/
void audit_free(struct task_struct *tsk)
void __audit_free(struct task_struct *tsk)
{
struct audit_context *context;
context = audit_get_context(tsk, 0, 0);
if (likely(!context))
if (!context)
return;
/* Check for system calls that do not go through the exit
......@@ -1583,7 +1786,7 @@ void audit_free(struct task_struct *tsk)
* will only be written if another part of the kernel requests that it
* be written).
*/
void audit_syscall_entry(int arch, int major,
void __audit_syscall_entry(int arch, int major,
unsigned long a1, unsigned long a2,
unsigned long a3, unsigned long a4)
{
......@@ -1591,7 +1794,7 @@ void audit_syscall_entry(int arch, int major,
struct audit_context *context = tsk->audit_context;
enum audit_state state;
if (unlikely(!context))
if (!context)
return;
/*
......@@ -1648,7 +1851,7 @@ void audit_syscall_entry(int arch, int major,
context->prio = 0;
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
}
if (likely(state == AUDIT_DISABLED))
if (state == AUDIT_DISABLED)
return;
context->serial = 0;
......@@ -1658,30 +1861,9 @@ void audit_syscall_entry(int arch, int major,
context->ppid = 0;
}
void audit_finish_fork(struct task_struct *child)
{
struct audit_context *ctx = current->audit_context;
struct audit_context *p = child->audit_context;
if (!p || !ctx)
return;
if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT)
return;
p->arch = ctx->arch;
p->major = ctx->major;
memcpy(p->argv, ctx->argv, sizeof(ctx->argv));
p->ctime = ctx->ctime;
p->dummy = ctx->dummy;
p->in_syscall = ctx->in_syscall;
p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL);
p->ppid = current->pid;
p->prio = ctx->prio;
p->current_state = ctx->current_state;
}
/**
* audit_syscall_exit - deallocate audit context after a system call
* @valid: success/failure flag
* @return_code: syscall return value
* @pt_regs: syscall registers
*
* Tear down after system call. If the audit context has been marked as
* auditable (either because of the AUDIT_RECORD_CONTEXT state from
......@@ -1689,14 +1871,18 @@ void audit_finish_fork(struct task_struct *child)
* message), then write out the syscall information. In call cases,
* free the names stored from getname().
*/
void audit_syscall_exit(int valid, long return_code)
void __audit_syscall_exit(int success, long return_code)
{
struct task_struct *tsk = current;
struct audit_context *context;
context = audit_get_context(tsk, valid, return_code);
if (success)
success = AUDITSC_SUCCESS;
else
success = AUDITSC_FAILURE;
if (likely(!context))
context = audit_get_context(tsk, success, return_code);
if (!context)
return;
if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
......@@ -1821,6 +2007,30 @@ static void handle_path(const struct dentry *dentry)
#endif
}
static struct audit_names *audit_alloc_name(struct audit_context *context)
{
struct audit_names *aname;
if (context->name_count < AUDIT_NAMES) {
aname = &context->preallocated_names[context->name_count];
memset(aname, 0, sizeof(*aname));
} else {
aname = kzalloc(sizeof(*aname), GFP_NOFS);
if (!aname)
return NULL;
aname->should_free = true;
}
aname->ino = (unsigned long)-1;
list_add_tail(&aname->list, &context->names_list);
context->name_count++;
#if AUDIT_DEBUG
context->ino_count++;
#endif
return aname;
}
/**
* audit_getname - add a name to the list
* @name: name to add
......@@ -1831,9 +2041,7 @@ static void handle_path(const struct dentry *dentry)
void __audit_getname(const char *name)
{
struct audit_context *context = current->audit_context;
if (IS_ERR(name) || !name)
return;
struct audit_names *n;
if (!context->in_syscall) {
#if AUDIT_DEBUG == 2
......@@ -1843,13 +2051,15 @@ void __audit_getname(const char *name)
#endif
return;
}
BUG_ON(context->name_count >= AUDIT_NAMES);
context->names[context->name_count].name = name;
context->names[context->name_count].name_len = AUDIT_NAME_FULL;
context->names[context->name_count].name_put = 1;
context->names[context->name_count].ino = (unsigned long)-1;
context->names[context->name_count].osid = 0;
++context->name_count;
n = audit_alloc_name(context);
if (!n)
return;
n->name = name;
n->name_len = AUDIT_NAME_FULL;
n->name_put = true;
if (!context->pwd.dentry)
get_fs_pwd(current->fs, &context->pwd);
}
......@@ -1871,12 +2081,13 @@ void audit_putname(const char *name)
printk(KERN_ERR "%s:%d(:%d): __putname(%p)\n",
__FILE__, __LINE__, context->serial, name);
if (context->name_count) {
struct audit_names *n;
int i;
for (i = 0; i < context->name_count; i++)
list_for_each_entry(n, &context->names_list, list)
printk(KERN_ERR "name[%d] = %p = %s\n", i,
context->names[i].name,
context->names[i].name ?: "(null)");
}
n->name, n->name ?: "(null)");
}
#endif
__putname(name);
}
......@@ -1897,39 +2108,11 @@ void audit_putname(const char *name)
#endif
}
static int audit_inc_name_count(struct audit_context *context,
const struct inode *inode)
{
if (context->name_count >= AUDIT_NAMES) {
if (inode)
printk(KERN_DEBUG "audit: name_count maxed, losing inode data: "
"dev=%02x:%02x, inode=%lu\n",
MAJOR(inode->i_sb->s_dev),
MINOR(inode->i_sb->s_dev),
inode->i_ino);
else
printk(KERN_DEBUG "name_count maxed, losing inode data\n");
return 1;
}
context->name_count++;
#if AUDIT_DEBUG
context->ino_count++;
#endif
return 0;
}
static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry *dentry)
{
struct cpu_vfs_cap_data caps;
int rc;
memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t));
memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t));
name->fcap.fE = 0;
name->fcap_ver = 0;
if (!dentry)
return 0;
......@@ -1969,30 +2152,25 @@ static void audit_copy_inode(struct audit_names *name, const struct dentry *dent
*/
void __audit_inode(const char *name, const struct dentry *dentry)
{
int idx;
struct audit_context *context = current->audit_context;
const struct inode *inode = dentry->d_inode;
struct audit_names *n;
if (!context->in_syscall)
return;
if (context->name_count
&& context->names[context->name_count-1].name
&& context->names[context->name_count-1].name == name)
idx = context->name_count - 1;
else if (context->name_count > 1
&& context->names[context->name_count-2].name
&& context->names[context->name_count-2].name == name)
idx = context->name_count - 2;
else {
/* FIXME: how much do we care about inodes that have no
* associated name? */
if (audit_inc_name_count(context, inode))
return;
idx = context->name_count - 1;
context->names[idx].name = NULL;
list_for_each_entry_reverse(n, &context->names_list, list) {
if (n->name && (n->name == name))
goto out;
}
/* unable to find the name from a previous getname() */
n = audit_alloc_name(context);
if (!n)
return;
out:
handle_path(dentry);
audit_copy_inode(&context->names[idx], dentry, inode);
audit_copy_inode(n, dentry, inode);
}
/**
......@@ -2011,11 +2189,11 @@ void __audit_inode(const char *name, const struct dentry *dentry)
void __audit_inode_child(const struct dentry *dentry,
const struct inode *parent)
{
int idx;
struct audit_context *context = current->audit_context;
const char *found_parent = NULL, *found_child = NULL;
const struct inode *inode = dentry->d_inode;
const char *dname = dentry->d_name.name;
struct audit_names *n;
int dirlen = 0;
if (!context->in_syscall)
......@@ -2025,9 +2203,7 @@ void __audit_inode_child(const struct dentry *dentry,
handle_one(inode);
/* parent is more likely, look for it first */
for (idx = 0; idx < context->name_count; idx++) {
struct audit_names *n = &context->names[idx];
list_for_each_entry(n, &context->names_list, list) {
if (!n->name)
continue;
......@@ -2040,9 +2216,7 @@ void __audit_inode_child(const struct dentry *dentry,
}
/* no matching parent, look for matching child */
for (idx = 0; idx < context->name_count; idx++) {
struct audit_names *n = &context->names[idx];
list_for_each_entry(n, &context->names_list, list) {
if (!n->name)
continue;
......@@ -2060,34 +2234,29 @@ void __audit_inode_child(const struct dentry *dentry,
add_names:
if (!found_parent) {
if (audit_inc_name_count(context, parent))
n = audit_alloc_name(context);
if (!n)
return;
idx = context->name_count - 1;
context->names[idx].name = NULL;
audit_copy_inode(&context->names[idx], NULL, parent);
audit_copy_inode(n, NULL, parent);
}
if (!found_child) {
if (audit_inc_name_count(context, inode))
n = audit_alloc_name(context);
if (!n)
return;
idx = context->name_count - 1;
/* Re-use the name belonging to the slot for a matching parent
* directory. All names for this context are relinquished in
* audit_free_names() */
if (found_parent) {
context->names[idx].name = found_parent;
context->names[idx].name_len = AUDIT_NAME_FULL;
n->name = found_parent;
n->name_len = AUDIT_NAME_FULL;
/* don't call __putname() */
context->names[idx].name_put = 0;
} else {
context->names[idx].name = NULL;
n->name_put = false;
}
if (inode)
audit_copy_inode(&context->names[idx], NULL, inode);
else
context->names[idx].ino = (unsigned long)-1;
audit_copy_inode(n, NULL, inode);
}
}
EXPORT_SYMBOL_GPL(__audit_inode_child);
......@@ -2121,19 +2290,28 @@ int auditsc_get_stamp(struct audit_context *ctx,
static atomic_t session_id = ATOMIC_INIT(0);
/**
* audit_set_loginuid - set a task's audit_context loginuid
* @task: task whose audit context is being modified
* audit_set_loginuid - set current task's audit_context loginuid
* @loginuid: loginuid value
*
* Returns 0.
*
* Called (set) from fs/proc/base.c::proc_loginuid_write().
*/
int audit_set_loginuid(struct task_struct *task, uid_t loginuid)
int audit_set_loginuid(uid_t loginuid)
{
unsigned int sessionid = atomic_inc_return(&session_id);
struct task_struct *task = current;
struct audit_context *context = task->audit_context;
unsigned int sessionid;
#ifdef CONFIG_AUDIT_LOGINUID_IMMUTABLE
if (task->loginuid != -1)
return -EPERM;
#else /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
if (!capable(CAP_AUDIT_CONTROL))
return -EPERM;
#endif /* CONFIG_AUDIT_LOGINUID_IMMUTABLE */
sessionid = atomic_inc_return(&session_id);
if (context && context->in_syscall) {
struct audit_buffer *ab;
......@@ -2271,14 +2449,11 @@ void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mo
context->ipc.has_perm = 1;
}
int audit_bprm(struct linux_binprm *bprm)
int __audit_bprm(struct linux_binprm *bprm)
{
struct audit_aux_data_execve *ax;
struct audit_context *context = current->audit_context;
if (likely(!audit_enabled || !context || context->dummy))
return 0;
ax = kmalloc(sizeof(*ax), GFP_KERNEL);
if (!ax)
return -ENOMEM;
......@@ -2299,13 +2474,10 @@ int audit_bprm(struct linux_binprm *bprm)
* @args: args array
*
*/
void audit_socketcall(int nargs, unsigned long *args)
void __audit_socketcall(int nargs, unsigned long *args)
{
struct audit_context *context = current->audit_context;
if (likely(!context || context->dummy))
return;
context->type = AUDIT_SOCKETCALL;
context->socketcall.nargs = nargs;
memcpy(context->socketcall.args, args, nargs * sizeof(unsigned long));
......@@ -2331,13 +2503,10 @@ void __audit_fd_pair(int fd1, int fd2)
*
* Returns 0 for success or NULL context or < 0 on error.
*/
int audit_sockaddr(int len, void *a)
int __audit_sockaddr(int len, void *a)
{
struct audit_context *context = current->audit_context;
if (likely(!context || context->dummy))
return 0;
if (!context->sockaddr) {
void *p = kmalloc(sizeof(struct sockaddr_storage), GFP_KERNEL);
if (!p)
......@@ -2499,6 +2668,25 @@ void __audit_mmap_fd(int fd, int flags)
context->type = AUDIT_MMAP;
}
static void audit_log_abend(struct audit_buffer *ab, char *reason, long signr)
{
uid_t auid, uid;
gid_t gid;
unsigned int sessionid;
auid = audit_get_loginuid(current);
sessionid = audit_get_sessionid(current);
current_uid_gid(&uid, &gid);
audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u",
auid, uid, gid, sessionid);
audit_log_task_context(ab);
audit_log_format(ab, " pid=%d comm=", current->pid);
audit_log_untrustedstring(ab, current->comm);
audit_log_format(ab, " reason=");
audit_log_string(ab, reason);
audit_log_format(ab, " sig=%ld", signr);
}
/**
* audit_core_dumps - record information about processes that end abnormally
* @signr: signal value
......@@ -2509,10 +2697,6 @@ void __audit_mmap_fd(int fd, int flags)
void audit_core_dumps(long signr)
{
struct audit_buffer *ab;
u32 sid;
uid_t auid = audit_get_loginuid(current), uid;
gid_t gid;
unsigned int sessionid = audit_get_sessionid(current);
if (!audit_enabled)
return;
......@@ -2521,24 +2705,17 @@ void audit_core_dumps(long signr)
return;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
current_uid_gid(&uid, &gid);
audit_log_format(ab, "auid=%u uid=%u gid=%u ses=%u",
auid, uid, gid, sessionid);
security_task_getsecid(current, &sid);
if (sid) {
char *ctx = NULL;
u32 len;
audit_log_abend(ab, "memory violation", signr);
audit_log_end(ab);
}
if (security_secid_to_secctx(sid, &ctx, &len))
audit_log_format(ab, " ssid=%u", sid);
else {
audit_log_format(ab, " subj=%s", ctx);
security_release_secctx(ctx, len);
}
}
audit_log_format(ab, " pid=%d comm=", current->pid);
audit_log_untrustedstring(ab, current->comm);
audit_log_format(ab, " sig=%ld", signr);
void __audit_seccomp(unsigned long syscall)
{
struct audit_buffer *ab;
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_ANOM_ABEND);
audit_log_abend(ab, "seccomp", SIGKILL);
audit_log_format(ab, " syscall=%ld", syscall);
audit_log_end(ab);
}
......
......@@ -964,8 +964,7 @@ void do_exit(long code)
acct_collect(code, group_dead);
if (group_dead)
tty_audit_exit();
if (unlikely(tsk->audit_context))
audit_free(tsk);
audit_free(tsk);
tsk->exit_code = code;
taskstats_exit(tsk, group_dead);
......
......@@ -1527,8 +1527,6 @@ long do_fork(unsigned long clone_flags,
init_completion(&vfork);
}
audit_finish_fork(p);
/*
* We set PF_STARTING at creation in case tracing wants to
* use this to distinguish a fully live task from one that
......
......@@ -6,6 +6,7 @@
* This defines a simple but solid secure-computing mode.
*/
#include <linux/audit.h>
#include <linux/seccomp.h>
#include <linux/sched.h>
#include <linux/compat.h>
......@@ -54,6 +55,7 @@ void __secure_computing(int this_syscall)
#ifdef SECCOMP_DEBUG
dump_stack();
#endif
audit_seccomp(this_syscall);
do_exit(SIGKILL);
}
......
......@@ -56,9 +56,11 @@ void integrity_audit_msg(int audit_msgno, struct inode *inode,
audit_log_format(ab, " name=");
audit_log_untrustedstring(ab, fname);
}
if (inode)
audit_log_format(ab, " dev=%s ino=%lu",
inode->i_sb->s_id, inode->i_ino);
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
}
audit_log_format(ab, " res=%d", !result ? 0 : 1);
audit_log_end(ab);
}
......@@ -232,13 +232,14 @@ static void dump_common_audit_data(struct audit_buffer *ab,
case LSM_AUDIT_DATA_PATH: {
struct inode *inode;
audit_log_d_path(ab, "path=", &a->u.path);
audit_log_d_path(ab, " path=", &a->u.path);
inode = a->u.path.dentry->d_inode;
if (inode)
audit_log_format(ab, " dev=%s ino=%lu",
inode->i_sb->s_id,
inode->i_ino);
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
}
break;
}
case LSM_AUDIT_DATA_DENTRY: {
......@@ -248,10 +249,11 @@ static void dump_common_audit_data(struct audit_buffer *ab,
audit_log_untrustedstring(ab, a->u.dentry->d_name.name);
inode = a->u.dentry->d_inode;
if (inode)
audit_log_format(ab, " dev=%s ino=%lu",
inode->i_sb->s_id,
inode->i_ino);
if (inode) {
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
}
break;
}
case LSM_AUDIT_DATA_INODE: {
......@@ -266,8 +268,9 @@ static void dump_common_audit_data(struct audit_buffer *ab,
dentry->d_name.name);
dput(dentry);
}
audit_log_format(ab, " dev=%s ino=%lu", inode->i_sb->s_id,
inode->i_ino);
audit_log_format(ab, " dev=");
audit_log_untrustedstring(ab, inode->i_sb->s_id);
audit_log_format(ab, " ino=%lu", inode->i_ino);
break;
}
case LSM_AUDIT_DATA_TASK:
......@@ -315,7 +318,7 @@ static void dump_common_audit_data(struct audit_buffer *ab,
.dentry = u->dentry,
.mnt = u->mnt
};
audit_log_d_path(ab, "path=", &path);
audit_log_d_path(ab, " path=", &path);
break;
}
if (!u->addr)
......
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