diff options
| -rw-r--r-- | include/linux/cred.h | 17 | ||||
| -rw-r--r-- | kernel/cred.c | 127 | ||||
| -rw-r--r-- | security/keys/keyctl.c | 11 | ||||
| -rw-r--r-- | security/keys/process_keys.c | 64 | ||||
| -rw-r--r-- | security/keys/request_key.c | 10 | 
5 files changed, 49 insertions, 180 deletions
diff --git a/include/linux/cred.h b/include/linux/cred.h index ebbed2ce663..0142aacb70b 100644 --- a/include/linux/cred.h +++ b/include/linux/cred.h @@ -77,21 +77,6 @@ extern int in_group_p(kgid_t);  extern int in_egroup_p(kgid_t);  /* - * The common credentials for a thread group - * - shared by CLONE_THREAD - */ -#ifdef CONFIG_KEYS -struct thread_group_cred { -	atomic_t	usage; -	pid_t		tgid;			/* thread group process ID */ -	spinlock_t	lock; -	struct key __rcu *session_keyring;	/* keyring inherited over fork */ -	struct key	*process_keyring;	/* keyring private to this process */ -	struct rcu_head	rcu;			/* RCU deletion hook */ -}; -#endif - -/*   * The security context of a task   *   * The parts of the context break down into two categories: @@ -139,6 +124,8 @@ struct cred {  #ifdef CONFIG_KEYS  	unsigned char	jit_keyring;	/* default keyring to attach requested  					 * keys to */ +	struct key __rcu *session_keyring; /* keyring inherited over fork */ +	struct key	*process_keyring; /* keyring private to this process */  	struct key	*thread_keyring; /* keyring private to this thread */  	struct key	*request_key_auth; /* assumed request_key authority */  	struct thread_group_cred *tgcred; /* thread-group shared credentials */ diff --git a/kernel/cred.c b/kernel/cred.c index de728ac50d8..3f7ad1ec2ae 100644 --- a/kernel/cred.c +++ b/kernel/cred.c @@ -30,17 +30,6 @@  static struct kmem_cache *cred_jar;  /* - * The common credentials for the initial task's thread group - */ -#ifdef CONFIG_KEYS -static struct thread_group_cred init_tgcred = { -	.usage	= ATOMIC_INIT(2), -	.tgid	= 0, -	.lock	= __SPIN_LOCK_UNLOCKED(init_cred.tgcred.lock), -}; -#endif - -/*   * The initial credentials for the initial task   */  struct cred init_cred = { @@ -65,9 +54,6 @@ struct cred init_cred = {  	.user			= INIT_USER,  	.user_ns		= &init_user_ns,  	.group_info		= &init_groups, -#ifdef CONFIG_KEYS -	.tgcred			= &init_tgcred, -#endif  };  static inline void set_cred_subscribers(struct cred *cred, int n) @@ -96,36 +82,6 @@ static inline void alter_cred_subscribers(const struct cred *_cred, int n)  }  /* - * Dispose of the shared task group credentials - */ -#ifdef CONFIG_KEYS -static void release_tgcred_rcu(struct rcu_head *rcu) -{ -	struct thread_group_cred *tgcred = -		container_of(rcu, struct thread_group_cred, rcu); - -	BUG_ON(atomic_read(&tgcred->usage) != 0); - -	key_put(tgcred->session_keyring); -	key_put(tgcred->process_keyring); -	kfree(tgcred); -} -#endif - -/* - * Release a set of thread group credentials. - */ -static void release_tgcred(struct cred *cred) -{ -#ifdef CONFIG_KEYS -	struct thread_group_cred *tgcred = cred->tgcred; - -	if (atomic_dec_and_test(&tgcred->usage)) -		call_rcu(&tgcred->rcu, release_tgcred_rcu); -#endif -} - -/*   * The RCU callback to actually dispose of a set of credentials   */  static void put_cred_rcu(struct rcu_head *rcu) @@ -150,9 +106,10 @@ static void put_cred_rcu(struct rcu_head *rcu)  #endif  	security_cred_free(cred); +	key_put(cred->session_keyring); +	key_put(cred->process_keyring);  	key_put(cred->thread_keyring);  	key_put(cred->request_key_auth); -	release_tgcred(cred);  	if (cred->group_info)  		put_group_info(cred->group_info);  	free_uid(cred->user); @@ -246,15 +203,6 @@ struct cred *cred_alloc_blank(void)  	if (!new)  		return NULL; -#ifdef CONFIG_KEYS -	new->tgcred = kzalloc(sizeof(*new->tgcred), GFP_KERNEL); -	if (!new->tgcred) { -		kmem_cache_free(cred_jar, new); -		return NULL; -	} -	atomic_set(&new->tgcred->usage, 1); -#endif -  	atomic_set(&new->usage, 1);  #ifdef CONFIG_DEBUG_CREDENTIALS  	new->magic = CRED_MAGIC; @@ -308,9 +256,10 @@ struct cred *prepare_creds(void)  	get_user_ns(new->user_ns);  #ifdef CONFIG_KEYS +	key_get(new->session_keyring); +	key_get(new->process_keyring);  	key_get(new->thread_keyring);  	key_get(new->request_key_auth); -	atomic_inc(&new->tgcred->usage);  #endif  #ifdef CONFIG_SECURITY @@ -334,39 +283,20 @@ EXPORT_SYMBOL(prepare_creds);   */  struct cred *prepare_exec_creds(void)  { -	struct thread_group_cred *tgcred = NULL;  	struct cred *new; -#ifdef CONFIG_KEYS -	tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); -	if (!tgcred) -		return NULL; -#endif -  	new = prepare_creds(); -	if (!new) { -		kfree(tgcred); +	if (!new)  		return new; -	}  #ifdef CONFIG_KEYS  	/* newly exec'd tasks don't get a thread keyring */  	key_put(new->thread_keyring);  	new->thread_keyring = NULL; -	/* create a new per-thread-group creds for all this set of threads to -	 * share */ -	memcpy(tgcred, new->tgcred, sizeof(struct thread_group_cred)); - -	atomic_set(&tgcred->usage, 1); -	spin_lock_init(&tgcred->lock); -  	/* inherit the session keyring; new process keyring */ -	key_get(tgcred->session_keyring); -	tgcred->process_keyring = NULL; - -	release_tgcred(new); -	new->tgcred = tgcred; +	key_put(new->process_keyring); +	new->process_keyring = NULL;  #endif  	return new; @@ -383,9 +313,6 @@ struct cred *prepare_exec_creds(void)   */  int copy_creds(struct task_struct *p, unsigned long clone_flags)  { -#ifdef CONFIG_KEYS -	struct thread_group_cred *tgcred; -#endif  	struct cred *new;  	int ret; @@ -425,22 +352,12 @@ int copy_creds(struct task_struct *p, unsigned long clone_flags)  			install_thread_keyring_to_cred(new);  	} -	/* we share the process and session keyrings between all the threads in -	 * a process - this is slightly icky as we violate COW credentials a -	 * bit */ +	/* The process keyring is only shared between the threads in a process; +	 * anything outside of those threads doesn't inherit. +	 */  	if (!(clone_flags & CLONE_THREAD)) { -		tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); -		if (!tgcred) { -			ret = -ENOMEM; -			goto error_put; -		} -		atomic_set(&tgcred->usage, 1); -		spin_lock_init(&tgcred->lock); -		tgcred->process_keyring = NULL; -		tgcred->session_keyring = key_get(new->tgcred->session_keyring); - -		release_tgcred(new); -		new->tgcred = tgcred; +		key_put(new->process_keyring); +		new->process_keyring = NULL;  	}  #endif @@ -643,9 +560,6 @@ void __init cred_init(void)   */  struct cred *prepare_kernel_cred(struct task_struct *daemon)  { -#ifdef CONFIG_KEYS -	struct thread_group_cred *tgcred; -#endif  	const struct cred *old;  	struct cred *new; @@ -653,14 +567,6 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)  	if (!new)  		return NULL; -#ifdef CONFIG_KEYS -	tgcred = kmalloc(sizeof(*tgcred), GFP_KERNEL); -	if (!tgcred) { -		kmem_cache_free(cred_jar, new); -		return NULL; -	} -#endif -  	kdebug("prepare_kernel_cred() alloc %p", new);  	if (daemon) @@ -678,13 +584,10 @@ struct cred *prepare_kernel_cred(struct task_struct *daemon)  	get_group_info(new->group_info);  #ifdef CONFIG_KEYS -	atomic_set(&tgcred->usage, 1); -	spin_lock_init(&tgcred->lock); -	tgcred->process_keyring = NULL; -	tgcred->session_keyring = NULL; -	new->tgcred = tgcred; -	new->request_key_auth = NULL; +	new->session_keyring = NULL; +	new->process_keyring = NULL;  	new->thread_keyring = NULL; +	new->request_key_auth = NULL;  	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;  #endif diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index a0d373f7681..65b38417c21 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1475,7 +1475,8 @@ long keyctl_session_to_parent(void)  		goto error_keyring;  	newwork = &cred->rcu; -	cred->tgcred->session_keyring = key_ref_to_ptr(keyring_r); +	cred->session_keyring = key_ref_to_ptr(keyring_r); +	keyring_r = NULL;  	init_task_work(newwork, key_change_session_keyring);  	me = current; @@ -1500,7 +1501,7 @@ long keyctl_session_to_parent(void)  	mycred = current_cred();  	pcred = __task_cred(parent);  	if (mycred == pcred || -	    mycred->tgcred->session_keyring == pcred->tgcred->session_keyring) { +	    mycred->session_keyring == pcred->session_keyring) {  		ret = 0;  		goto unlock;  	} @@ -1516,9 +1517,9 @@ long keyctl_session_to_parent(void)  		goto unlock;  	/* the keyrings must have the same UID */ -	if ((pcred->tgcred->session_keyring && -	     pcred->tgcred->session_keyring->uid != mycred->euid) || -	    mycred->tgcred->session_keyring->uid != mycred->euid) +	if ((pcred->session_keyring && +	     pcred->session_keyring->uid != mycred->euid) || +	    mycred->session_keyring->uid != mycred->euid)  		goto unlock;  	/* cancel an already pending keyring replacement */ diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 178b8c3b130..9de5dc59827 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -169,9 +169,8 @@ static int install_thread_keyring(void)  int install_process_keyring_to_cred(struct cred *new)  {  	struct key *keyring; -	int ret; -	if (new->tgcred->process_keyring) +	if (new->process_keyring)  		return -EEXIST;  	keyring = keyring_alloc("_pid", new->uid, new->gid, @@ -179,17 +178,8 @@ int install_process_keyring_to_cred(struct cred *new)  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); -	spin_lock_irq(&new->tgcred->lock); -	if (!new->tgcred->process_keyring) { -		new->tgcred->process_keyring = keyring; -		keyring = NULL; -		ret = 0; -	} else { -		ret = -EEXIST; -	} -	spin_unlock_irq(&new->tgcred->lock); -	key_put(keyring); -	return ret; +	new->process_keyring = keyring; +	return 0;  }  /* @@ -230,7 +220,7 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	/* create an empty session keyring */  	if (!keyring) {  		flags = KEY_ALLOC_QUOTA_OVERRUN; -		if (cred->tgcred->session_keyring) +		if (cred->session_keyring)  			flags = KEY_ALLOC_IN_QUOTA;  		keyring = keyring_alloc("_ses", cred->uid, cred->gid, @@ -242,17 +232,11 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	}  	/* install the keyring */ -	spin_lock_irq(&cred->tgcred->lock); -	old = cred->tgcred->session_keyring; -	rcu_assign_pointer(cred->tgcred->session_keyring, keyring); -	spin_unlock_irq(&cred->tgcred->lock); +	old = cred->session_keyring; +	rcu_assign_pointer(cred->session_keyring, keyring); -	/* we're using RCU on the pointer, but there's no point synchronising -	 * on it if it didn't previously point to anything */ -	if (old) { -		synchronize_rcu(); +	if (old)  		key_put(old); -	}  	return 0;  } @@ -367,9 +351,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the process keyring second */ -	if (cred->tgcred->process_keyring) { +	if (cred->process_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->tgcred->process_keyring, 1), +			make_key_ref(cred->process_keyring, 1),  			cred, type, description, match, no_state_check);  		if (!IS_ERR(key_ref))  			goto found; @@ -388,12 +372,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the session keyring */ -	if (cred->tgcred->session_keyring) { +	if (cred->session_keyring) {  		rcu_read_lock();  		key_ref = keyring_search_aux( -			make_key_ref(rcu_dereference( -					     cred->tgcred->session_keyring), -				     1), +			make_key_ref(rcu_dereference(cred->session_keyring), 1),  			cred, type, description, match, no_state_check);  		rcu_read_unlock(); @@ -563,7 +545,7 @@ try_again:  		break;  	case KEY_SPEC_PROCESS_KEYRING: -		if (!cred->tgcred->process_keyring) { +		if (!cred->process_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -575,13 +557,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->tgcred->process_keyring; +		key = cred->process_keyring;  		atomic_inc(&key->usage);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_SESSION_KEYRING: -		if (!cred->tgcred->session_keyring) { +		if (!cred->session_keyring) {  			/* always install a session keyring upon access if one  			 * doesn't exist yet */  			ret = install_user_keyrings(); @@ -596,7 +578,7 @@ try_again:  			if (ret < 0)  				goto error;  			goto reget_creds; -		} else if (cred->tgcred->session_keyring == +		} else if (cred->session_keyring ==  			   cred->user->session_keyring &&  			   lflags & KEY_LOOKUP_CREATE) {  			ret = join_session_keyring(NULL); @@ -606,7 +588,7 @@ try_again:  		}  		rcu_read_lock(); -		key = rcu_dereference(cred->tgcred->session_keyring); +		key = rcu_dereference(cred->session_keyring);  		atomic_inc(&key->usage);  		rcu_read_unlock();  		key_ref = make_key_ref(key, 1); @@ -766,12 +748,6 @@ long join_session_keyring(const char *name)  	struct key *keyring;  	long ret, serial; -	/* only permit this if there's a single thread in the thread group - -	 * this avoids us having to adjust the creds on all threads and risking -	 * ENOMEM */ -	if (!current_is_single_threaded()) -		return -EMLINK; -  	new = prepare_creds();  	if (!new)  		return -ENOMEM; @@ -783,7 +759,7 @@ long join_session_keyring(const char *name)  		if (ret < 0)  			goto error; -		serial = new->tgcred->session_keyring->serial; +		serial = new->session_keyring->serial;  		ret = commit_creds(new);  		if (ret == 0)  			ret = serial; @@ -806,6 +782,9 @@ long join_session_keyring(const char *name)  	} else if (IS_ERR(keyring)) {  		ret = PTR_ERR(keyring);  		goto error2; +	} else if (keyring == new->session_keyring) { +		ret = 0; +		goto error2;  	}  	/* we've got a keyring - now to install it */ @@ -862,8 +841,7 @@ void key_change_session_keyring(struct callback_head *twork)  	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); +	new->process_keyring	= key_get(old->process_keyring);  	security_transfer_creds(new, old); diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 000e7501752..275c4f9e4b8 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -150,12 +150,12 @@ static int call_sbin_request_key(struct key_construction *cons,  		cred->thread_keyring ? cred->thread_keyring->serial : 0);  	prkey = 0; -	if (cred->tgcred->process_keyring) -		prkey = cred->tgcred->process_keyring->serial; +	if (cred->process_keyring) +		prkey = cred->process_keyring->serial;  	sprintf(keyring_str[1], "%d", prkey);  	rcu_read_lock(); -	session = rcu_dereference(cred->tgcred->session_keyring); +	session = rcu_dereference(cred->session_keyring);  	if (!session)  		session = cred->user->session_keyring;  	sskey = session->serial; @@ -297,14 +297,14 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  				break;  		case KEY_REQKEY_DEFL_PROCESS_KEYRING: -			dest_keyring = key_get(cred->tgcred->process_keyring); +			dest_keyring = key_get(cred->process_keyring);  			if (dest_keyring)  				break;  		case KEY_REQKEY_DEFL_SESSION_KEYRING:  			rcu_read_lock();  			dest_keyring = key_get( -				rcu_dereference(cred->tgcred->session_keyring)); +				rcu_dereference(cred->session_keyring));  			rcu_read_unlock();  			if (dest_keyring)  |