mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-11-18 23:54:26 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6: (57 commits) binfmt_elf: fix PT_INTERP bss handling TPM: Fixup boot probe timeout for tpm_tis driver sysfs: Add labeling support for sysfs LSM/SELinux: inode_{get,set,notify}secctx hooks to access LSM security context information. VFS: Factor out part of vfs_setxattr so it can be called from the SELinux hook for inode_setsecctx. KEYS: Add missing linux/tracehook.h #inclusions KEYS: Fix default security_session_to_parent() Security/SELinux: includecheck fix kernel/sysctl.c KEYS: security_cred_alloc_blank() should return int under all circumstances IMA: open new file for read KEYS: Add a keyctl to install a process's session keyring on its parent [try #6] KEYS: Extend TIF_NOTIFY_RESUME to (almost) all architectures [try #6] KEYS: Do some whitespace cleanups [try #6] KEYS: Make /proc/keys use keyid not numread as file position [try #6] KEYS: Add garbage collection for dead, revoked and expired keys. [try #6] KEYS: Flag dead keys to induce EKEYREVOKED [try #6] KEYS: Allow keyctl_revoke() on keys that have SETATTR but not WRITE perm [try #6] KEYS: Deal with dead-type keys appropriately [try #6] CRED: Add some configurable debugging [try #6] selinux: Support for the new TUN LSM hooks ...
This commit is contained in:
commit
f6f7919086
@ -26,7 +26,7 @@ This document has the following sections:
|
||||
- Notes on accessing payload contents
|
||||
- Defining a key type
|
||||
- Request-key callback service
|
||||
- Key access filesystem
|
||||
- Garbage collection
|
||||
|
||||
|
||||
============
|
||||
@ -113,6 +113,9 @@ Each key has a number of attributes:
|
||||
|
||||
(*) Dead. The key's type was unregistered, and so the key is now useless.
|
||||
|
||||
Keys in the last three states are subject to garbage collection. See the
|
||||
section on "Garbage collection".
|
||||
|
||||
|
||||
====================
|
||||
KEY SERVICE OVERVIEW
|
||||
@ -754,6 +757,26 @@ The keyctl syscall functions are:
|
||||
successful.
|
||||
|
||||
|
||||
(*) Install the calling process's session keyring on its parent.
|
||||
|
||||
long keyctl(KEYCTL_SESSION_TO_PARENT);
|
||||
|
||||
This functions attempts to install the calling process's session keyring
|
||||
on to the calling process's parent, replacing the parent's current session
|
||||
keyring.
|
||||
|
||||
The calling process must have the same ownership as its parent, the
|
||||
keyring must have the same ownership as the calling process, the calling
|
||||
process must have LINK permission on the keyring and the active LSM module
|
||||
mustn't deny permission, otherwise error EPERM will be returned.
|
||||
|
||||
Error ENOMEM will be returned if there was insufficient memory to complete
|
||||
the operation, otherwise 0 will be returned to indicate success.
|
||||
|
||||
The keyring will be replaced next time the parent process leaves the
|
||||
kernel and resumes executing userspace.
|
||||
|
||||
|
||||
===============
|
||||
KERNEL SERVICES
|
||||
===============
|
||||
@ -1231,3 +1254,17 @@ by executing:
|
||||
|
||||
In this case, the program isn't required to actually attach the key to a ring;
|
||||
the rings are provided for reference.
|
||||
|
||||
|
||||
==================
|
||||
GARBAGE COLLECTION
|
||||
==================
|
||||
|
||||
Dead keys (for which the type has been removed) will be automatically unlinked
|
||||
from those keyrings that point to them and deleted as soon as possible by a
|
||||
background garbage collector.
|
||||
|
||||
Similarly, revoked and expired keys will be garbage collected, but only after a
|
||||
certain amount of time has passed. This time is set as a number of seconds in:
|
||||
|
||||
/proc/sys/kernel/keys/gc_delay
|
||||
|
@ -75,6 +75,7 @@ register struct thread_info *__current_thread_info __asm__("$8");
|
||||
#define TIF_UAC_SIGBUS 7
|
||||
#define TIF_MEMDIE 8
|
||||
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal */
|
||||
#define TIF_NOTIFY_RESUME 10 /* callback before returning to user */
|
||||
#define TIF_FREEZE 16 /* is freezing for suspend */
|
||||
|
||||
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
|
||||
@ -82,10 +83,12 @@ register struct thread_info *__current_thread_info __asm__("$8");
|
||||
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
|
||||
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
|
||||
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
|
||||
#define _TIF_FREEZE (1<<TIF_FREEZE)
|
||||
|
||||
/* Work to do on interrupt/exception return. */
|
||||
#define _TIF_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
|
||||
#define _TIF_WORK_MASK (_TIF_SIGPENDING | _TIF_NEED_RESCHED | \
|
||||
_TIF_NOTIFY_RESUME)
|
||||
|
||||
/* Work to do on any return to userspace. */
|
||||
#define _TIF_ALLWORK_MASK (_TIF_WORK_MASK \
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/sigcontext.h>
|
||||
@ -683,4 +684,11 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
|
||||
{
|
||||
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs, sw, r0, r19);
|
||||
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -130,11 +130,13 @@ extern void vfp_sync_state(struct thread_info *thread);
|
||||
* TIF_SYSCALL_TRACE - syscall trace active
|
||||
* TIF_SIGPENDING - signal pending
|
||||
* TIF_NEED_RESCHED - rescheduling necessary
|
||||
* TIF_NOTIFY_RESUME - callback before returning to user
|
||||
* TIF_USEDFPU - FPU was used by this task this quantum (SMP)
|
||||
* TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED
|
||||
*/
|
||||
#define TIF_SIGPENDING 0
|
||||
#define TIF_NEED_RESCHED 1
|
||||
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
|
||||
#define TIF_SYSCALL_TRACE 8
|
||||
#define TIF_POLLING_NRFLAG 16
|
||||
#define TIF_USING_IWMMXT 17
|
||||
@ -143,6 +145,7 @@ extern void vfp_sync_state(struct thread_info *thread);
|
||||
|
||||
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
|
||||
#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_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
|
||||
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
|
||||
|
@ -51,7 +51,7 @@ fast_work_pending:
|
||||
work_pending:
|
||||
tst r1, #_TIF_NEED_RESCHED
|
||||
bne work_resched
|
||||
tst r1, #_TIF_SIGPENDING
|
||||
tst r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
|
||||
beq no_work_pending
|
||||
mov r0, sp @ 'regs'
|
||||
mov r2, why @ 'syscall'
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/personality.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/elf.h>
|
||||
#include <asm/cacheflush.h>
|
||||
@ -707,4 +708,11 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
|
||||
{
|
||||
if (thread_flags & _TIF_SIGPENDING)
|
||||
do_signal(¤t->blocked, regs, syscall);
|
||||
|
||||
if (thread_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,7 @@ static inline struct thread_info *current_thread_info(void)
|
||||
#define TIF_MEMDIE 6
|
||||
#define TIF_RESTORE_SIGMASK 7 /* restore signal mask in do_signal */
|
||||
#define TIF_CPU_GOING_TO_SLEEP 8 /* CPU is entering sleep 0 mode */
|
||||
#define TIF_NOTIFY_RESUME 9 /* callback before returning to user */
|
||||
#define TIF_FREEZE 29
|
||||
#define TIF_DEBUG 30 /* debugging enabled */
|
||||
#define TIF_USERSPACE 31 /* true if FS sets userspace */
|
||||
@ -96,6 +97,7 @@ static inline struct thread_info *current_thread_info(void)
|
||||
#define _TIF_MEMDIE (1 << TIF_MEMDIE)
|
||||
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_CPU_GOING_TO_SLEEP (1 << TIF_CPU_GOING_TO_SLEEP)
|
||||
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
|
||||
#define _TIF_FREEZE (1 << TIF_FREEZE)
|
||||
|
||||
/* Note: The masks below must never span more than 16 bits! */
|
||||
@ -103,13 +105,15 @@ static inline struct thread_info *current_thread_info(void)
|
||||
/* work to do on interrupt/exception return */
|
||||
#define _TIF_WORK_MASK \
|
||||
((1 << TIF_SIGPENDING) \
|
||||
| _TIF_NOTIFY_RESUME \
|
||||
| (1 << TIF_NEED_RESCHED) \
|
||||
| (1 << TIF_POLLING_NRFLAG) \
|
||||
| (1 << TIF_BREAKPOINT) \
|
||||
| (1 << TIF_RESTORE_SIGMASK))
|
||||
|
||||
/* work to do on any return to userspace */
|
||||
#define _TIF_ALLWORK_MASK (_TIF_WORK_MASK | (1 << TIF_SYSCALL_TRACE))
|
||||
#define _TIF_ALLWORK_MASK (_TIF_WORK_MASK | (1 << TIF_SYSCALL_TRACE) | \
|
||||
_TIF_NOTIFY_RESUME)
|
||||
/* work to do on return from debug mode */
|
||||
#define _TIF_DBGWORK_MASK (_TIF_WORK_MASK & ~(1 << TIF_BREAKPOINT))
|
||||
|
||||
|
@ -281,7 +281,7 @@ syscall_exit_work:
|
||||
ld.w r1, r0[TI_flags]
|
||||
rjmp 1b
|
||||
|
||||
2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK
|
||||
2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NOTIFY_RESUME
|
||||
tst r1, r2
|
||||
breq 3f
|
||||
unmask_interrupts
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/ucontext.h>
|
||||
@ -322,4 +323,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
|
||||
|
||||
if (ti->flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs, ¤t->blocked, syscall);
|
||||
|
||||
if (ti->flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/user.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/page.h>
|
||||
@ -36,4 +37,11 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
|
||||
/* deal with pending signal delivery */
|
||||
if (thread_info_flags & _TIF_SIGPENDING)
|
||||
do_signal(canrestart,regs);
|
||||
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -572,6 +572,8 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags)
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(__frame);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
|
||||
} /* end do_notify_resume() */
|
||||
|
@ -89,6 +89,7 @@ static inline struct thread_info *current_thread_info(void)
|
||||
TIF_NEED_RESCHED */
|
||||
#define TIF_MEMDIE 4
|
||||
#define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */
|
||||
#define TIF_NOTIFY_RESUME 6 /* callback before returning to user */
|
||||
#define TIF_FREEZE 16 /* is freezing for suspend */
|
||||
|
||||
/* as above, but as bit values */
|
||||
@ -97,6 +98,7 @@ static inline struct thread_info *current_thread_info(void)
|
||||
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
|
||||
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
|
||||
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
|
||||
#define _TIF_FREEZE (1<<TIF_FREEZE)
|
||||
|
||||
#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include <linux/tty.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/setup.h>
|
||||
#include <asm/uaccess.h>
|
||||
@ -552,4 +553,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
|
||||
{
|
||||
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs, NULL);
|
||||
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -192,6 +192,8 @@ do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
|
||||
if (test_thread_flag(TIF_NOTIFY_RESUME)) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(&scr->pt);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
|
||||
/* copy user rbs to kernel rbs */
|
||||
|
@ -149,6 +149,7 @@ static inline unsigned int get_thread_fault_code(void)
|
||||
#define TIF_NEED_RESCHED 2 /* rescheduling necessary */
|
||||
#define TIF_SINGLESTEP 3 /* restore singlestep on return to user mode */
|
||||
#define TIF_IRET 4 /* return with iret */
|
||||
#define TIF_NOTIFY_RESUME 5 /* callback before returning to user */
|
||||
#define TIF_RESTORE_SIGMASK 8 /* restore signal mask in do_signal() */
|
||||
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */
|
||||
#define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */
|
||||
@ -160,6 +161,7 @@ static inline unsigned int get_thread_fault_code(void)
|
||||
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
|
||||
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
|
||||
#define _TIF_IRET (1<<TIF_IRET)
|
||||
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
|
||||
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_USEDFPU (1<<TIF_USEDFPU)
|
||||
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/uaccess.h>
|
||||
@ -408,5 +409,12 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset,
|
||||
if (thread_info_flags & _TIF_SIGPENDING)
|
||||
do_signal(regs,oldset);
|
||||
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
|
||||
clear_thread_flag(TIF_IRET);
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
|
||||
#define TIF_NEED_RESCHED 2 /* rescheduling necessary */
|
||||
#define TIF_SYSCALL_AUDIT 3 /* syscall auditing active */
|
||||
#define TIF_SECCOMP 4 /* secure computing */
|
||||
#define TIF_NOTIFY_RESUME 5 /* callback before returning to user */
|
||||
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
|
||||
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */
|
||||
#define TIF_POLLING_NRFLAG 17 /* true if poll_idle() is polling TIF_NEED_RESCHED */
|
||||
@ -139,6 +140,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
|
||||
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
|
||||
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
|
||||
#define _TIF_SECCOMP (1<<TIF_SECCOMP)
|
||||
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
|
||||
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_USEDFPU (1<<TIF_USEDFPU)
|
||||
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/tracehook.h>
|
||||
|
||||
#include <asm/abi.h>
|
||||
#include <asm/asm.h>
|
||||
@ -700,4 +701,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
|
||||
/* deal with pending signal delivery */
|
||||
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs);
|
||||
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -568,5 +568,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(__frame);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ struct thread_info {
|
||||
#define TIF_MEMDIE 5
|
||||
#define TIF_RESTORE_SIGMASK 6 /* restore saved signal mask */
|
||||
#define TIF_FREEZE 7 /* is freezing for suspend */
|
||||
#define TIF_NOTIFY_RESUME 8 /* callback before returning to user */
|
||||
|
||||
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
|
||||
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
|
||||
@ -67,8 +68,9 @@ struct thread_info {
|
||||
#define _TIF_32BIT (1 << TIF_32BIT)
|
||||
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
|
||||
#define _TIF_FREEZE (1 << TIF_FREEZE)
|
||||
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
|
||||
|
||||
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | \
|
||||
#define _TIF_USER_WORK_MASK (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | \
|
||||
_TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK)
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -948,7 +948,7 @@ intr_check_sig:
|
||||
/* As above */
|
||||
mfctl %cr30,%r1
|
||||
LDREG TI_FLAGS(%r1),%r19
|
||||
ldi (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK), %r20
|
||||
ldi (_TIF_SIGPENDING|_TIF_RESTORE_SIGMASK|_TIF_NOTIFY_RESUME), %r20
|
||||
and,COND(<>) %r19, %r20, %r0
|
||||
b,n intr_restore /* skip past if we've nothing to do */
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <linux/stddef.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/tracehook.h>
|
||||
#include <asm/ucontext.h>
|
||||
#include <asm/rt_sigframe.h>
|
||||
#include <asm/uaccess.h>
|
||||
@ -645,4 +646,11 @@ void do_notify_resume(struct pt_regs *regs, long in_syscall)
|
||||
if (test_thread_flag(TIF_SIGPENDING) ||
|
||||
test_thread_flag(TIF_RESTORE_SIGMASK))
|
||||
do_signal(regs, in_syscall);
|
||||
|
||||
if (test_thread_flag(TIF_NOTIFY_RESUME)) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -536,4 +536,6 @@ void do_notify_resume(struct pt_regs *regs)
|
||||
{
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
|
@ -640,5 +640,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -772,5 +772,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
@ -590,6 +590,8 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0,
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -613,5 +613,8 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0, unsigned long
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -869,6 +869,8 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
|
||||
if (thread_info_flags & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
if (current->replacement_session_keyring)
|
||||
key_replace_session_keyring();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_X86_32
|
||||
|
@ -450,6 +450,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
if (request_locality(chip, 0) != 0) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
@ -457,12 +463,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
|
||||
|
||||
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
dev_info(dev,
|
||||
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
|
||||
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
|
||||
|
@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk)
|
||||
static int tun_attach(struct tun_struct *tun, struct file *file)
|
||||
{
|
||||
struct tun_file *tfile = file->private_data;
|
||||
const struct cred *cred = current_cred();
|
||||
int err;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* Check permissions */
|
||||
if (((tun->owner != -1 && cred->euid != tun->owner) ||
|
||||
(tun->group != -1 && !in_egroup_p(tun->group))) &&
|
||||
!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
netif_tx_lock_bh(tun->dev);
|
||||
|
||||
err = -EINVAL;
|
||||
@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
||||
|
||||
dev = __dev_get_by_name(net, ifr->ifr_name);
|
||||
if (dev) {
|
||||
const struct cred *cred = current_cred();
|
||||
|
||||
if (ifr->ifr_flags & IFF_TUN_EXCL)
|
||||
return -EBUSY;
|
||||
if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops)
|
||||
@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
if (((tun->owner != -1 && cred->euid != tun->owner) ||
|
||||
(tun->group != -1 && !in_egroup_p(tun->group))) &&
|
||||
!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
err = security_tun_dev_attach(tun->sk);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = tun_attach(tun, file);
|
||||
if (err < 0)
|
||||
return err;
|
||||
@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return -EPERM;
|
||||
err = security_tun_dev_create();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* Set dev type */
|
||||
if (ifr->ifr_flags & IFF_TUN) {
|
||||
@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
|
||||
tun->sk = sk;
|
||||
container_of(sk, struct tun_sock, sk)->tun = tun;
|
||||
|
||||
security_tun_dev_post_create(sk);
|
||||
|
||||
tun_net_init(dev);
|
||||
|
||||
if (strchr(dev->name, '%')) {
|
||||
|
@ -1752,12 +1752,12 @@ static int comedi_open(struct inode *inode, struct file *file)
|
||||
mutex_lock(&dev->mutex);
|
||||
if (dev->attached)
|
||||
goto ok;
|
||||
if (!capable(CAP_SYS_MODULE) && dev->in_request_module) {
|
||||
if (!capable(CAP_NET_ADMIN) && dev->in_request_module) {
|
||||
DPRINTK("in request module\n");
|
||||
mutex_unlock(&dev->mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (capable(CAP_SYS_MODULE) && dev->in_request_module)
|
||||
if (capable(CAP_NET_ADMIN) && dev->in_request_module)
|
||||
goto ok;
|
||||
|
||||
dev->in_request_module = 1;
|
||||
@ -1770,8 +1770,8 @@ static int comedi_open(struct inode *inode, struct file *file)
|
||||
|
||||
dev->in_request_module = 0;
|
||||
|
||||
if (!dev->attached && !capable(CAP_SYS_MODULE)) {
|
||||
DPRINTK("not attached and not CAP_SYS_MODULE\n");
|
||||
if (!dev->attached && !capable(CAP_NET_ADMIN)) {
|
||||
DPRINTK("not attached and not CAP_NET_ADMIN\n");
|
||||
mutex_unlock(&dev->mutex);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1591,7 +1591,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
|
||||
if (can_sleep)
|
||||
lock->fl_flags |= FL_SLEEP;
|
||||
|
||||
error = security_file_lock(filp, cmd);
|
||||
error = security_file_lock(filp, lock->fl_type);
|
||||
if (error)
|
||||
goto out_free;
|
||||
|
||||
|
@ -1533,9 +1533,11 @@ int may_open(struct path *path, int acc_mode, int flag)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ima_path_check(path,
|
||||
acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
error = ima_path_check(path, acc_mode ?
|
||||
acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
|
||||
ACC_MODE(flag) & (MAY_READ | MAY_WRITE),
|
||||
IMA_COUNT_UPDATE);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
/*
|
||||
|
@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
int flags = nfsexp_flags(rqstp, exp);
|
||||
int ret;
|
||||
|
||||
validate_process_creds();
|
||||
|
||||
/* discard any old override before preparing the new set */
|
||||
revert_creds(get_cred(current->real_cred));
|
||||
new = prepare_creds();
|
||||
@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
|
||||
else
|
||||
new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
|
||||
new->cap_permitted);
|
||||
validate_process_creds();
|
||||
put_cred(override_creds(new));
|
||||
put_cred(new);
|
||||
validate_process_creds();
|
||||
return 0;
|
||||
|
||||
oom:
|
||||
|
@ -496,7 +496,9 @@ nfsd(void *vrqstp)
|
||||
/* Lock the export hash tables for reading. */
|
||||
exp_readlock();
|
||||
|
||||
validate_process_creds();
|
||||
svc_process(rqstp);
|
||||
validate_process_creds();
|
||||
|
||||
/* Unlock export hash tables */
|
||||
exp_readunlock();
|
||||
|
@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||
__be32 err;
|
||||
int host_err;
|
||||
|
||||
validate_process_creds();
|
||||
|
||||
/*
|
||||
* If we get here, then the client has already done an "open",
|
||||
* and (hopefully) checked permission - so allow OWNER_OVERRIDE
|
||||
@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||
out_nfserr:
|
||||
err = nfserrno(host_err);
|
||||
out:
|
||||
validate_process_creds();
|
||||
return err;
|
||||
}
|
||||
|
||||
|
12
fs/open.c
12
fs/open.c
@ -199,7 +199,7 @@ out:
|
||||
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||
struct file *filp)
|
||||
{
|
||||
int err;
|
||||
int ret;
|
||||
struct iattr newattrs;
|
||||
|
||||
/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */
|
||||
@ -214,12 +214,14 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
|
||||
}
|
||||
|
||||
/* Remove suid/sgid on truncate too */
|
||||
newattrs.ia_valid |= should_remove_suid(dentry);
|
||||
ret = should_remove_suid(dentry);
|
||||
if (ret)
|
||||
newattrs.ia_valid |= ret | ATTR_FORCE;
|
||||
|
||||
mutex_lock(&dentry->d_inode->i_mutex);
|
||||
err = notify_change(dentry, &newattrs);
|
||||
ret = notify_change(dentry, &newattrs);
|
||||
mutex_unlock(&dentry->d_inode->i_mutex);
|
||||
return err;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long do_sys_truncate(const char __user *pathname, loff_t length)
|
||||
@ -957,6 +959,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
|
||||
int error;
|
||||
struct file *f;
|
||||
|
||||
validate_creds(cred);
|
||||
|
||||
/*
|
||||
* We must always pass in a valid mount pointer. Historically
|
||||
* callers got away with not passing it, but we must enforce this at
|
||||
|
@ -760,6 +760,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
|
||||
const struct inode_operations sysfs_dir_inode_operations = {
|
||||
.lookup = sysfs_lookup,
|
||||
.setattr = sysfs_setattr,
|
||||
.setxattr = sysfs_setxattr,
|
||||
};
|
||||
|
||||
static void remove_dir(struct sysfs_dirent *sd)
|
||||
|
130
fs/sysfs/inode.c
130
fs/sysfs/inode.c
@ -18,6 +18,8 @@
|
||||
#include <linux/capability.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/security.h>
|
||||
#include "sysfs.h"
|
||||
|
||||
extern struct super_block * sysfs_sb;
|
||||
@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = {
|
||||
|
||||
static const struct inode_operations sysfs_inode_operations ={
|
||||
.setattr = sysfs_setattr,
|
||||
.setxattr = sysfs_setxattr,
|
||||
};
|
||||
|
||||
int __init sysfs_inode_init(void)
|
||||
@ -42,18 +45,37 @@ int __init sysfs_inode_init(void)
|
||||
return bdi_init(&sysfs_backing_dev_info);
|
||||
}
|
||||
|
||||
struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd)
|
||||
{
|
||||
struct sysfs_inode_attrs *attrs;
|
||||
struct iattr *iattrs;
|
||||
|
||||
attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL);
|
||||
if (!attrs)
|
||||
return NULL;
|
||||
iattrs = &attrs->ia_iattr;
|
||||
|
||||
/* assign default attributes */
|
||||
iattrs->ia_mode = sd->s_mode;
|
||||
iattrs->ia_uid = 0;
|
||||
iattrs->ia_gid = 0;
|
||||
iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME;
|
||||
|
||||
return attrs;
|
||||
}
|
||||
int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
||||
{
|
||||
struct inode * inode = dentry->d_inode;
|
||||
struct sysfs_dirent * sd = dentry->d_fsdata;
|
||||
struct iattr * sd_iattr;
|
||||
struct sysfs_inode_attrs *sd_attrs;
|
||||
struct iattr *iattrs;
|
||||
unsigned int ia_valid = iattr->ia_valid;
|
||||
int error;
|
||||
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
|
||||
sd_iattr = sd->s_iattr;
|
||||
sd_attrs = sd->s_iattr;
|
||||
|
||||
error = inode_change_ok(inode, iattr);
|
||||
if (error)
|
||||
@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (!sd_iattr) {
|
||||
if (!sd_attrs) {
|
||||
/* setting attributes for the first time, allocate now */
|
||||
sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL);
|
||||
if (!sd_iattr)
|
||||
sd_attrs = sysfs_init_inode_attrs(sd);
|
||||
if (!sd_attrs)
|
||||
return -ENOMEM;
|
||||
/* assign default attributes */
|
||||
sd_iattr->ia_mode = sd->s_mode;
|
||||
sd_iattr->ia_uid = 0;
|
||||
sd_iattr->ia_gid = 0;
|
||||
sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
|
||||
sd->s_iattr = sd_iattr;
|
||||
sd->s_iattr = sd_attrs;
|
||||
} else {
|
||||
/* attributes were changed at least once in past */
|
||||
iattrs = &sd_attrs->ia_iattr;
|
||||
|
||||
if (ia_valid & ATTR_UID)
|
||||
iattrs->ia_uid = iattr->ia_uid;
|
||||
if (ia_valid & ATTR_GID)
|
||||
iattrs->ia_gid = iattr->ia_gid;
|
||||
if (ia_valid & ATTR_ATIME)
|
||||
iattrs->ia_atime = timespec_trunc(iattr->ia_atime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MTIME)
|
||||
iattrs->ia_mtime = timespec_trunc(iattr->ia_mtime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_CTIME)
|
||||
iattrs->ia_ctime = timespec_trunc(iattr->ia_ctime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MODE) {
|
||||
umode_t mode = iattr->ia_mode;
|
||||
|
||||
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
|
||||
mode &= ~S_ISGID;
|
||||
iattrs->ia_mode = sd->s_mode = mode;
|
||||
}
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/* attributes were changed atleast once in past */
|
||||
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct sysfs_dirent *sd = dentry->d_fsdata;
|
||||
struct sysfs_inode_attrs *iattrs;
|
||||
void *secdata;
|
||||
int error;
|
||||
u32 secdata_len = 0;
|
||||
|
||||
if (ia_valid & ATTR_UID)
|
||||
sd_iattr->ia_uid = iattr->ia_uid;
|
||||
if (ia_valid & ATTR_GID)
|
||||
sd_iattr->ia_gid = iattr->ia_gid;
|
||||
if (ia_valid & ATTR_ATIME)
|
||||
sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MTIME)
|
||||
sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_CTIME)
|
||||
sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime,
|
||||
inode->i_sb->s_time_gran);
|
||||
if (ia_valid & ATTR_MODE) {
|
||||
umode_t mode = iattr->ia_mode;
|
||||
if (!sd)
|
||||
return -EINVAL;
|
||||
if (!sd->s_iattr)
|
||||
sd->s_iattr = sysfs_init_inode_attrs(sd);
|
||||
if (!sd->s_iattr)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID))
|
||||
mode &= ~S_ISGID;
|
||||
sd_iattr->ia_mode = sd->s_mode = mode;
|
||||
}
|
||||
iattrs = sd->s_iattr;
|
||||
|
||||
if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
|
||||
const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
|
||||
error = security_inode_setsecurity(dentry->d_inode, suffix,
|
||||
value, size, flags);
|
||||
if (error)
|
||||
goto out;
|
||||
error = security_inode_getsecctx(dentry->d_inode,
|
||||
&secdata, &secdata_len);
|
||||
if (error)
|
||||
goto out;
|
||||
if (iattrs->ia_secdata)
|
||||
security_release_secctx(iattrs->ia_secdata,
|
||||
iattrs->ia_secdata_len);
|
||||
iattrs->ia_secdata = secdata;
|
||||
iattrs->ia_secdata_len = secdata_len;
|
||||
|
||||
} else
|
||||
return -EINVAL;
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -146,6 +203,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd)
|
||||
static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
||||
{
|
||||
struct bin_attribute *bin_attr;
|
||||
struct sysfs_inode_attrs *iattrs;
|
||||
|
||||
inode->i_private = sysfs_get(sd);
|
||||
inode->i_mapping->a_ops = &sysfs_aops;
|
||||
@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
|
||||
inode->i_ino = sd->s_ino;
|
||||
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);
|
||||
|
||||
if (sd->s_iattr) {
|
||||
iattrs = sd->s_iattr;
|
||||
if (iattrs) {
|
||||
/* sysfs_dirent has non-default attributes
|
||||
* get them for the new inode from persistent copy
|
||||
* in sysfs_dirent
|
||||
*/
|
||||
set_inode_attr(inode, sd->s_iattr);
|
||||
set_inode_attr(inode, &iattrs->ia_iattr);
|
||||
if (iattrs->ia_secdata)
|
||||
security_inode_notifysecctx(inode,
|
||||
iattrs->ia_secdata,
|
||||
iattrs->ia_secdata_len);
|
||||
} else
|
||||
set_default_inode_attr(inode, sd->s_mode);
|
||||
|
||||
|
||||
/* initialize inode according to type */
|
||||
switch (sysfs_type(sd)) {
|
||||
case SYSFS_DIR:
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/security.h>
|
||||
|
||||
#include "sysfs.h"
|
||||
|
||||
@ -209,6 +210,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co
|
||||
}
|
||||
|
||||
const struct inode_operations sysfs_symlink_inode_operations = {
|
||||
.setxattr = sysfs_setxattr,
|
||||
.readlink = generic_readlink,
|
||||
.follow_link = sysfs_follow_link,
|
||||
.put_link = sysfs_put_link,
|
||||
|
@ -8,6 +8,8 @@
|
||||
* This file is released under the GPLv2.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
struct sysfs_open_dirent;
|
||||
|
||||
/* type-specific structures for sysfs_dirent->s_* union members */
|
||||
@ -31,6 +33,12 @@ struct sysfs_elem_bin_attr {
|
||||
struct hlist_head buffers;
|
||||
};
|
||||
|
||||
struct sysfs_inode_attrs {
|
||||
struct iattr ia_iattr;
|
||||
void *ia_secdata;
|
||||
u32 ia_secdata_len;
|
||||
};
|
||||
|
||||
/*
|
||||
* sysfs_dirent - the building block of sysfs hierarchy. Each and
|
||||
* every sysfs node is represented by single sysfs_dirent.
|
||||
@ -56,7 +64,7 @@ struct sysfs_dirent {
|
||||
unsigned int s_flags;
|
||||
ino_t s_ino;
|
||||
umode_t s_mode;
|
||||
struct iattr *s_iattr;
|
||||
struct sysfs_inode_attrs *s_iattr;
|
||||
};
|
||||
|
||||
#define SD_DEACTIVATED_BIAS INT_MIN
|
||||
@ -148,6 +156,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
|
||||
struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
|
||||
void sysfs_delete_inode(struct inode *inode);
|
||||
int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
|
||||
int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags);
|
||||
int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
|
||||
int sysfs_inode_init(void);
|
||||
|
||||
|
55
fs/xattr.c
55
fs/xattr.c
@ -66,22 +66,28 @@ xattr_permission(struct inode *inode, const char *name, int mask)
|
||||
return inode_permission(inode, mask);
|
||||
}
|
||||
|
||||
int
|
||||
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
/**
|
||||
* __vfs_setxattr_noperm - perform setxattr operation without performing
|
||||
* permission checks.
|
||||
*
|
||||
* @dentry - object to perform setxattr on
|
||||
* @name - xattr name to set
|
||||
* @value - value to set @name to
|
||||
* @size - size of @value
|
||||
* @flags - flags to pass into filesystem operations
|
||||
*
|
||||
* returns the result of the internal setxattr or setsecurity operations.
|
||||
*
|
||||
* This function requires the caller to lock the inode's i_mutex before it
|
||||
* is executed. It also assumes that the caller will make the appropriate
|
||||
* permission checks.
|
||||
*/
|
||||
int __vfs_setxattr_noperm(struct dentry *dentry, const char *name,
|
||||
const void *value, size_t size, int flags)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error;
|
||||
int error = -EOPNOTSUPP;
|
||||
|
||||
error = xattr_permission(inode, name, MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = security_inode_setxattr(dentry, name, value, size, flags);
|
||||
if (error)
|
||||
goto out;
|
||||
error = -EOPNOTSUPP;
|
||||
if (inode->i_op->setxattr) {
|
||||
error = inode->i_op->setxattr(dentry, name, value, size, flags);
|
||||
if (!error) {
|
||||
@ -97,6 +103,29 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
if (!error)
|
||||
fsnotify_xattr(dentry);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
|
||||
size_t size, int flags)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
int error;
|
||||
|
||||
error = xattr_permission(inode, name, MAY_WRITE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = security_inode_setxattr(dentry, name, value, size, flags);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = __vfs_setxattr_noperm(dentry, name, value, size, flags);
|
||||
|
||||
out:
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return error;
|
||||
|
@ -114,6 +114,13 @@ struct thread_group_cred {
|
||||
*/
|
||||
struct cred {
|
||||
atomic_t usage;
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
atomic_t subscribers; /* number of processes subscribed */
|
||||
void *put_addr;
|
||||
unsigned magic;
|
||||
#define CRED_MAGIC 0x43736564
|
||||
#define CRED_MAGIC_DEAD 0x44656144
|
||||
#endif
|
||||
uid_t uid; /* real UID of the task */
|
||||
gid_t gid; /* real GID of the task */
|
||||
uid_t suid; /* saved UID of the task */
|
||||
@ -143,7 +150,9 @@ struct cred {
|
||||
};
|
||||
|
||||
extern void __put_cred(struct cred *);
|
||||
extern void exit_creds(struct task_struct *);
|
||||
extern int copy_creds(struct task_struct *, unsigned long);
|
||||
extern struct cred *cred_alloc_blank(void);
|
||||
extern struct cred *prepare_creds(void);
|
||||
extern struct cred *prepare_exec_creds(void);
|
||||
extern struct cred *prepare_usermodehelper_creds(void);
|
||||
@ -158,6 +167,60 @@ extern int set_security_override_from_ctx(struct cred *, const char *);
|
||||
extern int set_create_files_as(struct cred *, struct inode *);
|
||||
extern void __init cred_init(void);
|
||||
|
||||
/*
|
||||
* check for validity of credentials
|
||||
*/
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
extern void __invalid_creds(const struct cred *, const char *, unsigned);
|
||||
extern void __validate_process_creds(struct task_struct *,
|
||||
const char *, unsigned);
|
||||
|
||||
static inline bool creds_are_invalid(const struct cred *cred)
|
||||
{
|
||||
if (cred->magic != CRED_MAGIC)
|
||||
return true;
|
||||
if (atomic_read(&cred->usage) < atomic_read(&cred->subscribers))
|
||||
return true;
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
if ((unsigned long) cred->security < PAGE_SIZE)
|
||||
return true;
|
||||
if ((*(u32*)cred->security & 0xffffff00) ==
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void __validate_creds(const struct cred *cred,
|
||||
const char *file, unsigned line)
|
||||
{
|
||||
if (unlikely(creds_are_invalid(cred)))
|
||||
__invalid_creds(cred, file, line);
|
||||
}
|
||||
|
||||
#define validate_creds(cred) \
|
||||
do { \
|
||||
__validate_creds((cred), __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
#define validate_process_creds() \
|
||||
do { \
|
||||
__validate_process_creds(current, __FILE__, __LINE__); \
|
||||
} while(0)
|
||||
|
||||
extern void validate_creds_for_do_exit(struct task_struct *);
|
||||
#else
|
||||
static inline void validate_creds(const struct cred *cred)
|
||||
{
|
||||
}
|
||||
static inline void validate_creds_for_do_exit(struct task_struct *tsk)
|
||||
{
|
||||
}
|
||||
static inline void validate_process_creds(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* get_new_cred - Get a reference on a new set of credentials
|
||||
* @cred: The new credentials to reference
|
||||
@ -186,7 +249,9 @@ static inline struct cred *get_new_cred(struct cred *cred)
|
||||
*/
|
||||
static inline const struct cred *get_cred(const struct cred *cred)
|
||||
{
|
||||
return get_new_cred((struct cred *) cred);
|
||||
struct cred *nonconst_cred = (struct cred *) cred;
|
||||
validate_creds(cred);
|
||||
return get_new_cred(nonconst_cred);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,7 +269,7 @@ static inline void put_cred(const struct cred *_cred)
|
||||
{
|
||||
struct cred *cred = (struct cred *) _cred;
|
||||
|
||||
BUG_ON(atomic_read(&(cred)->usage) <= 0);
|
||||
validate_creds(cred);
|
||||
if (atomic_dec_and_test(&(cred)->usage))
|
||||
__put_cred(cred);
|
||||
}
|
||||
|
@ -129,7 +129,10 @@ struct key {
|
||||
struct rw_semaphore sem; /* change vs change sem */
|
||||
struct key_user *user; /* owner of this key */
|
||||
void *security; /* security data for this key */
|
||||
time_t expiry; /* time at which key expires (or 0) */
|
||||
union {
|
||||
time_t expiry; /* time at which key expires (or 0) */
|
||||
time_t revoked_at; /* time at which key was revoked */
|
||||
};
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
key_perm_t perm; /* access permissions */
|
||||
@ -275,6 +278,8 @@ static inline key_serial_t key_serial(struct key *key)
|
||||
extern ctl_table key_sysctls[];
|
||||
#endif
|
||||
|
||||
extern void key_replace_session_keyring(void);
|
||||
|
||||
/*
|
||||
* the userspace interface
|
||||
*/
|
||||
@ -297,6 +302,7 @@ extern void key_init(void);
|
||||
#define key_fsuid_changed(t) do { } while(0)
|
||||
#define key_fsgid_changed(t) do { } while(0)
|
||||
#define key_init() do { } while(0)
|
||||
#define key_replace_session_keyring() do { } while(0)
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
#endif /* __KERNEL__ */
|
||||
|
@ -52,5 +52,6 @@
|
||||
#define KEYCTL_SET_TIMEOUT 15 /* set key timeout */
|
||||
#define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */
|
||||
#define KEYCTL_GET_SECURITY 17 /* get key security label */
|
||||
#define KEYCTL_SESSION_TO_PARENT 18 /* apply session keyring to parent process */
|
||||
|
||||
#endif /* _LINUX_KEYCTL_H */
|
||||
|
@ -33,6 +33,7 @@ struct common_audit_data {
|
||||
#define LSM_AUDIT_DATA_IPC 4
|
||||
#define LSM_AUDIT_DATA_TASK 5
|
||||
#define LSM_AUDIT_DATA_KEY 6
|
||||
#define LSM_AUDIT_NO_AUDIT 7
|
||||
struct task_struct *tsk;
|
||||
union {
|
||||
struct {
|
||||
@ -66,16 +67,19 @@ struct common_audit_data {
|
||||
} key_struct;
|
||||
#endif
|
||||
} u;
|
||||
const char *function;
|
||||
/* this union contains LSM specific data */
|
||||
union {
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
/* SMACK data */
|
||||
struct smack_audit_data {
|
||||
const char *function;
|
||||
char *subject;
|
||||
char *object;
|
||||
char *request;
|
||||
int result;
|
||||
} smack_audit_data;
|
||||
#endif
|
||||
#ifdef CONFIG_SECURITY_SELINUX
|
||||
/* SELinux data */
|
||||
struct {
|
||||
u32 ssid;
|
||||
@ -83,10 +87,12 @@ struct common_audit_data {
|
||||
u16 tclass;
|
||||
u32 requested;
|
||||
u32 audited;
|
||||
u32 denied;
|
||||
struct av_decision *avd;
|
||||
int result;
|
||||
} selinux_audit_data;
|
||||
} lsm_priv;
|
||||
#endif
|
||||
};
|
||||
/* these callback will be implemented by a specific LSM */
|
||||
void (*lsm_pre_audit)(struct audit_buffer *, void *);
|
||||
void (*lsm_post_audit)(struct audit_buffer *, void *);
|
||||
@ -104,7 +110,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
|
||||
/* Initialize an LSM audit data structure. */
|
||||
#define COMMON_AUDIT_DATA_INIT(_d, _t) \
|
||||
{ memset((_d), 0, sizeof(struct common_audit_data)); \
|
||||
(_d)->type = LSM_AUDIT_DATA_##_t; (_d)->function = __func__; }
|
||||
(_d)->type = LSM_AUDIT_DATA_##_t; }
|
||||
|
||||
void common_lsm_audit(struct common_audit_data *a);
|
||||
|
||||
|
@ -1292,6 +1292,7 @@ struct task_struct {
|
||||
struct mutex cred_guard_mutex; /* guard against foreign influences on
|
||||
* credential calculations
|
||||
* (notably. ptrace) */
|
||||
struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */
|
||||
|
||||
char comm[TASK_COMM_LEN]; /* executable name excluding path
|
||||
- access with [gs]et_task_comm (which lock
|
||||
@ -2077,7 +2078,7 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
|
||||
#define for_each_process(p) \
|
||||
for (p = &init_task ; (p = next_task(p)) != &init_task ; )
|
||||
|
||||
extern bool is_single_threaded(struct task_struct *);
|
||||
extern bool current_is_single_threaded(void);
|
||||
|
||||
/*
|
||||
* Careful: do_each_thread/while_each_thread is a double loop so
|
||||
|
@ -53,7 +53,7 @@ struct audit_krule;
|
||||
extern int cap_capable(struct task_struct *tsk, const struct cred *cred,
|
||||
int cap, int audit);
|
||||
extern int cap_settime(struct timespec *ts, struct timezone *tz);
|
||||
extern int cap_ptrace_may_access(struct task_struct *child, unsigned int mode);
|
||||
extern int cap_ptrace_access_check(struct task_struct *child, unsigned int mode);
|
||||
extern int cap_ptrace_traceme(struct task_struct *parent);
|
||||
extern int cap_capget(struct task_struct *target, kernel_cap_t *effective, kernel_cap_t *inheritable, kernel_cap_t *permitted);
|
||||
extern int cap_capset(struct cred *new, const struct cred *old,
|
||||
@ -653,6 +653,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* manual page for definitions of the @clone_flags.
|
||||
* @clone_flags contains the flags indicating what should be shared.
|
||||
* Return 0 if permission is granted.
|
||||
* @cred_alloc_blank:
|
||||
* @cred points to the credentials.
|
||||
* @gfp indicates the atomicity of any memory allocations.
|
||||
* Only allocate sufficient memory and attach to @cred such that
|
||||
* cred_transfer() will not get ENOMEM.
|
||||
* @cred_free:
|
||||
* @cred points to the credentials.
|
||||
* Deallocate and clear the cred->security field in a set of credentials.
|
||||
@ -665,6 +670,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @new points to the new credentials.
|
||||
* @old points to the original credentials.
|
||||
* Install a new set of credentials.
|
||||
* @cred_transfer:
|
||||
* @new points to the new credentials.
|
||||
* @old points to the original credentials.
|
||||
* Transfer data from original creds to new creds
|
||||
* @kernel_act_as:
|
||||
* Set the credentials for a kernel service to act as (subjective context).
|
||||
* @new points to the credentials to be modified.
|
||||
@ -678,6 +687,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @inode points to the inode to use as a reference.
|
||||
* The current task must be the one that nominated @inode.
|
||||
* Return 0 if successful.
|
||||
* @kernel_module_request:
|
||||
* Ability to trigger the kernel to automatically upcall to userspace for
|
||||
* userspace to load a kernel module with the given name.
|
||||
* Return 0 if successful.
|
||||
* @task_setuid:
|
||||
* Check permission before setting one or more of the user identity
|
||||
* attributes of the current process. The @flags parameter indicates
|
||||
@ -994,6 +1007,17 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* Sets the connection's peersid to the secmark on skb.
|
||||
* @req_classify_flow:
|
||||
* Sets the flow's sid to the openreq sid.
|
||||
* @tun_dev_create:
|
||||
* Check permissions prior to creating a new TUN device.
|
||||
* @tun_dev_post_create:
|
||||
* This hook allows a module to update or allocate a per-socket security
|
||||
* structure.
|
||||
* @sk contains the newly created sock structure.
|
||||
* @tun_dev_attach:
|
||||
* Check permissions prior to attaching to a persistent TUN device. This
|
||||
* hook can also be used by the module to update any security state
|
||||
* associated with the TUN device's sock structure.
|
||||
* @sk contains the existing sock structure.
|
||||
*
|
||||
* Security hooks for XFRM operations.
|
||||
*
|
||||
@ -1088,6 +1112,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* Return the length of the string (including terminating NUL) or -ve if
|
||||
* an error.
|
||||
* May also return 0 (and a NULL buffer pointer) if there is no label.
|
||||
* @key_session_to_parent:
|
||||
* Forcibly assign the session keyring from a process to its parent
|
||||
* process.
|
||||
* @cred: Pointer to process's credentials
|
||||
* @parent_cred: Pointer to parent process's credentials
|
||||
* @keyring: Proposed new session keyring
|
||||
* Return 0 if permission is granted, -ve error otherwise.
|
||||
*
|
||||
* Security hooks affecting all System V IPC operations.
|
||||
*
|
||||
@ -1229,7 +1260,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* @alter contains the flag indicating whether changes are to be made.
|
||||
* Return 0 if permission is granted.
|
||||
*
|
||||
* @ptrace_may_access:
|
||||
* @ptrace_access_check:
|
||||
* Check permission before allowing the current process to trace the
|
||||
* @child process.
|
||||
* Security modules may also want to perform a process tracing check
|
||||
@ -1244,7 +1275,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* Check that the @parent process has sufficient permission to trace the
|
||||
* current process before allowing the current process to present itself
|
||||
* to the @parent process for tracing.
|
||||
* The parent process will still have to undergo the ptrace_may_access
|
||||
* The parent process will still have to undergo the ptrace_access_check
|
||||
* checks before it is allowed to trace this one.
|
||||
* @parent contains the task_struct structure for debugger process.
|
||||
* Return 0 if permission is granted.
|
||||
@ -1351,12 +1382,47 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
|
||||
* audit_rule_init.
|
||||
* @rule contains the allocated rule
|
||||
*
|
||||
* @inode_notifysecctx:
|
||||
* Notify the security module of what the security context of an inode
|
||||
* should be. Initializes the incore security context managed by the
|
||||
* security module for this inode. Example usage: NFS client invokes
|
||||
* this hook to initialize the security context in its incore inode to the
|
||||
* value provided by the server for the file when the server returned the
|
||||
* file's attributes to the client.
|
||||
*
|
||||
* Must be called with inode->i_mutex locked.
|
||||
*
|
||||
* @inode we wish to set the security context of.
|
||||
* @ctx contains the string which we wish to set in the inode.
|
||||
* @ctxlen contains the length of @ctx.
|
||||
*
|
||||
* @inode_setsecctx:
|
||||
* Change the security context of an inode. Updates the
|
||||
* incore security context managed by the security module and invokes the
|
||||
* fs code as needed (via __vfs_setxattr_noperm) to update any backing
|
||||
* xattrs that represent the context. Example usage: NFS server invokes
|
||||
* this hook to change the security context in its incore inode and on the
|
||||
* backing filesystem to a value provided by the client on a SETATTR
|
||||
* operation.
|
||||
*
|
||||
* Must be called with inode->i_mutex locked.
|
||||
*
|
||||
* @dentry contains the inode we wish to set the security context of.
|
||||
* @ctx contains the string which we wish to set in the inode.
|
||||
* @ctxlen contains the length of @ctx.
|
||||
*
|
||||
* @inode_getsecctx:
|
||||
* Returns a string containing all relavent security context information
|
||||
*
|
||||
* @inode we wish to set the security context of.
|
||||
* @ctx is a pointer in which to place the allocated security context.
|
||||
* @ctxlen points to the place to put the length of @ctx.
|
||||
* This is the main security structure.
|
||||
*/
|
||||
struct security_operations {
|
||||
char name[SECURITY_NAME_MAX + 1];
|
||||
|
||||
int (*ptrace_may_access) (struct task_struct *child, unsigned int mode);
|
||||
int (*ptrace_access_check) (struct task_struct *child, unsigned int mode);
|
||||
int (*ptrace_traceme) (struct task_struct *parent);
|
||||
int (*capget) (struct task_struct *target,
|
||||
kernel_cap_t *effective,
|
||||
@ -1483,12 +1549,15 @@ struct security_operations {
|
||||
int (*dentry_open) (struct file *file, const struct cred *cred);
|
||||
|
||||
int (*task_create) (unsigned long clone_flags);
|
||||
int (*cred_alloc_blank) (struct cred *cred, gfp_t gfp);
|
||||
void (*cred_free) (struct cred *cred);
|
||||
int (*cred_prepare)(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp);
|
||||
void (*cred_commit)(struct cred *new, const struct cred *old);
|
||||
void (*cred_transfer)(struct cred *new, const struct cred *old);
|
||||
int (*kernel_act_as)(struct cred *new, u32 secid);
|
||||
int (*kernel_create_files_as)(struct cred *new, struct inode *inode);
|
||||
int (*kernel_module_request)(void);
|
||||
int (*task_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
|
||||
int (*task_fix_setuid) (struct cred *new, const struct cred *old,
|
||||
int flags);
|
||||
@ -1556,6 +1625,10 @@ struct security_operations {
|
||||
int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
|
||||
void (*release_secctx) (char *secdata, u32 seclen);
|
||||
|
||||
int (*inode_notifysecctx)(struct inode *inode, void *ctx, u32 ctxlen);
|
||||
int (*inode_setsecctx)(struct dentry *dentry, void *ctx, u32 ctxlen);
|
||||
int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
int (*unix_stream_connect) (struct socket *sock,
|
||||
struct socket *other, struct sock *newsk);
|
||||
@ -1592,6 +1665,9 @@ struct security_operations {
|
||||
void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req);
|
||||
void (*inet_conn_established) (struct sock *sk, struct sk_buff *skb);
|
||||
void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl);
|
||||
int (*tun_dev_create)(void);
|
||||
void (*tun_dev_post_create)(struct sock *sk);
|
||||
int (*tun_dev_attach)(struct sock *sk);
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
@ -1620,6 +1696,9 @@ struct security_operations {
|
||||
const struct cred *cred,
|
||||
key_perm_t perm);
|
||||
int (*key_getsecurity)(struct key *key, char **_buffer);
|
||||
int (*key_session_to_parent)(const struct cred *cred,
|
||||
const struct cred *parent_cred,
|
||||
struct key *key);
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
@ -1637,7 +1716,7 @@ extern int security_module_enable(struct security_operations *ops);
|
||||
extern int register_security(struct security_operations *ops);
|
||||
|
||||
/* Security operations */
|
||||
int security_ptrace_may_access(struct task_struct *child, unsigned int mode);
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode);
|
||||
int security_ptrace_traceme(struct task_struct *parent);
|
||||
int security_capget(struct task_struct *target,
|
||||
kernel_cap_t *effective,
|
||||
@ -1736,11 +1815,14 @@ int security_file_send_sigiotask(struct task_struct *tsk,
|
||||
int security_file_receive(struct file *file);
|
||||
int security_dentry_open(struct file *file, const struct cred *cred);
|
||||
int security_task_create(unsigned long clone_flags);
|
||||
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
|
||||
void security_cred_free(struct cred *cred);
|
||||
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp);
|
||||
void security_commit_creds(struct cred *new, const struct cred *old);
|
||||
void security_transfer_creds(struct cred *new, const struct cred *old);
|
||||
int security_kernel_act_as(struct cred *new, u32 secid);
|
||||
int security_kernel_create_files_as(struct cred *new, struct inode *inode);
|
||||
int security_kernel_module_request(void);
|
||||
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags);
|
||||
int security_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
int flags);
|
||||
@ -1796,6 +1878,9 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen);
|
||||
int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
|
||||
void security_release_secctx(char *secdata, u32 seclen);
|
||||
|
||||
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
|
||||
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
|
||||
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
|
||||
#else /* CONFIG_SECURITY */
|
||||
struct security_mnt_opts {
|
||||
};
|
||||
@ -1818,10 +1903,10 @@ static inline int security_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_ptrace_may_access(struct task_struct *child,
|
||||
static inline int security_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
return cap_ptrace_may_access(child, mode);
|
||||
return cap_ptrace_access_check(child, mode);
|
||||
}
|
||||
|
||||
static inline int security_ptrace_traceme(struct task_struct *parent)
|
||||
@ -2266,6 +2351,11 @@ static inline int security_task_create(unsigned long clone_flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_cred_free(struct cred *cred)
|
||||
{ }
|
||||
|
||||
@ -2281,6 +2371,11 @@ static inline void security_commit_creds(struct cred *new,
|
||||
{
|
||||
}
|
||||
|
||||
static inline void security_transfer_creds(struct cred *new,
|
||||
const struct cred *old)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int security_kernel_act_as(struct cred *cred, u32 secid)
|
||||
{
|
||||
return 0;
|
||||
@ -2292,6 +2387,11 @@ static inline int security_kernel_create_files_as(struct cred *cred,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_kernel_module_request(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
|
||||
int flags)
|
||||
{
|
||||
@ -2537,6 +2637,19 @@ static inline int security_secctx_to_secid(const char *secdata,
|
||||
static inline void security_release_secctx(char *secdata, u32 seclen)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
@ -2575,6 +2688,9 @@ void security_inet_csk_clone(struct sock *newsk,
|
||||
const struct request_sock *req);
|
||||
void security_inet_conn_established(struct sock *sk,
|
||||
struct sk_buff *skb);
|
||||
int security_tun_dev_create(void);
|
||||
void security_tun_dev_post_create(struct sock *sk);
|
||||
int security_tun_dev_attach(struct sock *sk);
|
||||
|
||||
#else /* CONFIG_SECURITY_NETWORK */
|
||||
static inline int security_unix_stream_connect(struct socket *sock,
|
||||
@ -2725,6 +2841,20 @@ static inline void security_inet_conn_established(struct sock *sk,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int security_tun_dev_create(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_tun_dev_post_create(struct sock *sk)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int security_tun_dev_attach(struct sock *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
@ -2881,6 +3011,9 @@ void security_key_free(struct key *key);
|
||||
int security_key_permission(key_ref_t key_ref,
|
||||
const struct cred *cred, key_perm_t perm);
|
||||
int security_key_getsecurity(struct key *key, char **_buffer);
|
||||
int security_key_session_to_parent(const struct cred *cred,
|
||||
const struct cred *parent_cred,
|
||||
struct key *key);
|
||||
|
||||
#else
|
||||
|
||||
@ -2908,6 +3041,13 @@ static inline int security_key_getsecurity(struct key *key, char **_buffer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_key_session_to_parent(const struct cred *cred,
|
||||
const struct cred *parent_cred,
|
||||
struct key *key)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
|
@ -49,6 +49,7 @@ struct xattr_handler {
|
||||
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t);
|
||||
ssize_t vfs_getxattr(struct dentry *, const char *, void *, size_t);
|
||||
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size);
|
||||
int __vfs_setxattr_noperm(struct dentry *, const char *, const void *, size_t, int);
|
||||
int vfs_setxattr(struct dentry *, const char *, const void *, size_t, int);
|
||||
int vfs_removexattr(struct dentry *, const char *);
|
||||
|
||||
|
@ -491,13 +491,17 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
u64 run_time;
|
||||
struct timespec uptime;
|
||||
struct tty_struct *tty;
|
||||
const struct cred *orig_cred;
|
||||
|
||||
/* Perform file operations on behalf of whoever enabled accounting */
|
||||
orig_cred = override_creds(file->f_cred);
|
||||
|
||||
/*
|
||||
* First check to see if there is enough free_space to continue
|
||||
* the process accounting system.
|
||||
*/
|
||||
if (!check_free_space(acct, file))
|
||||
return;
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Fill the accounting struct with the needed info as recorded
|
||||
@ -578,6 +582,8 @@ static void do_acct_process(struct bsd_acct_struct *acct,
|
||||
sizeof(acct_t), &file->f_pos);
|
||||
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
|
||||
set_fs(fs);
|
||||
out:
|
||||
revert_creds(orig_cred);
|
||||
}
|
||||
|
||||
/**
|
||||
|
293
kernel/cred.c
293
kernel/cred.c
@ -18,6 +18,18 @@
|
||||
#include <linux/cn_proc.h>
|
||||
#include "cred-internals.h"
|
||||
|
||||
#if 0
|
||||
#define kdebug(FMT, ...) \
|
||||
printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#else
|
||||
static inline __attribute__((format(printf, 1, 2)))
|
||||
void no_printk(const char *fmt, ...)
|
||||
{
|
||||
}
|
||||
#define kdebug(FMT, ...) \
|
||||
no_printk("[%-5.5s%5u] "FMT"\n", current->comm, current->pid ,##__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
static struct kmem_cache *cred_jar;
|
||||
|
||||
/*
|
||||
@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = {
|
||||
*/
|
||||
struct cred init_cred = {
|
||||
.usage = ATOMIC_INIT(4),
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
.subscribers = ATOMIC_INIT(2),
|
||||
.magic = CRED_MAGIC,
|
||||
#endif
|
||||
.securebits = SECUREBITS_DEFAULT,
|
||||
.cap_inheritable = CAP_INIT_INH_SET,
|
||||
.cap_permitted = CAP_FULL_SET,
|
||||
@ -48,6 +64,31 @@ struct cred init_cred = {
|
||||
#endif
|
||||
};
|
||||
|
||||
static inline void set_cred_subscribers(struct cred *cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
atomic_set(&cred->subscribers, n);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int read_cred_subscribers(const struct cred *cred)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
return atomic_read(&cred->subscribers);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void alter_cred_subscribers(const struct cred *_cred, int n)
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
struct cred *cred = (struct cred *) _cred;
|
||||
|
||||
atomic_add(n, &cred->subscribers);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Dispose of the shared task group credentials
|
||||
*/
|
||||
@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct cred *cred = container_of(rcu, struct cred, rcu);
|
||||
|
||||
kdebug("put_cred_rcu(%p)", cred);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
if (cred->magic != CRED_MAGIC_DEAD ||
|
||||
atomic_read(&cred->usage) != 0 ||
|
||||
read_cred_subscribers(cred) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with"
|
||||
" mag %x, put %p, usage %d, subscr %d\n",
|
||||
cred, cred->magic, cred->put_addr,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
#else
|
||||
if (atomic_read(&cred->usage) != 0)
|
||||
panic("CRED: put_cred_rcu() sees %p with usage %d\n",
|
||||
cred, atomic_read(&cred->usage));
|
||||
#endif
|
||||
|
||||
security_cred_free(cred);
|
||||
key_put(cred->thread_keyring);
|
||||
@ -106,12 +160,90 @@ static void put_cred_rcu(struct rcu_head *rcu)
|
||||
*/
|
||||
void __put_cred(struct cred *cred)
|
||||
{
|
||||
kdebug("__put_cred(%p{%d,%d})", cred,
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
|
||||
BUG_ON(atomic_read(&cred->usage) != 0);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(cred) != 0);
|
||||
cred->magic = CRED_MAGIC_DEAD;
|
||||
cred->put_addr = __builtin_return_address(0);
|
||||
#endif
|
||||
BUG_ON(cred == current->cred);
|
||||
BUG_ON(cred == current->real_cred);
|
||||
|
||||
call_rcu(&cred->rcu, put_cred_rcu);
|
||||
}
|
||||
EXPORT_SYMBOL(__put_cred);
|
||||
|
||||
/*
|
||||
* Clean up a task's credentials when it exits
|
||||
*/
|
||||
void exit_creds(struct task_struct *tsk)
|
||||
{
|
||||
struct cred *cred;
|
||||
|
||||
kdebug("exit_creds(%u,%p,%p,{%d,%d})", tsk->pid, tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
cred = (struct cred *) tsk->real_cred;
|
||||
tsk->real_cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->cred;
|
||||
tsk->cred = NULL;
|
||||
validate_creds(cred);
|
||||
alter_cred_subscribers(cred, -1);
|
||||
put_cred(cred);
|
||||
|
||||
cred = (struct cred *) tsk->replacement_session_keyring;
|
||||
if (cred) {
|
||||
tsk->replacement_session_keyring = NULL;
|
||||
validate_creds(cred);
|
||||
put_cred(cred);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate blank credentials, such that the credentials can be filled in at a
|
||||
* later date without risk of ENOMEM.
|
||||
*/
|
||||
struct cred *cred_alloc_blank(void)
|
||||
{
|
||||
struct cred *new;
|
||||
|
||||
new = kmem_cache_zalloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_KEYS
|
||||
new->tgcred = kzalloc(sizeof(*new->tgcred), GFP_KERNEL);
|
||||
if (!new->tgcred) {
|
||||
kfree(new);
|
||||
return NULL;
|
||||
}
|
||||
atomic_set(&new->tgcred->usage, 1);
|
||||
#endif
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
|
||||
if (security_cred_alloc_blank(new, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
new->magic = CRED_MAGIC;
|
||||
#endif
|
||||
return new;
|
||||
|
||||
error:
|
||||
abort_creds(new);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepare_creds - Prepare a new set of credentials for modification
|
||||
*
|
||||
@ -132,16 +264,19 @@ struct cred *prepare_creds(void)
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 1);
|
||||
validate_process_creds();
|
||||
|
||||
new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_creds() alloc %p", new);
|
||||
|
||||
old = task->cred;
|
||||
memcpy(new, old, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@ -157,6 +292,7 @@ struct cred *prepare_creds(void)
|
||||
|
||||
if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@ -229,9 +365,12 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_usermodehelper_creds() alloc %p", new);
|
||||
|
||||
memcpy(new, &init_cred, sizeof(struct cred));
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
get_group_info(new->group_info);
|
||||
get_uid(new->user);
|
||||
|
||||
@ -250,6 +389,7 @@ struct cred *prepare_usermodehelper_creds(void)
|
||||
#endif
|
||||
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
|
||||
goto error;
|
||||
validate_creds(new);
|
||||
|
||||
BUG_ON(atomic_read(&new->usage) != 1);
|
||||
return new;
|
||||
@ -286,6 +426,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
) {
|
||||
p->real_cred = get_cred(p->cred);
|
||||
get_cred(p->cred);
|
||||
alter_cred_subscribers(p->cred, 2);
|
||||
kdebug("share_creds(%p{%d,%d})",
|
||||
p->cred, atomic_read(&p->cred->usage),
|
||||
read_cred_subscribers(p->cred));
|
||||
atomic_inc(&p->cred->user->processes);
|
||||
return 0;
|
||||
}
|
||||
@ -331,6 +475,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
|
||||
|
||||
atomic_inc(&new->user->processes);
|
||||
p->cred = p->real_cred = get_cred(new);
|
||||
alter_cred_subscribers(new, 2);
|
||||
validate_creds(new);
|
||||
return 0;
|
||||
|
||||
error_put:
|
||||
@ -355,13 +501,20 @@ error_put:
|
||||
int commit_creds(struct cred *new)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
const struct cred *old;
|
||||
const struct cred *old = task->real_cred;
|
||||
|
||||
BUG_ON(task->cred != task->real_cred);
|
||||
BUG_ON(atomic_read(&task->real_cred->usage) < 2);
|
||||
kdebug("commit_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
BUG_ON(task->cred != old);
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(old) < 2);
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
#endif
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
|
||||
old = task->real_cred;
|
||||
security_commit_creds(new, old);
|
||||
|
||||
get_cred(new); /* we will require a ref for the subj creds too */
|
||||
@ -390,12 +543,14 @@ int commit_creds(struct cred *new)
|
||||
* cheaply with the new uid cache, so if it matters
|
||||
* we should be checking for it. -DaveM
|
||||
*/
|
||||
alter_cred_subscribers(new, 2);
|
||||
if (new->user != old->user)
|
||||
atomic_inc(&new->user->processes);
|
||||
rcu_assign_pointer(task->real_cred, new);
|
||||
rcu_assign_pointer(task->cred, new);
|
||||
if (new->user != old->user)
|
||||
atomic_dec(&old->user->processes);
|
||||
alter_cred_subscribers(old, -2);
|
||||
|
||||
sched_switch_user(task);
|
||||
|
||||
@ -428,6 +583,13 @@ EXPORT_SYMBOL(commit_creds);
|
||||
*/
|
||||
void abort_creds(struct cred *new)
|
||||
{
|
||||
kdebug("abort_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
BUG_ON(read_cred_subscribers(new) != 0);
|
||||
#endif
|
||||
BUG_ON(atomic_read(&new->usage) < 1);
|
||||
put_cred(new);
|
||||
}
|
||||
@ -444,7 +606,20 @@ const struct cred *override_creds(const struct cred *new)
|
||||
{
|
||||
const struct cred *old = current->cred;
|
||||
|
||||
rcu_assign_pointer(current->cred, get_cred(new));
|
||||
kdebug("override_creds(%p{%d,%d})", new,
|
||||
atomic_read(&new->usage),
|
||||
read_cred_subscribers(new));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(new);
|
||||
get_cred(new);
|
||||
alter_cred_subscribers(new, 1);
|
||||
rcu_assign_pointer(current->cred, new);
|
||||
alter_cred_subscribers(old, -1);
|
||||
|
||||
kdebug("override_creds() = %p{%d,%d}", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
return old;
|
||||
}
|
||||
EXPORT_SYMBOL(override_creds);
|
||||
@ -460,7 +635,15 @@ void revert_creds(const struct cred *old)
|
||||
{
|
||||
const struct cred *override = current->cred;
|
||||
|
||||
kdebug("revert_creds(%p{%d,%d})", old,
|
||||
atomic_read(&old->usage),
|
||||
read_cred_subscribers(old));
|
||||
|
||||
validate_creds(old);
|
||||
validate_creds(override);
|
||||
alter_cred_subscribers(old, 1);
|
||||
rcu_assign_pointer(current->cred, old);
|
||||
alter_cred_subscribers(override, -1);
|
||||
put_cred(override);
|
||||
}
|
||||
EXPORT_SYMBOL(revert_creds);
|
||||
@ -502,11 +685,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
kdebug("prepare_kernel_cred() alloc %p", new);
|
||||
|
||||
if (daemon)
|
||||
old = get_task_cred(daemon);
|
||||
else
|
||||
old = get_cred(&init_cred);
|
||||
|
||||
validate_creds(old);
|
||||
|
||||
*new = *old;
|
||||
get_uid(new->user);
|
||||
get_group_info(new->group_info);
|
||||
@ -526,7 +713,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
|
||||
goto error;
|
||||
|
||||
atomic_set(&new->usage, 1);
|
||||
set_cred_subscribers(new, 0);
|
||||
put_cred(old);
|
||||
validate_creds(new);
|
||||
return new;
|
||||
|
||||
error:
|
||||
@ -589,3 +778,95 @@ int set_create_files_as(struct cred *new, struct inode *inode)
|
||||
return security_kernel_create_files_as(new, inode);
|
||||
}
|
||||
EXPORT_SYMBOL(set_create_files_as);
|
||||
|
||||
#ifdef CONFIG_DEBUG_CREDENTIALS
|
||||
|
||||
/*
|
||||
* dump invalid credentials
|
||||
*/
|
||||
static void dump_invalid_creds(const struct cred *cred, const char *label,
|
||||
const struct task_struct *tsk)
|
||||
{
|
||||
printk(KERN_ERR "CRED: %s credentials: %p %s%s%s\n",
|
||||
label, cred,
|
||||
cred == &init_cred ? "[init]" : "",
|
||||
cred == tsk->real_cred ? "[real]" : "",
|
||||
cred == tsk->cred ? "[eff]" : "");
|
||||
printk(KERN_ERR "CRED: ->magic=%x, put_addr=%p\n",
|
||||
cred->magic, cred->put_addr);
|
||||
printk(KERN_ERR "CRED: ->usage=%d, subscr=%d\n",
|
||||
atomic_read(&cred->usage),
|
||||
read_cred_subscribers(cred));
|
||||
printk(KERN_ERR "CRED: ->*uid = { %d,%d,%d,%d }\n",
|
||||
cred->uid, cred->euid, cred->suid, cred->fsuid);
|
||||
printk(KERN_ERR "CRED: ->*gid = { %d,%d,%d,%d }\n",
|
||||
cred->gid, cred->egid, cred->sgid, cred->fsgid);
|
||||
#ifdef CONFIG_SECURITY
|
||||
printk(KERN_ERR "CRED: ->security is %p\n", cred->security);
|
||||
if ((unsigned long) cred->security >= PAGE_SIZE &&
|
||||
(((unsigned long) cred->security & 0xffffff00) !=
|
||||
(POISON_FREE << 24 | POISON_FREE << 16 | POISON_FREE << 8)))
|
||||
printk(KERN_ERR "CRED: ->security {%x, %x}\n",
|
||||
((u32*)cred->security)[0],
|
||||
((u32*)cred->security)[1]);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* report use of invalid credentials
|
||||
*/
|
||||
void __invalid_creds(const struct cred *cred, const char *file, unsigned line)
|
||||
{
|
||||
printk(KERN_ERR "CRED: Invalid credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
dump_invalid_creds(cred, "Specified", current);
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__invalid_creds);
|
||||
|
||||
/*
|
||||
* check the credentials on a process
|
||||
*/
|
||||
void __validate_process_creds(struct task_struct *tsk,
|
||||
const char *file, unsigned line)
|
||||
{
|
||||
if (tsk->cred == tsk->real_cred) {
|
||||
if (unlikely(read_cred_subscribers(tsk->cred) < 2 ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
} else {
|
||||
if (unlikely(read_cred_subscribers(tsk->real_cred) < 1 ||
|
||||
read_cred_subscribers(tsk->cred) < 1 ||
|
||||
creds_are_invalid(tsk->real_cred) ||
|
||||
creds_are_invalid(tsk->cred)))
|
||||
goto invalid_creds;
|
||||
}
|
||||
return;
|
||||
|
||||
invalid_creds:
|
||||
printk(KERN_ERR "CRED: Invalid process credentials\n");
|
||||
printk(KERN_ERR "CRED: At %s:%u\n", file, line);
|
||||
|
||||
dump_invalid_creds(tsk->real_cred, "Real", tsk);
|
||||
if (tsk->cred != tsk->real_cred)
|
||||
dump_invalid_creds(tsk->cred, "Effective", tsk);
|
||||
else
|
||||
printk(KERN_ERR "CRED: Effective creds == Real creds\n");
|
||||
BUG();
|
||||
}
|
||||
EXPORT_SYMBOL(__validate_process_creds);
|
||||
|
||||
/*
|
||||
* check creds for do_exit()
|
||||
*/
|
||||
void validate_creds_for_do_exit(struct task_struct *tsk)
|
||||
{
|
||||
kdebug("validate_creds_for_do_exit(%p,%p{%d,%d})",
|
||||
tsk->real_cred, tsk->cred,
|
||||
atomic_read(&tsk->cred->usage),
|
||||
read_cred_subscribers(tsk->cred));
|
||||
|
||||
__validate_process_creds(tsk, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_DEBUG_CREDENTIALS */
|
||||
|
@ -901,6 +901,8 @@ NORET_TYPE void do_exit(long code)
|
||||
|
||||
tracehook_report_exit(&code);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
/*
|
||||
* We're taking recursive faults here in do_exit. Safest is to just
|
||||
* leave this task alone and wait for reboot.
|
||||
@ -1009,6 +1011,8 @@ NORET_TYPE void do_exit(long code)
|
||||
if (tsk->splice_pipe)
|
||||
__free_pipe_info(tsk->splice_pipe);
|
||||
|
||||
validate_creds_for_do_exit(tsk);
|
||||
|
||||
preempt_disable();
|
||||
/* causes final put_task_struct in finish_task_switch(). */
|
||||
tsk->state = TASK_DEAD;
|
||||
|
@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk)
|
||||
WARN_ON(atomic_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
put_cred(tsk->real_cred);
|
||||
put_cred(tsk->cred);
|
||||
exit_creds(tsk);
|
||||
delayacct_tsk_free(tsk);
|
||||
|
||||
if (!profile_handoff_task(tsk))
|
||||
@ -1297,8 +1296,7 @@ bad_fork_cleanup_put_domain:
|
||||
module_put(task_thread_info(p)->exec_domain->module);
|
||||
bad_fork_cleanup_count:
|
||||
atomic_dec(&p->cred->user->processes);
|
||||
put_cred(p->real_cred);
|
||||
put_cred(p->cred);
|
||||
exit_creds(p);
|
||||
bad_fork_free:
|
||||
free_task(p);
|
||||
fork_out:
|
||||
|
@ -78,6 +78,10 @@ int __request_module(bool wait, const char *fmt, ...)
|
||||
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
|
||||
static int kmod_loop_msg;
|
||||
|
||||
ret = security_kernel_module_request();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
va_start(args, fmt);
|
||||
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
|
||||
va_end(args);
|
||||
@ -462,6 +466,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
|
||||
int retval = 0;
|
||||
|
||||
BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
|
||||
validate_creds(sub_info->cred);
|
||||
|
||||
helper_lock();
|
||||
if (sub_info->path[0] == '\0')
|
||||
|
@ -152,7 +152,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
if (!dumpable && !capable(CAP_SYS_PTRACE))
|
||||
return -EPERM;
|
||||
|
||||
return security_ptrace_may_access(task, mode);
|
||||
return security_ptrace_access_check(task, mode);
|
||||
}
|
||||
|
||||
bool ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
|
@ -49,7 +49,6 @@
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/ftrace.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/slow-work.h>
|
||||
#include <linux/perf_counter.h>
|
||||
|
||||
|
@ -653,6 +653,21 @@ config DEBUG_NOTIFIERS
|
||||
This is a relatively cheap check but if you care about maximum
|
||||
performance, say N.
|
||||
|
||||
config DEBUG_CREDENTIALS
|
||||
bool "Debug credential management"
|
||||
depends on DEBUG_KERNEL
|
||||
help
|
||||
Enable this to turn on some debug checking for credential
|
||||
management. The additional code keeps track of the number of
|
||||
pointers from task_structs to any given cred struct, and checks to
|
||||
see that this number never exceeds the usage count of the cred
|
||||
struct.
|
||||
|
||||
Furthermore, if SELinux is enabled, this also checks that the
|
||||
security pointer in the cred struct is never seen to be invalid.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
#
|
||||
# Select this config option from the architecture Kconfig, if it
|
||||
# it is preferred to always offer frame pointers as a config
|
||||
|
@ -12,34 +12,47 @@
|
||||
|
||||
#include <linux/sched.h>
|
||||
|
||||
/**
|
||||
* is_single_threaded - Determine if a thread group is single-threaded or not
|
||||
* @p: A task in the thread group in question
|
||||
*
|
||||
* This returns true if the thread group to which a task belongs is single
|
||||
* threaded, false if it is not.
|
||||
/*
|
||||
* Returns true if the task does not share ->mm with another thread/process.
|
||||
*/
|
||||
bool is_single_threaded(struct task_struct *p)
|
||||
bool current_is_single_threaded(void)
|
||||
{
|
||||
struct task_struct *g, *t;
|
||||
struct mm_struct *mm = p->mm;
|
||||
struct task_struct *task = current;
|
||||
struct mm_struct *mm = task->mm;
|
||||
struct task_struct *p, *t;
|
||||
bool ret;
|
||||
|
||||
if (atomic_read(&p->signal->count) != 1)
|
||||
goto no;
|
||||
if (atomic_read(&task->signal->live) != 1)
|
||||
return false;
|
||||
|
||||
if (atomic_read(&p->mm->mm_users) != 1) {
|
||||
read_lock(&tasklist_lock);
|
||||
do_each_thread(g, t) {
|
||||
if (t->mm == mm && t != p)
|
||||
goto no_unlock;
|
||||
} while_each_thread(g, t);
|
||||
read_unlock(&tasklist_lock);
|
||||
if (atomic_read(&mm->mm_users) == 1)
|
||||
return true;
|
||||
|
||||
ret = false;
|
||||
rcu_read_lock();
|
||||
for_each_process(p) {
|
||||
if (unlikely(p->flags & PF_KTHREAD))
|
||||
continue;
|
||||
if (unlikely(p == task->group_leader))
|
||||
continue;
|
||||
|
||||
t = p;
|
||||
do {
|
||||
if (unlikely(t->mm == mm))
|
||||
goto found;
|
||||
if (likely(t->mm))
|
||||
break;
|
||||
/*
|
||||
* t->mm == NULL. Make sure next_thread/next_task
|
||||
* will see other CLONE_VM tasks which might be
|
||||
* forked before exiting.
|
||||
*/
|
||||
smp_rmb();
|
||||
} while_each_thread(p, t);
|
||||
}
|
||||
ret = true;
|
||||
found:
|
||||
rcu_read_unlock();
|
||||
|
||||
return true;
|
||||
|
||||
no_unlock:
|
||||
read_unlock(&tasklist_lock);
|
||||
no:
|
||||
return false;
|
||||
return ret;
|
||||
}
|
||||
|
@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name)
|
||||
dev = __dev_get_by_name(net, name);
|
||||
read_unlock(&dev_base_lock);
|
||||
|
||||
if (!dev && capable(CAP_SYS_MODULE))
|
||||
if (!dev && capable(CAP_NET_ADMIN))
|
||||
request_module("%s", name);
|
||||
}
|
||||
|
||||
|
@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char *name)
|
||||
spin_lock(&tcp_cong_list_lock);
|
||||
ca = tcp_ca_find(name);
|
||||
#ifdef CONFIG_MODULES
|
||||
if (!ca && capable(CAP_SYS_MODULE)) {
|
||||
if (!ca && capable(CAP_NET_ADMIN)) {
|
||||
spin_unlock(&tcp_cong_list_lock);
|
||||
|
||||
request_module("tcp_%s", name);
|
||||
@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
/* not found attempt to autoload module */
|
||||
if (!ca && capable(CAP_SYS_MODULE)) {
|
||||
if (!ca && capable(CAP_NET_ADMIN)) {
|
||||
rcu_read_unlock();
|
||||
request_module("tcp_%s", name);
|
||||
rcu_read_lock();
|
||||
|
@ -16,9 +16,7 @@ obj-$(CONFIG_SECURITYFS) += inode.o
|
||||
# Must precede capability.o in order to stack properly.
|
||||
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
|
||||
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
|
||||
ifeq ($(CONFIG_AUDIT),y)
|
||||
obj-$(CONFIG_SECURITY_SMACK) += lsm_audit.o
|
||||
endif
|
||||
obj-$(CONFIG_AUDIT) += lsm_audit.o
|
||||
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
|
||||
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
|
||||
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
|
||||
|
@ -373,6 +373,11 @@ static int cap_task_create(unsigned long clone_flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cap_cred_free(struct cred *cred)
|
||||
{
|
||||
}
|
||||
@ -386,6 +391,10 @@ static void cap_cred_commit(struct cred *new, const struct cred *old)
|
||||
{
|
||||
}
|
||||
|
||||
static void cap_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
}
|
||||
|
||||
static int cap_kernel_act_as(struct cred *new, u32 secid)
|
||||
{
|
||||
return 0;
|
||||
@ -396,6 +405,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_kernel_module_request(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
|
||||
{
|
||||
return 0;
|
||||
@ -701,10 +715,26 @@ static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void cap_req_classify_flow(const struct request_sock *req,
|
||||
struct flowi *fl)
|
||||
{
|
||||
}
|
||||
|
||||
static int cap_tun_dev_create(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cap_tun_dev_post_create(struct sock *sk)
|
||||
{
|
||||
}
|
||||
|
||||
static int cap_tun_dev_attach(struct sock *sk)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
@ -792,6 +822,20 @@ static void cap_release_secctx(char *secdata, u32 seclen)
|
||||
{
|
||||
}
|
||||
|
||||
static int cap_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_KEYS
|
||||
static int cap_key_alloc(struct key *key, const struct cred *cred,
|
||||
unsigned long flags)
|
||||
@ -815,6 +859,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cap_key_session_to_parent(const struct cred *cred,
|
||||
const struct cred *parent_cred,
|
||||
struct key *key)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
@ -854,7 +905,7 @@ struct security_operations default_security_ops = {
|
||||
|
||||
void security_fixup_ops(struct security_operations *ops)
|
||||
{
|
||||
set_to_cap_if_null(ops, ptrace_may_access);
|
||||
set_to_cap_if_null(ops, ptrace_access_check);
|
||||
set_to_cap_if_null(ops, ptrace_traceme);
|
||||
set_to_cap_if_null(ops, capget);
|
||||
set_to_cap_if_null(ops, capset);
|
||||
@ -940,11 +991,14 @@ void security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, file_receive);
|
||||
set_to_cap_if_null(ops, dentry_open);
|
||||
set_to_cap_if_null(ops, task_create);
|
||||
set_to_cap_if_null(ops, cred_alloc_blank);
|
||||
set_to_cap_if_null(ops, cred_free);
|
||||
set_to_cap_if_null(ops, cred_prepare);
|
||||
set_to_cap_if_null(ops, cred_commit);
|
||||
set_to_cap_if_null(ops, cred_transfer);
|
||||
set_to_cap_if_null(ops, kernel_act_as);
|
||||
set_to_cap_if_null(ops, kernel_create_files_as);
|
||||
set_to_cap_if_null(ops, kernel_module_request);
|
||||
set_to_cap_if_null(ops, task_setuid);
|
||||
set_to_cap_if_null(ops, task_fix_setuid);
|
||||
set_to_cap_if_null(ops, task_setgid);
|
||||
@ -992,6 +1046,9 @@ void security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, secid_to_secctx);
|
||||
set_to_cap_if_null(ops, secctx_to_secid);
|
||||
set_to_cap_if_null(ops, release_secctx);
|
||||
set_to_cap_if_null(ops, inode_notifysecctx);
|
||||
set_to_cap_if_null(ops, inode_setsecctx);
|
||||
set_to_cap_if_null(ops, inode_getsecctx);
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
set_to_cap_if_null(ops, unix_stream_connect);
|
||||
set_to_cap_if_null(ops, unix_may_send);
|
||||
@ -1020,6 +1077,9 @@ void security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, inet_csk_clone);
|
||||
set_to_cap_if_null(ops, inet_conn_established);
|
||||
set_to_cap_if_null(ops, req_classify_flow);
|
||||
set_to_cap_if_null(ops, tun_dev_create);
|
||||
set_to_cap_if_null(ops, tun_dev_post_create);
|
||||
set_to_cap_if_null(ops, tun_dev_attach);
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
set_to_cap_if_null(ops, xfrm_policy_alloc_security);
|
||||
@ -1038,6 +1098,7 @@ void security_fixup_ops(struct security_operations *ops)
|
||||
set_to_cap_if_null(ops, key_free);
|
||||
set_to_cap_if_null(ops, key_permission);
|
||||
set_to_cap_if_null(ops, key_getsecurity);
|
||||
set_to_cap_if_null(ops, key_session_to_parent);
|
||||
#endif /* CONFIG_KEYS */
|
||||
#ifdef CONFIG_AUDIT
|
||||
set_to_cap_if_null(ops, audit_rule_init);
|
||||
|
@ -101,7 +101,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
|
||||
}
|
||||
|
||||
/**
|
||||
* cap_ptrace_may_access - Determine whether the current process may access
|
||||
* cap_ptrace_access_check - Determine whether the current process may access
|
||||
* another
|
||||
* @child: The process to be accessed
|
||||
* @mode: The mode of attachment.
|
||||
@ -109,7 +109,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
|
||||
* Determine whether a process may access another, returning 0 if permission
|
||||
* granted, -ve if denied.
|
||||
*/
|
||||
int cap_ptrace_may_access(struct task_struct *child, unsigned int mode)
|
||||
int cap_ptrace_access_check(struct task_struct *child, unsigned int mode)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#
|
||||
|
||||
obj-y := \
|
||||
gc.o \
|
||||
key.o \
|
||||
keyring.o \
|
||||
keyctl.o \
|
||||
|
@ -82,6 +82,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
|
||||
case KEYCTL_GET_SECURITY:
|
||||
return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
|
||||
|
||||
case KEYCTL_SESSION_TO_PARENT:
|
||||
return keyctl_session_to_parent();
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
194
security/keys/gc.c
Normal file
194
security/keys/gc.c
Normal file
@ -0,0 +1,194 @@
|
||||
/* Key garbage collector
|
||||
*
|
||||
* Copyright (C) 2009 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <keys/keyring-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
/*
|
||||
* Delay between key revocation/expiry in seconds
|
||||
*/
|
||||
unsigned key_gc_delay = 5 * 60;
|
||||
|
||||
/*
|
||||
* Reaper
|
||||
*/
|
||||
static void key_gc_timer_func(unsigned long);
|
||||
static void key_garbage_collector(struct work_struct *);
|
||||
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
||||
static DECLARE_WORK(key_gc_work, key_garbage_collector);
|
||||
static key_serial_t key_gc_cursor; /* the last key the gc considered */
|
||||
static unsigned long key_gc_executing;
|
||||
static time_t key_gc_next_run = LONG_MAX;
|
||||
|
||||
/*
|
||||
* Schedule a garbage collection run
|
||||
* - precision isn't particularly important
|
||||
*/
|
||||
void key_schedule_gc(time_t gc_at)
|
||||
{
|
||||
unsigned long expires;
|
||||
time_t now = current_kernel_time().tv_sec;
|
||||
|
||||
kenter("%ld", gc_at - now);
|
||||
|
||||
gc_at += key_gc_delay;
|
||||
|
||||
if (now >= gc_at) {
|
||||
schedule_work(&key_gc_work);
|
||||
} else if (gc_at < key_gc_next_run) {
|
||||
expires = jiffies + (gc_at - now) * HZ;
|
||||
mod_timer(&key_gc_timer, expires);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The garbage collector timer kicked off
|
||||
*/
|
||||
static void key_gc_timer_func(unsigned long data)
|
||||
{
|
||||
kenter("");
|
||||
key_gc_next_run = LONG_MAX;
|
||||
schedule_work(&key_gc_work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collect pointers from a keyring
|
||||
* - return true if we altered the keyring
|
||||
*/
|
||||
static bool key_gc_keyring(struct key *keyring, time_t limit)
|
||||
__releases(key_serial_lock)
|
||||
{
|
||||
struct keyring_list *klist;
|
||||
struct key *key;
|
||||
int loop;
|
||||
|
||||
kenter("%x", key_serial(keyring));
|
||||
|
||||
if (test_bit(KEY_FLAG_REVOKED, &keyring->flags))
|
||||
goto dont_gc;
|
||||
|
||||
/* scan the keyring looking for dead keys */
|
||||
klist = rcu_dereference(keyring->payload.subscriptions);
|
||||
if (!klist)
|
||||
goto dont_gc;
|
||||
|
||||
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
|
||||
key = klist->keys[loop];
|
||||
if (test_bit(KEY_FLAG_DEAD, &key->flags) ||
|
||||
(key->expiry > 0 && key->expiry <= limit))
|
||||
goto do_gc;
|
||||
}
|
||||
|
||||
dont_gc:
|
||||
kleave(" = false");
|
||||
return false;
|
||||
|
||||
do_gc:
|
||||
key_gc_cursor = keyring->serial;
|
||||
key_get(keyring);
|
||||
spin_unlock(&key_serial_lock);
|
||||
keyring_gc(keyring, limit);
|
||||
key_put(keyring);
|
||||
kleave(" = true");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Garbage collector for keys
|
||||
* - this involves scanning the keyrings for dead, expired and revoked keys
|
||||
* that have overstayed their welcome
|
||||
*/
|
||||
static void key_garbage_collector(struct work_struct *work)
|
||||
{
|
||||
struct rb_node *rb;
|
||||
key_serial_t cursor;
|
||||
struct key *key, *xkey;
|
||||
time_t new_timer = LONG_MAX, limit;
|
||||
|
||||
kenter("");
|
||||
|
||||
if (test_and_set_bit(0, &key_gc_executing)) {
|
||||
key_schedule_gc(current_kernel_time().tv_sec);
|
||||
return;
|
||||
}
|
||||
|
||||
limit = current_kernel_time().tv_sec;
|
||||
if (limit > key_gc_delay)
|
||||
limit -= key_gc_delay;
|
||||
else
|
||||
limit = key_gc_delay;
|
||||
|
||||
spin_lock(&key_serial_lock);
|
||||
|
||||
if (RB_EMPTY_ROOT(&key_serial_tree))
|
||||
goto reached_the_end;
|
||||
|
||||
cursor = key_gc_cursor;
|
||||
if (cursor < 0)
|
||||
cursor = 0;
|
||||
|
||||
/* find the first key above the cursor */
|
||||
key = NULL;
|
||||
rb = key_serial_tree.rb_node;
|
||||
while (rb) {
|
||||
xkey = rb_entry(rb, struct key, serial_node);
|
||||
if (cursor < xkey->serial) {
|
||||
key = xkey;
|
||||
rb = rb->rb_left;
|
||||
} else if (cursor > xkey->serial) {
|
||||
rb = rb->rb_right;
|
||||
} else {
|
||||
rb = rb_next(rb);
|
||||
if (!rb)
|
||||
goto reached_the_end;
|
||||
key = rb_entry(rb, struct key, serial_node);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!key)
|
||||
goto reached_the_end;
|
||||
|
||||
/* trawl through the keys looking for keyrings */
|
||||
for (;;) {
|
||||
if (key->expiry > 0 && key->expiry < new_timer)
|
||||
new_timer = key->expiry;
|
||||
|
||||
if (key->type == &key_type_keyring &&
|
||||
key_gc_keyring(key, limit)) {
|
||||
/* the gc ate our lock */
|
||||
schedule_work(&key_gc_work);
|
||||
goto no_unlock;
|
||||
}
|
||||
|
||||
rb = rb_next(&key->serial_node);
|
||||
if (!rb) {
|
||||
key_gc_cursor = 0;
|
||||
break;
|
||||
}
|
||||
key = rb_entry(rb, struct key, serial_node);
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock(&key_serial_lock);
|
||||
no_unlock:
|
||||
clear_bit(0, &key_gc_executing);
|
||||
if (new_timer < LONG_MAX)
|
||||
key_schedule_gc(new_timer);
|
||||
|
||||
kleave("");
|
||||
return;
|
||||
|
||||
reached_the_end:
|
||||
key_gc_cursor = 0;
|
||||
goto out;
|
||||
}
|
@ -124,11 +124,18 @@ extern struct key *request_key_and_link(struct key_type *type,
|
||||
struct key *dest_keyring,
|
||||
unsigned long flags);
|
||||
|
||||
extern key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
|
||||
extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
|
||||
key_perm_t perm);
|
||||
#define KEY_LOOKUP_CREATE 0x01
|
||||
#define KEY_LOOKUP_PARTIAL 0x02
|
||||
#define KEY_LOOKUP_FOR_UNLINK 0x04
|
||||
|
||||
extern long join_session_keyring(const char *name);
|
||||
|
||||
extern unsigned key_gc_delay;
|
||||
extern void keyring_gc(struct key *keyring, time_t limit);
|
||||
extern void key_schedule_gc(time_t expiry_at);
|
||||
|
||||
/*
|
||||
* check to see whether permission is granted to use a key in the desired way
|
||||
*/
|
||||
@ -194,6 +201,7 @@ extern long keyctl_set_timeout(key_serial_t, unsigned);
|
||||
extern long keyctl_assume_authority(key_serial_t);
|
||||
extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
|
||||
size_t buflen);
|
||||
extern long keyctl_session_to_parent(void);
|
||||
|
||||
/*
|
||||
* debugging key validation
|
||||
|
@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key,
|
||||
set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
|
||||
now = current_kernel_time();
|
||||
key->expiry = now.tv_sec + timeout;
|
||||
key_schedule_gc(key->expiry);
|
||||
|
||||
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
|
||||
awaken = 1;
|
||||
@ -642,10 +643,8 @@ struct key *key_lookup(key_serial_t id)
|
||||
goto error;
|
||||
|
||||
found:
|
||||
/* pretend it doesn't exist if it's dead */
|
||||
if (atomic_read(&key->usage) == 0 ||
|
||||
test_bit(KEY_FLAG_DEAD, &key->flags) ||
|
||||
key->type == &key_type_dead)
|
||||
/* pretend it doesn't exist if it is awaiting deletion */
|
||||
if (atomic_read(&key->usage) == 0)
|
||||
goto not_found;
|
||||
|
||||
/* this races with key_put(), but that doesn't matter since key_put()
|
||||
@ -890,6 +889,9 @@ EXPORT_SYMBOL(key_update);
|
||||
*/
|
||||
void key_revoke(struct key *key)
|
||||
{
|
||||
struct timespec now;
|
||||
time_t time;
|
||||
|
||||
key_check(key);
|
||||
|
||||
/* make sure no one's trying to change or use the key when we mark it
|
||||
@ -902,6 +904,14 @@ void key_revoke(struct key *key)
|
||||
key->type->revoke)
|
||||
key->type->revoke(key);
|
||||
|
||||
/* set the death time to no more than the expiry time */
|
||||
now = current_kernel_time();
|
||||
time = now.tv_sec;
|
||||
if (key->revoked_at == 0 || key->revoked_at > time) {
|
||||
key->revoked_at = time;
|
||||
key_schedule_gc(key->revoked_at);
|
||||
}
|
||||
|
||||
up_write(&key->sem);
|
||||
|
||||
} /* end key_revoke() */
|
||||
@ -958,8 +968,10 @@ void unregister_key_type(struct key_type *ktype)
|
||||
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
|
||||
key = rb_entry(_n, struct key, serial_node);
|
||||
|
||||
if (key->type == ktype)
|
||||
if (key->type == ktype) {
|
||||
key->type = &key_type_dead;
|
||||
set_bit(KEY_FLAG_DEAD, &key->flags);
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock(&key_serial_lock);
|
||||
@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype)
|
||||
spin_unlock(&key_serial_lock);
|
||||
up_write(&key_types_sem);
|
||||
|
||||
key_schedule_gc(0);
|
||||
|
||||
} /* end unregister_key_type() */
|
||||
|
||||
EXPORT_SYMBOL(unregister_key_type);
|
||||
|
@ -103,7 +103,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
|
||||
}
|
||||
|
||||
/* find the target keyring (which must be writable) */
|
||||
keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
|
||||
keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto error3;
|
||||
@ -185,7 +185,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
|
||||
/* get the destination keyring if specified */
|
||||
dest_ref = NULL;
|
||||
if (destringid) {
|
||||
dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
|
||||
dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
|
||||
KEY_WRITE);
|
||||
if (IS_ERR(dest_ref)) {
|
||||
ret = PTR_ERR(dest_ref);
|
||||
goto error3;
|
||||
@ -233,9 +234,11 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
|
||||
long keyctl_get_keyring_ID(key_serial_t id, int create)
|
||||
{
|
||||
key_ref_t key_ref;
|
||||
unsigned long lflags;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, create, 0, KEY_SEARCH);
|
||||
lflags = create ? KEY_LOOKUP_CREATE : 0;
|
||||
key_ref = lookup_user_key(id, lflags, KEY_SEARCH);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
@ -309,7 +312,7 @@ long keyctl_update_key(key_serial_t id,
|
||||
}
|
||||
|
||||
/* find the target key (which must be writable) */
|
||||
key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
|
||||
key_ref = lookup_user_key(id, 0, KEY_WRITE);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error2;
|
||||
@ -337,10 +340,16 @@ long keyctl_revoke_key(key_serial_t id)
|
||||
key_ref_t key_ref;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, 0, 0, KEY_WRITE);
|
||||
key_ref = lookup_user_key(id, 0, KEY_WRITE);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
if (ret != -EACCES)
|
||||
goto error;
|
||||
key_ref = lookup_user_key(id, 0, KEY_SETATTR);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
key_revoke(key_ref_to_ptr(key_ref));
|
||||
@ -363,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
|
||||
key_ref_t keyring_ref;
|
||||
long ret;
|
||||
|
||||
keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
|
||||
keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto error;
|
||||
@ -389,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
|
||||
key_ref_t keyring_ref, key_ref;
|
||||
long ret;
|
||||
|
||||
keyring_ref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
|
||||
keyring_ref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto error;
|
||||
}
|
||||
|
||||
key_ref = lookup_user_key(id, 1, 0, KEY_LINK);
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE, KEY_LINK);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error2;
|
||||
@ -423,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
|
||||
key_ref_t keyring_ref, key_ref;
|
||||
long ret;
|
||||
|
||||
keyring_ref = lookup_user_key(ringid, 0, 0, KEY_WRITE);
|
||||
keyring_ref = lookup_user_key(ringid, 0, KEY_WRITE);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto error;
|
||||
}
|
||||
|
||||
key_ref = lookup_user_key(id, 0, 0, 0);
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_FOR_UNLINK, 0);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error2;
|
||||
@ -465,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid,
|
||||
char *tmpbuf;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
|
||||
key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
|
||||
if (IS_ERR(key_ref)) {
|
||||
/* viewing a key under construction is permitted if we have the
|
||||
* authorisation token handy */
|
||||
@ -474,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid,
|
||||
if (!IS_ERR(instkey)) {
|
||||
key_put(instkey);
|
||||
key_ref = lookup_user_key(keyid,
|
||||
0, 1, 0);
|
||||
KEY_LOOKUP_PARTIAL,
|
||||
0);
|
||||
if (!IS_ERR(key_ref))
|
||||
goto okay;
|
||||
}
|
||||
@ -558,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid,
|
||||
}
|
||||
|
||||
/* get the keyring at which to begin the search */
|
||||
keyring_ref = lookup_user_key(ringid, 0, 0, KEY_SEARCH);
|
||||
keyring_ref = lookup_user_key(ringid, 0, KEY_SEARCH);
|
||||
if (IS_ERR(keyring_ref)) {
|
||||
ret = PTR_ERR(keyring_ref);
|
||||
goto error2;
|
||||
@ -567,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid,
|
||||
/* get the destination keyring if specified */
|
||||
dest_ref = NULL;
|
||||
if (destringid) {
|
||||
dest_ref = lookup_user_key(destringid, 1, 0, KEY_WRITE);
|
||||
dest_ref = lookup_user_key(destringid, KEY_LOOKUP_CREATE,
|
||||
KEY_WRITE);
|
||||
if (IS_ERR(dest_ref)) {
|
||||
ret = PTR_ERR(dest_ref);
|
||||
goto error3;
|
||||
@ -637,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
|
||||
long ret;
|
||||
|
||||
/* find the key first */
|
||||
key_ref = lookup_user_key(keyid, 0, 0, 0);
|
||||
key_ref = lookup_user_key(keyid, 0, 0);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = -ENOKEY;
|
||||
goto error;
|
||||
@ -700,7 +711,8 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid)
|
||||
if (uid == (uid_t) -1 && gid == (gid_t) -1)
|
||||
goto error;
|
||||
|
||||
key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
|
||||
KEY_SETATTR);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
@ -805,7 +817,8 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm)
|
||||
if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
|
||||
goto error;
|
||||
|
||||
key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
|
||||
KEY_SETATTR);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
@ -847,7 +860,7 @@ static long get_instantiation_keyring(key_serial_t ringid,
|
||||
|
||||
/* if a specific keyring is nominated by ID, then use that */
|
||||
if (ringid > 0) {
|
||||
dkref = lookup_user_key(ringid, 1, 0, KEY_WRITE);
|
||||
dkref = lookup_user_key(ringid, KEY_LOOKUP_CREATE, KEY_WRITE);
|
||||
if (IS_ERR(dkref))
|
||||
return PTR_ERR(dkref);
|
||||
*_dest_keyring = key_ref_to_ptr(dkref);
|
||||
@ -1083,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
|
||||
time_t expiry;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(id, 1, 1, KEY_SETATTR);
|
||||
key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL,
|
||||
KEY_SETATTR);
|
||||
if (IS_ERR(key_ref)) {
|
||||
ret = PTR_ERR(key_ref);
|
||||
goto error;
|
||||
@ -1101,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
|
||||
}
|
||||
|
||||
key->expiry = expiry;
|
||||
key_schedule_gc(key->expiry);
|
||||
|
||||
up_write(&key->sem);
|
||||
key_put(key);
|
||||
@ -1170,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid,
|
||||
char *context;
|
||||
long ret;
|
||||
|
||||
key_ref = lookup_user_key(keyid, 0, 1, KEY_VIEW);
|
||||
key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, KEY_VIEW);
|
||||
if (IS_ERR(key_ref)) {
|
||||
if (PTR_ERR(key_ref) != -EACCES)
|
||||
return PTR_ERR(key_ref);
|
||||
@ -1182,7 +1197,7 @@ long keyctl_get_security(key_serial_t keyid,
|
||||
return PTR_ERR(key_ref);
|
||||
key_put(instkey);
|
||||
|
||||
key_ref = lookup_user_key(keyid, 0, 1, 0);
|
||||
key_ref = lookup_user_key(keyid, KEY_LOOKUP_PARTIAL, 0);
|
||||
if (IS_ERR(key_ref))
|
||||
return PTR_ERR(key_ref);
|
||||
}
|
||||
@ -1213,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* attempt to install the calling process's session keyring on the process's
|
||||
* parent process
|
||||
* - the keyring must exist and must grant us LINK permission
|
||||
* - implements keyctl(KEYCTL_SESSION_TO_PARENT)
|
||||
*/
|
||||
long keyctl_session_to_parent(void)
|
||||
{
|
||||
struct task_struct *me, *parent;
|
||||
const struct cred *mycred, *pcred;
|
||||
struct cred *cred, *oldcred;
|
||||
key_ref_t keyring_r;
|
||||
int ret;
|
||||
|
||||
keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK);
|
||||
if (IS_ERR(keyring_r))
|
||||
return PTR_ERR(keyring_r);
|
||||
|
||||
/* our parent is going to need a new cred struct, a new tgcred struct
|
||||
* and new security data, so we allocate them here to prevent ENOMEM in
|
||||
* our parent */
|
||||
ret = -ENOMEM;
|
||||
cred = cred_alloc_blank();
|
||||
if (!cred)
|
||||
goto error_keyring;
|
||||
|
||||
cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r);
|
||||
keyring_r = NULL;
|
||||
|
||||
me = current;
|
||||
write_lock_irq(&tasklist_lock);
|
||||
|
||||
parent = me->real_parent;
|
||||
ret = -EPERM;
|
||||
|
||||
/* the parent mustn't be init and mustn't be a kernel thread */
|
||||
if (parent->pid <= 1 || !parent->mm)
|
||||
goto not_permitted;
|
||||
|
||||
/* the parent must be single threaded */
|
||||
if (atomic_read(&parent->signal->count) != 1)
|
||||
goto not_permitted;
|
||||
|
||||
/* the parent and the child must have different session keyrings or
|
||||
* there's no point */
|
||||
mycred = current_cred();
|
||||
pcred = __task_cred(parent);
|
||||
if (mycred == pcred ||
|
||||
mycred->tgcred->session_keyring == pcred->tgcred->session_keyring)
|
||||
goto already_same;
|
||||
|
||||
/* the parent must have the same effective ownership and mustn't be
|
||||
* SUID/SGID */
|
||||
if (pcred-> uid != mycred->euid ||
|
||||
pcred->euid != mycred->euid ||
|
||||
pcred->suid != mycred->euid ||
|
||||
pcred-> gid != mycred->egid ||
|
||||
pcred->egid != mycred->egid ||
|
||||
pcred->sgid != mycred->egid)
|
||||
goto not_permitted;
|
||||
|
||||
/* the keyrings must have the same UID */
|
||||
if (pcred ->tgcred->session_keyring->uid != mycred->euid ||
|
||||
mycred->tgcred->session_keyring->uid != mycred->euid)
|
||||
goto not_permitted;
|
||||
|
||||
/* the LSM must permit the replacement of the parent's keyring with the
|
||||
* keyring from this process */
|
||||
ret = security_key_session_to_parent(mycred, pcred,
|
||||
key_ref_to_ptr(keyring_r));
|
||||
if (ret < 0)
|
||||
goto not_permitted;
|
||||
|
||||
/* if there's an already pending keyring replacement, then we replace
|
||||
* that */
|
||||
oldcred = parent->replacement_session_keyring;
|
||||
|
||||
/* the replacement session keyring is applied just prior to userspace
|
||||
* restarting */
|
||||
parent->replacement_session_keyring = cred;
|
||||
cred = NULL;
|
||||
set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME);
|
||||
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
if (oldcred)
|
||||
put_cred(oldcred);
|
||||
return 0;
|
||||
|
||||
already_same:
|
||||
ret = 0;
|
||||
not_permitted:
|
||||
put_cred(cred);
|
||||
return ret;
|
||||
|
||||
error_keyring:
|
||||
key_ref_put(keyring_r);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* the key control system call
|
||||
@ -1298,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
(char __user *) arg3,
|
||||
(size_t) arg4);
|
||||
|
||||
case KEYCTL_SESSION_TO_PARENT:
|
||||
return keyctl_session_to_parent();
|
||||
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
@ -1000,3 +1000,88 @@ static void keyring_revoke(struct key *keyring)
|
||||
}
|
||||
|
||||
} /* end keyring_revoke() */
|
||||
|
||||
/*
|
||||
* Determine whether a key is dead
|
||||
*/
|
||||
static bool key_is_dead(struct key *key, time_t limit)
|
||||
{
|
||||
return test_bit(KEY_FLAG_DEAD, &key->flags) ||
|
||||
(key->expiry > 0 && key->expiry <= limit);
|
||||
}
|
||||
|
||||
/*
|
||||
* Collect garbage from the contents of a keyring
|
||||
*/
|
||||
void keyring_gc(struct key *keyring, time_t limit)
|
||||
{
|
||||
struct keyring_list *klist, *new;
|
||||
struct key *key;
|
||||
int loop, keep, max;
|
||||
|
||||
kenter("%x", key_serial(keyring));
|
||||
|
||||
down_write(&keyring->sem);
|
||||
|
||||
klist = keyring->payload.subscriptions;
|
||||
if (!klist)
|
||||
goto just_return;
|
||||
|
||||
/* work out how many subscriptions we're keeping */
|
||||
keep = 0;
|
||||
for (loop = klist->nkeys - 1; loop >= 0; loop--)
|
||||
if (!key_is_dead(klist->keys[loop], limit));
|
||||
keep++;
|
||||
|
||||
if (keep == klist->nkeys)
|
||||
goto just_return;
|
||||
|
||||
/* allocate a new keyring payload */
|
||||
max = roundup(keep, 4);
|
||||
new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *),
|
||||
GFP_KERNEL);
|
||||
if (!new)
|
||||
goto just_return;
|
||||
new->maxkeys = max;
|
||||
new->nkeys = 0;
|
||||
new->delkey = 0;
|
||||
|
||||
/* install the live keys
|
||||
* - must take care as expired keys may be updated back to life
|
||||
*/
|
||||
keep = 0;
|
||||
for (loop = klist->nkeys - 1; loop >= 0; loop--) {
|
||||
key = klist->keys[loop];
|
||||
if (!key_is_dead(key, limit)) {
|
||||
if (keep >= max)
|
||||
goto discard_new;
|
||||
new->keys[keep++] = key_get(key);
|
||||
}
|
||||
}
|
||||
new->nkeys = keep;
|
||||
|
||||
/* adjust the quota */
|
||||
key_payload_reserve(keyring,
|
||||
sizeof(struct keyring_list) +
|
||||
KEYQUOTA_LINK_BYTES * keep);
|
||||
|
||||
if (keep == 0) {
|
||||
rcu_assign_pointer(keyring->payload.subscriptions, NULL);
|
||||
kfree(new);
|
||||
} else {
|
||||
rcu_assign_pointer(keyring->payload.subscriptions, new);
|
||||
}
|
||||
|
||||
up_write(&keyring->sem);
|
||||
|
||||
call_rcu(&klist->rcu, keyring_clear_rcu_disposal);
|
||||
kleave(" [yes]");
|
||||
return;
|
||||
|
||||
discard_new:
|
||||
new->nkeys = keep;
|
||||
keyring_clear_rcu_disposal(&new->rcu);
|
||||
just_return:
|
||||
up_write(&keyring->sem);
|
||||
kleave(" [no]");
|
||||
}
|
||||
|
@ -91,59 +91,94 @@ __initcall(key_proc_init);
|
||||
*/
|
||||
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS
|
||||
|
||||
static struct rb_node *__key_serial_next(struct rb_node *n)
|
||||
static struct rb_node *key_serial_next(struct rb_node *n)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
|
||||
n = rb_next(n);
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (key->user->user_ns == current_user_ns())
|
||||
if (key->user->user_ns == user_ns)
|
||||
break;
|
||||
n = rb_next(n);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static struct rb_node *key_serial_next(struct rb_node *n)
|
||||
{
|
||||
return __key_serial_next(rb_next(n));
|
||||
}
|
||||
|
||||
static struct rb_node *key_serial_first(struct rb_root *r)
|
||||
{
|
||||
struct rb_node *n = rb_first(r);
|
||||
return __key_serial_next(n);
|
||||
}
|
||||
|
||||
static int proc_keys_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &proc_keys_ops);
|
||||
}
|
||||
|
||||
static struct key *find_ge_key(key_serial_t id)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
struct rb_node *n = key_serial_tree.rb_node;
|
||||
struct key *minkey = NULL;
|
||||
|
||||
while (n) {
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
if (id < key->serial) {
|
||||
if (!minkey || minkey->serial > key->serial)
|
||||
minkey = key;
|
||||
n = n->rb_left;
|
||||
} else if (id > key->serial) {
|
||||
n = n->rb_right;
|
||||
} else {
|
||||
minkey = key;
|
||||
break;
|
||||
}
|
||||
key = NULL;
|
||||
}
|
||||
|
||||
if (!minkey)
|
||||
return NULL;
|
||||
|
||||
for (;;) {
|
||||
if (minkey->user->user_ns == user_ns)
|
||||
return minkey;
|
||||
n = rb_next(&minkey->serial_node);
|
||||
if (!n)
|
||||
return NULL;
|
||||
minkey = rb_entry(n, struct key, serial_node);
|
||||
}
|
||||
}
|
||||
|
||||
static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
|
||||
__acquires(key_serial_lock)
|
||||
{
|
||||
struct rb_node *_p;
|
||||
loff_t pos = *_pos;
|
||||
key_serial_t pos = *_pos;
|
||||
struct key *key;
|
||||
|
||||
spin_lock(&key_serial_lock);
|
||||
|
||||
_p = key_serial_first(&key_serial_tree);
|
||||
while (pos > 0 && _p) {
|
||||
pos--;
|
||||
_p = key_serial_next(_p);
|
||||
}
|
||||
|
||||
return _p;
|
||||
if (*_pos > INT_MAX)
|
||||
return NULL;
|
||||
key = find_ge_key(pos);
|
||||
if (!key)
|
||||
return NULL;
|
||||
*_pos = key->serial;
|
||||
return &key->serial_node;
|
||||
}
|
||||
|
||||
static inline key_serial_t key_node_serial(struct rb_node *n)
|
||||
{
|
||||
struct key *key = rb_entry(n, struct key, serial_node);
|
||||
return key->serial;
|
||||
}
|
||||
|
||||
static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
|
||||
{
|
||||
(*_pos)++;
|
||||
return key_serial_next((struct rb_node *) v);
|
||||
struct rb_node *n;
|
||||
|
||||
n = key_serial_next(v);
|
||||
if (n)
|
||||
*_pos = key_node_serial(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
static void proc_keys_stop(struct seq_file *p, void *v)
|
||||
__releases(key_serial_lock)
|
||||
{
|
||||
spin_unlock(&key_serial_lock);
|
||||
}
|
||||
@ -174,11 +209,9 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||
/* come up with a suitable timeout value */
|
||||
if (key->expiry == 0) {
|
||||
memcpy(xbuf, "perm", 5);
|
||||
}
|
||||
else if (now.tv_sec >= key->expiry) {
|
||||
} else if (now.tv_sec >= key->expiry) {
|
||||
memcpy(xbuf, "expd", 5);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
timo = key->expiry - now.tv_sec;
|
||||
|
||||
if (timo < 60)
|
||||
@ -218,9 +251,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
|
||||
seq_putc(m, '\n');
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
|
||||
@ -246,6 +277,7 @@ static struct rb_node *key_user_first(struct rb_root *r)
|
||||
struct rb_node *n = rb_first(r);
|
||||
return __key_user_next(n);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* implement "/proc/key-users" to provides a list of the key users
|
||||
@ -253,10 +285,10 @@ static struct rb_node *key_user_first(struct rb_root *r)
|
||||
static int proc_key_users_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &proc_key_users_ops);
|
||||
|
||||
}
|
||||
|
||||
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
|
||||
__acquires(key_user_lock)
|
||||
{
|
||||
struct rb_node *_p;
|
||||
loff_t pos = *_pos;
|
||||
@ -270,17 +302,16 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
|
||||
}
|
||||
|
||||
return _p;
|
||||
|
||||
}
|
||||
|
||||
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
|
||||
{
|
||||
(*_pos)++;
|
||||
return key_user_next((struct rb_node *) v);
|
||||
|
||||
}
|
||||
|
||||
static void proc_key_users_stop(struct seq_file *p, void *v)
|
||||
__releases(key_user_lock)
|
||||
{
|
||||
spin_unlock(&key_user_lock);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/user_namespace.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include "internal.h"
|
||||
@ -487,7 +488,7 @@ static int lookup_user_key_possessed(const struct key *key, const void *target)
|
||||
* - don't create special keyrings unless so requested
|
||||
* - partially constructed keys aren't found unless requested
|
||||
*/
|
||||
key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
|
||||
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
|
||||
key_perm_t perm)
|
||||
{
|
||||
struct request_key_auth *rka;
|
||||
@ -503,7 +504,7 @@ try_again:
|
||||
switch (id) {
|
||||
case KEY_SPEC_THREAD_KEYRING:
|
||||
if (!cred->thread_keyring) {
|
||||
if (!create)
|
||||
if (!(lflags & KEY_LOOKUP_CREATE))
|
||||
goto error;
|
||||
|
||||
ret = install_thread_keyring();
|
||||
@ -521,7 +522,7 @@ try_again:
|
||||
|
||||
case KEY_SPEC_PROCESS_KEYRING:
|
||||
if (!cred->tgcred->process_keyring) {
|
||||
if (!create)
|
||||
if (!(lflags & KEY_LOOKUP_CREATE))
|
||||
goto error;
|
||||
|
||||
ret = install_process_keyring();
|
||||
@ -642,7 +643,14 @@ try_again:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!partial) {
|
||||
/* unlink does not use the nominated key in any way, so can skip all
|
||||
* the permission checks as it is only concerned with the keyring */
|
||||
if (lflags & KEY_LOOKUP_FOR_UNLINK) {
|
||||
ret = 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!(lflags & KEY_LOOKUP_PARTIAL)) {
|
||||
ret = wait_for_key_construction(key, true);
|
||||
switch (ret) {
|
||||
case -ERESTARTSYS:
|
||||
@ -660,7 +668,8 @@ try_again:
|
||||
}
|
||||
|
||||
ret = -EIO;
|
||||
if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
||||
if (!(lflags & KEY_LOOKUP_PARTIAL) &&
|
||||
!test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
||||
goto invalid_key;
|
||||
|
||||
/* check the permissions */
|
||||
@ -702,7 +711,7 @@ long join_session_keyring(const char *name)
|
||||
/* only permit this if there's a single thread in the thread group -
|
||||
* this avoids us having to adjust the creds on all threads and risking
|
||||
* ENOMEM */
|
||||
if (!is_single_threaded(current))
|
||||
if (!current_is_single_threaded())
|
||||
return -EMLINK;
|
||||
|
||||
new = prepare_creds();
|
||||
@ -760,3 +769,51 @@ error:
|
||||
abort_creds(new);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace a process's session keyring when that process resumes userspace on
|
||||
* behalf of one of its children
|
||||
*/
|
||||
void key_replace_session_keyring(void)
|
||||
{
|
||||
const struct cred *old;
|
||||
struct cred *new;
|
||||
|
||||
if (!current->replacement_session_keyring)
|
||||
return;
|
||||
|
||||
write_lock_irq(&tasklist_lock);
|
||||
new = current->replacement_session_keyring;
|
||||
current->replacement_session_keyring = NULL;
|
||||
write_unlock_irq(&tasklist_lock);
|
||||
|
||||
if (!new)
|
||||
return;
|
||||
|
||||
old = current_cred();
|
||||
new-> uid = old-> uid;
|
||||
new-> euid = old-> euid;
|
||||
new-> suid = old-> suid;
|
||||
new->fsuid = old->fsuid;
|
||||
new-> gid = old-> gid;
|
||||
new-> egid = old-> egid;
|
||||
new-> sgid = old-> sgid;
|
||||
new->fsgid = old->fsgid;
|
||||
new->user = get_uid(old->user);
|
||||
new->group_info = get_group_info(old->group_info);
|
||||
|
||||
new->securebits = old->securebits;
|
||||
new->cap_inheritable = old->cap_inheritable;
|
||||
new->cap_permitted = old->cap_permitted;
|
||||
new->cap_effective = old->cap_effective;
|
||||
new->cap_bset = old->cap_bset;
|
||||
|
||||
new->jit_keyring = old->jit_keyring;
|
||||
new->thread_keyring = key_get(old->thread_keyring);
|
||||
new->tgcred->tgid = old->tgcred->tgid;
|
||||
new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
|
||||
|
||||
security_transfer_creds(new, old);
|
||||
|
||||
commit_creds(new);
|
||||
}
|
||||
|
@ -13,6 +13,8 @@
|
||||
#include <linux/sysctl.h>
|
||||
#include "internal.h"
|
||||
|
||||
static const int zero, one = 1, max = INT_MAX;
|
||||
|
||||
ctl_table key_sysctls[] = {
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
@ -20,7 +22,9 @@ ctl_table key_sysctls[] = {
|
||||
.data = &key_quota_maxkeys,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
@ -28,7 +32,9 @@ ctl_table key_sysctls[] = {
|
||||
.data = &key_quota_maxbytes,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
@ -36,7 +42,9 @@ ctl_table key_sysctls[] = {
|
||||
.data = &key_quota_root_maxkeys,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
@ -44,7 +52,19 @@ ctl_table key_sysctls[] = {
|
||||
.data = &key_quota_root_maxbytes,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (void *) &one,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{
|
||||
.ctl_name = CTL_UNNUMBERED,
|
||||
.procname = "gc_delay",
|
||||
.data = &key_gc_delay,
|
||||
.maxlen = sizeof(unsigned),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.extra1 = (void *) &zero,
|
||||
.extra2 = (void *) &max,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
@ -220,6 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
|
||||
}
|
||||
|
||||
switch (a->type) {
|
||||
case LSM_AUDIT_NO_AUDIT:
|
||||
return;
|
||||
case LSM_AUDIT_DATA_IPC:
|
||||
audit_log_format(ab, " key=%d ", a->u.ipc_id);
|
||||
break;
|
||||
|
@ -124,9 +124,9 @@ int register_security(struct security_operations *ops)
|
||||
|
||||
/* Security operations */
|
||||
|
||||
int security_ptrace_may_access(struct task_struct *child, unsigned int mode)
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
|
||||
{
|
||||
return security_ops->ptrace_may_access(child, mode);
|
||||
return security_ops->ptrace_access_check(child, mode);
|
||||
}
|
||||
|
||||
int security_ptrace_traceme(struct task_struct *parent)
|
||||
@ -684,6 +684,11 @@ int security_task_create(unsigned long clone_flags)
|
||||
return security_ops->task_create(clone_flags);
|
||||
}
|
||||
|
||||
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
return security_ops->cred_alloc_blank(cred, gfp);
|
||||
}
|
||||
|
||||
void security_cred_free(struct cred *cred)
|
||||
{
|
||||
security_ops->cred_free(cred);
|
||||
@ -699,6 +704,11 @@ void security_commit_creds(struct cred *new, const struct cred *old)
|
||||
security_ops->cred_commit(new, old);
|
||||
}
|
||||
|
||||
void security_transfer_creds(struct cred *new, const struct cred *old)
|
||||
{
|
||||
security_ops->cred_transfer(new, old);
|
||||
}
|
||||
|
||||
int security_kernel_act_as(struct cred *new, u32 secid)
|
||||
{
|
||||
return security_ops->kernel_act_as(new, secid);
|
||||
@ -709,6 +719,11 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
return security_ops->kernel_create_files_as(new, inode);
|
||||
}
|
||||
|
||||
int security_kernel_module_request(void)
|
||||
{
|
||||
return security_ops->kernel_module_request();
|
||||
}
|
||||
|
||||
int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
|
||||
{
|
||||
return security_ops->task_setuid(id0, id1, id2, flags);
|
||||
@ -959,6 +974,24 @@ void security_release_secctx(char *secdata, u32 seclen)
|
||||
}
|
||||
EXPORT_SYMBOL(security_release_secctx);
|
||||
|
||||
int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return security_ops->inode_notifysecctx(inode, ctx, ctxlen);
|
||||
}
|
||||
EXPORT_SYMBOL(security_inode_notifysecctx);
|
||||
|
||||
int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return security_ops->inode_setsecctx(dentry, ctx, ctxlen);
|
||||
}
|
||||
EXPORT_SYMBOL(security_inode_setsecctx);
|
||||
|
||||
int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
{
|
||||
return security_ops->inode_getsecctx(inode, ctx, ctxlen);
|
||||
}
|
||||
EXPORT_SYMBOL(security_inode_getsecctx);
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK
|
||||
|
||||
int security_unix_stream_connect(struct socket *sock, struct socket *other,
|
||||
@ -1112,6 +1145,24 @@ void security_inet_conn_established(struct sock *sk,
|
||||
security_ops->inet_conn_established(sk, skb);
|
||||
}
|
||||
|
||||
int security_tun_dev_create(void)
|
||||
{
|
||||
return security_ops->tun_dev_create();
|
||||
}
|
||||
EXPORT_SYMBOL(security_tun_dev_create);
|
||||
|
||||
void security_tun_dev_post_create(struct sock *sk)
|
||||
{
|
||||
return security_ops->tun_dev_post_create(sk);
|
||||
}
|
||||
EXPORT_SYMBOL(security_tun_dev_post_create);
|
||||
|
||||
int security_tun_dev_attach(struct sock *sk)
|
||||
{
|
||||
return security_ops->tun_dev_attach(sk);
|
||||
}
|
||||
EXPORT_SYMBOL(security_tun_dev_attach);
|
||||
|
||||
#endif /* CONFIG_SECURITY_NETWORK */
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
@ -1218,6 +1269,13 @@ int security_key_getsecurity(struct key *key, char **_buffer)
|
||||
return security_ops->key_getsecurity(key, _buffer);
|
||||
}
|
||||
|
||||
int security_key_session_to_parent(const struct cred *cred,
|
||||
const struct cred *parent_cred,
|
||||
struct key *key)
|
||||
{
|
||||
return security_ops->key_session_to_parent(cred, parent_cred, key);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_KEYS */
|
||||
|
||||
#ifdef CONFIG_AUDIT
|
||||
|
@ -137,7 +137,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
|
||||
* @tclass: target security class
|
||||
* @av: access vector
|
||||
*/
|
||||
void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
|
||||
static void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av)
|
||||
{
|
||||
const char **common_pts = NULL;
|
||||
u32 common_base = 0;
|
||||
@ -492,23 +492,35 @@ out:
|
||||
return node;
|
||||
}
|
||||
|
||||
static inline void avc_print_ipv6_addr(struct audit_buffer *ab,
|
||||
struct in6_addr *addr, __be16 port,
|
||||
char *name1, char *name2)
|
||||
/**
|
||||
* avc_audit_pre_callback - SELinux specific information
|
||||
* will be called by generic audit code
|
||||
* @ab: the audit buffer
|
||||
* @a: audit_data
|
||||
*/
|
||||
static void avc_audit_pre_callback(struct audit_buffer *ab, void *a)
|
||||
{
|
||||
if (!ipv6_addr_any(addr))
|
||||
audit_log_format(ab, " %s=%pI6", name1, addr);
|
||||
if (port)
|
||||
audit_log_format(ab, " %s=%d", name2, ntohs(port));
|
||||
struct common_audit_data *ad = a;
|
||||
audit_log_format(ab, "avc: %s ",
|
||||
ad->selinux_audit_data.denied ? "denied" : "granted");
|
||||
avc_dump_av(ab, ad->selinux_audit_data.tclass,
|
||||
ad->selinux_audit_data.audited);
|
||||
audit_log_format(ab, " for ");
|
||||
}
|
||||
|
||||
static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
|
||||
__be16 port, char *name1, char *name2)
|
||||
/**
|
||||
* avc_audit_post_callback - SELinux specific information
|
||||
* will be called by generic audit code
|
||||
* @ab: the audit buffer
|
||||
* @a: audit_data
|
||||
*/
|
||||
static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
|
||||
{
|
||||
if (addr)
|
||||
audit_log_format(ab, " %s=%pI4", name1, &addr);
|
||||
if (port)
|
||||
audit_log_format(ab, " %s=%d", name2, ntohs(port));
|
||||
struct common_audit_data *ad = a;
|
||||
audit_log_format(ab, " ");
|
||||
avc_dump_query(ab, ad->selinux_audit_data.ssid,
|
||||
ad->selinux_audit_data.tsid,
|
||||
ad->selinux_audit_data.tclass);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -532,13 +544,10 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
|
||||
*/
|
||||
void avc_audit(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct av_decision *avd, int result, struct avc_audit_data *a)
|
||||
struct av_decision *avd, int result, struct common_audit_data *a)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct inode *inode = NULL;
|
||||
struct common_audit_data stack_data;
|
||||
u32 denied, audited;
|
||||
struct audit_buffer *ab;
|
||||
|
||||
denied = requested & ~avd->allowed;
|
||||
if (denied) {
|
||||
audited = denied;
|
||||
@ -551,144 +560,20 @@ void avc_audit(u32 ssid, u32 tsid,
|
||||
if (!(audited & avd->auditallow))
|
||||
return;
|
||||
}
|
||||
|
||||
ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC);
|
||||
if (!ab)
|
||||
return; /* audit_panic has been called */
|
||||
audit_log_format(ab, "avc: %s ", denied ? "denied" : "granted");
|
||||
avc_dump_av(ab, tclass, audited);
|
||||
audit_log_format(ab, " for ");
|
||||
if (a && a->tsk)
|
||||
tsk = a->tsk;
|
||||
if (tsk && tsk->pid) {
|
||||
audit_log_format(ab, " pid=%d comm=", tsk->pid);
|
||||
audit_log_untrustedstring(ab, tsk->comm);
|
||||
if (!a) {
|
||||
a = &stack_data;
|
||||
memset(a, 0, sizeof(*a));
|
||||
a->type = LSM_AUDIT_NO_AUDIT;
|
||||
}
|
||||
if (a) {
|
||||
switch (a->type) {
|
||||
case AVC_AUDIT_DATA_IPC:
|
||||
audit_log_format(ab, " key=%d", a->u.ipc_id);
|
||||
break;
|
||||
case AVC_AUDIT_DATA_CAP:
|
||||
audit_log_format(ab, " capability=%d", a->u.cap);
|
||||
break;
|
||||
case AVC_AUDIT_DATA_FS:
|
||||
if (a->u.fs.path.dentry) {
|
||||
struct dentry *dentry = a->u.fs.path.dentry;
|
||||
if (a->u.fs.path.mnt) {
|
||||
audit_log_d_path(ab, "path=",
|
||||
&a->u.fs.path);
|
||||
} else {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab, dentry->d_name.name);
|
||||
}
|
||||
inode = dentry->d_inode;
|
||||
} else if (a->u.fs.inode) {
|
||||
struct dentry *dentry;
|
||||
inode = a->u.fs.inode;
|
||||
dentry = d_find_alias(inode);
|
||||
if (dentry) {
|
||||
audit_log_format(ab, " name=");
|
||||
audit_log_untrustedstring(ab, dentry->d_name.name);
|
||||
dput(dentry);
|
||||
}
|
||||
}
|
||||
if (inode)
|
||||
audit_log_format(ab, " dev=%s ino=%lu",
|
||||
inode->i_sb->s_id,
|
||||
inode->i_ino);
|
||||
break;
|
||||
case AVC_AUDIT_DATA_NET:
|
||||
if (a->u.net.sk) {
|
||||
struct sock *sk = a->u.net.sk;
|
||||
struct unix_sock *u;
|
||||
int len = 0;
|
||||
char *p = NULL;
|
||||
|
||||
switch (sk->sk_family) {
|
||||
case AF_INET: {
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
|
||||
avc_print_ipv4_addr(ab, inet->rcv_saddr,
|
||||
inet->sport,
|
||||
"laddr", "lport");
|
||||
avc_print_ipv4_addr(ab, inet->daddr,
|
||||
inet->dport,
|
||||
"faddr", "fport");
|
||||
break;
|
||||
}
|
||||
case AF_INET6: {
|
||||
struct inet_sock *inet = inet_sk(sk);
|
||||
struct ipv6_pinfo *inet6 = inet6_sk(sk);
|
||||
|
||||
avc_print_ipv6_addr(ab, &inet6->rcv_saddr,
|
||||
inet->sport,
|
||||
"laddr", "lport");
|
||||
avc_print_ipv6_addr(ab, &inet6->daddr,
|
||||
inet->dport,
|
||||
"faddr", "fport");
|
||||
break;
|
||||
}
|
||||
case AF_UNIX:
|
||||
u = unix_sk(sk);
|
||||
if (u->dentry) {
|
||||
struct path path = {
|
||||
.dentry = u->dentry,
|
||||
.mnt = u->mnt
|
||||
};
|
||||
audit_log_d_path(ab, "path=",
|
||||
&path);
|
||||
break;
|
||||
}
|
||||
if (!u->addr)
|
||||
break;
|
||||
len = u->addr->len-sizeof(short);
|
||||
p = &u->addr->name->sun_path[0];
|
||||
audit_log_format(ab, " path=");
|
||||
if (*p)
|
||||
audit_log_untrustedstring(ab, p);
|
||||
else
|
||||
audit_log_n_hex(ab, p, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (a->u.net.family) {
|
||||
case AF_INET:
|
||||
avc_print_ipv4_addr(ab, a->u.net.v4info.saddr,
|
||||
a->u.net.sport,
|
||||
"saddr", "src");
|
||||
avc_print_ipv4_addr(ab, a->u.net.v4info.daddr,
|
||||
a->u.net.dport,
|
||||
"daddr", "dest");
|
||||
break;
|
||||
case AF_INET6:
|
||||
avc_print_ipv6_addr(ab, &a->u.net.v6info.saddr,
|
||||
a->u.net.sport,
|
||||
"saddr", "src");
|
||||
avc_print_ipv6_addr(ab, &a->u.net.v6info.daddr,
|
||||
a->u.net.dport,
|
||||
"daddr", "dest");
|
||||
break;
|
||||
}
|
||||
if (a->u.net.netif > 0) {
|
||||
struct net_device *dev;
|
||||
|
||||
/* NOTE: we always use init's namespace */
|
||||
dev = dev_get_by_index(&init_net,
|
||||
a->u.net.netif);
|
||||
if (dev) {
|
||||
audit_log_format(ab, " netif=%s",
|
||||
dev->name);
|
||||
dev_put(dev);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
audit_log_format(ab, " ");
|
||||
avc_dump_query(ab, ssid, tsid, tclass);
|
||||
audit_log_end(ab);
|
||||
a->selinux_audit_data.tclass = tclass;
|
||||
a->selinux_audit_data.requested = requested;
|
||||
a->selinux_audit_data.ssid = ssid;
|
||||
a->selinux_audit_data.tsid = tsid;
|
||||
a->selinux_audit_data.audited = audited;
|
||||
a->selinux_audit_data.denied = denied;
|
||||
a->lsm_pre_audit = avc_audit_pre_callback;
|
||||
a->lsm_post_audit = avc_audit_post_callback;
|
||||
common_lsm_audit(a);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -956,7 +841,7 @@ out:
|
||||
* another -errno upon other errors.
|
||||
*/
|
||||
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass,
|
||||
u32 requested, struct avc_audit_data *auditdata)
|
||||
u32 requested, struct common_audit_data *auditdata)
|
||||
{
|
||||
struct av_decision avd;
|
||||
int rc;
|
||||
@ -970,3 +855,9 @@ u32 avc_policy_seqno(void)
|
||||
{
|
||||
return avc_cache.latest_notif;
|
||||
}
|
||||
|
||||
void avc_disable(void)
|
||||
{
|
||||
if (avc_node_cachep)
|
||||
kmem_cache_destroy(avc_node_cachep);
|
||||
}
|
||||
|
@ -13,8 +13,8 @@
|
||||
* Eric Paris <eparis@redhat.com>
|
||||
* Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
|
||||
* <dgoeddel@trustedcs.com>
|
||||
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
|
||||
* Paul Moore <paul.moore@hp.com>
|
||||
* Copyright (C) 2006, 2007, 2009 Hewlett-Packard Development Company, L.P.
|
||||
* Paul Moore <paul.moore@hp.com>
|
||||
* Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
|
||||
* Yuichi Nakamura <ynakam@hitachisoft.jp>
|
||||
*
|
||||
@ -448,6 +448,10 @@ static int sb_finish_set_opts(struct super_block *sb)
|
||||
sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
|
||||
sbsec->flags &= ~SE_SBLABELSUPP;
|
||||
|
||||
/* Special handling for sysfs. Is genfs but also has setxattr handler*/
|
||||
if (strncmp(sb->s_type->name, "sysfs", sizeof("sysfs")) == 0)
|
||||
sbsec->flags |= SE_SBLABELSUPP;
|
||||
|
||||
/* Initialize the root inode. */
|
||||
rc = inode_doinit_with_dentry(root_inode, root);
|
||||
|
||||
@ -1479,14 +1483,14 @@ static int task_has_capability(struct task_struct *tsk,
|
||||
const struct cred *cred,
|
||||
int cap, int audit)
|
||||
{
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
struct av_decision avd;
|
||||
u16 sclass;
|
||||
u32 sid = cred_sid(cred);
|
||||
u32 av = CAP_TO_MASK(cap);
|
||||
int rc;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, CAP);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, CAP);
|
||||
ad.tsk = tsk;
|
||||
ad.u.cap = cap;
|
||||
|
||||
@ -1525,12 +1529,14 @@ static int task_has_system(struct task_struct *tsk,
|
||||
static int inode_has_perm(const struct cred *cred,
|
||||
struct inode *inode,
|
||||
u32 perms,
|
||||
struct avc_audit_data *adp)
|
||||
struct common_audit_data *adp)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid;
|
||||
|
||||
validate_creds(cred);
|
||||
|
||||
if (unlikely(IS_PRIVATE(inode)))
|
||||
return 0;
|
||||
|
||||
@ -1539,7 +1545,7 @@ static int inode_has_perm(const struct cred *cred,
|
||||
|
||||
if (!adp) {
|
||||
adp = &ad;
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.inode = inode;
|
||||
}
|
||||
|
||||
@ -1555,9 +1561,9 @@ static inline int dentry_has_perm(const struct cred *cred,
|
||||
u32 av)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.mnt = mnt;
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
return inode_has_perm(cred, inode, av, &ad);
|
||||
@ -1577,11 +1583,11 @@ static int file_has_perm(const struct cred *cred,
|
||||
{
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = cred_sid(cred);
|
||||
int rc;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path = file->f_path;
|
||||
|
||||
if (sid != fsec->sid) {
|
||||
@ -1612,7 +1618,7 @@ static int may_create(struct inode *dir,
|
||||
struct inode_security_struct *dsec;
|
||||
struct superblock_security_struct *sbsec;
|
||||
u32 sid, newsid;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
dsec = dir->i_security;
|
||||
@ -1621,7 +1627,7 @@ static int may_create(struct inode *dir,
|
||||
sid = tsec->sid;
|
||||
newsid = tsec->create_sid;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
|
||||
rc = avc_has_perm(sid, dsec->sid, SECCLASS_DIR,
|
||||
@ -1665,7 +1671,7 @@ static int may_link(struct inode *dir,
|
||||
|
||||
{
|
||||
struct inode_security_struct *dsec, *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
u32 av;
|
||||
int rc;
|
||||
@ -1673,7 +1679,7 @@ static int may_link(struct inode *dir,
|
||||
dsec = dir->i_security;
|
||||
isec = dentry->d_inode->i_security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
|
||||
av = DIR__SEARCH;
|
||||
@ -1708,7 +1714,7 @@ static inline int may_rename(struct inode *old_dir,
|
||||
struct dentry *new_dentry)
|
||||
{
|
||||
struct inode_security_struct *old_dsec, *new_dsec, *old_isec, *new_isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
u32 av;
|
||||
int old_is_dir, new_is_dir;
|
||||
@ -1719,7 +1725,7 @@ static inline int may_rename(struct inode *old_dir,
|
||||
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
|
||||
new_dsec = new_dir->i_security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
|
||||
ad.u.fs.path.dentry = old_dentry;
|
||||
rc = avc_has_perm(sid, old_dsec->sid, SECCLASS_DIR,
|
||||
@ -1761,7 +1767,7 @@ static inline int may_rename(struct inode *old_dir,
|
||||
static int superblock_has_perm(const struct cred *cred,
|
||||
struct super_block *sb,
|
||||
u32 perms,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
struct superblock_security_struct *sbsec;
|
||||
u32 sid = cred_sid(cred);
|
||||
@ -1855,12 +1861,12 @@ static inline u32 open_file_to_av(struct file *file)
|
||||
|
||||
/* Hook functions begin here. */
|
||||
|
||||
static int selinux_ptrace_may_access(struct task_struct *child,
|
||||
static int selinux_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = cap_ptrace_may_access(child, mode);
|
||||
rc = cap_ptrace_access_check(child, mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -2101,7 +2107,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||
const struct task_security_struct *old_tsec;
|
||||
struct task_security_struct *new_tsec;
|
||||
struct inode_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
struct inode *inode = bprm->file->f_path.dentry->d_inode;
|
||||
int rc;
|
||||
|
||||
@ -2139,7 +2145,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||
return rc;
|
||||
}
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path = bprm->file->f_path;
|
||||
|
||||
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
|
||||
@ -2232,7 +2238,7 @@ extern struct dentry *selinux_null;
|
||||
static inline void flush_unauthorized_files(const struct cred *cred,
|
||||
struct files_struct *files)
|
||||
{
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
struct file *file, *devnull = NULL;
|
||||
struct tty_struct *tty;
|
||||
struct fdtable *fdt;
|
||||
@ -2266,7 +2272,7 @@ static inline void flush_unauthorized_files(const struct cred *cred,
|
||||
|
||||
/* Revalidate access to inherited open files. */
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
|
||||
spin_lock(&files->file_lock);
|
||||
for (;;) {
|
||||
@ -2515,7 +2521,7 @@ out:
|
||||
static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
rc = superblock_doinit(sb, data);
|
||||
@ -2526,7 +2532,7 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
if (flags & MS_KERNMOUNT)
|
||||
return 0;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = sb->s_root;
|
||||
return superblock_has_perm(cred, sb, FILESYSTEM__MOUNT, &ad);
|
||||
}
|
||||
@ -2534,9 +2540,9 @@ static int selinux_sb_kern_mount(struct super_block *sb, int flags, void *data)
|
||||
static int selinux_sb_statfs(struct dentry *dentry)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry->d_sb->s_root;
|
||||
return superblock_has_perm(cred, dentry->d_sb, FILESYSTEM__GETATTR, &ad);
|
||||
}
|
||||
@ -2711,12 +2717,18 @@ static int selinux_inode_permission(struct inode *inode, int mask)
|
||||
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
unsigned int ia_valid = iattr->ia_valid;
|
||||
|
||||
if (iattr->ia_valid & ATTR_FORCE)
|
||||
return 0;
|
||||
/* ATTR_FORCE is just used for ATTR_KILL_S[UG]ID. */
|
||||
if (ia_valid & ATTR_FORCE) {
|
||||
ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE |
|
||||
ATTR_FORCE);
|
||||
if (!ia_valid)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (iattr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_ATIME_SET | ATTR_MTIME_SET))
|
||||
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
|
||||
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__SETATTR);
|
||||
|
||||
return dentry_has_perm(cred, NULL, dentry, FILE__WRITE);
|
||||
@ -2756,7 +2768,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
struct superblock_security_struct *sbsec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 newsid, sid = current_sid();
|
||||
int rc = 0;
|
||||
|
||||
@ -2770,7 +2782,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
|
||||
if (!is_owner_or_cap(inode))
|
||||
return -EPERM;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, FS);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, FS);
|
||||
ad.u.fs.path.dentry = dentry;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid, isec->sclass,
|
||||
@ -2915,6 +2927,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
|
||||
return rc;
|
||||
|
||||
isec->sid = newsid;
|
||||
isec->initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -2939,11 +2952,6 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
|
||||
const struct cred *cred = current_cred();
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
|
||||
if (!mask) {
|
||||
/* No permission to check. Existence test. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* file_mask_to_av won't add FILE__WRITE if MAY_APPEND is set */
|
||||
if ((file->f_flags & O_APPEND) && (mask & MAY_WRITE))
|
||||
mask |= MAY_APPEND;
|
||||
@ -2954,10 +2962,20 @@ static int selinux_revalidate_file_permission(struct file *file, int mask)
|
||||
|
||||
static int selinux_file_permission(struct file *file, int mask)
|
||||
{
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct file_security_struct *fsec = file->f_security;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
u32 sid = current_sid();
|
||||
|
||||
if (!mask)
|
||||
/* No permission to check. Existence test. */
|
||||
return 0;
|
||||
|
||||
if (sid == fsec->sid && fsec->isid == isec->sid &&
|
||||
fsec->pseqno == avc_policy_seqno())
|
||||
/* No change since dentry_open check. */
|
||||
return 0;
|
||||
|
||||
return selinux_revalidate_file_permission(file, mask);
|
||||
}
|
||||
|
||||
@ -3219,13 +3237,30 @@ static int selinux_task_create(unsigned long clone_flags)
|
||||
return current_has_perm(current, PROCESS__FORK);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate the SELinux part of blank credentials
|
||||
*/
|
||||
static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
struct task_security_struct *tsec;
|
||||
|
||||
tsec = kzalloc(sizeof(struct task_security_struct), gfp);
|
||||
if (!tsec)
|
||||
return -ENOMEM;
|
||||
|
||||
cred->security = tsec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* detach and free the LSM part of a set of credentials
|
||||
*/
|
||||
static void selinux_cred_free(struct cred *cred)
|
||||
{
|
||||
struct task_security_struct *tsec = cred->security;
|
||||
cred->security = NULL;
|
||||
|
||||
BUG_ON((unsigned long) cred->security < PAGE_SIZE);
|
||||
cred->security = (void *) 0x7UL;
|
||||
kfree(tsec);
|
||||
}
|
||||
|
||||
@ -3248,6 +3283,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* transfer the SELinux data to a blank set of creds
|
||||
*/
|
||||
static void selinux_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
const struct task_security_struct *old_tsec = old->security;
|
||||
struct task_security_struct *tsec = new->security;
|
||||
|
||||
*tsec = *old_tsec;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the security data for a kernel service
|
||||
* - all the creation contexts are set to unlabelled
|
||||
@ -3292,6 +3338,11 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_kernel_module_request(void)
|
||||
{
|
||||
return task_has_system(current, SYSTEM__MODULE_REQUEST);
|
||||
}
|
||||
|
||||
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
|
||||
{
|
||||
return current_has_perm(p, PROCESS__SETPGID);
|
||||
@ -3409,7 +3460,7 @@ static void selinux_task_to_inode(struct task_struct *p,
|
||||
|
||||
/* Returns error only if unable to parse addresses */
|
||||
static int selinux_parse_skb_ipv4(struct sk_buff *skb,
|
||||
struct avc_audit_data *ad, u8 *proto)
|
||||
struct common_audit_data *ad, u8 *proto)
|
||||
{
|
||||
int offset, ihlen, ret = -EINVAL;
|
||||
struct iphdr _iph, *ih;
|
||||
@ -3490,7 +3541,7 @@ out:
|
||||
|
||||
/* Returns error only if unable to parse addresses */
|
||||
static int selinux_parse_skb_ipv6(struct sk_buff *skb,
|
||||
struct avc_audit_data *ad, u8 *proto)
|
||||
struct common_audit_data *ad, u8 *proto)
|
||||
{
|
||||
u8 nexthdr;
|
||||
int ret = -EINVAL, offset;
|
||||
@ -3561,7 +3612,7 @@ out:
|
||||
|
||||
#endif /* IPV6 */
|
||||
|
||||
static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad,
|
||||
static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad,
|
||||
char **_addrp, int src, u8 *proto)
|
||||
{
|
||||
char *addrp;
|
||||
@ -3643,7 +3694,7 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
|
||||
u32 perms)
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid;
|
||||
int err = 0;
|
||||
|
||||
@ -3653,7 +3704,7 @@ static int socket_has_perm(struct task_struct *task, struct socket *sock,
|
||||
goto out;
|
||||
sid = task_sid(task);
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.sk = sock->sk;
|
||||
err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
|
||||
|
||||
@ -3740,7 +3791,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
|
||||
if (family == PF_INET || family == PF_INET6) {
|
||||
char *addrp;
|
||||
struct inode_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
struct sockaddr_in *addr4 = NULL;
|
||||
struct sockaddr_in6 *addr6 = NULL;
|
||||
unsigned short snum;
|
||||
@ -3769,7 +3820,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
|
||||
snum, &sid);
|
||||
if (err)
|
||||
goto out;
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.sport = htons(snum);
|
||||
ad.u.net.family = family;
|
||||
err = avc_has_perm(isec->sid, sid,
|
||||
@ -3802,7 +3853,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.sport = htons(snum);
|
||||
ad.u.net.family = family;
|
||||
|
||||
@ -3836,7 +3887,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
|
||||
isec = SOCK_INODE(sock)->i_security;
|
||||
if (isec->sclass == SECCLASS_TCP_SOCKET ||
|
||||
isec->sclass == SECCLASS_DCCP_SOCKET) {
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
struct sockaddr_in *addr4 = NULL;
|
||||
struct sockaddr_in6 *addr6 = NULL;
|
||||
unsigned short snum;
|
||||
@ -3861,7 +3912,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
|
||||
perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
|
||||
TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.dport = htons(snum);
|
||||
ad.u.net.family = sk->sk_family;
|
||||
err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
|
||||
@ -3951,13 +4002,13 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
|
||||
struct sk_security_struct *ssec;
|
||||
struct inode_security_struct *isec;
|
||||
struct inode_security_struct *other_isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
int err;
|
||||
|
||||
isec = SOCK_INODE(sock)->i_security;
|
||||
other_isec = SOCK_INODE(other)->i_security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.sk = other->sk;
|
||||
|
||||
err = avc_has_perm(isec->sid, other_isec->sid,
|
||||
@ -3983,13 +4034,13 @@ static int selinux_socket_unix_may_send(struct socket *sock,
|
||||
{
|
||||
struct inode_security_struct *isec;
|
||||
struct inode_security_struct *other_isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
int err;
|
||||
|
||||
isec = SOCK_INODE(sock)->i_security;
|
||||
other_isec = SOCK_INODE(other)->i_security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.sk = other->sk;
|
||||
|
||||
err = avc_has_perm(isec->sid, other_isec->sid,
|
||||
@ -4002,7 +4053,7 @@ static int selinux_socket_unix_may_send(struct socket *sock,
|
||||
|
||||
static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
|
||||
u32 peer_sid,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
int err;
|
||||
u32 if_sid;
|
||||
@ -4030,10 +4081,10 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb,
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
u32 peer_sid;
|
||||
u32 sk_sid = sksec->sid;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
char *addrp;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.netif = skb->iif;
|
||||
ad.u.net.family = family;
|
||||
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
|
||||
@ -4071,7 +4122,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
u16 family = sk->sk_family;
|
||||
u32 sk_sid = sksec->sid;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
char *addrp;
|
||||
u8 secmark_active;
|
||||
u8 peerlbl_active;
|
||||
@ -4095,7 +4146,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
|
||||
if (!secmark_active && !peerlbl_active)
|
||||
return 0;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.netif = skb->iif;
|
||||
ad.u.net.family = family;
|
||||
err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL);
|
||||
@ -4309,6 +4360,59 @@ static void selinux_req_classify_flow(const struct request_sock *req,
|
||||
fl->secid = req->secid;
|
||||
}
|
||||
|
||||
static int selinux_tun_dev_create(void)
|
||||
{
|
||||
u32 sid = current_sid();
|
||||
|
||||
/* we aren't taking into account the "sockcreate" SID since the socket
|
||||
* that is being created here is not a socket in the traditional sense,
|
||||
* instead it is a private sock, accessible only to the kernel, and
|
||||
* representing a wide range of network traffic spanning multiple
|
||||
* connections unlike traditional sockets - check the TUN driver to
|
||||
* get a better understanding of why this socket is special */
|
||||
|
||||
return avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET, TUN_SOCKET__CREATE,
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void selinux_tun_dev_post_create(struct sock *sk)
|
||||
{
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
|
||||
/* we don't currently perform any NetLabel based labeling here and it
|
||||
* isn't clear that we would want to do so anyway; while we could apply
|
||||
* labeling without the support of the TUN user the resulting labeled
|
||||
* traffic from the other end of the connection would almost certainly
|
||||
* cause confusion to the TUN user that had no idea network labeling
|
||||
* protocols were being used */
|
||||
|
||||
/* see the comments in selinux_tun_dev_create() about why we don't use
|
||||
* the sockcreate SID here */
|
||||
|
||||
sksec->sid = current_sid();
|
||||
sksec->sclass = SECCLASS_TUN_SOCKET;
|
||||
}
|
||||
|
||||
static int selinux_tun_dev_attach(struct sock *sk)
|
||||
{
|
||||
struct sk_security_struct *sksec = sk->sk_security;
|
||||
u32 sid = current_sid();
|
||||
int err;
|
||||
|
||||
err = avc_has_perm(sid, sksec->sid, SECCLASS_TUN_SOCKET,
|
||||
TUN_SOCKET__RELABELFROM, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
err = avc_has_perm(sid, sid, SECCLASS_TUN_SOCKET,
|
||||
TUN_SOCKET__RELABELTO, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
sksec->sid = sid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
int err = 0;
|
||||
@ -4353,7 +4457,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
|
||||
int err;
|
||||
char *addrp;
|
||||
u32 peer_sid;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u8 secmark_active;
|
||||
u8 netlbl_active;
|
||||
u8 peerlbl_active;
|
||||
@ -4370,7 +4474,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
|
||||
if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
|
||||
return NF_DROP;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.netif = ifindex;
|
||||
ad.u.net.family = family;
|
||||
if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
|
||||
@ -4458,7 +4562,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
|
||||
{
|
||||
struct sock *sk = skb->sk;
|
||||
struct sk_security_struct *sksec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
char *addrp;
|
||||
u8 proto;
|
||||
|
||||
@ -4466,7 +4570,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
|
||||
return NF_ACCEPT;
|
||||
sksec = sk->sk_security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.netif = ifindex;
|
||||
ad.u.net.family = family;
|
||||
if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto))
|
||||
@ -4490,7 +4594,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
|
||||
u32 secmark_perm;
|
||||
u32 peer_sid;
|
||||
struct sock *sk;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
char *addrp;
|
||||
u8 secmark_active;
|
||||
u8 peerlbl_active;
|
||||
@ -4549,7 +4653,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
|
||||
secmark_perm = PACKET__SEND;
|
||||
}
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, NET);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, NET);
|
||||
ad.u.net.netif = ifindex;
|
||||
ad.u.net.family = family;
|
||||
if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL))
|
||||
@ -4619,13 +4723,13 @@ static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
|
||||
static int selinux_netlink_recv(struct sk_buff *skb, int capability)
|
||||
{
|
||||
int err;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
|
||||
err = cap_netlink_recv(skb, capability);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, CAP);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, CAP);
|
||||
ad.u.cap = capability;
|
||||
|
||||
return avc_has_perm(NETLINK_CB(skb).sid, NETLINK_CB(skb).sid,
|
||||
@ -4684,12 +4788,12 @@ static int ipc_has_perm(struct kern_ipc_perm *ipc_perms,
|
||||
u32 perms)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = ipc_perms->security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = ipc_perms->key;
|
||||
|
||||
return avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
|
||||
@ -4709,7 +4813,7 @@ static void selinux_msg_msg_free_security(struct msg_msg *msg)
|
||||
static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
@ -4719,7 +4823,7 @@ static int selinux_msg_queue_alloc_security(struct msg_queue *msq)
|
||||
|
||||
isec = msq->q_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = msq->q_perm.key;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
|
||||
@ -4739,12 +4843,12 @@ static void selinux_msg_queue_free_security(struct msg_queue *msq)
|
||||
static int selinux_msg_queue_associate(struct msg_queue *msq, int msqflg)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = msq->q_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = msq->q_perm.key;
|
||||
|
||||
return avc_has_perm(sid, isec->sid, SECCLASS_MSGQ,
|
||||
@ -4783,7 +4887,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct msg_security_struct *msec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
@ -4804,7 +4908,7 @@ static int selinux_msg_queue_msgsnd(struct msg_queue *msq, struct msg_msg *msg,
|
||||
return rc;
|
||||
}
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = msq->q_perm.key;
|
||||
|
||||
/* Can this process write to the queue? */
|
||||
@ -4828,14 +4932,14 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct msg_security_struct *msec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = task_sid(target);
|
||||
int rc;
|
||||
|
||||
isec = msq->q_perm.security;
|
||||
msec = msg->security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = msq->q_perm.key;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid,
|
||||
@ -4850,7 +4954,7 @@ static int selinux_msg_queue_msgrcv(struct msg_queue *msq, struct msg_msg *msg,
|
||||
static int selinux_shm_alloc_security(struct shmid_kernel *shp)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
@ -4860,7 +4964,7 @@ static int selinux_shm_alloc_security(struct shmid_kernel *shp)
|
||||
|
||||
isec = shp->shm_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = shp->shm_perm.key;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid, SECCLASS_SHM,
|
||||
@ -4880,12 +4984,12 @@ static void selinux_shm_free_security(struct shmid_kernel *shp)
|
||||
static int selinux_shm_associate(struct shmid_kernel *shp, int shmflg)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = shp->shm_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = shp->shm_perm.key;
|
||||
|
||||
return avc_has_perm(sid, isec->sid, SECCLASS_SHM,
|
||||
@ -4942,7 +5046,7 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
|
||||
static int selinux_sem_alloc_security(struct sem_array *sma)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
int rc;
|
||||
|
||||
@ -4952,7 +5056,7 @@ static int selinux_sem_alloc_security(struct sem_array *sma)
|
||||
|
||||
isec = sma->sem_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = sma->sem_perm.key;
|
||||
|
||||
rc = avc_has_perm(sid, isec->sid, SECCLASS_SEM,
|
||||
@ -4972,12 +5076,12 @@ static void selinux_sem_free_security(struct sem_array *sma)
|
||||
static int selinux_sem_associate(struct sem_array *sma, int semflg)
|
||||
{
|
||||
struct ipc_security_struct *isec;
|
||||
struct avc_audit_data ad;
|
||||
struct common_audit_data ad;
|
||||
u32 sid = current_sid();
|
||||
|
||||
isec = sma->sem_perm.security;
|
||||
|
||||
AVC_AUDIT_DATA_INIT(&ad, IPC);
|
||||
COMMON_AUDIT_DATA_INIT(&ad, IPC);
|
||||
ad.u.ipc_id = sma->sem_perm.key;
|
||||
|
||||
return avc_has_perm(sid, isec->sid, SECCLASS_SEM,
|
||||
@ -5195,7 +5299,7 @@ static int selinux_setprocattr(struct task_struct *p,
|
||||
|
||||
/* Only allow single threaded processes to change context */
|
||||
error = -EPERM;
|
||||
if (!is_single_threaded(p)) {
|
||||
if (!current_is_single_threaded()) {
|
||||
error = security_bounded_transition(tsec->sid, sid);
|
||||
if (error)
|
||||
goto abort_change;
|
||||
@ -5252,6 +5356,32 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
|
||||
kfree(secdata);
|
||||
}
|
||||
|
||||
/*
|
||||
* called with inode->i_mutex locked
|
||||
*/
|
||||
static int selinux_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return selinux_inode_setsecurity(inode, XATTR_SELINUX_SUFFIX, ctx, ctxlen, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* called with inode->i_mutex locked
|
||||
*/
|
||||
static int selinux_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_SELINUX, ctx, ctxlen, 0);
|
||||
}
|
||||
|
||||
static int selinux_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
{
|
||||
int len = 0;
|
||||
len = selinux_inode_getsecurity(inode, XATTR_SELINUX_SUFFIX,
|
||||
ctx, true);
|
||||
if (len < 0)
|
||||
return len;
|
||||
*ctxlen = len;
|
||||
return 0;
|
||||
}
|
||||
#ifdef CONFIG_KEYS
|
||||
|
||||
static int selinux_key_alloc(struct key *k, const struct cred *cred,
|
||||
@ -5323,7 +5453,7 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer)
|
||||
static struct security_operations selinux_ops = {
|
||||
.name = "selinux",
|
||||
|
||||
.ptrace_may_access = selinux_ptrace_may_access,
|
||||
.ptrace_access_check = selinux_ptrace_access_check,
|
||||
.ptrace_traceme = selinux_ptrace_traceme,
|
||||
.capget = selinux_capget,
|
||||
.capset = selinux_capset,
|
||||
@ -5396,10 +5526,13 @@ static struct security_operations selinux_ops = {
|
||||
.dentry_open = selinux_dentry_open,
|
||||
|
||||
.task_create = selinux_task_create,
|
||||
.cred_alloc_blank = selinux_cred_alloc_blank,
|
||||
.cred_free = selinux_cred_free,
|
||||
.cred_prepare = selinux_cred_prepare,
|
||||
.cred_transfer = selinux_cred_transfer,
|
||||
.kernel_act_as = selinux_kernel_act_as,
|
||||
.kernel_create_files_as = selinux_kernel_create_files_as,
|
||||
.kernel_module_request = selinux_kernel_module_request,
|
||||
.task_setpgid = selinux_task_setpgid,
|
||||
.task_getpgid = selinux_task_getpgid,
|
||||
.task_getsid = selinux_task_getsid,
|
||||
@ -5448,6 +5581,9 @@ static struct security_operations selinux_ops = {
|
||||
.secid_to_secctx = selinux_secid_to_secctx,
|
||||
.secctx_to_secid = selinux_secctx_to_secid,
|
||||
.release_secctx = selinux_release_secctx,
|
||||
.inode_notifysecctx = selinux_inode_notifysecctx,
|
||||
.inode_setsecctx = selinux_inode_setsecctx,
|
||||
.inode_getsecctx = selinux_inode_getsecctx,
|
||||
|
||||
.unix_stream_connect = selinux_socket_unix_stream_connect,
|
||||
.unix_may_send = selinux_socket_unix_may_send,
|
||||
@ -5477,6 +5613,9 @@ static struct security_operations selinux_ops = {
|
||||
.inet_csk_clone = selinux_inet_csk_clone,
|
||||
.inet_conn_established = selinux_inet_conn_established,
|
||||
.req_classify_flow = selinux_req_classify_flow,
|
||||
.tun_dev_create = selinux_tun_dev_create,
|
||||
.tun_dev_post_create = selinux_tun_dev_post_create,
|
||||
.tun_dev_attach = selinux_tun_dev_attach,
|
||||
|
||||
#ifdef CONFIG_SECURITY_NETWORK_XFRM
|
||||
.xfrm_policy_alloc_security = selinux_xfrm_policy_alloc,
|
||||
@ -5691,6 +5830,9 @@ int selinux_disable(void)
|
||||
selinux_disabled = 1;
|
||||
selinux_enabled = 0;
|
||||
|
||||
/* Try to destroy the avc node cache */
|
||||
avc_disable();
|
||||
|
||||
/* Reset security_ops to the secondary module, dummy or capability. */
|
||||
security_ops = secondary_ops;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
|
||||
S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
|
||||
S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
|
||||
S_(SECCLASS_TUN_SOCKET, socket, 0x00400000UL)
|
||||
S_(SECCLASS_IPC, ipc, 0x00000200UL)
|
||||
S_(SECCLASS_SEM, ipc, 0x00000200UL)
|
||||
S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
|
||||
|
@ -107,6 +107,7 @@
|
||||
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
|
||||
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
|
||||
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console")
|
||||
S_(SECCLASS_SYSTEM, SYSTEM__MODULE_REQUEST, "module_request")
|
||||
S_(SECCLASS_CAPABILITY, CAPABILITY__CHOWN, "chown")
|
||||
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
|
||||
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
|
||||
|
@ -423,6 +423,28 @@
|
||||
#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
|
||||
#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
|
||||
#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL
|
||||
#define TUN_SOCKET__IOCTL 0x00000001UL
|
||||
#define TUN_SOCKET__READ 0x00000002UL
|
||||
#define TUN_SOCKET__WRITE 0x00000004UL
|
||||
#define TUN_SOCKET__CREATE 0x00000008UL
|
||||
#define TUN_SOCKET__GETATTR 0x00000010UL
|
||||
#define TUN_SOCKET__SETATTR 0x00000020UL
|
||||
#define TUN_SOCKET__LOCK 0x00000040UL
|
||||
#define TUN_SOCKET__RELABELFROM 0x00000080UL
|
||||
#define TUN_SOCKET__RELABELTO 0x00000100UL
|
||||
#define TUN_SOCKET__APPEND 0x00000200UL
|
||||
#define TUN_SOCKET__BIND 0x00000400UL
|
||||
#define TUN_SOCKET__CONNECT 0x00000800UL
|
||||
#define TUN_SOCKET__LISTEN 0x00001000UL
|
||||
#define TUN_SOCKET__ACCEPT 0x00002000UL
|
||||
#define TUN_SOCKET__GETOPT 0x00004000UL
|
||||
#define TUN_SOCKET__SETOPT 0x00008000UL
|
||||
#define TUN_SOCKET__SHUTDOWN 0x00010000UL
|
||||
#define TUN_SOCKET__RECVFROM 0x00020000UL
|
||||
#define TUN_SOCKET__SENDTO 0x00040000UL
|
||||
#define TUN_SOCKET__RECV_MSG 0x00080000UL
|
||||
#define TUN_SOCKET__SEND_MSG 0x00100000UL
|
||||
#define TUN_SOCKET__NAME_BIND 0x00200000UL
|
||||
#define PROCESS__FORK 0x00000001UL
|
||||
#define PROCESS__TRANSITION 0x00000002UL
|
||||
#define PROCESS__SIGCHLD 0x00000004UL
|
||||
@ -508,6 +530,7 @@
|
||||
#define SYSTEM__SYSLOG_READ 0x00000002UL
|
||||
#define SYSTEM__SYSLOG_MOD 0x00000004UL
|
||||
#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
|
||||
#define SYSTEM__MODULE_REQUEST 0x00000010UL
|
||||
#define CAPABILITY__CHOWN 0x00000001UL
|
||||
#define CAPABILITY__DAC_OVERRIDE 0x00000002UL
|
||||
#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/audit.h>
|
||||
#include <linux/lsm_audit.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/path.h>
|
||||
#include <asm/system.h>
|
||||
@ -36,48 +37,6 @@ struct inode;
|
||||
struct sock;
|
||||
struct sk_buff;
|
||||
|
||||
/* Auxiliary data to use in generating the audit record. */
|
||||
struct avc_audit_data {
|
||||
char type;
|
||||
#define AVC_AUDIT_DATA_FS 1
|
||||
#define AVC_AUDIT_DATA_NET 2
|
||||
#define AVC_AUDIT_DATA_CAP 3
|
||||
#define AVC_AUDIT_DATA_IPC 4
|
||||
struct task_struct *tsk;
|
||||
union {
|
||||
struct {
|
||||
struct path path;
|
||||
struct inode *inode;
|
||||
} fs;
|
||||
struct {
|
||||
int netif;
|
||||
struct sock *sk;
|
||||
u16 family;
|
||||
__be16 dport;
|
||||
__be16 sport;
|
||||
union {
|
||||
struct {
|
||||
__be32 daddr;
|
||||
__be32 saddr;
|
||||
} v4;
|
||||
struct {
|
||||
struct in6_addr daddr;
|
||||
struct in6_addr saddr;
|
||||
} v6;
|
||||
} fam;
|
||||
} net;
|
||||
int cap;
|
||||
int ipc_id;
|
||||
} u;
|
||||
};
|
||||
|
||||
#define v4info fam.v4
|
||||
#define v6info fam.v6
|
||||
|
||||
/* Initialize an AVC audit data structure. */
|
||||
#define AVC_AUDIT_DATA_INIT(_d,_t) \
|
||||
{ memset((_d), 0, sizeof(struct avc_audit_data)); (_d)->type = AVC_AUDIT_DATA_##_t; }
|
||||
|
||||
/*
|
||||
* AVC statistics
|
||||
*/
|
||||
@ -98,7 +57,9 @@ void __init avc_init(void);
|
||||
|
||||
void avc_audit(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct av_decision *avd, int result, struct avc_audit_data *auditdata);
|
||||
struct av_decision *avd,
|
||||
int result,
|
||||
struct common_audit_data *a);
|
||||
|
||||
#define AVC_STRICT 1 /* Ignore permissive mode. */
|
||||
int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
@ -108,7 +69,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
||||
|
||||
int avc_has_perm(u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 requested,
|
||||
struct avc_audit_data *auditdata);
|
||||
struct common_audit_data *auditdata);
|
||||
|
||||
u32 avc_policy_seqno(void);
|
||||
|
||||
@ -127,13 +88,13 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
|
||||
u32 events, u32 ssid, u32 tsid,
|
||||
u16 tclass, u32 perms);
|
||||
|
||||
/* Shows permission in human readable form */
|
||||
void avc_dump_av(struct audit_buffer *ab, u16 tclass, u32 av);
|
||||
|
||||
/* Exported to selinuxfs */
|
||||
int avc_get_hash_stats(char *page);
|
||||
extern unsigned int avc_cache_threshold;
|
||||
|
||||
/* Attempt to free avc node cache */
|
||||
void avc_disable(void);
|
||||
|
||||
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
|
||||
DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
|
||||
#endif
|
||||
|
@ -77,3 +77,4 @@
|
||||
S_(NULL)
|
||||
S_(NULL)
|
||||
S_("kernel_service")
|
||||
S_("tun_socket")
|
||||
|
@ -53,6 +53,7 @@
|
||||
#define SECCLASS_PEER 68
|
||||
#define SECCLASS_CAPABILITY2 69
|
||||
#define SECCLASS_KERNEL_SERVICE 74
|
||||
#define SECCLASS_TUN_SOCKET 75
|
||||
|
||||
/*
|
||||
* Security identifier indices for initial entities
|
||||
|
@ -59,7 +59,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family);
|
||||
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
||||
struct sk_buff *skb,
|
||||
u16 family,
|
||||
struct avc_audit_data *ad);
|
||||
struct common_audit_data *ad);
|
||||
int selinux_netlbl_socket_setsockopt(struct socket *sock,
|
||||
int level,
|
||||
int optname);
|
||||
@ -129,7 +129,7 @@ static inline int selinux_netlbl_socket_post_create(struct sock *sk,
|
||||
static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
||||
struct sk_buff *skb,
|
||||
u16 family,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -41,9 +41,9 @@ static inline int selinux_xfrm_enabled(void)
|
||||
}
|
||||
|
||||
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad);
|
||||
struct common_audit_data *ad);
|
||||
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad, u8 proto);
|
||||
struct common_audit_data *ad, u8 proto);
|
||||
int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
|
||||
|
||||
static inline void selinux_xfrm_notify_policyload(void)
|
||||
@ -57,13 +57,13 @@ static inline int selinux_xfrm_enabled(void)
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad, u8 proto)
|
||||
struct common_audit_data *ad, u8 proto)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -342,7 +342,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family)
|
||||
int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
|
||||
struct sk_buff *skb,
|
||||
u16 family,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
int rc;
|
||||
u32 nlbl_sid;
|
||||
|
@ -22,6 +22,11 @@
|
||||
*
|
||||
* Added validation of kernel classes and permissions
|
||||
*
|
||||
* Updated: KaiGai Kohei <kaigai@ak.jp.nec.com>
|
||||
*
|
||||
* Added support for bounds domain and audit messaged on masked permissions
|
||||
*
|
||||
* Copyright (C) 2008, 2009 NEC Corporation
|
||||
* Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
|
||||
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
|
||||
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
|
||||
@ -278,6 +283,95 @@ mls_ops:
|
||||
return s[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* security_dump_masked_av - dumps masked permissions during
|
||||
* security_compute_av due to RBAC, MLS/Constraint and Type bounds.
|
||||
*/
|
||||
static int dump_masked_av_helper(void *k, void *d, void *args)
|
||||
{
|
||||
struct perm_datum *pdatum = d;
|
||||
char **permission_names = args;
|
||||
|
||||
BUG_ON(pdatum->value < 1 || pdatum->value > 32);
|
||||
|
||||
permission_names[pdatum->value - 1] = (char *)k;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void security_dump_masked_av(struct context *scontext,
|
||||
struct context *tcontext,
|
||||
u16 tclass,
|
||||
u32 permissions,
|
||||
const char *reason)
|
||||
{
|
||||
struct common_datum *common_dat;
|
||||
struct class_datum *tclass_dat;
|
||||
struct audit_buffer *ab;
|
||||
char *tclass_name;
|
||||
char *scontext_name = NULL;
|
||||
char *tcontext_name = NULL;
|
||||
char *permission_names[32];
|
||||
int index, length;
|
||||
bool need_comma = false;
|
||||
|
||||
if (!permissions)
|
||||
return;
|
||||
|
||||
tclass_name = policydb.p_class_val_to_name[tclass - 1];
|
||||
tclass_dat = policydb.class_val_to_struct[tclass - 1];
|
||||
common_dat = tclass_dat->comdatum;
|
||||
|
||||
/* init permission_names */
|
||||
if (common_dat &&
|
||||
hashtab_map(common_dat->permissions.table,
|
||||
dump_masked_av_helper, permission_names) < 0)
|
||||
goto out;
|
||||
|
||||
if (hashtab_map(tclass_dat->permissions.table,
|
||||
dump_masked_av_helper, permission_names) < 0)
|
||||
goto out;
|
||||
|
||||
/* get scontext/tcontext in text form */
|
||||
if (context_struct_to_string(scontext,
|
||||
&scontext_name, &length) < 0)
|
||||
goto out;
|
||||
|
||||
if (context_struct_to_string(tcontext,
|
||||
&tcontext_name, &length) < 0)
|
||||
goto out;
|
||||
|
||||
/* audit a message */
|
||||
ab = audit_log_start(current->audit_context,
|
||||
GFP_ATOMIC, AUDIT_SELINUX_ERR);
|
||||
if (!ab)
|
||||
goto out;
|
||||
|
||||
audit_log_format(ab, "op=security_compute_av reason=%s "
|
||||
"scontext=%s tcontext=%s tclass=%s perms=",
|
||||
reason, scontext_name, tcontext_name, tclass_name);
|
||||
|
||||
for (index = 0; index < 32; index++) {
|
||||
u32 mask = (1 << index);
|
||||
|
||||
if ((mask & permissions) == 0)
|
||||
continue;
|
||||
|
||||
audit_log_format(ab, "%s%s",
|
||||
need_comma ? "," : "",
|
||||
permission_names[index]
|
||||
? permission_names[index] : "????");
|
||||
need_comma = true;
|
||||
}
|
||||
audit_log_end(ab);
|
||||
out:
|
||||
/* release scontext/tcontext */
|
||||
kfree(tcontext_name);
|
||||
kfree(scontext_name);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* security_boundary_permission - drops violated permissions
|
||||
* on boundary constraint.
|
||||
@ -347,28 +441,12 @@ static void type_attribute_bounds_av(struct context *scontext,
|
||||
}
|
||||
|
||||
if (masked) {
|
||||
struct audit_buffer *ab;
|
||||
char *stype_name
|
||||
= policydb.p_type_val_to_name[source->value - 1];
|
||||
char *ttype_name
|
||||
= policydb.p_type_val_to_name[target->value - 1];
|
||||
char *tclass_name
|
||||
= policydb.p_class_val_to_name[tclass - 1];
|
||||
|
||||
/* mask violated permissions */
|
||||
avd->allowed &= ~masked;
|
||||
|
||||
/* notice to userspace via audit message */
|
||||
ab = audit_log_start(current->audit_context,
|
||||
GFP_ATOMIC, AUDIT_SELINUX_ERR);
|
||||
if (!ab)
|
||||
return;
|
||||
|
||||
audit_log_format(ab, "av boundary violation: "
|
||||
"source=%s target=%s tclass=%s",
|
||||
stype_name, ttype_name, tclass_name);
|
||||
avc_dump_av(ab, tclass, masked);
|
||||
audit_log_end(ab);
|
||||
/* audit masked permissions */
|
||||
security_dump_masked_av(scontext, tcontext,
|
||||
tclass, masked, "bounds");
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,7 +558,7 @@ static int context_struct_compute_av(struct context *scontext,
|
||||
if ((constraint->permissions & (avd->allowed)) &&
|
||||
!constraint_expr_eval(scontext, tcontext, NULL,
|
||||
constraint->expr)) {
|
||||
avd->allowed = (avd->allowed) & ~(constraint->permissions);
|
||||
avd->allowed &= ~(constraint->permissions);
|
||||
}
|
||||
constraint = constraint->next;
|
||||
}
|
||||
@ -499,8 +577,8 @@ static int context_struct_compute_av(struct context *scontext,
|
||||
break;
|
||||
}
|
||||
if (!ra)
|
||||
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION |
|
||||
PROCESS__DYNTRANSITION);
|
||||
avd->allowed &= ~(PROCESS__TRANSITION |
|
||||
PROCESS__DYNTRANSITION);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -687,6 +765,26 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
|
||||
}
|
||||
index = type->bounds;
|
||||
}
|
||||
|
||||
if (rc) {
|
||||
char *old_name = NULL;
|
||||
char *new_name = NULL;
|
||||
int length;
|
||||
|
||||
if (!context_struct_to_string(old_context,
|
||||
&old_name, &length) &&
|
||||
!context_struct_to_string(new_context,
|
||||
&new_name, &length)) {
|
||||
audit_log(current->audit_context,
|
||||
GFP_ATOMIC, AUDIT_SELINUX_ERR,
|
||||
"op=security_bounded_transition "
|
||||
"result=denied "
|
||||
"oldcontext=%s newcontext=%s",
|
||||
old_name, new_name);
|
||||
}
|
||||
kfree(new_name);
|
||||
kfree(old_name);
|
||||
}
|
||||
out:
|
||||
read_unlock(&policy_rwlock);
|
||||
|
||||
|
@ -401,7 +401,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
|
||||
* gone thru the IPSec process.
|
||||
*/
|
||||
int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad)
|
||||
struct common_audit_data *ad)
|
||||
{
|
||||
int i, rc = 0;
|
||||
struct sec_path *sp;
|
||||
@ -442,7 +442,7 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb,
|
||||
* checked in the selinux_xfrm_state_pol_flow_match hook above.
|
||||
*/
|
||||
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb,
|
||||
struct avc_audit_data *ad, u8 proto)
|
||||
struct common_audit_data *ad, u8 proto)
|
||||
{
|
||||
struct dst_entry *dst;
|
||||
int rc = 0;
|
||||
|
@ -275,7 +275,7 @@ static inline void smk_ad_init(struct smk_audit_info *a, const char *func,
|
||||
{
|
||||
memset(a, 0, sizeof(*a));
|
||||
a->a.type = type;
|
||||
a->a.function = func;
|
||||
a->a.smack_audit_data.function = func;
|
||||
}
|
||||
|
||||
static inline void smk_ad_setfield_u_tsk(struct smk_audit_info *a,
|
||||
|
@ -240,8 +240,9 @@ static inline void smack_str_from_perm(char *string, int access)
|
||||
static void smack_log_callback(struct audit_buffer *ab, void *a)
|
||||
{
|
||||
struct common_audit_data *ad = a;
|
||||
struct smack_audit_data *sad = &ad->lsm_priv.smack_audit_data;
|
||||
audit_log_format(ab, "lsm=SMACK fn=%s action=%s", ad->function,
|
||||
struct smack_audit_data *sad = &ad->smack_audit_data;
|
||||
audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
|
||||
ad->smack_audit_data.function,
|
||||
sad->result ? "denied" : "granted");
|
||||
audit_log_format(ab, " subject=");
|
||||
audit_log_untrustedstring(ab, sad->subject);
|
||||
@ -274,11 +275,11 @@ void smack_log(char *subject_label, char *object_label, int request,
|
||||
if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
|
||||
return;
|
||||
|
||||
if (a->function == NULL)
|
||||
a->function = "unknown";
|
||||
if (a->smack_audit_data.function == NULL)
|
||||
a->smack_audit_data.function = "unknown";
|
||||
|
||||
/* end preparing the audit data */
|
||||
sad = &a->lsm_priv.smack_audit_data;
|
||||
sad = &a->smack_audit_data;
|
||||
smack_str_from_perm(request_buffer, request);
|
||||
sad->subject = subject_label;
|
||||
sad->object = object_label;
|
||||
|
@ -91,7 +91,7 @@ struct inode_smack *new_inode_smack(char *smack)
|
||||
*/
|
||||
|
||||
/**
|
||||
* smack_ptrace_may_access - Smack approval on PTRACE_ATTACH
|
||||
* smack_ptrace_access_check - Smack approval on PTRACE_ATTACH
|
||||
* @ctp: child task pointer
|
||||
* @mode: ptrace attachment mode
|
||||
*
|
||||
@ -99,13 +99,13 @@ struct inode_smack *new_inode_smack(char *smack)
|
||||
*
|
||||
* Do the capability checks, and require read and write.
|
||||
*/
|
||||
static int smack_ptrace_may_access(struct task_struct *ctp, unsigned int mode)
|
||||
static int smack_ptrace_access_check(struct task_struct *ctp, unsigned int mode)
|
||||
{
|
||||
int rc;
|
||||
struct smk_audit_info ad;
|
||||
char *sp, *tsp;
|
||||
|
||||
rc = cap_ptrace_may_access(ctp, mode);
|
||||
rc = cap_ptrace_access_check(ctp, mode);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
@ -1079,6 +1079,22 @@ static int smack_file_receive(struct file *file)
|
||||
* Task hooks
|
||||
*/
|
||||
|
||||
/**
|
||||
* smack_cred_alloc_blank - "allocate" blank task-level security credentials
|
||||
* @new: the new credentials
|
||||
* @gfp: the atomicity of any memory allocations
|
||||
*
|
||||
* Prepare a blank set of credentials for modification. This must allocate all
|
||||
* the memory the LSM module might require such that cred_transfer() can
|
||||
* complete without error.
|
||||
*/
|
||||
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
|
||||
{
|
||||
cred->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* smack_cred_free - "free" task-level security credentials
|
||||
* @cred: the credentials in question
|
||||
@ -1116,6 +1132,18 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_cred_transfer - Transfer the old credentials to the new credentials
|
||||
* @new: the new credentials
|
||||
* @old: the original credentials
|
||||
*
|
||||
* Fill in a set of blank credentials from another set of credentials.
|
||||
*/
|
||||
static void smack_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
new->security = old->security;
|
||||
}
|
||||
|
||||
/**
|
||||
* smack_kernel_act_as - Set the subjective context in a set of credentials
|
||||
* @new: points to the set of credentials to be modified.
|
||||
@ -1638,6 +1666,7 @@ static int smack_inode_setsecurity(struct inode *inode, const char *name,
|
||||
|
||||
if (strcmp(name, XATTR_SMACK_SUFFIX) == 0) {
|
||||
nsp->smk_inode = sp;
|
||||
nsp->smk_flags |= SMK_INODE_INSTANT;
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
@ -2464,7 +2493,7 @@ static int smack_socket_sendmsg(struct socket *sock, struct msghdr *msg,
|
||||
/*
|
||||
* Perfectly reasonable for this to be NULL
|
||||
*/
|
||||
if (sip == NULL || sip->sin_family != PF_INET)
|
||||
if (sip == NULL || sip->sin_family != AF_INET)
|
||||
return 0;
|
||||
|
||||
return smack_netlabel_send(sock->sk, sip);
|
||||
@ -3029,10 +3058,31 @@ static void smack_release_secctx(char *secdata, u32 seclen)
|
||||
{
|
||||
}
|
||||
|
||||
static int smack_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return smack_inode_setsecurity(inode, XATTR_SMACK_SUFFIX, ctx, ctxlen, 0);
|
||||
}
|
||||
|
||||
static int smack_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen)
|
||||
{
|
||||
return __vfs_setxattr_noperm(dentry, XATTR_NAME_SMACK, ctx, ctxlen, 0);
|
||||
}
|
||||
|
||||
static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen)
|
||||
{
|
||||
int len = 0;
|
||||
len = smack_inode_getsecurity(inode, XATTR_SMACK_SUFFIX, ctx, true);
|
||||
|
||||
if (len < 0)
|
||||
return len;
|
||||
*ctxlen = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct security_operations smack_ops = {
|
||||
.name = "smack",
|
||||
|
||||
.ptrace_may_access = smack_ptrace_may_access,
|
||||
.ptrace_access_check = smack_ptrace_access_check,
|
||||
.ptrace_traceme = smack_ptrace_traceme,
|
||||
.syslog = smack_syslog,
|
||||
|
||||
@ -3073,9 +3123,11 @@ struct security_operations smack_ops = {
|
||||
.file_send_sigiotask = smack_file_send_sigiotask,
|
||||
.file_receive = smack_file_receive,
|
||||
|
||||
.cred_alloc_blank = smack_cred_alloc_blank,
|
||||
.cred_free = smack_cred_free,
|
||||
.cred_prepare = smack_cred_prepare,
|
||||
.cred_commit = smack_cred_commit,
|
||||
.cred_transfer = smack_cred_transfer,
|
||||
.kernel_act_as = smack_kernel_act_as,
|
||||
.kernel_create_files_as = smack_kernel_create_files_as,
|
||||
.task_setpgid = smack_task_setpgid,
|
||||
@ -3155,6 +3207,9 @@ struct security_operations smack_ops = {
|
||||
.secid_to_secctx = smack_secid_to_secctx,
|
||||
.secctx_to_secid = smack_secctx_to_secid,
|
||||
.release_secctx = smack_release_secctx,
|
||||
.inode_notifysecctx = smack_inode_notifysecctx,
|
||||
.inode_setsecctx = smack_inode_setsecctx,
|
||||
.inode_getsecctx = smack_inode_getsecctx,
|
||||
};
|
||||
|
||||
|
||||
|
@ -1284,6 +1284,36 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_delete_domain - Delete a domain.
|
||||
*
|
||||
* @domainname: The name of domain.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
static int tomoyo_delete_domain(char *domainname)
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
struct tomoyo_path_info name;
|
||||
|
||||
name.name = domainname;
|
||||
tomoyo_fill_path_info(&name);
|
||||
down_write(&tomoyo_domain_list_lock);
|
||||
/* Is there an active domain? */
|
||||
list_for_each_entry(domain, &tomoyo_domain_list, list) {
|
||||
/* Never delete tomoyo_kernel_domain */
|
||||
if (domain == &tomoyo_kernel_domain)
|
||||
continue;
|
||||
if (domain->is_deleted ||
|
||||
tomoyo_pathcmp(domain->domainname, &name))
|
||||
continue;
|
||||
domain->is_deleted = true;
|
||||
break;
|
||||
}
|
||||
up_write(&tomoyo_domain_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_write_domain_policy - Write domain policy.
|
||||
*
|
||||
|
@ -339,8 +339,6 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain);
|
||||
const char *tomoyo_get_msg(const bool is_enforce);
|
||||
/* Convert single path operation to operation name. */
|
||||
const char *tomoyo_sp2keyword(const u8 operation);
|
||||
/* Delete a domain. */
|
||||
int tomoyo_delete_domain(char *data);
|
||||
/* Create "alias" entry in exception policy. */
|
||||
int tomoyo_write_alias_policy(char *data, const bool is_delete);
|
||||
/*
|
||||
|
@ -717,38 +717,6 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
|
||||
return tomoyo_update_alias_entry(data, cp, is_delete);
|
||||
}
|
||||
|
||||
/* Domain create/delete handler. */
|
||||
|
||||
/**
|
||||
* tomoyo_delete_domain - Delete a domain.
|
||||
*
|
||||
* @domainname: The name of domain.
|
||||
*
|
||||
* Returns 0.
|
||||
*/
|
||||
int tomoyo_delete_domain(char *domainname)
|
||||
{
|
||||
struct tomoyo_domain_info *domain;
|
||||
struct tomoyo_path_info name;
|
||||
|
||||
name.name = domainname;
|
||||
tomoyo_fill_path_info(&name);
|
||||
down_write(&tomoyo_domain_list_lock);
|
||||
/* Is there an active domain? */
|
||||
list_for_each_entry(domain, &tomoyo_domain_list, list) {
|
||||
/* Never delete tomoyo_kernel_domain */
|
||||
if (domain == &tomoyo_kernel_domain)
|
||||
continue;
|
||||
if (domain->is_deleted ||
|
||||
tomoyo_pathcmp(domain->domainname, &name))
|
||||
continue;
|
||||
domain->is_deleted = true;
|
||||
break;
|
||||
}
|
||||
up_write(&tomoyo_domain_list_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tomoyo_find_or_assign_new_domain - Create a domain.
|
||||
*
|
||||
@ -818,13 +786,11 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
|
||||
/**
|
||||
* tomoyo_find_next_domain - Find a domain.
|
||||
*
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
* @next_domain: Pointer to pointer to "struct tomoyo_domain_info".
|
||||
* @bprm: Pointer to "struct linux_binprm".
|
||||
*
|
||||
* Returns 0 on success, negative value otherwise.
|
||||
*/
|
||||
int tomoyo_find_next_domain(struct linux_binprm *bprm,
|
||||
struct tomoyo_domain_info **next_domain)
|
||||
int tomoyo_find_next_domain(struct linux_binprm *bprm)
|
||||
{
|
||||
/*
|
||||
* This function assumes that the size of buffer returned by
|
||||
@ -946,9 +912,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
|
||||
tomoyo_set_domain_flag(old_domain, false,
|
||||
TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
|
||||
out:
|
||||
if (!domain)
|
||||
domain = old_domain;
|
||||
bprm->cred->security = domain;
|
||||
tomoyo_free(real_program_name);
|
||||
tomoyo_free(symlink_program_name);
|
||||
*next_domain = domain ? domain : old_domain;
|
||||
tomoyo_free(tmp);
|
||||
return retval;
|
||||
}
|
||||
|
@ -14,6 +14,12 @@
|
||||
#include "tomoyo.h"
|
||||
#include "realpath.h"
|
||||
|
||||
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
|
||||
{
|
||||
new->security = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
|
||||
gfp_t gfp)
|
||||
{
|
||||
@ -25,6 +31,15 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
|
||||
{
|
||||
/*
|
||||
* Since "struct tomoyo_domain_info *" is a sharable pointer,
|
||||
* we don't need to duplicate.
|
||||
*/
|
||||
new->security = old->security;
|
||||
}
|
||||
|
||||
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
||||
{
|
||||
int rc;
|
||||
@ -61,14 +76,8 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm)
|
||||
* Execute permission is checked against pathname passed to do_execve()
|
||||
* using current domain.
|
||||
*/
|
||||
if (!domain) {
|
||||
struct tomoyo_domain_info *next_domain = NULL;
|
||||
int retval = tomoyo_find_next_domain(bprm, &next_domain);
|
||||
|
||||
if (!retval)
|
||||
bprm->cred->security = next_domain;
|
||||
return retval;
|
||||
}
|
||||
if (!domain)
|
||||
return tomoyo_find_next_domain(bprm);
|
||||
/*
|
||||
* Read permission is checked against interpreters using next domain.
|
||||
* '1' is the result of open_to_namei_flags(O_RDONLY).
|
||||
@ -268,7 +277,9 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
|
||||
*/
|
||||
static struct security_operations tomoyo_security_ops = {
|
||||
.name = "tomoyo",
|
||||
.cred_alloc_blank = tomoyo_cred_alloc_blank,
|
||||
.cred_prepare = tomoyo_cred_prepare,
|
||||
.cred_transfer = tomoyo_cred_transfer,
|
||||
.bprm_set_creds = tomoyo_bprm_set_creds,
|
||||
.bprm_check_security = tomoyo_bprm_check_security,
|
||||
#ifdef CONFIG_SYSCTL
|
||||
|
@ -31,8 +31,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
|
||||
struct path *path2);
|
||||
int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
|
||||
struct file *filp);
|
||||
int tomoyo_find_next_domain(struct linux_binprm *bprm,
|
||||
struct tomoyo_domain_info **next_domain);
|
||||
int tomoyo_find_next_domain(struct linux_binprm *bprm);
|
||||
|
||||
/* Index numbers for Access Controls. */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user