Commit ee18d64c authored by David Howells's avatar David Howells Committed by James Morris

KEYS: Add a keyctl to install a process's session keyring on its parent [try #6]

Add a keyctl to install a process's session keyring onto its parent.  This
replaces the parent's session keyring.  Because the COW credential code does
not permit one process to change another process's credentials directly, the
change is deferred until userspace next starts executing again.  Normally this
will be after a wait*() syscall.

To support this, three new security hooks have been provided:
cred_alloc_blank() to allocate unset security creds, cred_transfer() to fill in
the blank security creds and key_session_to_parent() - which asks the LSM if
the process may replace its parent's session keyring.

The replacement may only happen if the process has the same ownership details
as its parent, and the process has LINK permission on the session keyring, and
the session keyring is owned by the process, and the LSM permits it.

Note that this requires alteration to each architecture's notify_resume path.
This has been done for all arches barring blackfin, m68k* and xtensa, all of
which need assembly alteration to support TIF_NOTIFY_RESUME.  This allows the
replacement to be performed at the point the parent process resumes userspace
execution.

This allows the userspace AFS pioctl emulation to fully emulate newpag() and
the VIOCSETTOK and VIOCSETTOK2 pioctls, all of which require the ability to
alter the parent process's PAG membership.  However, since kAFS doesn't use
PAGs per se, but rather dumps the keys into the session keyring, the session
keyring of the parent must be replaced if, for example, VIOCSETTOK is passed
the newpag flag.

This can be tested with the following program:

	#include <stdio.h>
	#include <stdlib.h>
	#include <keyutils.h>

	#define KEYCTL_SESSION_TO_PARENT	18

	#define OSERROR(X, S) do { if ((long)(X) == -1) { perror(S); exit(1); } } while(0)

	int main(int argc, char **argv)
	{
		key_serial_t keyring, key;
		long ret;

		keyring = keyctl_join_session_keyring(argv[1]);
		OSERROR(keyring, "keyctl_join_session_keyring");

		key = add_key("user", "a", "b", 1, keyring);
		OSERROR(key, "add_key");

		ret = keyctl(KEYCTL_SESSION_TO_PARENT);
		OSERROR(ret, "KEYCTL_SESSION_TO_PARENT");

		return 0;
	}

Compiled and linked with -lkeyutils, you should see something like:

	[dhowells@andromeda ~]$ keyctl show
	Session Keyring
	       -3 --alswrv   4043  4043  keyring: _ses
	355907932 --alswrv   4043    -1   \_ keyring: _uid.4043
	[dhowells@andromeda ~]$ /tmp/newpag
	[dhowells@andromeda ~]$ keyctl show
	Session Keyring
	       -3 --alswrv   4043  4043  keyring: _ses
	1055658746 --alswrv   4043  4043   \_ user: a
	[dhowells@andromeda ~]$ /tmp/newpag hello
	[dhowells@andromeda ~]$ keyctl show
	Session Keyring
	       -3 --alswrv   4043  4043  keyring: hello
	340417692 --alswrv   4043  4043   \_ user: a

