提交 f6f79190 编写于 作者: L Linus Torvalds

Merge branch 'for-linus' of...

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
  ...
...@@ -26,7 +26,7 @@ This document has the following sections: ...@@ -26,7 +26,7 @@ This document has the following sections:
- Notes on accessing payload contents - Notes on accessing payload contents
- Defining a key type - Defining a key type
- Request-key callback service - Request-key callback service
- Key access filesystem - Garbage collection
============ ============
...@@ -113,6 +113,9 @@ Each key has a number of attributes: ...@@ -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. (*) 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 KEY SERVICE OVERVIEW
...@@ -754,6 +757,26 @@ The keyctl syscall functions are: ...@@ -754,6 +757,26 @@ The keyctl syscall functions are:
successful. 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 KERNEL SERVICES
=============== ===============
...@@ -1231,3 +1254,17 @@ by executing: ...@@ -1231,3 +1254,17 @@ by executing:
In this case, the program isn't required to actually attach the key to a ring; In this case, the program isn't required to actually attach the key to a ring;
the rings are provided for reference. 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"); ...@@ -75,6 +75,7 @@ register struct thread_info *__current_thread_info __asm__("$8");
#define TIF_UAC_SIGBUS 7 #define TIF_UAC_SIGBUS 7
#define TIF_MEMDIE 8 #define TIF_MEMDIE 8
#define TIF_RESTORE_SIGMASK 9 /* restore signal mask in do_signal */ #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_FREEZE 16 /* is freezing for suspend */
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
...@@ -82,10 +83,12 @@ register struct thread_info *__current_thread_info __asm__("$8"); ...@@ -82,10 +83,12 @@ register struct thread_info *__current_thread_info __asm__("$8");
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE) #define _TIF_FREEZE (1<<TIF_FREEZE)
/* Work to do on interrupt/exception return. */ /* 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. */ /* Work to do on any return to userspace. */
#define _TIF_ALLWORK_MASK (_TIF_WORK_MASK \ #define _TIF_ALLWORK_MASK (_TIF_WORK_MASK \
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/bitops.h> #include <linux/bitops.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/tracehook.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/sigcontext.h> #include <asm/sigcontext.h>
...@@ -683,4 +684,11 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw, ...@@ -683,4 +684,11 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
{ {
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs, sw, r0, r19); 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); ...@@ -130,11 +130,13 @@ extern void vfp_sync_state(struct thread_info *thread);
* TIF_SYSCALL_TRACE - syscall trace active * TIF_SYSCALL_TRACE - syscall trace active
* TIF_SIGPENDING - signal pending * TIF_SIGPENDING - signal pending
* TIF_NEED_RESCHED - rescheduling necessary * 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_USEDFPU - FPU was used by this task this quantum (SMP)
* TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED * TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED
*/ */
#define TIF_SIGPENDING 0 #define TIF_SIGPENDING 0
#define TIF_NEED_RESCHED 1 #define TIF_NEED_RESCHED 1
#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
#define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_TRACE 8
#define TIF_POLLING_NRFLAG 16 #define TIF_POLLING_NRFLAG 16
#define TIF_USING_IWMMXT 17 #define TIF_USING_IWMMXT 17
...@@ -143,6 +145,7 @@ extern void vfp_sync_state(struct thread_info *thread); ...@@ -143,6 +145,7 @@ extern void vfp_sync_state(struct thread_info *thread);
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG)
#define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT)
......
...@@ -51,7 +51,7 @@ fast_work_pending: ...@@ -51,7 +51,7 @@ fast_work_pending:
work_pending: work_pending:
tst r1, #_TIF_NEED_RESCHED tst r1, #_TIF_NEED_RESCHED
bne work_resched bne work_resched
tst r1, #_TIF_SIGPENDING tst r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
beq no_work_pending beq no_work_pending
mov r0, sp @ 'regs' mov r0, sp @ 'regs'
mov r2, why @ 'syscall' mov r2, why @ 'syscall'
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/tracehook.h>
#include <asm/elf.h> #include <asm/elf.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
...@@ -707,4 +708,11 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) ...@@ -707,4 +708,11 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{ {
if (thread_flags & _TIF_SIGPENDING) if (thread_flags & _TIF_SIGPENDING)
do_signal(&current->blocked, regs, syscall); do_signal(&current->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) ...@@ -84,6 +84,7 @@ static inline struct thread_info *current_thread_info(void)
#define TIF_MEMDIE 6 #define TIF_MEMDIE 6
#define TIF_RESTORE_SIGMASK 7 /* restore signal mask in do_signal */ #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_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_FREEZE 29
#define TIF_DEBUG 30 /* debugging enabled */ #define TIF_DEBUG 30 /* debugging enabled */
#define TIF_USERSPACE 31 /* true if FS sets userspace */ #define TIF_USERSPACE 31 /* true if FS sets userspace */
...@@ -96,6 +97,7 @@ static inline struct thread_info *current_thread_info(void) ...@@ -96,6 +97,7 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_MEMDIE (1 << TIF_MEMDIE) #define _TIF_MEMDIE (1 << TIF_MEMDIE)
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
#define _TIF_CPU_GOING_TO_SLEEP (1 << TIF_CPU_GOING_TO_SLEEP) #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) #define _TIF_FREEZE (1 << TIF_FREEZE)
/* Note: The masks below must never span more than 16 bits! */ /* Note: The masks below must never span more than 16 bits! */
...@@ -103,13 +105,15 @@ static inline struct thread_info *current_thread_info(void) ...@@ -103,13 +105,15 @@ static inline struct thread_info *current_thread_info(void)
/* work to do on interrupt/exception return */ /* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \ #define _TIF_WORK_MASK \
((1 << TIF_SIGPENDING) \ ((1 << TIF_SIGPENDING) \
| _TIF_NOTIFY_RESUME \
| (1 << TIF_NEED_RESCHED) \ | (1 << TIF_NEED_RESCHED) \
| (1 << TIF_POLLING_NRFLAG) \ | (1 << TIF_POLLING_NRFLAG) \
| (1 << TIF_BREAKPOINT) \ | (1 << TIF_BREAKPOINT) \
| (1 << TIF_RESTORE_SIGMASK)) | (1 << TIF_RESTORE_SIGMASK))
/* work to do on any return to userspace */ /* 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 */ /* work to do on return from debug mode */
#define _TIF_DBGWORK_MASK (_TIF_WORK_MASK & ~(1 << TIF_BREAKPOINT)) #define _TIF_DBGWORK_MASK (_TIF_WORK_MASK & ~(1 << TIF_BREAKPOINT))
......
...@@ -281,7 +281,7 @@ syscall_exit_work: ...@@ -281,7 +281,7 @@ syscall_exit_work:
ld.w r1, r0[TI_flags] ld.w r1, r0[TI_flags]
rjmp 1b rjmp 1b
2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK 2: mov r2, _TIF_SIGPENDING | _TIF_RESTORE_SIGMASK | _TIF_NOTIFY_RESUME
tst r1, r2 tst r1, r2
breq 3f breq 3f
unmask_interrupts unmask_interrupts
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/tracehook.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
...@@ -322,4 +323,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti) ...@@ -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)) if (ti->flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs, &current->blocked, syscall); do_signal(regs, &current->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 @@ ...@@ -16,6 +16,7 @@
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/user.h> #include <linux/user.h>
#include <linux/tracehook.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
...@@ -36,4 +37,11 @@ void do_notify_resume(int canrestart, struct pt_regs *regs, ...@@ -36,4 +37,11 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
/* deal with pending signal delivery */ /* deal with pending signal delivery */
if (thread_info_flags & _TIF_SIGPENDING) if (thread_info_flags & _TIF_SIGPENDING)
do_signal(canrestart,regs); 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) ...@@ -572,6 +572,8 @@ asmlinkage void do_notify_resume(__u32 thread_info_flags)
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(__frame); tracehook_notify_resume(__frame);
if (current->replacement_session_keyring)
key_replace_session_keyring();
} }
} /* end do_notify_resume() */ } /* end do_notify_resume() */
...@@ -89,6 +89,7 @@ static inline struct thread_info *current_thread_info(void) ...@@ -89,6 +89,7 @@ static inline struct thread_info *current_thread_info(void)
TIF_NEED_RESCHED */ TIF_NEED_RESCHED */
#define TIF_MEMDIE 4 #define TIF_MEMDIE 4
#define TIF_RESTORE_SIGMASK 5 /* restore signal mask in do_signal() */ #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 */ #define TIF_FREEZE 16 /* is freezing for suspend */
/* as above, but as bit values */ /* as above, but as bit values */
...@@ -97,6 +98,7 @@ static inline struct thread_info *current_thread_info(void) ...@@ -97,6 +98,7 @@ static inline struct thread_info *current_thread_info(void)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
#define _TIF_FREEZE (1<<TIF_FREEZE) #define _TIF_FREEZE (1<<TIF_FREEZE)
#define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */ #define _TIF_WORK_MASK 0x0000FFFE /* work to do on interrupt/exception return */
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <linux/tty.h> #include <linux/tty.h>
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/tracehook.h>
#include <asm/setup.h> #include <asm/setup.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -552,4 +553,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) ...@@ -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)) if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs, NULL); 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) ...@@ -192,6 +192,8 @@ do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
if (test_thread_flag(TIF_NOTIFY_RESUME)) { if (test_thread_flag(TIF_NOTIFY_RESUME)) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(&scr->pt); tracehook_notify_resume(&scr->pt);
if (current->replacement_session_keyring)
key_replace_session_keyring();
} }
/* copy user rbs to kernel rbs */ /* copy user rbs to kernel rbs */
......
...@@ -149,6 +149,7 @@ static inline unsigned int get_thread_fault_code(void) ...@@ -149,6 +149,7 @@ static inline unsigned int get_thread_fault_code(void)
#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ #define TIF_NEED_RESCHED 2 /* rescheduling necessary */
#define TIF_SINGLESTEP 3 /* restore singlestep on return to user mode */ #define TIF_SINGLESTEP 3 /* restore singlestep on return to user mode */
#define TIF_IRET 4 /* return with iret */ #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_RESTORE_SIGMASK 8 /* restore signal mask in do_signal() */
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ #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 */ #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) ...@@ -160,6 +161,7 @@ static inline unsigned int get_thread_fault_code(void)
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP) #define _TIF_SINGLESTEP (1<<TIF_SINGLESTEP)
#define _TIF_IRET (1<<TIF_IRET) #define _TIF_IRET (1<<TIF_IRET)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_USEDFPU (1<<TIF_USEDFPU) #define _TIF_USEDFPU (1<<TIF_USEDFPU)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/tracehook.h>
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -408,5 +409,12 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, ...@@ -408,5 +409,12 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset,
if (thread_info_flags & _TIF_SIGPENDING) if (thread_info_flags & _TIF_SIGPENDING)
do_signal(regs,oldset); 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); clear_thread_flag(TIF_IRET);
} }
...@@ -115,6 +115,7 @@ register struct thread_info *__current_thread_info __asm__("$28"); ...@@ -115,6 +115,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define TIF_NEED_RESCHED 2 /* rescheduling necessary */ #define TIF_NEED_RESCHED 2 /* rescheduling necessary */
#define TIF_SYSCALL_AUDIT 3 /* syscall auditing active */ #define TIF_SYSCALL_AUDIT 3 /* syscall auditing active */
#define TIF_SECCOMP 4 /* secure computing */ #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_RESTORE_SIGMASK 9 /* restore signal mask in do_signal() */
#define TIF_USEDFPU 16 /* FPU was used by this task this quantum (SMP) */ #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 */ #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"); ...@@ -139,6 +140,7 @@ register struct thread_info *__current_thread_info __asm__("$28");
#define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED) #define _TIF_NEED_RESCHED (1<<TIF_NEED_RESCHED)
#define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT) #define _TIF_SYSCALL_AUDIT (1<<TIF_SYSCALL_AUDIT)
#define _TIF_SECCOMP (1<<TIF_SECCOMP) #define _TIF_SECCOMP (1<<TIF_SECCOMP)
#define _TIF_NOTIFY_RESUME (1<<TIF_NOTIFY_RESUME)
#define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1<<TIF_RESTORE_SIGMASK)
#define _TIF_USEDFPU (1<<TIF_USEDFPU) #define _TIF_USEDFPU (1<<TIF_USEDFPU)
#define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG) #define _TIF_POLLING_NRFLAG (1<<TIF_POLLING_NRFLAG)
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/tracehook.h>
#include <asm/abi.h> #include <asm/abi.h>
#include <asm/asm.h> #include <asm/asm.h>
...@@ -700,4 +701,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, ...@@ -700,4 +701,11 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
/* deal with pending signal delivery */ /* deal with pending signal delivery */
if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK)) if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
do_signal(regs); 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) ...@@ -568,5 +568,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags)
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(__frame); tracehook_notify_resume(__frame);
if (current->replacement_session_keyring)
key_replace_session_keyring();
} }
} }
...@@ -59,6 +59,7 @@ struct thread_info { ...@@ -59,6 +59,7 @@ struct thread_info {
#define TIF_MEMDIE 5 #define TIF_MEMDIE 5
#define TIF_RESTORE_SIGMASK 6 /* restore saved signal mask */ #define TIF_RESTORE_SIGMASK 6 /* restore saved signal mask */
#define TIF_FREEZE 7 /* is freezing for suspend */ #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_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE)
#define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
...@@ -67,8 +68,9 @@ struct thread_info { ...@@ -67,8 +68,9 @@ struct thread_info {
#define _TIF_32BIT (1 << TIF_32BIT) #define _TIF_32BIT (1 << TIF_32BIT)
#define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK)
#define _TIF_FREEZE (1 << TIF_FREEZE) #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) _TIF_NEED_RESCHED | _TIF_RESTORE_SIGMASK)
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -948,7 +948,7 @@ intr_check_sig: ...@@ -948,7 +948,7 @@ intr_check_sig:
/* As above */ /* As above */
mfctl %cr30,%r1 mfctl %cr30,%r1
LDREG TI_FLAGS(%r1),%r19 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 and,COND(<>) %r19, %r20, %r0
b,n intr_restore /* skip past if we've nothing to do */ b,n intr_restore /* skip past if we've nothing to do */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/elf.h> #include <linux/elf.h>
#include <linux/tracehook.h>
#include <asm/ucontext.h> #include <asm/ucontext.h>
#include <asm/rt_sigframe.h> #include <asm/rt_sigframe.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -645,4 +646,11 @@ void do_notify_resume(struct pt_regs *regs, long in_syscall) ...@@ -645,4 +646,11 @@ void do_notify_resume(struct pt_regs *regs, long in_syscall)
if (test_thread_flag(TIF_SIGPENDING) || if (test_thread_flag(TIF_SIGPENDING) ||
test_thread_flag(TIF_RESTORE_SIGMASK)) test_thread_flag(TIF_RESTORE_SIGMASK))
do_signal(regs, in_syscall); 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) ...@@ -536,4 +536,6 @@ void do_notify_resume(struct pt_regs *regs)
{ {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); 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, ...@@ -640,5 +640,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned int save_r0,
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); 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 ...@@ -772,5 +772,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, unsigned long thread_info
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); 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, ...@@ -590,6 +590,8 @@ void do_notify_resume(struct pt_regs *regs, unsigned long orig_i0,
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); 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 ...@@ -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) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); 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) ...@@ -869,6 +869,8 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
if (thread_info_flags & _TIF_NOTIFY_RESUME) { if (thread_info_flags & _TIF_NOTIFY_RESUME) {
clear_thread_flag(TIF_NOTIFY_RESUME); clear_thread_flag(TIF_NOTIFY_RESUME);
tracehook_notify_resume(regs); tracehook_notify_resume(regs);
if (current->replacement_session_keyring)
key_replace_session_keyring();
} }
#ifdef CONFIG_X86_32 #ifdef CONFIG_X86_32
......
...@@ -450,6 +450,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, ...@@ -450,6 +450,12 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
goto out_err; 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) { if (request_locality(chip, 0) != 0) {
rc = -ENODEV; rc = -ENODEV;
goto out_err; goto out_err;
...@@ -457,12 +463,6 @@ static int tpm_tis_init(struct device *dev, resource_size_t start, ...@@ -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)); 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, dev_info(dev,
"1.2 TPM (device-id 0x%X, rev-id %d)\n", "1.2 TPM (device-id 0x%X, rev-id %d)\n",
vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0))); vendor >> 16, ioread8(chip->vendor.iobase + TPM_RID(0)));
......
...@@ -130,17 +130,10 @@ static inline struct tun_sock *tun_sk(struct sock *sk) ...@@ -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) static int tun_attach(struct tun_struct *tun, struct file *file)
{ {
struct tun_file *tfile = file->private_data; struct tun_file *tfile = file->private_data;
const struct cred *cred = current_cred();
int err; int err;
ASSERT_RTNL(); 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); netif_tx_lock_bh(tun->dev);
err = -EINVAL; err = -EINVAL;
...@@ -926,6 +919,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ...@@ -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); dev = __dev_get_by_name(net, ifr->ifr_name);
if (dev) { if (dev) {
const struct cred *cred = current_cred();
if (ifr->ifr_flags & IFF_TUN_EXCL) if (ifr->ifr_flags & IFF_TUN_EXCL)
return -EBUSY; return -EBUSY;
if ((ifr->ifr_flags & IFF_TUN) && dev->netdev_ops == &tun_netdev_ops) 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) ...@@ -935,6 +930,14 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
else else
return -EINVAL; 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); err = tun_attach(tun, file);
if (err < 0) if (err < 0)
return err; return err;
...@@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ...@@ -947,6 +950,9 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
if (!capable(CAP_NET_ADMIN)) if (!capable(CAP_NET_ADMIN))
return -EPERM; return -EPERM;
err = security_tun_dev_create();
if (err < 0)
return err;
/* Set dev type */ /* Set dev type */
if (ifr->ifr_flags & IFF_TUN) { if (ifr->ifr_flags & IFF_TUN) {
...@@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) ...@@ -989,6 +995,8 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr)
tun->sk = sk; tun->sk = sk;
container_of(sk, struct tun_sock, sk)->tun = tun; container_of(sk, struct tun_sock, sk)->tun = tun;
security_tun_dev_post_create(sk);
tun_net_init(dev); tun_net_init(dev);
if (strchr(dev->name, '%')) { if (strchr(dev->name, '%')) {
......
...@@ -1752,12 +1752,12 @@ static int comedi_open(struct inode *inode, struct file *file) ...@@ -1752,12 +1752,12 @@ static int comedi_open(struct inode *inode, struct file *file)
mutex_lock(&dev->mutex); mutex_lock(&dev->mutex);
if (dev->attached) if (dev->attached)
goto ok; 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"); DPRINTK("in request module\n");
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
return -ENODEV; return -ENODEV;
} }
if (capable(CAP_SYS_MODULE) && dev->in_request_module) if (capable(CAP_NET_ADMIN) && dev->in_request_module)
goto ok; goto ok;
dev->in_request_module = 1; dev->in_request_module = 1;
...@@ -1770,8 +1770,8 @@ static int comedi_open(struct inode *inode, struct file *file) ...@@ -1770,8 +1770,8 @@ static int comedi_open(struct inode *inode, struct file *file)
dev->in_request_module = 0; dev->in_request_module = 0;
if (!dev->attached && !capable(CAP_SYS_MODULE)) { if (!dev->attached && !capable(CAP_NET_ADMIN)) {
DPRINTK("not attached and not CAP_SYS_MODULE\n"); DPRINTK("not attached and not CAP_NET_ADMIN\n");
mutex_unlock(&dev->mutex); mutex_unlock(&dev->mutex);
return -ENODEV; return -ENODEV;
} }
......
...@@ -1591,7 +1591,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd) ...@@ -1591,7 +1591,7 @@ SYSCALL_DEFINE2(flock, unsigned int, fd, unsigned int, cmd)
if (can_sleep) if (can_sleep)
lock->fl_flags |= FL_SLEEP; lock->fl_flags |= FL_SLEEP;
error = security_file_lock(filp, cmd); error = security_file_lock(filp, lock->fl_type);
if (error) if (error)
goto out_free; goto out_free;
......
...@@ -1533,9 +1533,11 @@ int may_open(struct path *path, int acc_mode, int flag) ...@@ -1533,9 +1533,11 @@ int may_open(struct path *path, int acc_mode, int flag)
if (error) if (error)
return error; return error;
error = ima_path_check(path, error = ima_path_check(path, acc_mode ?
acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC), acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC) :
ACC_MODE(flag) & (MAY_READ | MAY_WRITE),
IMA_COUNT_UPDATE); IMA_COUNT_UPDATE);
if (error) if (error)
return error; return error;
/* /*
......
...@@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -34,6 +34,8 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
int flags = nfsexp_flags(rqstp, exp); int flags = nfsexp_flags(rqstp, exp);
int ret; int ret;
validate_process_creds();
/* discard any old override before preparing the new set */ /* discard any old override before preparing the new set */
revert_creds(get_cred(current->real_cred)); revert_creds(get_cred(current->real_cred));
new = prepare_creds(); new = prepare_creds();
...@@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp) ...@@ -86,8 +88,10 @@ int nfsd_setuser(struct svc_rqst *rqstp, struct svc_export *exp)
else else
new->cap_effective = cap_raise_nfsd_set(new->cap_effective, new->cap_effective = cap_raise_nfsd_set(new->cap_effective,
new->cap_permitted); new->cap_permitted);
validate_process_creds();
put_cred(override_creds(new)); put_cred(override_creds(new));
put_cred(new); put_cred(new);
validate_process_creds();
return 0; return 0;
oom: oom:
......
...@@ -496,7 +496,9 @@ nfsd(void *vrqstp) ...@@ -496,7 +496,9 @@ nfsd(void *vrqstp)
/* Lock the export hash tables for reading. */ /* Lock the export hash tables for reading. */
exp_readlock(); exp_readlock();
validate_process_creds();
svc_process(rqstp); svc_process(rqstp);
validate_process_creds();
/* Unlock export hash tables */ /* Unlock export hash tables */
exp_readunlock(); exp_readunlock();
......
...@@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -684,6 +684,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
__be32 err; __be32 err;
int host_err; int host_err;
validate_process_creds();
/* /*
* If we get here, then the client has already done an "open", * If we get here, then the client has already done an "open",
* and (hopefully) checked permission - so allow OWNER_OVERRIDE * and (hopefully) checked permission - so allow OWNER_OVERRIDE
...@@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, ...@@ -740,6 +742,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
out_nfserr: out_nfserr:
err = nfserrno(host_err); err = nfserrno(host_err);
out: out:
validate_process_creds();
return err; return err;
} }
......
...@@ -199,7 +199,7 @@ SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user ...@@ -199,7 +199,7 @@ SYSCALL_DEFINE3(fstatfs64, unsigned int, fd, size_t, sz, struct statfs64 __user
int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs, int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
struct file *filp) struct file *filp)
{ {
int err; int ret;
struct iattr newattrs; struct iattr newattrs;
/* Not pretty: "inode->i_size" shouldn't really be signed. But it is. */ /* 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, ...@@ -214,12 +214,14 @@ int do_truncate(struct dentry *dentry, loff_t length, unsigned int time_attrs,
} }
/* Remove suid/sgid on truncate too */ /* 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); mutex_lock(&dentry->d_inode->i_mutex);
err = notify_change(dentry, &newattrs); ret = notify_change(dentry, &newattrs);
mutex_unlock(&dentry->d_inode->i_mutex); mutex_unlock(&dentry->d_inode->i_mutex);
return err; return ret;
} }
static long do_sys_truncate(const char __user *pathname, loff_t length) 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, ...@@ -957,6 +959,8 @@ struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags,
int error; int error;
struct file *f; struct file *f;
validate_creds(cred);
/* /*
* We must always pass in a valid mount pointer. Historically * We must always pass in a valid mount pointer. Historically
* callers got away with not passing it, but we must enforce this at * 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, ...@@ -760,6 +760,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
const struct inode_operations sysfs_dir_inode_operations = { const struct inode_operations sysfs_dir_inode_operations = {
.lookup = sysfs_lookup, .lookup = sysfs_lookup,
.setattr = sysfs_setattr, .setattr = sysfs_setattr,
.setxattr = sysfs_setxattr,
}; };
static void remove_dir(struct sysfs_dirent *sd) static void remove_dir(struct sysfs_dirent *sd)
......
...@@ -18,6 +18,8 @@ ...@@ -18,6 +18,8 @@
#include <linux/capability.h> #include <linux/capability.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include "sysfs.h" #include "sysfs.h"
extern struct super_block * sysfs_sb; extern struct super_block * sysfs_sb;
...@@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = { ...@@ -35,6 +37,7 @@ static struct backing_dev_info sysfs_backing_dev_info = {
static const struct inode_operations sysfs_inode_operations ={ static const struct inode_operations sysfs_inode_operations ={
.setattr = sysfs_setattr, .setattr = sysfs_setattr,
.setxattr = sysfs_setxattr,
}; };
int __init sysfs_inode_init(void) int __init sysfs_inode_init(void)
...@@ -42,18 +45,37 @@ int __init sysfs_inode_init(void) ...@@ -42,18 +45,37 @@ int __init sysfs_inode_init(void)
return bdi_init(&sysfs_backing_dev_info); 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) int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
{ {
struct inode * inode = dentry->d_inode; struct inode * inode = dentry->d_inode;
struct sysfs_dirent * sd = dentry->d_fsdata; 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; unsigned int ia_valid = iattr->ia_valid;
int error; int error;
if (!sd) if (!sd)
return -EINVAL; return -EINVAL;
sd_iattr = sd->s_iattr; sd_attrs = sd->s_iattr;
error = inode_change_ok(inode, iattr); error = inode_change_ok(inode, iattr);
if (error) if (error)
...@@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr) ...@@ -65,42 +87,77 @@ int sysfs_setattr(struct dentry * dentry, struct iattr * iattr)
if (error) if (error)
return error; return error;
if (!sd_iattr) { if (!sd_attrs) {
/* setting attributes for the first time, allocate now */ /* setting attributes for the first time, allocate now */
sd_iattr = kzalloc(sizeof(struct iattr), GFP_KERNEL); sd_attrs = sysfs_init_inode_attrs(sd);
if (!sd_iattr) if (!sd_attrs)
return -ENOMEM; return -ENOMEM;
/* assign default attributes */ sd->s_iattr = sd_attrs;
sd_iattr->ia_mode = sd->s_mode; } else {
sd_iattr->ia_uid = 0; /* attributes were changed at least once in past */
sd_iattr->ia_gid = 0; iattrs = &sd_attrs->ia_iattr;
sd_iattr->ia_atime = sd_iattr->ia_mtime = sd_iattr->ia_ctime = CURRENT_TIME;
sd->s_iattr = sd_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)
if (ia_valid & ATTR_UID) {
sd_iattr->ia_uid = iattr->ia_uid; struct sysfs_dirent *sd = dentry->d_fsdata;
if (ia_valid & ATTR_GID) struct sysfs_inode_attrs *iattrs;
sd_iattr->ia_gid = iattr->ia_gid; void *secdata;
if (ia_valid & ATTR_ATIME) int error;
sd_iattr->ia_atime = timespec_trunc(iattr->ia_atime, u32 secdata_len = 0;
inode->i_sb->s_time_gran);
if (ia_valid & ATTR_MTIME) if (!sd)
sd_iattr->ia_mtime = timespec_trunc(iattr->ia_mtime, return -EINVAL;
inode->i_sb->s_time_gran); if (!sd->s_iattr)
if (ia_valid & ATTR_CTIME) sd->s_iattr = sysfs_init_inode_attrs(sd);
sd_iattr->ia_ctime = timespec_trunc(iattr->ia_ctime, if (!sd->s_iattr)
inode->i_sb->s_time_gran); return -ENOMEM;
if (ia_valid & ATTR_MODE) {
umode_t mode = iattr->ia_mode; iattrs = sd->s_iattr;
if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) {
mode &= ~S_ISGID; const char *suffix = name + XATTR_SECURITY_PREFIX_LEN;
sd_iattr->ia_mode = sd->s_mode = mode; 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; return error;
} }
...@@ -146,6 +203,7 @@ static int sysfs_count_nlink(struct sysfs_dirent *sd) ...@@ -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) static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
{ {
struct bin_attribute *bin_attr; struct bin_attribute *bin_attr;
struct sysfs_inode_attrs *iattrs;
inode->i_private = sysfs_get(sd); inode->i_private = sysfs_get(sd);
inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->a_ops = &sysfs_aops;
...@@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) ...@@ -154,16 +212,20 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode)
inode->i_ino = sd->s_ino; inode->i_ino = sd->s_ino;
lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); 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 /* sysfs_dirent has non-default attributes
* get them for the new inode from persistent copy * get them for the new inode from persistent copy
* in sysfs_dirent * 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 } else
set_default_inode_attr(inode, sd->s_mode); set_default_inode_attr(inode, sd->s_mode);
/* initialize inode according to type */ /* initialize inode according to type */
switch (sysfs_type(sd)) { switch (sysfs_type(sd)) {
case SYSFS_DIR: case SYSFS_DIR:
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/security.h>
#include "sysfs.h" #include "sysfs.h"
...@@ -209,6 +210,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, void *co ...@@ -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 = { const struct inode_operations sysfs_symlink_inode_operations = {
.setxattr = sysfs_setxattr,
.readlink = generic_readlink, .readlink = generic_readlink,
.follow_link = sysfs_follow_link, .follow_link = sysfs_follow_link,
.put_link = sysfs_put_link, .put_link = sysfs_put_link,
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
* This file is released under the GPLv2. * This file is released under the GPLv2.
*/ */
#include <linux/fs.h>
struct sysfs_open_dirent; struct sysfs_open_dirent;
/* type-specific structures for sysfs_dirent->s_* union members */ /* type-specific structures for sysfs_dirent->s_* union members */
...@@ -31,6 +33,12 @@ struct sysfs_elem_bin_attr { ...@@ -31,6 +33,12 @@ struct sysfs_elem_bin_attr {
struct hlist_head buffers; 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 * sysfs_dirent - the building block of sysfs hierarchy. Each and
* every sysfs node is represented by single sysfs_dirent. * every sysfs node is represented by single sysfs_dirent.
...@@ -56,7 +64,7 @@ struct sysfs_dirent { ...@@ -56,7 +64,7 @@ struct sysfs_dirent {
unsigned int s_flags; unsigned int s_flags;
ino_t s_ino; ino_t s_ino;
umode_t s_mode; umode_t s_mode;
struct iattr *s_iattr; struct sysfs_inode_attrs *s_iattr;
}; };
#define SD_DEACTIVATED_BIAS INT_MIN #define SD_DEACTIVATED_BIAS INT_MIN
...@@ -148,6 +156,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) ...@@ -148,6 +156,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
struct inode *sysfs_get_inode(struct sysfs_dirent *sd); struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
void sysfs_delete_inode(struct inode *inode); void sysfs_delete_inode(struct inode *inode);
int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); 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_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
int sysfs_inode_init(void); int sysfs_inode_init(void);
......
...@@ -66,22 +66,28 @@ xattr_permission(struct inode *inode, const char *name, int mask) ...@@ -66,22 +66,28 @@ xattr_permission(struct inode *inode, const char *name, int mask)
return inode_permission(inode, mask); return inode_permission(inode, mask);
} }
int /**
vfs_setxattr(struct dentry *dentry, const char *name, const void *value, * __vfs_setxattr_noperm - perform setxattr operation without performing
size_t size, int flags) * 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; 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) { if (inode->i_op->setxattr) {
error = inode->i_op->setxattr(dentry, name, value, size, flags); error = inode->i_op->setxattr(dentry, name, value, size, flags);
if (!error) { if (!error) {
...@@ -97,6 +103,29 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value, ...@@ -97,6 +103,29 @@ vfs_setxattr(struct dentry *dentry, const char *name, const void *value,
if (!error) if (!error)
fsnotify_xattr(dentry); 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: out:
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
return error; return error;
......
...@@ -114,6 +114,13 @@ struct thread_group_cred { ...@@ -114,6 +114,13 @@ struct thread_group_cred {
*/ */
struct cred { struct cred {
atomic_t usage; 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 */ uid_t uid; /* real UID of the task */
gid_t gid; /* real GID of the task */ gid_t gid; /* real GID of the task */
uid_t suid; /* saved UID of the task */ uid_t suid; /* saved UID of the task */
...@@ -143,7 +150,9 @@ struct cred { ...@@ -143,7 +150,9 @@ struct cred {
}; };
extern void __put_cred(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 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_creds(void);
extern struct cred *prepare_exec_creds(void); extern struct cred *prepare_exec_creds(void);
extern struct cred *prepare_usermodehelper_creds(void); extern struct cred *prepare_usermodehelper_creds(void);
...@@ -158,6 +167,60 @@ extern int set_security_override_from_ctx(struct cred *, const char *); ...@@ -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 int set_create_files_as(struct cred *, struct inode *);
extern void __init cred_init(void); 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 * get_new_cred - Get a reference on a new set of credentials
* @cred: The new credentials to reference * @cred: The new credentials to reference
...@@ -186,7 +249,9 @@ static inline struct cred *get_new_cred(struct cred *cred) ...@@ -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) 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) ...@@ -204,7 +269,7 @@ static inline void put_cred(const struct cred *_cred)
{ {
struct cred *cred = (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)) if (atomic_dec_and_test(&(cred)->usage))
__put_cred(cred); __put_cred(cred);
} }
......
...@@ -129,7 +129,10 @@ struct key { ...@@ -129,7 +129,10 @@ struct key {
struct rw_semaphore sem; /* change vs change sem */ struct rw_semaphore sem; /* change vs change sem */
struct key_user *user; /* owner of this key */ struct key_user *user; /* owner of this key */
void *security; /* security data for 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; uid_t uid;
gid_t gid; gid_t gid;
key_perm_t perm; /* access permissions */ key_perm_t perm; /* access permissions */
...@@ -275,6 +278,8 @@ static inline key_serial_t key_serial(struct key *key) ...@@ -275,6 +278,8 @@ static inline key_serial_t key_serial(struct key *key)
extern ctl_table key_sysctls[]; extern ctl_table key_sysctls[];
#endif #endif
extern void key_replace_session_keyring(void);
/* /*
* the userspace interface * the userspace interface
*/ */
...@@ -297,6 +302,7 @@ extern void key_init(void); ...@@ -297,6 +302,7 @@ extern void key_init(void);
#define key_fsuid_changed(t) do { } while(0) #define key_fsuid_changed(t) do { } while(0)
#define key_fsgid_changed(t) do { } while(0) #define key_fsgid_changed(t) do { } while(0)
#define key_init() do { } while(0) #define key_init() do { } while(0)
#define key_replace_session_keyring() do { } while(0)
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
......
...@@ -52,5 +52,6 @@ ...@@ -52,5 +52,6 @@
#define KEYCTL_SET_TIMEOUT 15 /* set key timeout */ #define KEYCTL_SET_TIMEOUT 15 /* set key timeout */
#define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */ #define KEYCTL_ASSUME_AUTHORITY 16 /* assume request_key() authorisation */
#define KEYCTL_GET_SECURITY 17 /* get key security label */ #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 */ #endif /* _LINUX_KEYCTL_H */
...@@ -33,6 +33,7 @@ struct common_audit_data { ...@@ -33,6 +33,7 @@ struct common_audit_data {
#define LSM_AUDIT_DATA_IPC 4 #define LSM_AUDIT_DATA_IPC 4
#define LSM_AUDIT_DATA_TASK 5 #define LSM_AUDIT_DATA_TASK 5
#define LSM_AUDIT_DATA_KEY 6 #define LSM_AUDIT_DATA_KEY 6
#define LSM_AUDIT_NO_AUDIT 7
struct task_struct *tsk; struct task_struct *tsk;
union { union {
struct { struct {
...@@ -66,16 +67,19 @@ struct common_audit_data { ...@@ -66,16 +67,19 @@ struct common_audit_data {
} key_struct; } key_struct;
#endif #endif
} u; } u;
const char *function;
/* this union contains LSM specific data */ /* this union contains LSM specific data */
union { union {
#ifdef CONFIG_SECURITY_SMACK
/* SMACK data */ /* SMACK data */
struct smack_audit_data { struct smack_audit_data {
const char *function;
char *subject; char *subject;
char *object; char *object;
char *request; char *request;
int result; int result;
} smack_audit_data; } smack_audit_data;
#endif
#ifdef CONFIG_SECURITY_SELINUX
/* SELinux data */ /* SELinux data */
struct { struct {
u32 ssid; u32 ssid;
...@@ -83,10 +87,12 @@ struct common_audit_data { ...@@ -83,10 +87,12 @@ struct common_audit_data {
u16 tclass; u16 tclass;
u32 requested; u32 requested;
u32 audited; u32 audited;
u32 denied;
struct av_decision *avd; struct av_decision *avd;
int result; int result;
} selinux_audit_data; } selinux_audit_data;
} lsm_priv; #endif
};
/* these callback will be implemented by a specific LSM */ /* these callback will be implemented by a specific LSM */
void (*lsm_pre_audit)(struct audit_buffer *, void *); void (*lsm_pre_audit)(struct audit_buffer *, void *);
void (*lsm_post_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, ...@@ -104,7 +110,7 @@ int ipv6_skb_to_auditdata(struct sk_buff *skb,
/* Initialize an LSM audit data structure. */ /* Initialize an LSM audit data structure. */
#define COMMON_AUDIT_DATA_INIT(_d, _t) \ #define COMMON_AUDIT_DATA_INIT(_d, _t) \
{ memset((_d), 0, sizeof(struct common_audit_data)); \ { 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); void common_lsm_audit(struct common_audit_data *a);
......
...@@ -1292,6 +1292,7 @@ struct task_struct { ...@@ -1292,6 +1292,7 @@ struct task_struct {
struct mutex cred_guard_mutex; /* guard against foreign influences on struct mutex cred_guard_mutex; /* guard against foreign influences on
* credential calculations * credential calculations
* (notably. ptrace) */ * (notably. ptrace) */
struct cred *replacement_session_keyring; /* for KEYCTL_SESSION_TO_PARENT */
char comm[TASK_COMM_LEN]; /* executable name excluding path char comm[TASK_COMM_LEN]; /* executable name excluding path
- access with [gs]et_task_comm (which lock - access with [gs]et_task_comm (which lock
...@@ -2077,7 +2078,7 @@ static inline unsigned long wait_task_inactive(struct task_struct *p, ...@@ -2077,7 +2078,7 @@ static inline unsigned long wait_task_inactive(struct task_struct *p,
#define for_each_process(p) \ #define for_each_process(p) \
for (p = &init_task ; (p = next_task(p)) != &init_task ; ) 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 * Careful: do_each_thread/while_each_thread is a double loop so
......
...@@ -53,7 +53,7 @@ struct audit_krule; ...@@ -53,7 +53,7 @@ struct audit_krule;
extern int cap_capable(struct task_struct *tsk, const struct cred *cred, extern int cap_capable(struct task_struct *tsk, const struct cred *cred,
int cap, int audit); int cap, int audit);
extern int cap_settime(struct timespec *ts, struct timezone *tz); 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_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_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, 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) ...@@ -653,6 +653,11 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* manual page for definitions of the @clone_flags. * manual page for definitions of the @clone_flags.
* @clone_flags contains the flags indicating what should be shared. * @clone_flags contains the flags indicating what should be shared.
* Return 0 if permission is granted. * 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_free:
* @cred points to the credentials. * @cred points to the credentials.
* Deallocate and clear the cred->security field in a set of 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) ...@@ -665,6 +670,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @new points to the new credentials. * @new points to the new credentials.
* @old points to the original credentials. * @old points to the original credentials.
* Install a new set of 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: * @kernel_act_as:
* Set the credentials for a kernel service to act as (subjective context). * Set the credentials for a kernel service to act as (subjective context).
* @new points to the credentials to be modified. * @new points to the credentials to be modified.
...@@ -678,6 +687,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -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. * @inode points to the inode to use as a reference.
* The current task must be the one that nominated @inode. * The current task must be the one that nominated @inode.
* Return 0 if successful. * 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: * @task_setuid:
* Check permission before setting one or more of the user identity * Check permission before setting one or more of the user identity
* attributes of the current process. The @flags parameter indicates * 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) ...@@ -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. * Sets the connection's peersid to the secmark on skb.
* @req_classify_flow: * @req_classify_flow:
* Sets the flow's sid to the openreq sid. * 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. * Security hooks for XFRM operations.
* *
...@@ -1088,6 +1112,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -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 * Return the length of the string (including terminating NUL) or -ve if
* an error. * an error.
* May also return 0 (and a NULL buffer pointer) if there is no label. * 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. * Security hooks affecting all System V IPC operations.
* *
...@@ -1229,7 +1260,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -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. * @alter contains the flag indicating whether changes are to be made.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* *
* @ptrace_may_access: * @ptrace_access_check:
* Check permission before allowing the current process to trace the * Check permission before allowing the current process to trace the
* @child process. * @child process.
* Security modules may also want to perform a process tracing check * 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) ...@@ -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 * Check that the @parent process has sufficient permission to trace the
* current process before allowing the current process to present itself * current process before allowing the current process to present itself
* to the @parent process for tracing. * 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. * checks before it is allowed to trace this one.
* @parent contains the task_struct structure for debugger process. * @parent contains the task_struct structure for debugger process.
* Return 0 if permission is granted. * Return 0 if permission is granted.
...@@ -1351,12 +1382,47 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -1351,12 +1382,47 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* audit_rule_init. * audit_rule_init.
* @rule contains the allocated rule * @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. * This is the main security structure.
*/ */
struct security_operations { struct security_operations {
char name[SECURITY_NAME_MAX + 1]; 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 (*ptrace_traceme) (struct task_struct *parent);
int (*capget) (struct task_struct *target, int (*capget) (struct task_struct *target,
kernel_cap_t *effective, kernel_cap_t *effective,
...@@ -1483,12 +1549,15 @@ struct security_operations { ...@@ -1483,12 +1549,15 @@ struct security_operations {
int (*dentry_open) (struct file *file, const struct cred *cred); int (*dentry_open) (struct file *file, const struct cred *cred);
int (*task_create) (unsigned long clone_flags); int (*task_create) (unsigned long clone_flags);
int (*cred_alloc_blank) (struct cred *cred, gfp_t gfp);
void (*cred_free) (struct cred *cred); void (*cred_free) (struct cred *cred);
int (*cred_prepare)(struct cred *new, const struct cred *old, int (*cred_prepare)(struct cred *new, const struct cred *old,
gfp_t gfp); gfp_t gfp);
void (*cred_commit)(struct cred *new, const struct cred *old); 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_act_as)(struct cred *new, u32 secid);
int (*kernel_create_files_as)(struct cred *new, struct inode *inode); 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_setuid) (uid_t id0, uid_t id1, uid_t id2, int flags);
int (*task_fix_setuid) (struct cred *new, const struct cred *old, int (*task_fix_setuid) (struct cred *new, const struct cred *old,
int flags); int flags);
...@@ -1556,6 +1625,10 @@ struct security_operations { ...@@ -1556,6 +1625,10 @@ struct security_operations {
int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid); int (*secctx_to_secid) (const char *secdata, u32 seclen, u32 *secid);
void (*release_secctx) (char *secdata, u32 seclen); 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 #ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket *sock, int (*unix_stream_connect) (struct socket *sock,
struct socket *other, struct sock *newsk); struct socket *other, struct sock *newsk);
...@@ -1592,6 +1665,9 @@ struct security_operations { ...@@ -1592,6 +1665,9 @@ struct security_operations {
void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req); void (*inet_csk_clone) (struct sock *newsk, const struct request_sock *req);
void (*inet_conn_established) (struct sock *sk, struct sk_buff *skb); void (*inet_conn_established) (struct sock *sk, struct sk_buff *skb);
void (*req_classify_flow) (const struct request_sock *req, struct flowi *fl); 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 */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
...@@ -1620,6 +1696,9 @@ struct security_operations { ...@@ -1620,6 +1696,9 @@ struct security_operations {
const struct cred *cred, const struct cred *cred,
key_perm_t perm); key_perm_t perm);
int (*key_getsecurity)(struct key *key, char **_buffer); 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 */ #endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
...@@ -1637,7 +1716,7 @@ extern int security_module_enable(struct security_operations *ops); ...@@ -1637,7 +1716,7 @@ extern int security_module_enable(struct security_operations *ops);
extern int register_security(struct security_operations *ops); extern int register_security(struct security_operations *ops);
/* Security operations */ /* 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_ptrace_traceme(struct task_struct *parent);
int security_capget(struct task_struct *target, int security_capget(struct task_struct *target,
kernel_cap_t *effective, kernel_cap_t *effective,
...@@ -1736,11 +1815,14 @@ int security_file_send_sigiotask(struct task_struct *tsk, ...@@ -1736,11 +1815,14 @@ int security_file_send_sigiotask(struct task_struct *tsk,
int security_file_receive(struct file *file); int security_file_receive(struct file *file);
int security_dentry_open(struct file *file, const struct cred *cred); int security_dentry_open(struct file *file, const struct cred *cred);
int security_task_create(unsigned long clone_flags); 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); void security_cred_free(struct cred *cred);
int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); 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_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_act_as(struct cred *new, u32 secid);
int security_kernel_create_files_as(struct cred *new, struct inode *inode); 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_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 security_task_fix_setuid(struct cred *new, const struct cred *old,
int flags); int flags);
...@@ -1796,6 +1878,9 @@ int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); ...@@ -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); int security_secctx_to_secid(const char *secdata, u32 seclen, u32 *secid);
void security_release_secctx(char *secdata, u32 seclen); 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 */ #else /* CONFIG_SECURITY */
struct security_mnt_opts { struct security_mnt_opts {
}; };
...@@ -1818,10 +1903,10 @@ static inline int security_init(void) ...@@ -1818,10 +1903,10 @@ static inline int security_init(void)
return 0; 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) 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) static inline int security_ptrace_traceme(struct task_struct *parent)
...@@ -2266,6 +2351,11 @@ static inline int security_task_create(unsigned long clone_flags) ...@@ -2266,6 +2351,11 @@ static inline int security_task_create(unsigned long clone_flags)
return 0; 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) static inline void security_cred_free(struct cred *cred)
{ } { }
...@@ -2281,6 +2371,11 @@ static inline void security_commit_creds(struct cred *new, ...@@ -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) static inline int security_kernel_act_as(struct cred *cred, u32 secid)
{ {
return 0; return 0;
...@@ -2292,6 +2387,11 @@ static inline int security_kernel_create_files_as(struct cred *cred, ...@@ -2292,6 +2387,11 @@ static inline int security_kernel_create_files_as(struct cred *cred,
return 0; 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, static inline int security_task_setuid(uid_t id0, uid_t id1, uid_t id2,
int flags) int flags)
{ {
...@@ -2537,6 +2637,19 @@ static inline int security_secctx_to_secid(const char *secdata, ...@@ -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 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 */ #endif /* CONFIG_SECURITY */
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
...@@ -2575,6 +2688,9 @@ void security_inet_csk_clone(struct sock *newsk, ...@@ -2575,6 +2688,9 @@ void security_inet_csk_clone(struct sock *newsk,
const struct request_sock *req); const struct request_sock *req);
void security_inet_conn_established(struct sock *sk, void security_inet_conn_established(struct sock *sk,
struct sk_buff *skb); 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 */ #else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket *sock, static inline int security_unix_stream_connect(struct socket *sock,
...@@ -2725,6 +2841,20 @@ static inline void security_inet_conn_established(struct sock *sk, ...@@ -2725,6 +2841,20 @@ static inline void security_inet_conn_established(struct sock *sk,
struct sk_buff *skb) 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 */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
...@@ -2881,6 +3011,9 @@ void security_key_free(struct key *key); ...@@ -2881,6 +3011,9 @@ void security_key_free(struct key *key);
int security_key_permission(key_ref_t key_ref, int security_key_permission(key_ref_t key_ref,
const struct cred *cred, key_perm_t perm); const struct cred *cred, key_perm_t perm);
int security_key_getsecurity(struct key *key, char **_buffer); 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 #else
...@@ -2908,6 +3041,13 @@ static inline int security_key_getsecurity(struct key *key, char **_buffer) ...@@ -2908,6 +3041,13 @@ static inline int security_key_getsecurity(struct key *key, char **_buffer)
return 0; 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
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
......
...@@ -49,6 +49,7 @@ struct xattr_handler { ...@@ -49,6 +49,7 @@ struct xattr_handler {
ssize_t xattr_getsecurity(struct inode *, const char *, void *, size_t); 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_getxattr(struct dentry *, const char *, void *, size_t);
ssize_t vfs_listxattr(struct dentry *d, char *list, size_t size); 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_setxattr(struct dentry *, const char *, const void *, size_t, int);
int vfs_removexattr(struct dentry *, const char *); int vfs_removexattr(struct dentry *, const char *);
......
...@@ -491,13 +491,17 @@ static void do_acct_process(struct bsd_acct_struct *acct, ...@@ -491,13 +491,17 @@ static void do_acct_process(struct bsd_acct_struct *acct,
u64 run_time; u64 run_time;
struct timespec uptime; struct timespec uptime;
struct tty_struct *tty; 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 * First check to see if there is enough free_space to continue
* the process accounting system. * the process accounting system.
*/ */
if (!check_free_space(acct, file)) if (!check_free_space(acct, file))
return; goto out;
/* /*
* Fill the accounting struct with the needed info as recorded * Fill the accounting struct with the needed info as recorded
...@@ -578,6 +582,8 @@ static void do_acct_process(struct bsd_acct_struct *acct, ...@@ -578,6 +582,8 @@ static void do_acct_process(struct bsd_acct_struct *acct,
sizeof(acct_t), &file->f_pos); sizeof(acct_t), &file->f_pos);
current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim; current->signal->rlim[RLIMIT_FSIZE].rlim_cur = flim;
set_fs(fs); set_fs(fs);
out:
revert_creds(orig_cred);
} }
/** /**
......
...@@ -18,6 +18,18 @@ ...@@ -18,6 +18,18 @@
#include <linux/cn_proc.h> #include <linux/cn_proc.h>
#include "cred-internals.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; static struct kmem_cache *cred_jar;
/* /*
...@@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = { ...@@ -36,6 +48,10 @@ static struct thread_group_cred init_tgcred = {
*/ */
struct cred init_cred = { struct cred init_cred = {
.usage = ATOMIC_INIT(4), .usage = ATOMIC_INIT(4),
#ifdef CONFIG_DEBUG_CREDENTIALS
.subscribers = ATOMIC_INIT(2),
.magic = CRED_MAGIC,
#endif
.securebits = SECUREBITS_DEFAULT, .securebits = SECUREBITS_DEFAULT,
.cap_inheritable = CAP_INIT_INH_SET, .cap_inheritable = CAP_INIT_INH_SET,
.cap_permitted = CAP_FULL_SET, .cap_permitted = CAP_FULL_SET,
...@@ -48,6 +64,31 @@ struct cred init_cred = { ...@@ -48,6 +64,31 @@ struct cred init_cred = {
#endif #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 * Dispose of the shared task group credentials
*/ */
...@@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu) ...@@ -85,9 +126,22 @@ static void put_cred_rcu(struct rcu_head *rcu)
{ {
struct cred *cred = container_of(rcu, struct cred, 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) if (atomic_read(&cred->usage) != 0)
panic("CRED: put_cred_rcu() sees %p with usage %d\n", panic("CRED: put_cred_rcu() sees %p with usage %d\n",
cred, atomic_read(&cred->usage)); cred, atomic_read(&cred->usage));
#endif
security_cred_free(cred); security_cred_free(cred);
key_put(cred->thread_keyring); key_put(cred->thread_keyring);
...@@ -106,12 +160,90 @@ static void put_cred_rcu(struct rcu_head *rcu) ...@@ -106,12 +160,90 @@ static void put_cred_rcu(struct rcu_head *rcu)
*/ */
void __put_cred(struct cred *cred) 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); 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); call_rcu(&cred->rcu, put_cred_rcu);
} }
EXPORT_SYMBOL(__put_cred); 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 * prepare_creds - Prepare a new set of credentials for modification
* *
...@@ -132,16 +264,19 @@ struct cred *prepare_creds(void) ...@@ -132,16 +264,19 @@ struct cred *prepare_creds(void)
const struct cred *old; const struct cred *old;
struct cred *new; struct cred *new;
BUG_ON(atomic_read(&task->real_cred->usage) < 1); validate_process_creds();
new = kmem_cache_alloc(cred_jar, GFP_KERNEL); new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
if (!new) if (!new)
return NULL; return NULL;
kdebug("prepare_creds() alloc %p", new);
old = task->cred; old = task->cred;
memcpy(new, old, sizeof(struct cred)); memcpy(new, old, sizeof(struct cred));
atomic_set(&new->usage, 1); atomic_set(&new->usage, 1);
set_cred_subscribers(new, 0);
get_group_info(new->group_info); get_group_info(new->group_info);
get_uid(new->user); get_uid(new->user);
...@@ -157,6 +292,7 @@ struct cred *prepare_creds(void) ...@@ -157,6 +292,7 @@ struct cred *prepare_creds(void)
if (security_prepare_creds(new, old, GFP_KERNEL) < 0) if (security_prepare_creds(new, old, GFP_KERNEL) < 0)
goto error; goto error;
validate_creds(new);
return new; return new;
error: error:
...@@ -229,9 +365,12 @@ struct cred *prepare_usermodehelper_creds(void) ...@@ -229,9 +365,12 @@ struct cred *prepare_usermodehelper_creds(void)
if (!new) if (!new)
return NULL; return NULL;
kdebug("prepare_usermodehelper_creds() alloc %p", new);
memcpy(new, &init_cred, sizeof(struct cred)); memcpy(new, &init_cred, sizeof(struct cred));
atomic_set(&new->usage, 1); atomic_set(&new->usage, 1);
set_cred_subscribers(new, 0);
get_group_info(new->group_info); get_group_info(new->group_info);
get_uid(new->user); get_uid(new->user);
...@@ -250,6 +389,7 @@ struct cred *prepare_usermodehelper_creds(void) ...@@ -250,6 +389,7 @@ struct cred *prepare_usermodehelper_creds(void)
#endif #endif
if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0) if (security_prepare_creds(new, &init_cred, GFP_ATOMIC) < 0)
goto error; goto error;
validate_creds(new);
BUG_ON(atomic_read(&new->usage) != 1); BUG_ON(atomic_read(&new->usage) != 1);
return new; return new;
...@@ -286,6 +426,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) ...@@ -286,6 +426,10 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
) { ) {
p->real_cred = get_cred(p->cred); p->real_cred = get_cred(p->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); atomic_inc(&p->cred->user->processes);
return 0; return 0;
} }
...@@ -331,6 +475,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) ...@@ -331,6 +475,8 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
atomic_inc(&new->user->processes); atomic_inc(&new->user->processes);
p->cred = p->real_cred = get_cred(new); p->cred = p->real_cred = get_cred(new);
alter_cred_subscribers(new, 2);
validate_creds(new);
return 0; return 0;
error_put: error_put:
...@@ -355,13 +501,20 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags) ...@@ -355,13 +501,20 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)
int commit_creds(struct cred *new) int commit_creds(struct cred *new)
{ {
struct task_struct *task = current; struct task_struct *task = current;
const struct cred *old; const struct cred *old = task->real_cred;
BUG_ON(task->cred != task->real_cred); kdebug("commit_creds(%p{%d,%d})", new,
BUG_ON(atomic_read(&task->real_cred->usage) < 2); 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); BUG_ON(atomic_read(&new->usage) < 1);
old = task->real_cred;
security_commit_creds(new, old); security_commit_creds(new, old);
get_cred(new); /* we will require a ref for the subj creds too */ get_cred(new); /* we will require a ref for the subj creds too */
...@@ -390,12 +543,14 @@ int commit_creds(struct cred *new) ...@@ -390,12 +543,14 @@ int commit_creds(struct cred *new)
* cheaply with the new uid cache, so if it matters * cheaply with the new uid cache, so if it matters
* we should be checking for it. -DaveM * we should be checking for it. -DaveM
*/ */
alter_cred_subscribers(new, 2);
if (new->user != old->user) if (new->user != old->user)
atomic_inc(&new->user->processes); atomic_inc(&new->user->processes);
rcu_assign_pointer(task->real_cred, new); rcu_assign_pointer(task->real_cred, new);
rcu_assign_pointer(task->cred, new); rcu_assign_pointer(task->cred, new);
if (new->user != old->user) if (new->user != old->user)
atomic_dec(&old->user->processes); atomic_dec(&old->user->processes);
alter_cred_subscribers(old, -2);
sched_switch_user(task); sched_switch_user(task);
...@@ -428,6 +583,13 @@ EXPORT_SYMBOL(commit_creds); ...@@ -428,6 +583,13 @@ EXPORT_SYMBOL(commit_creds);
*/ */
void abort_creds(struct cred *new) 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); BUG_ON(atomic_read(&new->usage) < 1);
put_cred(new); put_cred(new);
} }
...@@ -444,7 +606,20 @@ const struct cred *override_creds(const struct cred *new) ...@@ -444,7 +606,20 @@ const struct cred *override_creds(const struct cred *new)
{ {
const struct cred *old = current->cred; 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; return old;
} }
EXPORT_SYMBOL(override_creds); EXPORT_SYMBOL(override_creds);
...@@ -460,7 +635,15 @@ void revert_creds(const struct cred *old) ...@@ -460,7 +635,15 @@ void revert_creds(const struct cred *old)
{ {
const struct cred *override = current->cred; 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); rcu_assign_pointer(current->cred, old);
alter_cred_subscribers(override, -1);
put_cred(override); put_cred(override);
} }
EXPORT_SYMBOL(revert_creds); EXPORT_SYMBOL(revert_creds);
...@@ -502,11 +685,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) ...@@ -502,11 +685,15 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
if (!new) if (!new)
return NULL; return NULL;
kdebug("prepare_kernel_cred() alloc %p", new);
if (daemon) if (daemon)
old = get_task_cred(daemon); old = get_task_cred(daemon);
else else
old = get_cred(&init_cred); old = get_cred(&init_cred);
validate_creds(old);
*new = *old; *new = *old;
get_uid(new->user); get_uid(new->user);
get_group_info(new->group_info); get_group_info(new->group_info);
...@@ -526,7 +713,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon) ...@@ -526,7 +713,9 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)
goto error; goto error;
atomic_set(&new->usage, 1); atomic_set(&new->usage, 1);
set_cred_subscribers(new, 0);
put_cred(old); put_cred(old);
validate_creds(new);
return new; return new;
error: error:
...@@ -589,3 +778,95 @@ int set_create_files_as(struct cred *new, struct inode *inode) ...@@ -589,3 +778,95 @@ int set_create_files_as(struct cred *new, struct inode *inode)
return security_kernel_create_files_as(new, inode); return security_kernel_create_files_as(new, inode);
} }
EXPORT_SYMBOL(set_create_files_as); 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) ...@@ -901,6 +901,8 @@ NORET_TYPE void do_exit(long code)
tracehook_report_exit(&code); tracehook_report_exit(&code);
validate_creds_for_do_exit(tsk);
/* /*
* We're taking recursive faults here in do_exit. Safest is to just * We're taking recursive faults here in do_exit. Safest is to just
* leave this task alone and wait for reboot. * leave this task alone and wait for reboot.
...@@ -1009,6 +1011,8 @@ NORET_TYPE void do_exit(long code) ...@@ -1009,6 +1011,8 @@ NORET_TYPE void do_exit(long code)
if (tsk->splice_pipe) if (tsk->splice_pipe)
__free_pipe_info(tsk->splice_pipe); __free_pipe_info(tsk->splice_pipe);
validate_creds_for_do_exit(tsk);
preempt_disable(); preempt_disable();
/* causes final put_task_struct in finish_task_switch(). */ /* causes final put_task_struct in finish_task_switch(). */
tsk->state = TASK_DEAD; tsk->state = TASK_DEAD;
......
...@@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk) ...@@ -152,8 +152,7 @@ void __put_task_struct(struct task_struct *tsk)
WARN_ON(atomic_read(&tsk->usage)); WARN_ON(atomic_read(&tsk->usage));
WARN_ON(tsk == current); WARN_ON(tsk == current);
put_cred(tsk->real_cred); exit_creds(tsk);
put_cred(tsk->cred);
delayacct_tsk_free(tsk); delayacct_tsk_free(tsk);
if (!profile_handoff_task(tsk)) if (!profile_handoff_task(tsk))
...@@ -1297,8 +1296,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, ...@@ -1297,8 +1296,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
module_put(task_thread_info(p)->exec_domain->module); module_put(task_thread_info(p)->exec_domain->module);
bad_fork_cleanup_count: bad_fork_cleanup_count:
atomic_dec(&p->cred->user->processes); atomic_dec(&p->cred->user->processes);
put_cred(p->real_cred); exit_creds(p);
put_cred(p->cred);
bad_fork_free: bad_fork_free:
free_task(p); free_task(p);
fork_out: fork_out:
......
...@@ -78,6 +78,10 @@ int __request_module(bool wait, const char *fmt, ...) ...@@ -78,6 +78,10 @@ int __request_module(bool wait, const char *fmt, ...)
#define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */ #define MAX_KMOD_CONCURRENT 50 /* Completely arbitrary value - KAO */
static int kmod_loop_msg; static int kmod_loop_msg;
ret = security_kernel_module_request();
if (ret)
return ret;
va_start(args, fmt); va_start(args, fmt);
ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args); ret = vsnprintf(module_name, MODULE_NAME_LEN, fmt, args);
va_end(args); va_end(args);
...@@ -462,6 +466,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info, ...@@ -462,6 +466,7 @@ int call_usermodehelper_exec(struct subprocess_info *sub_info,
int retval = 0; int retval = 0;
BUG_ON(atomic_read(&sub_info->cred->usage) != 1); BUG_ON(atomic_read(&sub_info->cred->usage) != 1);
validate_creds(sub_info->cred);
helper_lock(); helper_lock();
if (sub_info->path[0] == '\0') if (sub_info->path[0] == '\0')
......
...@@ -152,7 +152,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode) ...@@ -152,7 +152,7 @@ int __ptrace_may_access(struct task_struct *task, unsigned int mode)
if (!dumpable && !capable(CAP_SYS_PTRACE)) if (!dumpable && !capable(CAP_SYS_PTRACE))
return -EPERM; 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) bool ptrace_may_access(struct task_struct *task, unsigned int mode)
......
...@@ -49,7 +49,6 @@ ...@@ -49,7 +49,6 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/reboot.h> #include <linux/reboot.h>
#include <linux/ftrace.h> #include <linux/ftrace.h>
#include <linux/security.h>
#include <linux/slow-work.h> #include <linux/slow-work.h>
#include <linux/perf_counter.h> #include <linux/perf_counter.h>
......
...@@ -653,6 +653,21 @@ config DEBUG_NOTIFIERS ...@@ -653,6 +653,21 @@ config DEBUG_NOTIFIERS
This is a relatively cheap check but if you care about maximum This is a relatively cheap check but if you care about maximum
performance, say N. 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 # Select this config option from the architecture Kconfig, if it
# it is preferred to always offer frame pointers as a config # it is preferred to always offer frame pointers as a config
......
...@@ -12,34 +12,47 @@ ...@@ -12,34 +12,47 @@
#include <linux/sched.h> #include <linux/sched.h>
/** /*
* is_single_threaded - Determine if a thread group is single-threaded or not * Returns true if the task does not share ->mm with another thread/process.
* @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.
*/ */
bool is_single_threaded(struct task_struct *p) bool current_is_single_threaded(void)
{ {
struct task_struct *g, *t; struct task_struct *task = current;
struct mm_struct *mm = p->mm; struct mm_struct *mm = task->mm;
struct task_struct *p, *t;
bool ret;
if (atomic_read(&p->signal->count) != 1) if (atomic_read(&task->signal->live) != 1)
goto no; return false;
if (atomic_read(&p->mm->mm_users) != 1) { if (atomic_read(&mm->mm_users) == 1)
read_lock(&tasklist_lock); return true;
do_each_thread(g, t) {
if (t->mm == mm && t != p)
goto no_unlock;
} while_each_thread(g, t);
read_unlock(&tasklist_lock);
}
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();
no_unlock: return ret;
read_unlock(&tasklist_lock);
no:
return false;
} }
...@@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name) ...@@ -1031,7 +1031,7 @@ void dev_load(struct net *net, const char *name)
dev = __dev_get_by_name(net, name); dev = __dev_get_by_name(net, name);
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
if (!dev && capable(CAP_SYS_MODULE)) if (!dev && capable(CAP_NET_ADMIN))
request_module("%s", name); request_module("%s", name);
} }
......
...@@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char *name) ...@@ -116,7 +116,7 @@ int tcp_set_default_congestion_control(const char *name)
spin_lock(&tcp_cong_list_lock); spin_lock(&tcp_cong_list_lock);
ca = tcp_ca_find(name); ca = tcp_ca_find(name);
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
if (!ca && capable(CAP_SYS_MODULE)) { if (!ca && capable(CAP_NET_ADMIN)) {
spin_unlock(&tcp_cong_list_lock); spin_unlock(&tcp_cong_list_lock);
request_module("tcp_%s", name); request_module("tcp_%s", name);
...@@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const char *name) ...@@ -246,7 +246,7 @@ int tcp_set_congestion_control(struct sock *sk, const char *name)
#ifdef CONFIG_MODULES #ifdef CONFIG_MODULES
/* not found attempt to autoload module */ /* not found attempt to autoload module */
if (!ca && capable(CAP_SYS_MODULE)) { if (!ca && capable(CAP_NET_ADMIN)) {
rcu_read_unlock(); rcu_read_unlock();
request_module("tcp_%s", name); request_module("tcp_%s", name);
rcu_read_lock(); rcu_read_lock();
......
...@@ -16,9 +16,7 @@ obj-$(CONFIG_SECURITYFS) += inode.o ...@@ -16,9 +16,7 @@ obj-$(CONFIG_SECURITYFS) += inode.o
# Must precede capability.o in order to stack properly. # Must precede capability.o in order to stack properly.
obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
ifeq ($(CONFIG_AUDIT),y) obj-$(CONFIG_AUDIT) += lsm_audit.o
obj-$(CONFIG_SECURITY_SMACK) += lsm_audit.o
endif
obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
......
...@@ -373,6 +373,11 @@ static int cap_task_create(unsigned long clone_flags) ...@@ -373,6 +373,11 @@ static int cap_task_create(unsigned long clone_flags)
return 0; return 0;
} }
static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
return 0;
}
static void cap_cred_free(struct cred *cred) static void cap_cred_free(struct cred *cred)
{ {
} }
...@@ -386,6 +391,10 @@ static void cap_cred_commit(struct cred *new, const struct cred *old) ...@@ -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) static int cap_kernel_act_as(struct cred *new, u32 secid)
{ {
return 0; return 0;
...@@ -396,6 +405,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) ...@@ -396,6 +405,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode)
return 0; 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) static int cap_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{ {
return 0; return 0;
...@@ -701,10 +715,26 @@ static void cap_inet_conn_established(struct sock *sk, struct sk_buff *skb) ...@@ -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, static void cap_req_classify_flow(const struct request_sock *req,
struct flowi *fl) 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 */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
...@@ -792,6 +822,20 @@ static void cap_release_secctx(char *secdata, u32 seclen) ...@@ -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 #ifdef CONFIG_KEYS
static int cap_key_alloc(struct key *key, const struct cred *cred, static int cap_key_alloc(struct key *key, const struct cred *cred,
unsigned long flags) unsigned long flags)
...@@ -815,6 +859,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer) ...@@ -815,6 +859,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer)
return 0; 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 */ #endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
...@@ -854,7 +905,7 @@ struct security_operations default_security_ops = { ...@@ -854,7 +905,7 @@ struct security_operations default_security_ops = {
void security_fixup_ops(struct security_operations *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, ptrace_traceme);
set_to_cap_if_null(ops, capget); set_to_cap_if_null(ops, capget);
set_to_cap_if_null(ops, capset); set_to_cap_if_null(ops, capset);
...@@ -940,11 +991,14 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -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, file_receive);
set_to_cap_if_null(ops, dentry_open); set_to_cap_if_null(ops, dentry_open);
set_to_cap_if_null(ops, task_create); 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_free);
set_to_cap_if_null(ops, cred_prepare); set_to_cap_if_null(ops, cred_prepare);
set_to_cap_if_null(ops, cred_commit); 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_act_as);
set_to_cap_if_null(ops, kernel_create_files_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_setuid);
set_to_cap_if_null(ops, task_fix_setuid); set_to_cap_if_null(ops, task_fix_setuid);
set_to_cap_if_null(ops, task_setgid); set_to_cap_if_null(ops, task_setgid);
...@@ -992,6 +1046,9 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -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, secid_to_secctx);
set_to_cap_if_null(ops, secctx_to_secid); set_to_cap_if_null(ops, secctx_to_secid);
set_to_cap_if_null(ops, release_secctx); 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 #ifdef CONFIG_SECURITY_NETWORK
set_to_cap_if_null(ops, unix_stream_connect); set_to_cap_if_null(ops, unix_stream_connect);
set_to_cap_if_null(ops, unix_may_send); set_to_cap_if_null(ops, unix_may_send);
...@@ -1020,6 +1077,9 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -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_csk_clone);
set_to_cap_if_null(ops, inet_conn_established); set_to_cap_if_null(ops, inet_conn_established);
set_to_cap_if_null(ops, req_classify_flow); 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 */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
set_to_cap_if_null(ops, xfrm_policy_alloc_security); set_to_cap_if_null(ops, xfrm_policy_alloc_security);
...@@ -1038,6 +1098,7 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -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_free);
set_to_cap_if_null(ops, key_permission); set_to_cap_if_null(ops, key_permission);
set_to_cap_if_null(ops, key_getsecurity); set_to_cap_if_null(ops, key_getsecurity);
set_to_cap_if_null(ops, key_session_to_parent);
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
set_to_cap_if_null(ops, audit_rule_init); set_to_cap_if_null(ops, audit_rule_init);
......
...@@ -101,7 +101,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz) ...@@ -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 * another
* @child: The process to be accessed * @child: The process to be accessed
* @mode: The mode of attachment. * @mode: The mode of attachment.
...@@ -109,7 +109,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz) ...@@ -109,7 +109,7 @@ int cap_settime(struct timespec *ts, struct timezone *tz)
* Determine whether a process may access another, returning 0 if permission * Determine whether a process may access another, returning 0 if permission
* granted, -ve if denied. * 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; int ret = 0;
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# #
obj-y := \ obj-y := \
gc.o \
key.o \ key.o \
keyring.o \ keyring.o \
keyctl.o \ keyctl.o \
......
...@@ -82,6 +82,9 @@ asmlinkage long compat_sys_keyctl(u32 option, ...@@ -82,6 +82,9 @@ asmlinkage long compat_sys_keyctl(u32 option,
case KEYCTL_GET_SECURITY: case KEYCTL_GET_SECURITY:
return keyctl_get_security(arg2, compat_ptr(arg3), arg4); return keyctl_get_security(arg2, compat_ptr(arg3), arg4);
case KEYCTL_SESSION_TO_PARENT:
return keyctl_session_to_parent();
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
/* 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, ...@@ -124,11 +124,18 @@ extern struct key *request_key_and_link(struct key_type *type,
struct key *dest_keyring, struct key *dest_keyring,
unsigned long flags); 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); 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 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 * 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); ...@@ -194,6 +201,7 @@ extern long keyctl_set_timeout(key_serial_t, unsigned);
extern long keyctl_assume_authority(key_serial_t); extern long keyctl_assume_authority(key_serial_t);
extern long keyctl_get_security(key_serial_t keyid, char __user *buffer, extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,
size_t buflen); size_t buflen);
extern long keyctl_session_to_parent(void);
/* /*
* debugging key validation * debugging key validation
......
...@@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key, ...@@ -500,6 +500,7 @@ int key_negate_and_link(struct key *key,
set_bit(KEY_FLAG_INSTANTIATED, &key->flags); set_bit(KEY_FLAG_INSTANTIATED, &key->flags);
now = current_kernel_time(); now = current_kernel_time();
key->expiry = now.tv_sec + timeout; key->expiry = now.tv_sec + timeout;
key_schedule_gc(key->expiry);
if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags)) if (test_and_clear_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags))
awaken = 1; awaken = 1;
...@@ -642,10 +643,8 @@ struct key *key_lookup(key_serial_t id) ...@@ -642,10 +643,8 @@ struct key *key_lookup(key_serial_t id)
goto error; goto error;
found: found:
/* pretend it doesn't exist if it's dead */ /* pretend it doesn't exist if it is awaiting deletion */
if (atomic_read(&key->usage) == 0 || if (atomic_read(&key->usage) == 0)
test_bit(KEY_FLAG_DEAD, &key->flags) ||
key->type == &key_type_dead)
goto not_found; goto not_found;
/* this races with key_put(), but that doesn't matter since key_put() /* this races with key_put(), but that doesn't matter since key_put()
...@@ -890,6 +889,9 @@ EXPORT_SYMBOL(key_update); ...@@ -890,6 +889,9 @@ EXPORT_SYMBOL(key_update);
*/ */
void key_revoke(struct key *key) void key_revoke(struct key *key)
{ {
struct timespec now;
time_t time;
key_check(key); key_check(key);
/* make sure no one's trying to change or use the key when we mark it /* 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) ...@@ -902,6 +904,14 @@ void key_revoke(struct key *key)
key->type->revoke) key->type->revoke)
key->type->revoke(key); 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); up_write(&key->sem);
} /* end key_revoke() */ } /* end key_revoke() */
...@@ -958,8 +968,10 @@ void unregister_key_type(struct key_type *ktype) ...@@ -958,8 +968,10 @@ void unregister_key_type(struct key_type *ktype)
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) { for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
key = rb_entry(_n, struct key, serial_node); key = rb_entry(_n, struct key, serial_node);
if (key->type == ktype) if (key->type == ktype) {
key->type = &key_type_dead; key->type = &key_type_dead;
set_bit(KEY_FLAG_DEAD, &key->flags);
}
} }
spin_unlock(&key_serial_lock); spin_unlock(&key_serial_lock);
...@@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype) ...@@ -984,6 +996,8 @@ void unregister_key_type(struct key_type *ktype)
spin_unlock(&key_serial_lock); spin_unlock(&key_serial_lock);
up_write(&key_types_sem); up_write(&key_types_sem);
key_schedule_gc(0);
} /* end unregister_key_type() */ } /* end unregister_key_type() */
EXPORT_SYMBOL(unregister_key_type); EXPORT_SYMBOL(unregister_key_type);
......
...@@ -103,7 +103,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, ...@@ -103,7 +103,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type,
} }
/* find the target keyring (which must be writable) */ /* 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)) { if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref); ret = PTR_ERR(keyring_ref);
goto error3; goto error3;
...@@ -185,7 +185,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, ...@@ -185,7 +185,8 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
/* get the destination keyring if specified */ /* get the destination keyring if specified */
dest_ref = NULL; dest_ref = NULL;
if (destringid) { 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)) { if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref); ret = PTR_ERR(dest_ref);
goto error3; goto error3;
...@@ -233,9 +234,11 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type, ...@@ -233,9 +234,11 @@ SYSCALL_DEFINE4(request_key, const char __user *, _type,
long keyctl_get_keyring_ID(key_serial_t id, int create) long keyctl_get_keyring_ID(key_serial_t id, int create)
{ {
key_ref_t key_ref; key_ref_t key_ref;
unsigned long lflags;
long ret; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error; goto error;
...@@ -309,7 +312,7 @@ long keyctl_update_key(key_serial_t id, ...@@ -309,7 +312,7 @@ long keyctl_update_key(key_serial_t id,
} }
/* find the target key (which must be writable) */ /* 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error2; goto error2;
...@@ -337,10 +340,16 @@ long keyctl_revoke_key(key_serial_t id) ...@@ -337,10 +340,16 @@ long keyctl_revoke_key(key_serial_t id)
key_ref_t key_ref; key_ref_t key_ref;
long ret; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_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)); key_revoke(key_ref_to_ptr(key_ref));
...@@ -363,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid) ...@@ -363,7 +372,7 @@ long keyctl_keyring_clear(key_serial_t ringid)
key_ref_t keyring_ref; key_ref_t keyring_ref;
long ret; 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)) { if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref); ret = PTR_ERR(keyring_ref);
goto error; goto error;
...@@ -389,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid) ...@@ -389,13 +398,13 @@ long keyctl_keyring_link(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref; key_ref_t keyring_ref, key_ref;
long ret; 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)) { if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref); ret = PTR_ERR(keyring_ref);
goto error; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error2; goto error2;
...@@ -423,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid) ...@@ -423,13 +432,13 @@ long keyctl_keyring_unlink(key_serial_t id, key_serial_t ringid)
key_ref_t keyring_ref, key_ref; key_ref_t keyring_ref, key_ref;
long ret; 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)) { if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref); ret = PTR_ERR(keyring_ref);
goto error; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error2; goto error2;
...@@ -465,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid, ...@@ -465,7 +474,7 @@ long keyctl_describe_key(key_serial_t keyid,
char *tmpbuf; char *tmpbuf;
long ret; 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 (IS_ERR(key_ref)) {
/* viewing a key under construction is permitted if we have the /* viewing a key under construction is permitted if we have the
* authorisation token handy */ * authorisation token handy */
...@@ -474,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid, ...@@ -474,7 +483,8 @@ long keyctl_describe_key(key_serial_t keyid,
if (!IS_ERR(instkey)) { if (!IS_ERR(instkey)) {
key_put(instkey); key_put(instkey);
key_ref = lookup_user_key(keyid, key_ref = lookup_user_key(keyid,
0, 1, 0); KEY_LOOKUP_PARTIAL,
0);
if (!IS_ERR(key_ref)) if (!IS_ERR(key_ref))
goto okay; goto okay;
} }
...@@ -558,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid, ...@@ -558,7 +568,7 @@ long keyctl_keyring_search(key_serial_t ringid,
} }
/* get the keyring at which to begin the search */ /* 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)) { if (IS_ERR(keyring_ref)) {
ret = PTR_ERR(keyring_ref); ret = PTR_ERR(keyring_ref);
goto error2; goto error2;
...@@ -567,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid, ...@@ -567,7 +577,8 @@ long keyctl_keyring_search(key_serial_t ringid,
/* get the destination keyring if specified */ /* get the destination keyring if specified */
dest_ref = NULL; dest_ref = NULL;
if (destringid) { 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)) { if (IS_ERR(dest_ref)) {
ret = PTR_ERR(dest_ref); ret = PTR_ERR(dest_ref);
goto error3; goto error3;
...@@ -637,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen) ...@@ -637,7 +648,7 @@ long keyctl_read_key(key_serial_t keyid, char __user *buffer, size_t buflen)
long ret; long ret;
/* find the key first */ /* 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)) { if (IS_ERR(key_ref)) {
ret = -ENOKEY; ret = -ENOKEY;
goto error; goto error;
...@@ -700,7 +711,8 @@ long keyctl_chown_key(key_serial_t id, uid_t uid, gid_t gid) ...@@ -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) if (uid == (uid_t) -1 && gid == (gid_t) -1)
goto error; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error; goto error;
...@@ -805,7 +817,8 @@ long keyctl_setperm_key(key_serial_t id, key_perm_t perm) ...@@ -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)) if (perm & ~(KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL))
goto error; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error; goto error;
...@@ -847,7 +860,7 @@ static long get_instantiation_keyring(key_serial_t ringid, ...@@ -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 a specific keyring is nominated by ID, then use that */
if (ringid > 0) { 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)) if (IS_ERR(dkref))
return PTR_ERR(dkref); return PTR_ERR(dkref);
*_dest_keyring = key_ref_to_ptr(dkref); *_dest_keyring = key_ref_to_ptr(dkref);
...@@ -1083,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) ...@@ -1083,7 +1096,8 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
time_t expiry; time_t expiry;
long ret; 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)) { if (IS_ERR(key_ref)) {
ret = PTR_ERR(key_ref); ret = PTR_ERR(key_ref);
goto error; goto error;
...@@ -1101,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) ...@@ -1101,6 +1115,7 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout)
} }
key->expiry = expiry; key->expiry = expiry;
key_schedule_gc(key->expiry);
up_write(&key->sem); up_write(&key->sem);
key_put(key); key_put(key);
...@@ -1170,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid, ...@@ -1170,7 +1185,7 @@ long keyctl_get_security(key_serial_t keyid,
char *context; char *context;
long ret; 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 (IS_ERR(key_ref)) {
if (PTR_ERR(key_ref) != -EACCES) if (PTR_ERR(key_ref) != -EACCES)
return PTR_ERR(key_ref); return PTR_ERR(key_ref);
...@@ -1182,7 +1197,7 @@ long keyctl_get_security(key_serial_t keyid, ...@@ -1182,7 +1197,7 @@ long keyctl_get_security(key_serial_t keyid,
return PTR_ERR(key_ref); return PTR_ERR(key_ref);
key_put(instkey); 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)) if (IS_ERR(key_ref))
return PTR_ERR(key_ref); return PTR_ERR(key_ref);
} }
...@@ -1213,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid, ...@@ -1213,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid,
return ret; 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 * the key control system call
...@@ -1298,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, ...@@ -1298,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,
(char __user *) arg3, (char __user *) arg3,
(size_t) arg4); (size_t) arg4);
case KEYCTL_SESSION_TO_PARENT:
return keyctl_session_to_parent();
default: default:
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
......
...@@ -1000,3 +1000,88 @@ static void keyring_revoke(struct key *keyring) ...@@ -1000,3 +1000,88 @@ static void keyring_revoke(struct key *keyring)
} }
} /* end keyring_revoke() */ } /* 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); ...@@ -91,59 +91,94 @@ __initcall(key_proc_init);
*/ */
#ifdef CONFIG_KEYS_DEBUG_PROC_KEYS #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) { while (n) {
struct key *key = rb_entry(n, struct key, serial_node); 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; break;
n = rb_next(n); n = rb_next(n);
} }
return n; return n;
} }
static struct rb_node *key_serial_next(struct rb_node *n) static int proc_keys_open(struct inode *inode, struct file *file)
{ {
return __key_serial_next(rb_next(n)); return seq_open(file, &proc_keys_ops);
} }
static struct rb_node *key_serial_first(struct rb_root *r) static struct key *find_ge_key(key_serial_t id)
{ {
struct rb_node *n = rb_first(r); struct user_namespace *user_ns = current_user_ns();
return __key_serial_next(n); struct rb_node *n = key_serial_tree.rb_node;
} struct key *minkey = NULL;
static int proc_keys_open(struct inode *inode, struct file *file) while (n) {
{ struct key *key = rb_entry(n, struct key, serial_node);
return seq_open(file, &proc_keys_ops); 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) static void *proc_keys_start(struct seq_file *p, loff_t *_pos)
__acquires(key_serial_lock)
{ {
struct rb_node *_p; key_serial_t pos = *_pos;
loff_t pos = *_pos; struct key *key;
spin_lock(&key_serial_lock); spin_lock(&key_serial_lock);
_p = key_serial_first(&key_serial_tree); if (*_pos > INT_MAX)
while (pos > 0 && _p) { return NULL;
pos--; key = find_ge_key(pos);
_p = key_serial_next(_p); if (!key)
} return NULL;
*_pos = key->serial;
return _p; 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) static void *proc_keys_next(struct seq_file *p, void *v, loff_t *_pos)
{ {
(*_pos)++; struct rb_node *n;
return key_serial_next((struct rb_node *) v);
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) static void proc_keys_stop(struct seq_file *p, void *v)
__releases(key_serial_lock)
{ {
spin_unlock(&key_serial_lock); spin_unlock(&key_serial_lock);
} }
...@@ -174,11 +209,9 @@ static int proc_keys_show(struct seq_file *m, void *v) ...@@ -174,11 +209,9 @@ static int proc_keys_show(struct seq_file *m, void *v)
/* come up with a suitable timeout value */ /* come up with a suitable timeout value */
if (key->expiry == 0) { if (key->expiry == 0) {
memcpy(xbuf, "perm", 5); memcpy(xbuf, "perm", 5);
} } else if (now.tv_sec >= key->expiry) {
else if (now.tv_sec >= key->expiry) {
memcpy(xbuf, "expd", 5); memcpy(xbuf, "expd", 5);
} } else {
else {
timo = key->expiry - now.tv_sec; timo = key->expiry - now.tv_sec;
if (timo < 60) if (timo < 60)
...@@ -218,9 +251,7 @@ static int proc_keys_show(struct seq_file *m, void *v) ...@@ -218,9 +251,7 @@ static int proc_keys_show(struct seq_file *m, void *v)
seq_putc(m, '\n'); seq_putc(m, '\n');
rcu_read_unlock(); rcu_read_unlock();
return 0; return 0;
} }
#endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */ #endif /* CONFIG_KEYS_DEBUG_PROC_KEYS */
...@@ -246,6 +277,7 @@ static struct rb_node *key_user_first(struct rb_root *r) ...@@ -246,6 +277,7 @@ static struct rb_node *key_user_first(struct rb_root *r)
struct rb_node *n = rb_first(r); struct rb_node *n = rb_first(r);
return __key_user_next(n); return __key_user_next(n);
} }
/*****************************************************************************/ /*****************************************************************************/
/* /*
* implement "/proc/key-users" to provides a list of the key users * 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) ...@@ -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) static int proc_key_users_open(struct inode *inode, struct file *file)
{ {
return seq_open(file, &proc_key_users_ops); return seq_open(file, &proc_key_users_ops);
} }
static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
__acquires(key_user_lock)
{ {
struct rb_node *_p; struct rb_node *_p;
loff_t pos = *_pos; loff_t pos = *_pos;
...@@ -270,17 +302,16 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos) ...@@ -270,17 +302,16 @@ static void *proc_key_users_start(struct seq_file *p, loff_t *_pos)
} }
return _p; return _p;
} }
static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos) static void *proc_key_users_next(struct seq_file *p, void *v, loff_t *_pos)
{ {
(*_pos)++; (*_pos)++;
return key_user_next((struct rb_node *) v); return key_user_next((struct rb_node *) v);
} }
static void proc_key_users_stop(struct seq_file *p, void *v) static void proc_key_users_stop(struct seq_file *p, void *v)
__releases(key_user_lock)
{ {
spin_unlock(&key_user_lock); spin_unlock(&key_user_lock);
} }
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/security.h>
#include <linux/user_namespace.h> #include <linux/user_namespace.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include "internal.h" #include "internal.h"
...@@ -487,7 +488,7 @@ static int lookup_user_key_possessed(const struct key *key, const void *target) ...@@ -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 * - don't create special keyrings unless so requested
* - partially constructed keys aren't found unless 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) key_perm_t perm)
{ {
struct request_key_auth *rka; struct request_key_auth *rka;
...@@ -503,7 +504,7 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial, ...@@ -503,7 +504,7 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
switch (id) { switch (id) {
case KEY_SPEC_THREAD_KEYRING: case KEY_SPEC_THREAD_KEYRING:
if (!cred->thread_keyring) { if (!cred->thread_keyring) {
if (!create) if (!(lflags & KEY_LOOKUP_CREATE))
goto error; goto error;
ret = install_thread_keyring(); ret = install_thread_keyring();
...@@ -521,7 +522,7 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial, ...@@ -521,7 +522,7 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
case KEY_SPEC_PROCESS_KEYRING: case KEY_SPEC_PROCESS_KEYRING:
if (!cred->tgcred->process_keyring) { if (!cred->tgcred->process_keyring) {
if (!create) if (!(lflags & KEY_LOOKUP_CREATE))
goto error; goto error;
ret = install_process_keyring(); ret = install_process_keyring();
...@@ -642,7 +643,14 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial, ...@@ -642,7 +643,14 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
break; 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); ret = wait_for_key_construction(key, true);
switch (ret) { switch (ret) {
case -ERESTARTSYS: case -ERESTARTSYS:
...@@ -660,7 +668,8 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial, ...@@ -660,7 +668,8 @@ key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
} }
ret = -EIO; 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; goto invalid_key;
/* check the permissions */ /* check the permissions */
...@@ -702,7 +711,7 @@ long join_session_keyring(const char *name) ...@@ -702,7 +711,7 @@ long join_session_keyring(const char *name)
/* only permit this if there's a single thread in the thread group - /* 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 * this avoids us having to adjust the creds on all threads and risking
* ENOMEM */ * ENOMEM */
if (!is_single_threaded(current)) if (!current_is_single_threaded())
return -EMLINK; return -EMLINK;
new = prepare_creds(); new = prepare_creds();
...@@ -760,3 +769,51 @@ long join_session_keyring(const char *name) ...@@ -760,3 +769,51 @@ long join_session_keyring(const char *name)
abort_creds(new); abort_creds(new);
return ret; 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 @@ ...@@ -13,6 +13,8 @@
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include "internal.h" #include "internal.h"
static const int zero, one = 1, max = INT_MAX;
ctl_table key_sysctls[] = { ctl_table key_sysctls[] = {
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
...@@ -20,7 +22,9 @@ ctl_table key_sysctls[] = { ...@@ -20,7 +22,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_maxkeys, .data = &key_quota_maxkeys,
.maxlen = sizeof(unsigned), .maxlen = sizeof(unsigned),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_minmax,
.extra1 = (void *) &one,
.extra2 = (void *) &max,
}, },
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
...@@ -28,7 +32,9 @@ ctl_table key_sysctls[] = { ...@@ -28,7 +32,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_maxbytes, .data = &key_quota_maxbytes,
.maxlen = sizeof(unsigned), .maxlen = sizeof(unsigned),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_minmax,
.extra1 = (void *) &one,
.extra2 = (void *) &max,
}, },
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
...@@ -36,7 +42,9 @@ ctl_table key_sysctls[] = { ...@@ -36,7 +42,9 @@ ctl_table key_sysctls[] = {
.data = &key_quota_root_maxkeys, .data = &key_quota_root_maxkeys,
.maxlen = sizeof(unsigned), .maxlen = sizeof(unsigned),
.mode = 0644, .mode = 0644,
.proc_handler = &proc_dointvec, .proc_handler = &proc_dointvec_minmax,
.extra1 = (void *) &one,
.extra2 = (void *) &max,
}, },
{ {
.ctl_name = CTL_UNNUMBERED, .ctl_name = CTL_UNNUMBERED,
...@@ -44,7 +52,19 @@ ctl_table key_sysctls[] = { ...@@ -44,7 +52,19 @@ ctl_table key_sysctls[] = {
.data = &key_quota_root_maxbytes, .data = &key_quota_root_maxbytes,
.maxlen = sizeof(unsigned), .maxlen = sizeof(unsigned),
.mode = 0644, .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 } { .ctl_name = 0 }
}; };
...@@ -220,6 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab, ...@@ -220,6 +220,8 @@ static void dump_common_audit_data(struct audit_buffer *ab,
} }
switch (a->type) { switch (a->type) {
case LSM_AUDIT_NO_AUDIT:
return;
case LSM_AUDIT_DATA_IPC: case LSM_AUDIT_DATA_IPC:
audit_log_format(ab, " key=%d ", a->u.ipc_id); audit_log_format(ab, " key=%d ", a->u.ipc_id);
break; break;
......
...@@ -124,9 +124,9 @@ int register_security(struct security_operations *ops) ...@@ -124,9 +124,9 @@ int register_security(struct security_operations *ops)
/* Security operations */ /* 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) int security_ptrace_traceme(struct task_struct *parent)
...@@ -684,6 +684,11 @@ int security_task_create(unsigned long clone_flags) ...@@ -684,6 +684,11 @@ int security_task_create(unsigned long clone_flags)
return security_ops->task_create(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) void security_cred_free(struct cred *cred)
{ {
security_ops->cred_free(cred); security_ops->cred_free(cred);
...@@ -699,6 +704,11 @@ void security_commit_creds(struct cred *new, const struct cred *old) ...@@ -699,6 +704,11 @@ void security_commit_creds(struct cred *new, const struct cred *old)
security_ops->cred_commit(new, 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) int security_kernel_act_as(struct cred *new, u32 secid)
{ {
return security_ops->kernel_act_as(new, 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) ...@@ -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); 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) int security_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
{ {
return security_ops->task_setuid(id0, id1, id2, flags); return security_ops->task_setuid(id0, id1, id2, flags);
...@@ -959,6 +974,24 @@ void security_release_secctx(char *secdata, u32 seclen) ...@@ -959,6 +974,24 @@ void security_release_secctx(char *secdata, u32 seclen)
} }
EXPORT_SYMBOL(security_release_secctx); 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 #ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct socket *sock, struct socket *other, int security_unix_stream_connect(struct socket *sock, struct socket *other,
...@@ -1112,6 +1145,24 @@ void security_inet_conn_established(struct sock *sk, ...@@ -1112,6 +1145,24 @@ void security_inet_conn_established(struct sock *sk,
security_ops->inet_conn_established(sk, skb); 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 */ #endif /* CONFIG_SECURITY_NETWORK */
#ifdef CONFIG_SECURITY_NETWORK_XFRM #ifdef CONFIG_SECURITY_NETWORK_XFRM
...@@ -1218,6 +1269,13 @@ int security_key_getsecurity(struct key *key, char **_buffer) ...@@ -1218,6 +1269,13 @@ int security_key_getsecurity(struct key *key, char **_buffer)
return security_ops->key_getsecurity(key, _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 */ #endif /* CONFIG_KEYS */
#ifdef CONFIG_AUDIT #ifdef CONFIG_AUDIT
......
...@@ -137,7 +137,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass) ...@@ -137,7 +137,7 @@ static inline int avc_hash(u32 ssid, u32 tsid, u16 tclass)
* @tclass: target security class * @tclass: target security class
* @av: access vector * @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; const char **common_pts = NULL;
u32 common_base = 0; u32 common_base = 0;
...@@ -492,23 +492,35 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec ...@@ -492,23 +492,35 @@ static struct avc_node *avc_insert(u32 ssid, u32 tsid, u16 tclass, struct av_dec
return node; return node;
} }
static inline void avc_print_ipv6_addr(struct audit_buffer *ab, /**
struct in6_addr *addr, __be16 port, * avc_audit_pre_callback - SELinux specific information
char *name1, char *name2) * 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)) struct common_audit_data *ad = a;
audit_log_format(ab, " %s=%pI6", name1, addr); audit_log_format(ab, "avc: %s ",
if (port) ad->selinux_audit_data.denied ? "denied" : "granted");
audit_log_format(ab, " %s=%d", name2, ntohs(port)); 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) struct common_audit_data *ad = a;
audit_log_format(ab, " %s=%pI4", name1, &addr); audit_log_format(ab, " ");
if (port) avc_dump_query(ab, ad->selinux_audit_data.ssid,
audit_log_format(ab, " %s=%d", name2, ntohs(port)); 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, ...@@ -532,13 +544,10 @@ static inline void avc_print_ipv4_addr(struct audit_buffer *ab, __be32 addr,
*/ */
void avc_audit(u32 ssid, u32 tsid, void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested, u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *a) struct av_decision *avd, int result, struct common_audit_data *a)
{ {
struct task_struct *tsk = current; struct common_audit_data stack_data;
struct inode *inode = NULL;
u32 denied, audited; u32 denied, audited;
struct audit_buffer *ab;
denied = requested & ~avd->allowed; denied = requested & ~avd->allowed;
if (denied) { if (denied) {
audited = denied; audited = denied;
...@@ -551,144 +560,20 @@ void avc_audit(u32 ssid, u32 tsid, ...@@ -551,144 +560,20 @@ void avc_audit(u32 ssid, u32 tsid,
if (!(audited & avd->auditallow)) if (!(audited & avd->auditallow))
return; return;
} }
if (!a) {
ab = audit_log_start(current->audit_context, GFP_ATOMIC, AUDIT_AVC); a = &stack_data;
if (!ab) memset(a, 0, sizeof(*a));
return; /* audit_panic has been called */ a->type = LSM_AUDIT_NO_AUDIT;
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->selinux_audit_data.tclass = tclass;
switch (a->type) { a->selinux_audit_data.requested = requested;
case AVC_AUDIT_DATA_IPC: a->selinux_audit_data.ssid = ssid;
audit_log_format(ab, " key=%d", a->u.ipc_id); a->selinux_audit_data.tsid = tsid;
break; a->selinux_audit_data.audited = audited;
case AVC_AUDIT_DATA_CAP: a->selinux_audit_data.denied = denied;
audit_log_format(ab, " capability=%d", a->u.cap); a->lsm_pre_audit = avc_audit_pre_callback;
break; a->lsm_post_audit = avc_audit_post_callback;
case AVC_AUDIT_DATA_FS: common_lsm_audit(a);
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);
} }
/** /**
...@@ -956,7 +841,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, ...@@ -956,7 +841,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
* another -errno upon other errors. * another -errno upon other errors.
*/ */
int avc_has_perm(u32 ssid, u32 tsid, u16 tclass, 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; struct av_decision avd;
int rc; int rc;
...@@ -970,3 +855,9 @@ u32 avc_policy_seqno(void) ...@@ -970,3 +855,9 @@ u32 avc_policy_seqno(void)
{ {
return avc_cache.latest_notif; return avc_cache.latest_notif;
} }
void avc_disable(void)
{
if (avc_node_cachep)
kmem_cache_destroy(avc_node_cachep);
}
此差异已折叠。
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL) S_(SECCLASS_KEY_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL) S_(SECCLASS_UNIX_STREAM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL) S_(SECCLASS_UNIX_DGRAM_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_TUN_SOCKET, socket, 0x00400000UL)
S_(SECCLASS_IPC, ipc, 0x00000200UL) S_(SECCLASS_IPC, ipc, 0x00000200UL)
S_(SECCLASS_SEM, ipc, 0x00000200UL) S_(SECCLASS_SEM, ipc, 0x00000200UL)
S_(SECCLASS_MSGQ, ipc, 0x00000200UL) S_(SECCLASS_MSGQ, ipc, 0x00000200UL)
......
...@@ -107,6 +107,7 @@ ...@@ -107,6 +107,7 @@
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read") S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_READ, "syslog_read")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod") S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_MOD, "syslog_mod")
S_(SECCLASS_SYSTEM, SYSTEM__SYSLOG_CONSOLE, "syslog_console") 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__CHOWN, "chown")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override") S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_OVERRIDE, "dac_override")
S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search") S_(SECCLASS_CAPABILITY, CAPABILITY__DAC_READ_SEARCH, "dac_read_search")
......
...@@ -423,6 +423,28 @@ ...@@ -423,6 +423,28 @@
#define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL #define UNIX_DGRAM_SOCKET__RECV_MSG 0x00080000UL
#define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL #define UNIX_DGRAM_SOCKET__SEND_MSG 0x00100000UL
#define UNIX_DGRAM_SOCKET__NAME_BIND 0x00200000UL #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__FORK 0x00000001UL
#define PROCESS__TRANSITION 0x00000002UL #define PROCESS__TRANSITION 0x00000002UL
#define PROCESS__SIGCHLD 0x00000004UL #define PROCESS__SIGCHLD 0x00000004UL
...@@ -508,6 +530,7 @@ ...@@ -508,6 +530,7 @@
#define SYSTEM__SYSLOG_READ 0x00000002UL #define SYSTEM__SYSLOG_READ 0x00000002UL
#define SYSTEM__SYSLOG_MOD 0x00000004UL #define SYSTEM__SYSLOG_MOD 0x00000004UL
#define SYSTEM__SYSLOG_CONSOLE 0x00000008UL #define SYSTEM__SYSLOG_CONSOLE 0x00000008UL
#define SYSTEM__MODULE_REQUEST 0x00000010UL
#define CAPABILITY__CHOWN 0x00000001UL #define CAPABILITY__CHOWN 0x00000001UL
#define CAPABILITY__DAC_OVERRIDE 0x00000002UL #define CAPABILITY__DAC_OVERRIDE 0x00000002UL
#define CAPABILITY__DAC_READ_SEARCH 0x00000004UL #define CAPABILITY__DAC_READ_SEARCH 0x00000004UL
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/audit.h> #include <linux/audit.h>
#include <linux/lsm_audit.h>
#include <linux/in6.h> #include <linux/in6.h>
#include <linux/path.h> #include <linux/path.h>
#include <asm/system.h> #include <asm/system.h>
...@@ -36,48 +37,6 @@ struct inode; ...@@ -36,48 +37,6 @@ struct inode;
struct sock; struct sock;
struct sk_buff; 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 * AVC statistics
*/ */
...@@ -98,7 +57,9 @@ void __init avc_init(void); ...@@ -98,7 +57,9 @@ void __init avc_init(void);
void avc_audit(u32 ssid, u32 tsid, void avc_audit(u32 ssid, u32 tsid,
u16 tclass, u32 requested, u16 tclass, u32 requested,
struct av_decision *avd, int result, struct avc_audit_data *auditdata); struct av_decision *avd,
int result,
struct common_audit_data *a);
#define AVC_STRICT 1 /* Ignore permissive mode. */ #define AVC_STRICT 1 /* Ignore permissive mode. */
int avc_has_perm_noaudit(u32 ssid, u32 tsid, int avc_has_perm_noaudit(u32 ssid, u32 tsid,
...@@ -108,7 +69,7 @@ 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, int avc_has_perm(u32 ssid, u32 tsid,
u16 tclass, u32 requested, u16 tclass, u32 requested,
struct avc_audit_data *auditdata); struct common_audit_data *auditdata);
u32 avc_policy_seqno(void); u32 avc_policy_seqno(void);
...@@ -127,13 +88,13 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid, ...@@ -127,13 +88,13 @@ int avc_add_callback(int (*callback)(u32 event, u32 ssid, u32 tsid,
u32 events, u32 ssid, u32 tsid, u32 events, u32 ssid, u32 tsid,
u16 tclass, u32 perms); 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 */ /* Exported to selinuxfs */
int avc_get_hash_stats(char *page); int avc_get_hash_stats(char *page);
extern unsigned int avc_cache_threshold; extern unsigned int avc_cache_threshold;
/* Attempt to free avc node cache */
void avc_disable(void);
#ifdef CONFIG_SECURITY_SELINUX_AVC_STATS #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS
DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats); DECLARE_PER_CPU(struct avc_cache_stats, avc_cache_stats);
#endif #endif
......
...@@ -77,3 +77,4 @@ ...@@ -77,3 +77,4 @@
S_(NULL) S_(NULL)
S_(NULL) S_(NULL)
S_("kernel_service") S_("kernel_service")
S_("tun_socket")
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
#define SECCLASS_PEER 68 #define SECCLASS_PEER 68
#define SECCLASS_CAPABILITY2 69 #define SECCLASS_CAPABILITY2 69
#define SECCLASS_KERNEL_SERVICE 74 #define SECCLASS_KERNEL_SERVICE 74
#define SECCLASS_TUN_SOCKET 75
/* /*
* Security identifier indices for initial entities * Security identifier indices for initial entities
......
...@@ -59,7 +59,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family); ...@@ -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, int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb, struct sk_buff *skb,
u16 family, u16 family,
struct avc_audit_data *ad); struct common_audit_data *ad);
int selinux_netlbl_socket_setsockopt(struct socket *sock, int selinux_netlbl_socket_setsockopt(struct socket *sock,
int level, int level,
int optname); int optname);
...@@ -129,7 +129,7 @@ static inline int selinux_netlbl_socket_post_create(struct sock *sk, ...@@ -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, static inline int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb, struct sk_buff *skb,
u16 family, u16 family,
struct avc_audit_data *ad) struct common_audit_data *ad)
{ {
return 0; return 0;
} }
......
...@@ -41,9 +41,9 @@ static inline int selinux_xfrm_enabled(void) ...@@ -41,9 +41,9 @@ static inline int selinux_xfrm_enabled(void)
} }
int selinux_xfrm_sock_rcv_skb(u32 sid, struct sk_buff *skb, 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, 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); int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
static inline void selinux_xfrm_notify_policyload(void) static inline void selinux_xfrm_notify_policyload(void)
...@@ -57,13 +57,13 @@ static inline int selinux_xfrm_enabled(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, 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; return 0;
} }
static inline int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, 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; return 0;
} }
......
...@@ -342,7 +342,7 @@ int selinux_netlbl_socket_post_create(struct sock *sk, u16 family) ...@@ -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, int selinux_netlbl_sock_rcv_skb(struct sk_security_struct *sksec,
struct sk_buff *skb, struct sk_buff *skb,
u16 family, u16 family,
struct avc_audit_data *ad) struct common_audit_data *ad)
{ {
int rc; int rc;
u32 nlbl_sid; u32 nlbl_sid;
......
...@@ -22,6 +22,11 @@ ...@@ -22,6 +22,11 @@
* *
* Added validation of kernel classes and permissions * 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) 2006, 2007 Hewlett-Packard Development Company, L.P.
* Copyright (C) 2004-2006 Trusted Computer Solutions, Inc. * Copyright (C) 2004-2006 Trusted Computer Solutions, Inc.
* Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC * Copyright (C) 2003 - 2004, 2006 Tresys Technology, LLC
...@@ -278,6 +283,95 @@ static int constraint_expr_eval(struct context *scontext, ...@@ -278,6 +283,95 @@ static int constraint_expr_eval(struct context *scontext,
return s[0]; 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 * security_boundary_permission - drops violated permissions
* on boundary constraint. * on boundary constraint.
...@@ -347,28 +441,12 @@ static void type_attribute_bounds_av(struct context *scontext, ...@@ -347,28 +441,12 @@ static void type_attribute_bounds_av(struct context *scontext,
} }
if (masked) { 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 */ /* mask violated permissions */
avd->allowed &= ~masked; avd->allowed &= ~masked;
/* notice to userspace via audit message */ /* audit masked permissions */
ab = audit_log_start(current->audit_context, security_dump_masked_av(scontext, tcontext,
GFP_ATOMIC, AUDIT_SELINUX_ERR); tclass, masked, "bounds");
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);
} }
} }
...@@ -480,7 +558,7 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -480,7 +558,7 @@ static int context_struct_compute_av(struct context *scontext,
if ((constraint->permissions & (avd->allowed)) && if ((constraint->permissions & (avd->allowed)) &&
!constraint_expr_eval(scontext, tcontext, NULL, !constraint_expr_eval(scontext, tcontext, NULL,
constraint->expr)) { constraint->expr)) {
avd->allowed = (avd->allowed) & ~(constraint->permissions); avd->allowed &= ~(constraint->permissions);
} }
constraint = constraint->next; constraint = constraint->next;
} }
...@@ -499,8 +577,8 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -499,8 +577,8 @@ static int context_struct_compute_av(struct context *scontext,
break; break;
} }
if (!ra) if (!ra)
avd->allowed = (avd->allowed) & ~(PROCESS__TRANSITION | avd->allowed &= ~(PROCESS__TRANSITION |
PROCESS__DYNTRANSITION); PROCESS__DYNTRANSITION);
} }
/* /*
...@@ -687,6 +765,26 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) ...@@ -687,6 +765,26 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
} }
index = type->bounds; 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: out:
read_unlock(&policy_rwlock); read_unlock(&policy_rwlock);
......
...@@ -401,7 +401,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x) ...@@ -401,7 +401,7 @@ int selinux_xfrm_state_delete(struct xfrm_state *x)
* gone thru the IPSec process. * gone thru the IPSec process.
*/ */
int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, 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; int i, rc = 0;
struct sec_path *sp; struct sec_path *sp;
...@@ -442,7 +442,7 @@ int selinux_xfrm_sock_rcv_skb(u32 isec_sid, struct sk_buff *skb, ...@@ -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. * checked in the selinux_xfrm_state_pol_flow_match hook above.
*/ */
int selinux_xfrm_postroute_last(u32 isec_sid, struct sk_buff *skb, 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; struct dst_entry *dst;
int rc = 0; int rc = 0;
......
...@@ -275,7 +275,7 @@ static inline void smk_ad_init(struct smk_audit_info *a, const char *func, ...@@ -275,7 +275,7 @@ static inline void smk_ad_init(struct smk_audit_info *a, const char *func,
{ {
memset(a, 0, sizeof(*a)); memset(a, 0, sizeof(*a));
a->a.type = type; 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, 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) ...@@ -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) static void smack_log_callback(struct audit_buffer *ab, void *a)
{ {
struct common_audit_data *ad = a; struct common_audit_data *ad = a;
struct smack_audit_data *sad = &ad->lsm_priv.smack_audit_data; struct smack_audit_data *sad = &ad->smack_audit_data;
audit_log_format(ab, "lsm=SMACK fn=%s action=%s", ad->function, audit_log_format(ab, "lsm=SMACK fn=%s action=%s",
ad->smack_audit_data.function,
sad->result ? "denied" : "granted"); sad->result ? "denied" : "granted");
audit_log_format(ab, " subject="); audit_log_format(ab, " subject=");
audit_log_untrustedstring(ab, sad->subject); audit_log_untrustedstring(ab, sad->subject);
...@@ -274,11 +275,11 @@ void smack_log(char *subject_label, char *object_label, int request, ...@@ -274,11 +275,11 @@ void smack_log(char *subject_label, char *object_label, int request,
if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0) if (result == 0 && (log_policy & SMACK_AUDIT_ACCEPT) == 0)
return; return;
if (a->function == NULL) if (a->smack_audit_data.function == NULL)
a->function = "unknown"; a->smack_audit_data.function = "unknown";
/* end preparing the audit data */ /* end preparing the audit data */
sad = &a->lsm_priv.smack_audit_data; sad = &a->smack_audit_data;
smack_str_from_perm(request_buffer, request); smack_str_from_perm(request_buffer, request);
sad->subject = subject_label; sad->subject = subject_label;
sad->object = object_label; sad->object = object_label;
......
此差异已折叠。
...@@ -1284,6 +1284,36 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, ...@@ -1284,6 +1284,36 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head,
return true; 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. * tomoyo_write_domain_policy - Write domain policy.
* *
......
...@@ -339,8 +339,6 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); ...@@ -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); const char *tomoyo_get_msg(const bool is_enforce);
/* Convert single path operation to operation name. */ /* Convert single path operation to operation name. */
const char *tomoyo_sp2keyword(const u8 operation); const char *tomoyo_sp2keyword(const u8 operation);
/* Delete a domain. */
int tomoyo_delete_domain(char *data);
/* Create "alias" entry in exception policy. */ /* Create "alias" entry in exception policy. */
int tomoyo_write_alias_policy(char *data, const bool is_delete); int tomoyo_write_alias_policy(char *data, const bool is_delete);
/* /*
......
此差异已折叠。
此差异已折叠。
...@@ -31,8 +31,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain, ...@@ -31,8 +31,7 @@ int tomoyo_check_2path_perm(struct tomoyo_domain_info *domain,
struct path *path2); struct path *path2);
int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain, int tomoyo_check_rewrite_permission(struct tomoyo_domain_info *domain,
struct file *filp); struct file *filp);
int tomoyo_find_next_domain(struct linux_binprm *bprm, int tomoyo_find_next_domain(struct linux_binprm *bprm);
struct tomoyo_domain_info **next_domain);
/* Index numbers for Access Controls. */ /* Index numbers for Access Controls. */
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册