diff options
Diffstat (limited to 'security')
| -rw-r--r-- | security/capability.c | 19 | ||||
| -rw-r--r-- | security/keys/compat.c | 3 | ||||
| -rw-r--r-- | security/keys/gc.c | 1 | ||||
| -rw-r--r-- | security/keys/internal.h | 1 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 102 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 49 | ||||
| -rw-r--r-- | security/security.c | 17 | ||||
| -rw-r--r-- | security/selinux/hooks.c | 28 | ||||
| -rw-r--r-- | security/smack/smack_lsm.c | 30 | ||||
| -rw-r--r-- | security/tomoyo/tomoyo.c | 17 | 
10 files changed, 267 insertions, 0 deletions
diff --git a/security/capability.c b/security/capability.c index 06400cf0775..93a2ffe6590 100644 --- a/security/capability.c +++ b/security/capability.c @@ -373,6 +373,11 @@ static int cap_task_create(unsigned long clone_flags)  	return 0;  } +static int cap_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ +	return 0; +} +  static void cap_cred_free(struct cred *cred)  {  } @@ -386,6 +391,10 @@ static void cap_cred_commit(struct cred *new, const struct cred *old)  {  } +static void cap_cred_transfer(struct cred *new, const struct cred *old) +{ +} +  static int cap_kernel_act_as(struct cred *new, u32 secid)  {  	return 0; @@ -836,6 +845,13 @@ static int cap_key_getsecurity(struct key *key, char **_buffer)  	return 0;  } +static int cap_key_session_to_parent(const struct cred *cred, +				     const struct cred *parent_cred, +				     struct key *key) +{ +	return 0; +} +  #endif /* CONFIG_KEYS */  #ifdef CONFIG_AUDIT @@ -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, dentry_open);  	set_to_cap_if_null(ops, task_create); +	set_to_cap_if_null(ops, cred_alloc_blank);  	set_to_cap_if_null(ops, cred_free);  	set_to_cap_if_null(ops, cred_prepare);  	set_to_cap_if_null(ops, cred_commit); +	set_to_cap_if_null(ops, cred_transfer);  	set_to_cap_if_null(ops, kernel_act_as);  	set_to_cap_if_null(ops, kernel_create_files_as);  	set_to_cap_if_null(ops, kernel_module_request); @@ -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_permission);  	set_to_cap_if_null(ops, key_getsecurity); +	set_to_cap_if_null(ops, key_session_to_parent);  #endif	/* CONFIG_KEYS */  #ifdef CONFIG_AUDIT  	set_to_cap_if_null(ops, audit_rule_init); diff --git a/security/keys/compat.c b/security/keys/compat.c index c766c68a63b..792c0a611a6 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -82,6 +82,9 @@ asmlinkage long compat_sys_keyctl(u32 option,  	case KEYCTL_GET_SECURITY:  		return keyctl_get_security(arg2, compat_ptr(arg3), arg4); +	case KEYCTL_SESSION_TO_PARENT: +		return keyctl_session_to_parent(); +  	default:  		return -EOPNOTSUPP;  	} diff --git a/security/keys/gc.c b/security/keys/gc.c index 44adc325e15..1e616aef55f 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -65,6 +65,7 @@ static void key_gc_timer_func(unsigned long data)   * - 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; diff --git a/security/keys/internal.h b/security/keys/internal.h index fb830514c33..24ba0307b7a 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -201,6 +201,7 @@ extern long keyctl_set_timeout(key_serial_t, unsigned);  extern long keyctl_assume_authority(key_serial_t);  extern long keyctl_get_security(key_serial_t keyid, char __user *buffer,  				size_t buflen); +extern long keyctl_session_to_parent(void);  /*   * debugging key validation diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 736d7800f97..74c96852459 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1228,6 +1228,105 @@ long keyctl_get_security(key_serial_t keyid,  	return ret;  } +/* + * attempt to install the calling process's session keyring on the process's + * parent process + * - the keyring must exist and must grant us LINK permission + * - implements keyctl(KEYCTL_SESSION_TO_PARENT) + */ +long keyctl_session_to_parent(void) +{ +	struct task_struct *me, *parent; +	const struct cred *mycred, *pcred; +	struct cred *cred, *oldcred; +	key_ref_t keyring_r; +	int ret; + +	keyring_r = lookup_user_key(KEY_SPEC_SESSION_KEYRING, 0, KEY_LINK); +	if (IS_ERR(keyring_r)) +		return PTR_ERR(keyring_r); + +	/* our parent is going to need a new cred struct, a new tgcred struct +	 * and new security data, so we allocate them here to prevent ENOMEM in +	 * our parent */ +	ret = -ENOMEM; +	cred = cred_alloc_blank(); +	if (!cred) +		goto error_keyring; + +	cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); +	keyring_r = NULL; + +	me = current; +	write_lock_irq(&tasklist_lock); + +	parent = me->real_parent; +	ret = -EPERM; + +	/* the parent mustn't be init and mustn't be a kernel thread */ +	if (parent->pid <= 1 || !parent->mm) +		goto not_permitted; + +	/* the parent must be single threaded */ +	if (atomic_read(&parent->signal->count) != 1) +		goto not_permitted; + +	/* the parent and the child must have different session keyrings or +	 * there's no point */ +	mycred = current_cred(); +	pcred = __task_cred(parent); +	if (mycred == pcred || +	    mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) +		goto already_same; + +	/* the parent must have the same effective ownership and mustn't be +	 * SUID/SGID */ +	if (pcred-> uid	!= mycred->euid	|| +	    pcred->euid	!= mycred->euid	|| +	    pcred->suid	!= mycred->euid	|| +	    pcred-> gid	!= mycred->egid	|| +	    pcred->egid	!= mycred->egid	|| +	    pcred->sgid	!= mycred->egid) +		goto not_permitted; + +	/* the keyrings must have the same UID */ +	if (pcred ->tgcred->session_keyring->uid != mycred->euid || +	    mycred->tgcred->session_keyring->uid != mycred->euid) +		goto not_permitted; + +	/* the LSM must permit the replacement of the parent's keyring with the +	 * keyring from this process */ +	ret = security_key_session_to_parent(mycred, pcred, +					     key_ref_to_ptr(keyring_r)); +	if (ret < 0) +		goto not_permitted; + +	/* if there's an already pending keyring replacement, then we replace +	 * that */ +	oldcred = parent->replacement_session_keyring; + +	/* the replacement session keyring is applied just prior to userspace +	 * restarting */ +	parent->replacement_session_keyring = cred; +	cred = NULL; +	set_ti_thread_flag(task_thread_info(parent), TIF_NOTIFY_RESUME); + +	write_unlock_irq(&tasklist_lock); +	if (oldcred) +		put_cred(oldcred); +	return 0; + +already_same: +	ret = 0; +not_permitted: +	put_cred(cred); +	return ret; + +error_keyring: +	key_ref_put(keyring_r); +	return ret; +} +  /*****************************************************************************/  /*   * the key control system call @@ -1313,6 +1412,9 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3,  					   (char __user *) arg3,  					   (size_t) arg4); +	case KEYCTL_SESSION_TO_PARENT: +		return keyctl_session_to_parent(); +  	default:  		return -EOPNOTSUPP;  	} diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 4739cfbb41b..5c23afb31ec 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -17,6 +17,7 @@  #include <linux/fs.h>  #include <linux/err.h>  #include <linux/mutex.h> +#include <linux/security.h>  #include <linux/user_namespace.h>  #include <asm/uaccess.h>  #include "internal.h" @@ -768,3 +769,51 @@ error:  	abort_creds(new);  	return ret;  } + +/* + * Replace a process's session keyring when that process resumes userspace on + * behalf of one of its children + */ +void key_replace_session_keyring(void) +{ +	const struct cred *old; +	struct cred *new; + +	if (!current->replacement_session_keyring) +		return; + +	write_lock_irq(&tasklist_lock); +	new = current->replacement_session_keyring; +	current->replacement_session_keyring = NULL; +	write_unlock_irq(&tasklist_lock); + +	if (!new) +		return; + +	old = current_cred(); +	new->  uid	= old->  uid; +	new-> euid	= old-> euid; +	new-> suid	= old-> suid; +	new->fsuid	= old->fsuid; +	new->  gid	= old->  gid; +	new-> egid	= old-> egid; +	new-> sgid	= old-> sgid; +	new->fsgid	= old->fsgid; +	new->user	= get_uid(old->user); +	new->group_info	= get_group_info(old->group_info); + +	new->securebits	= old->securebits; +	new->cap_inheritable	= old->cap_inheritable; +	new->cap_permitted	= old->cap_permitted; +	new->cap_effective	= old->cap_effective; +	new->cap_bset		= old->cap_bset; + +	new->jit_keyring	= old->jit_keyring; +	new->thread_keyring	= key_get(old->thread_keyring); +	new->tgcred->tgid	= old->tgcred->tgid; +	new->tgcred->process_keyring = key_get(old->tgcred->process_keyring); + +	security_transfer_creds(new, old); + +	commit_creds(new); +} diff --git a/security/security.c b/security/security.c index f88eaf6b14c..d8b727637f0 100644 --- a/security/security.c +++ b/security/security.c @@ -684,6 +684,11 @@ int security_task_create(unsigned long clone_flags)  	return security_ops->task_create(clone_flags);  } +int security_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ +	return security_ops->cred_alloc_blank(cred, gfp); +} +  void security_cred_free(struct cred *cred)  {  	security_ops->cred_free(cred); @@ -699,6 +704,11 @@ void security_commit_creds(struct cred *new, const struct cred *old)  	security_ops->cred_commit(new, old);  } +void security_transfer_creds(struct cred *new, const struct cred *old) +{ +	security_ops->cred_transfer(new, old); +} +  int security_kernel_act_as(struct cred *new, u32 secid)  {  	return security_ops->kernel_act_as(new, secid); @@ -1241,6 +1251,13 @@ int security_key_getsecurity(struct key *key, char **_buffer)  	return security_ops->key_getsecurity(key, _buffer);  } +int security_key_session_to_parent(const struct cred *cred, +				   const struct cred *parent_cred, +				   struct key *key) +{ +	return security_ops->key_session_to_parent(cred, parent_cred, key); +} +  #endif	/* CONFIG_KEYS */  #ifdef CONFIG_AUDIT diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c3bb31ecc5a..134a9c0d200 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3233,6 +3233,21 @@ static int selinux_task_create(unsigned long clone_flags)  }  /* + * allocate the SELinux part of blank credentials + */ +static int selinux_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ +	struct task_security_struct *tsec; + +	tsec = kzalloc(sizeof(struct task_security_struct), gfp); +	if (!tsec) +		return -ENOMEM; + +	cred->security = tsec; +	return 0; +} + +/*   * detach and free the LSM part of a set of credentials   */  static void selinux_cred_free(struct cred *cred) @@ -3264,6 +3279,17 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,  }  /* + * transfer the SELinux data to a blank set of creds + */ +static void selinux_cred_transfer(struct cred *new, const struct cred *old) +{ +	const struct task_security_struct *old_tsec = old->security; +	struct task_security_struct *tsec = new->security; + +	*tsec = *old_tsec; +} + +/*   * set the security data for a kernel service   * - all the creation contexts are set to unlabelled   */ @@ -5469,8 +5495,10 @@ static struct security_operations selinux_ops = {  	.dentry_open =			selinux_dentry_open,  	.task_create =			selinux_task_create, +	.cred_alloc_blank =		selinux_cred_alloc_blank,  	.cred_free =			selinux_cred_free,  	.cred_prepare =			selinux_cred_prepare, +	.cred_transfer =		selinux_cred_transfer,  	.kernel_act_as =		selinux_kernel_act_as,  	.kernel_create_files_as =	selinux_kernel_create_files_as,  	.kernel_module_request =	selinux_kernel_module_request, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c243a2b2583..969f5fee190 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1080,6 +1080,22 @@ static int smack_file_receive(struct file *file)   */  /** + * smack_cred_alloc_blank - "allocate" blank task-level security credentials + * @new: the new credentials + * @gfp: the atomicity of any memory allocations + * + * Prepare a blank set of credentials for modification.  This must allocate all + * the memory the LSM module might require such that cred_transfer() can + * complete without error. + */ +static int smack_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ +	cred->security = NULL; +	return 0; +} + + +/**   * smack_cred_free - "free" task-level security credentials   * @cred: the credentials in question   * @@ -1117,6 +1133,18 @@ static void smack_cred_commit(struct cred *new, const struct cred *old)  }  /** + * smack_cred_transfer - Transfer the old credentials to the new credentials + * @new: the new credentials + * @old: the original credentials + * + * Fill in a set of blank credentials from another set of credentials. + */ +static void smack_cred_transfer(struct cred *new, const struct cred *old) +{ +	new->security = old->security; +} + +/**   * smack_kernel_act_as - Set the subjective context in a set of credentials   * @new: points to the set of credentials to be modified.   * @secid: specifies the security ID to be set @@ -3073,9 +3101,11 @@ struct security_operations smack_ops = {  	.file_send_sigiotask = 		smack_file_send_sigiotask,  	.file_receive = 		smack_file_receive, +	.cred_alloc_blank =		smack_cred_alloc_blank,  	.cred_free =			smack_cred_free,  	.cred_prepare =			smack_cred_prepare,  	.cred_commit =			smack_cred_commit, +	.cred_transfer =		smack_cred_transfer,  	.kernel_act_as =		smack_kernel_act_as,  	.kernel_create_files_as =	smack_kernel_create_files_as,  	.task_setpgid = 		smack_task_setpgid, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 35a13e7915e..9548a0984cc 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -14,6 +14,12 @@  #include "tomoyo.h"  #include "realpath.h" +static int tomoyo_cred_alloc_blank(struct cred *new, gfp_t gfp) +{ +	new->security = NULL; +	return 0; +} +  static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,  			       gfp_t gfp)  { @@ -25,6 +31,15 @@ static int tomoyo_cred_prepare(struct cred *new, const struct cred *old,  	return 0;  } +static void tomoyo_cred_transfer(struct cred *new, const struct cred *old) +{ +	/* +	 * Since "struct tomoyo_domain_info *" is a sharable pointer, +	 * we don't need to duplicate. +	 */ +	new->security = old->security; +} +  static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)  {  	int rc; @@ -262,7 +277,9 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred)   */  static struct security_operations tomoyo_security_ops = {  	.name                = "tomoyo", +	.cred_alloc_blank    = tomoyo_cred_alloc_blank,  	.cred_prepare        = tomoyo_cred_prepare, +	.cred_transfer	     = tomoyo_cred_transfer,  	.bprm_set_creds      = tomoyo_bprm_set_creds,  	.bprm_check_security = tomoyo_bprm_check_security,  #ifdef CONFIG_SYSCTL  |