Where the test program creates a new session keyring, sticks a user key named
'a' into it and then installs it on its parent.
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent d0420c83
...@@ -757,6 +757,26 @@ The keyctl syscall functions are: ...@@ -757,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
=============== ===============
......
...@@ -687,5 +687,7 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw, ...@@ -687,5 +687,7 @@ do_notify_resume(struct pt_regs *regs, struct switch_stack *sw,
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();
} }
} }
...@@ -711,5 +711,7 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall) ...@@ -711,5 +711,7 @@ do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
if (thread_flags & _TIF_NOTIFY_RESUME) { if (thread_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();
} }
} }
...@@ -326,5 +326,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti) ...@@ -326,5 +326,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, struct thread_info *ti)
if (ti->flags & _TIF_NOTIFY_RESUME) { if (ti->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();
} }
} }
...@@ -40,5 +40,7 @@ void do_notify_resume(int canrestart, struct pt_regs *regs, ...@@ -40,5 +40,7 @@ void do_notify_resume(int canrestart, struct pt_regs *regs,
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();
} }
} }
...@@ -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() */
...@@ -556,5 +556,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, u32 thread_info_flags) ...@@ -556,5 +556,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(regs); 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 */
......
...@@ -411,6 +411,8 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset, ...@@ -411,6 +411,8 @@ void do_notify_resume(struct pt_regs *regs, sigset_t *oldset,
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();
} }
clear_thread_flag(TIF_IRET); clear_thread_flag(TIF_IRET);
......
...@@ -704,5 +704,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused, ...@@ -704,5 +704,7 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
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();
} }
} }
...@@ -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();
} }
} }
...@@ -649,5 +649,7 @@ void do_notify_resume(struct pt_regs *regs, long in_syscall) ...@@ -649,5 +649,7 @@ void do_notify_resume(struct pt_regs *regs, 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(regs); 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
......
...@@ -152,6 +152,7 @@ struct cred { ...@@ -152,6 +152,7 @@ struct cred {
extern void __put_cred(struct cred *); extern void __put_cred(struct cred *);
extern void exit_creds(struct task_struct *); 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);
......
...@@ -278,6 +278,8 @@ static inline key_serial_t key_serial(struct key *key) ...@@ -278,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
*/ */
...@@ -300,6 +302,7 @@ extern void key_init(void); ...@@ -300,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 */
...@@ -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
......
...@@ -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.
...@@ -1103,6 +1112,13 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -1103,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.
* *
...@@ -1498,10 +1514,12 @@ struct security_operations { ...@@ -1498,10 +1514,12 @@ 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 (*kernel_module_request)(void);
...@@ -1639,6 +1657,9 @@ struct security_operations { ...@@ -1639,6 +1657,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
...@@ -1755,9 +1776,11 @@ int security_file_send_sigiotask(struct task_struct *tsk, ...@@ -1755,9 +1776,11 @@ 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_kernel_module_request(void);
...@@ -2286,6 +2309,9 @@ static inline int security_task_create(unsigned long clone_flags) ...@@ -2286,6 +2309,9 @@ static inline int security_task_create(unsigned long clone_flags)
return 0; return 0;
} }
static inline void security_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{ }
static inline void security_cred_free(struct cred *cred) static inline void security_cred_free(struct cred *cred)
{ } { }
...@@ -2301,6 +2327,11 @@ static inline void security_commit_creds(struct cred *new, ...@@ -2301,6 +2327,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;
...@@ -2923,6 +2954,9 @@ void security_key_free(struct key *key); ...@@ -2923,6 +2954,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
...@@ -2950,6 +2984,10 @@ static inline int security_key_getsecurity(struct key *key, char **_buffer) ...@@ -2950,6 +2984,10 @@ 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);
#endif #endif
#endif /* CONFIG_KEYS */ #endif /* CONFIG_KEYS */
......
...@@ -199,6 +199,49 @@ void exit_creds(struct task_struct *tsk) ...@@ -199,6 +199,49 @@ void exit_creds(struct task_struct *tsk)
validate_creds(cred); validate_creds(cred);
alter_cred_subscribers(cred, -1); alter_cred_subscribers(cred, -1);
put_cred(cred); 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;
} }
/** /**
......
...@@ -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;
...@@ -836,6 +845,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer) ...@@ -836,6 +845,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
...@@ -961,9 +977,11 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -961,9 +977,11 @@ 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, kernel_module_request);
...@@ -1063,6 +1081,7 @@ void security_fixup_ops(struct security_operations *ops) ...@@ -1063,6 +1081,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);
......
...@@ -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;
} }
......
...@@ -65,6 +65,7 @@ static void key_gc_timer_func(unsigned long data) ...@@ -65,6 +65,7 @@ static void key_gc_timer_func(unsigned long data)
* - return true if we altered the keyring * - return true if we altered the keyring
*/ */
static bool key_gc_keyring(struct key *keyring, time_t limit) static bool key_gc_keyring(struct key *keyring, time_t limit)
__releases(key_serial_lock)
{ {
struct keyring_list *klist; struct keyring_list *klist;
struct key *key; struct key *key;
......
...@@ -201,6 +201,7 @@ extern long keyctl_set_timeout(key_serial_t, unsigned); ...@@ -201,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
......
...@@ -1228,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid, ...@@ -1228,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
...@@ -1313,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, ...@@ -1313,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;
} }
......
...@@ -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"
...@@ -768,3 +769,51 @@ long join_session_keyring(const char *name) ...@@ -768,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);
}
...@@ -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);
...@@ -1241,6 +1251,13 @@ int security_key_getsecurity(struct key *key, char **_buffer) ...@@ -1241,6 +1251,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
......
...@@ -3232,6 +3232,21 @@ static int selinux_task_create(unsigned long clone_flags) ...@@ -3232,6 +3232,21 @@ static int selinux_task_create(unsigned long clone_flags)
return current_has_perm(current, PROCESS__FORK); return current_has_perm(current, PROCESS__FORK);
} }
/*
* allocate the SELinux part of blank credentials
*/
static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
struct task_security_struct *tsec;
tsec = kzalloc(sizeof(struct task_security_struct), gfp);
if (!tsec)
return -ENOMEM;
cred->security = tsec;
return 0;
}
/* /*
* detach and free the LSM part of a set of credentials * detach and free the LSM part of a set of credentials
*/ */
...@@ -3263,6 +3278,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old, ...@@ -3263,6 +3278,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
return 0; return 0;
} }
/*
* transfer the SELinux data to a blank set of creds
*/
static void selinux_cred_transfer(struct cred *new, const struct cred *old)
{
const struct task_security_struct *old_tsec = old->security;
struct task_security_struct *tsec = new->security;
*tsec = *old_tsec;
}
/* /*
* set the security data for a kernel service * set the security data for a kernel service
* - all the creation contexts are set to unlabelled * - all the creation contexts are set to unlabelled
...@@ -5469,8 +5495,10 @@ static struct security_operations selinux_ops = { ...@@ -5469,8 +5495,10 @@ static struct security_operations selinux_ops = {
.dentry_open = selinux_dentry_open, .dentry_open = selinux_dentry_open,
.task_create = selinux_task_create, .task_create = selinux_task_create,
.cred_alloc_blank = selinux_cred_alloc_blank,
.cred_free = selinux_cred_free, .cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare, .cred_prepare = selinux_cred_prepare,
.cred_transfer = selinux_cred_transfer,
.kernel_act_as = selinux_kernel_act_as, .kernel_act_as = selinux_kernel_act_as,
.kernel_create_files_as = selinux_kernel_create_files_as, .kernel_create_files_as = selinux_kernel_create_files_as,
.kernel_module_request = selinux_kernel_module_request, .kernel_module_request = selinux_kernel_module_request,
......
...@@ -1079,6 +1079,22 @@ static int smack_file_receive(struct file *file) ...@@ -1079,6 +1079,22 @@ static int smack_file_receive(struct file *file)
* Task hooks * Task hooks
*/ */
/**
* smack_cred_alloc_blank - "allocate" blank task-level security credentials
* @new: the new credentials
* @gfp: the atomicity of any memory allocations
*
* Prepare a blank set of credentials for modification. This must allocate all
* the memory the LSM module might require such that cred_transfer() can
* complete without error.
*/
static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp)
{
cred->security = NULL;
return 0;
}
/** /**
* smack_cred_free - "free" task-level security credentials * smack_cred_free - "free" task-level security credentials
* @cred: the credentials in question * @cred: the credentials in question
...@@ -1116,6 +1132,18 @@ static void smack_cred_commit(struct cred *new, const struct cred *old) ...@@ -1116,6 +1132,18 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)
{ {
} }
/**
* smack_cred_transfer - Transfer the old credentials to the new credentials
* @new: the new credentials
* @old: the original credentials
*
* Fill in a set of blank credentials from another set of credentials.
*/
static void smack_cred_transfer(struct cred *new, const struct cred *old)
{
new->security = old->security;
}
/** /**
* smack_kernel_act_as - Set the subjective context in a set of credentials * smack_kernel_act_as - Set the subjective context in a set of credentials
* @new: points to the set of credentials to be modified. * @new: points to the set of credentials to be modified.
...@@ -3073,9 +3101,11 @@ struct security_operations smack_ops = { ...@@ -3073,9 +3101,11 @@ struct security_operations smack_ops = {
.file_send_sigiotask = smack_file_send_sigiotask, .file_send_sigiotask = smack_file_send_sigiotask,
.file_receive = smack_file_receive, .file_receive = smack_file_receive,
.cred_alloc_blank = smack_cred_alloc_blank,
.cred_free = smack_cred_free, .cred_free = smack_cred_free,
.cred_prepare = smack_cred_prepare, .cred_prepare = smack_cred_prepare,
.cred_commit = smack_cred_commit, .cred_commit = smack_cred_commit,
.cred_transfer = smack_cred_transfer,
.kernel_act_as = smack_kernel_act_as, .kernel_act_as = smack_kernel_act_as,
.kernel_create_files_as = smack_kernel_create_files_as, .kernel_create_files_as = smack_kernel_create_files_as,
.task_setpgid = smack_task_setpgid, .task_setpgid = smack_task_setpgid,
......
...@@ -14,6 +14,12 @@ ...@@ -14,6 +14,12 @@
#include "tomoyo.h" #include "tomoyo.h"
#include "realpath.h" #include "realpath.h"
static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp)
{
new->security = NULL;
return 0;
}
static int tomoyo_cred_prepare(struct cred *new, const struct cred *old, static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
gfp_t gfp) gfp_t gfp)
{ {
...@@ -25,6 +31,15 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old, ...@@ -25,6 +31,15 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,
return 0; return 0;
} }
static void tomoyo_cred_transfer(struct cred *new, const struct cred *old)
{
/*
* Since "struct tomoyo_domain_info *" is a sharable pointer,
* we don't need to duplicate.
*/
new->security = old->security;
}
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm) static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
{ {
int rc; int rc;
...@@ -262,7 +277,9 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) ...@@ -262,7 +277,9 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)
*/ */
static struct security_operations tomoyo_security_ops = { static struct security_operations tomoyo_security_ops = {
.name = "tomoyo", .name = "tomoyo",
.cred_alloc_blank = tomoyo_cred_alloc_blank,
.cred_prepare = tomoyo_cred_prepare, .cred_prepare = tomoyo_cred_prepare,
.cred_transfer = tomoyo_cred_transfer,
.bprm_set_creds = tomoyo_bprm_set_creds, .bprm_set_creds = tomoyo_bprm_set_creds,
.bprm_check_security = tomoyo_bprm_check_security, .bprm_check_security = tomoyo_bprm_check_security,
#ifdef CONFIG_SYSCTL #ifdef CONFIG_SYSCTL
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment