xref: /linux/security/keys/process_keys.c (revision 67d12145)
1973c9f4fSDavid Howells /* Manage a process's keyrings
21da177e4SLinus Torvalds  *
369664cf1SDavid Howells  * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
41da177e4SLinus Torvalds  * Written by David Howells (dhowells@redhat.com)
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or
71da177e4SLinus Torvalds  * modify it under the terms of the GNU General Public License
81da177e4SLinus Torvalds  * as published by the Free Software Foundation; either version
91da177e4SLinus Torvalds  * 2 of the License, or (at your option) any later version.
101da177e4SLinus Torvalds  */
111da177e4SLinus Torvalds 
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/init.h>
141da177e4SLinus Torvalds #include <linux/sched.h>
151da177e4SLinus Torvalds #include <linux/keyctl.h>
161da177e4SLinus Torvalds #include <linux/fs.h>
171da177e4SLinus Torvalds #include <linux/err.h>
18bb003079SIngo Molnar #include <linux/mutex.h>
19ee18d64cSDavid Howells #include <linux/security.h>
201d1e9756SSerge E. Hallyn #include <linux/user_namespace.h>
211da177e4SLinus Torvalds #include <asm/uaccess.h>
221da177e4SLinus Torvalds #include "internal.h"
231da177e4SLinus Torvalds 
24973c9f4fSDavid Howells /* Session keyring create vs join semaphore */
25bb003079SIngo Molnar static DEFINE_MUTEX(key_session_mutex);
261da177e4SLinus Torvalds 
27973c9f4fSDavid Howells /* User keyring creation semaphore */
2869664cf1SDavid Howells static DEFINE_MUTEX(key_user_keyring_mutex);
2969664cf1SDavid Howells 
30973c9f4fSDavid Howells /* The root user's tracking struct */
311da177e4SLinus Torvalds struct key_user root_key_user = {
321da177e4SLinus Torvalds 	.usage		= ATOMIC_INIT(3),
3376181c13SDavid Howells 	.cons_lock	= __MUTEX_INITIALIZER(root_key_user.cons_lock),
346cfd76a2SPeter Zijlstra 	.lock		= __SPIN_LOCK_UNLOCKED(root_key_user.lock),
351da177e4SLinus Torvalds 	.nkeys		= ATOMIC_INIT(2),
361da177e4SLinus Torvalds 	.nikeys		= ATOMIC_INIT(2),
371da177e4SLinus Torvalds 	.uid		= 0,
381d1e9756SSerge E. Hallyn 	.user_ns	= &init_user_ns,
391da177e4SLinus Torvalds };
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds /*
42973c9f4fSDavid Howells  * Install the user and user session keyrings for the current process's UID.
431da177e4SLinus Torvalds  */
448bbf4976SDavid Howells int install_user_keyrings(void)
451da177e4SLinus Torvalds {
46d84f4f99SDavid Howells 	struct user_struct *user;
47d84f4f99SDavid Howells 	const struct cred *cred;
481da177e4SLinus Torvalds 	struct key *uid_keyring, *session_keyring;
491da177e4SLinus Torvalds 	char buf[20];
501da177e4SLinus Torvalds 	int ret;
511da177e4SLinus Torvalds 
52d84f4f99SDavid Howells 	cred = current_cred();
53d84f4f99SDavid Howells 	user = cred->user;
54d84f4f99SDavid Howells 
5569664cf1SDavid Howells 	kenter("%p{%u}", user, user->uid);
561da177e4SLinus Torvalds 
5769664cf1SDavid Howells 	if (user->uid_keyring) {
5869664cf1SDavid Howells 		kleave(" = 0 [exist]");
5969664cf1SDavid Howells 		return 0;
601da177e4SLinus Torvalds 	}
611da177e4SLinus Torvalds 
6269664cf1SDavid Howells 	mutex_lock(&key_user_keyring_mutex);
6369664cf1SDavid Howells 	ret = 0;
6469664cf1SDavid Howells 
6569664cf1SDavid Howells 	if (!user->uid_keyring) {
6669664cf1SDavid Howells 		/* get the UID-specific keyring
6769664cf1SDavid Howells 		 * - there may be one in existence already as it may have been
6869664cf1SDavid Howells 		 *   pinned by a session, but the user_struct pointing to it
6969664cf1SDavid Howells 		 *   may have been destroyed by setuid */
701da177e4SLinus Torvalds 		sprintf(buf, "_uid.%u", user->uid);
711da177e4SLinus Torvalds 
7269664cf1SDavid Howells 		uid_keyring = find_keyring_by_name(buf, true);
731da177e4SLinus Torvalds 		if (IS_ERR(uid_keyring)) {
7469664cf1SDavid Howells 			uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
75d84f4f99SDavid Howells 						    cred, KEY_ALLOC_IN_QUOTA,
7669664cf1SDavid Howells 						    NULL);
7769664cf1SDavid Howells 			if (IS_ERR(uid_keyring)) {
781da177e4SLinus Torvalds 				ret = PTR_ERR(uid_keyring);
791da177e4SLinus Torvalds 				goto error;
801da177e4SLinus Torvalds 			}
8169664cf1SDavid Howells 		}
8269664cf1SDavid Howells 
8369664cf1SDavid Howells 		/* get a default session keyring (which might also exist
8469664cf1SDavid Howells 		 * already) */
8569664cf1SDavid Howells 		sprintf(buf, "_uid_ses.%u", user->uid);
8669664cf1SDavid Howells 
8769664cf1SDavid Howells 		session_keyring = find_keyring_by_name(buf, true);
8869664cf1SDavid Howells 		if (IS_ERR(session_keyring)) {
8969664cf1SDavid Howells 			session_keyring =
9069664cf1SDavid Howells 				keyring_alloc(buf, user->uid, (gid_t) -1,
91d84f4f99SDavid Howells 					      cred, KEY_ALLOC_IN_QUOTA, NULL);
9269664cf1SDavid Howells 			if (IS_ERR(session_keyring)) {
9369664cf1SDavid Howells 				ret = PTR_ERR(session_keyring);
9469664cf1SDavid Howells 				goto error_release;
9569664cf1SDavid Howells 			}
9669664cf1SDavid Howells 
9769664cf1SDavid Howells 			/* we install a link from the user session keyring to
9869664cf1SDavid Howells 			 * the user keyring */
9969664cf1SDavid Howells 			ret = key_link(session_keyring, uid_keyring);
10069664cf1SDavid Howells 			if (ret < 0)
10169664cf1SDavid Howells 				goto error_release_both;
10269664cf1SDavid Howells 		}
1031da177e4SLinus Torvalds 
1041da177e4SLinus Torvalds 		/* install the keyrings */
1051da177e4SLinus Torvalds 		user->uid_keyring = uid_keyring;
1061da177e4SLinus Torvalds 		user->session_keyring = session_keyring;
10769664cf1SDavid Howells 	}
1081da177e4SLinus Torvalds 
10969664cf1SDavid Howells 	mutex_unlock(&key_user_keyring_mutex);
11069664cf1SDavid Howells 	kleave(" = 0");
11169664cf1SDavid Howells 	return 0;
11269664cf1SDavid Howells 
11369664cf1SDavid Howells error_release_both:
11469664cf1SDavid Howells 	key_put(session_keyring);
11569664cf1SDavid Howells error_release:
11669664cf1SDavid Howells 	key_put(uid_keyring);
1171da177e4SLinus Torvalds error:
11869664cf1SDavid Howells 	mutex_unlock(&key_user_keyring_mutex);
11969664cf1SDavid Howells 	kleave(" = %d", ret);
1201da177e4SLinus Torvalds 	return ret;
12169664cf1SDavid Howells }
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds /*
124973c9f4fSDavid Howells  * Install a fresh thread keyring directly to new credentials.  This keyring is
125973c9f4fSDavid Howells  * allowed to overrun the quota.
1261da177e4SLinus Torvalds  */
127d84f4f99SDavid Howells int install_thread_keyring_to_cred(struct cred *new)
1281da177e4SLinus Torvalds {
129d84f4f99SDavid Howells 	struct key *keyring;
1301da177e4SLinus Torvalds 
131d84f4f99SDavid Howells 	keyring = keyring_alloc("_tid", new->uid, new->gid, new,
132d84f4f99SDavid Howells 				KEY_ALLOC_QUOTA_OVERRUN, NULL);
133d84f4f99SDavid Howells 	if (IS_ERR(keyring))
134d84f4f99SDavid Howells 		return PTR_ERR(keyring);
1351da177e4SLinus Torvalds 
136d84f4f99SDavid Howells 	new->thread_keyring = keyring;
137d84f4f99SDavid Howells 	return 0;
1381da177e4SLinus Torvalds }
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds /*
141973c9f4fSDavid Howells  * Install a fresh thread keyring, discarding the old one.
1421da177e4SLinus Torvalds  */
143d84f4f99SDavid Howells static int install_thread_keyring(void)
1441da177e4SLinus Torvalds {
145d84f4f99SDavid Howells 	struct cred *new;
1461da177e4SLinus Torvalds 	int ret;
1471da177e4SLinus Torvalds 
148d84f4f99SDavid Howells 	new = prepare_creds();
149d84f4f99SDavid Howells 	if (!new)
150d84f4f99SDavid Howells 		return -ENOMEM;
1511da177e4SLinus Torvalds 
152d84f4f99SDavid Howells 	BUG_ON(new->thread_keyring);
153d84f4f99SDavid Howells 
154d84f4f99SDavid Howells 	ret = install_thread_keyring_to_cred(new);
155d84f4f99SDavid Howells 	if (ret < 0) {
156d84f4f99SDavid Howells 		abort_creds(new);
157d84f4f99SDavid Howells 		return ret;
1581da177e4SLinus Torvalds 	}
1591da177e4SLinus Torvalds 
160d84f4f99SDavid Howells 	return commit_creds(new);
161d84f4f99SDavid Howells }
1621da177e4SLinus Torvalds 
163d84f4f99SDavid Howells /*
164973c9f4fSDavid Howells  * Install a process keyring directly to a credentials struct.
165973c9f4fSDavid Howells  *
166973c9f4fSDavid Howells  * Returns -EEXIST if there was already a process keyring, 0 if one installed,
167973c9f4fSDavid Howells  * and other value on any other error
168d84f4f99SDavid Howells  */
169d84f4f99SDavid Howells int install_process_keyring_to_cred(struct cred *new)
170d84f4f99SDavid Howells {
171d84f4f99SDavid Howells 	struct key *keyring;
172d84f4f99SDavid Howells 	int ret;
173d84f4f99SDavid Howells 
174d84f4f99SDavid Howells 	if (new->tgcred->process_keyring)
175d84f4f99SDavid Howells 		return -EEXIST;
176d84f4f99SDavid Howells 
177d84f4f99SDavid Howells 	keyring = keyring_alloc("_pid", new->uid, new->gid,
178d84f4f99SDavid Howells 				new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
179d84f4f99SDavid Howells 	if (IS_ERR(keyring))
180d84f4f99SDavid Howells 		return PTR_ERR(keyring);
181d84f4f99SDavid Howells 
182d84f4f99SDavid Howells 	spin_lock_irq(&new->tgcred->lock);
183d84f4f99SDavid Howells 	if (!new->tgcred->process_keyring) {
184d84f4f99SDavid Howells 		new->tgcred->process_keyring = keyring;
185d84f4f99SDavid Howells 		keyring = NULL;
1861da177e4SLinus Torvalds 		ret = 0;
187d84f4f99SDavid Howells 	} else {
188d84f4f99SDavid Howells 		ret = -EEXIST;
189d84f4f99SDavid Howells 	}
190d84f4f99SDavid Howells 	spin_unlock_irq(&new->tgcred->lock);
191d84f4f99SDavid Howells 	key_put(keyring);
1921da177e4SLinus Torvalds 	return ret;
193d84f4f99SDavid Howells }
1941da177e4SLinus Torvalds 
1951da177e4SLinus Torvalds /*
196973c9f4fSDavid Howells  * Make sure a process keyring is installed for the current process.  The
197973c9f4fSDavid Howells  * existing process keyring is not replaced.
198973c9f4fSDavid Howells  *
199973c9f4fSDavid Howells  * Returns 0 if there is a process keyring by the end of this function, some
200973c9f4fSDavid Howells  * error otherwise.
2011da177e4SLinus Torvalds  */
202d84f4f99SDavid Howells static int install_process_keyring(void)
2031da177e4SLinus Torvalds {
204d84f4f99SDavid Howells 	struct cred *new;
2051da177e4SLinus Torvalds 	int ret;
2061da177e4SLinus Torvalds 
207d84f4f99SDavid Howells 	new = prepare_creds();
208d84f4f99SDavid Howells 	if (!new)
209d84f4f99SDavid Howells 		return -ENOMEM;
2101a26feb9SDavid Howells 
211d84f4f99SDavid Howells 	ret = install_process_keyring_to_cred(new);
212d84f4f99SDavid Howells 	if (ret < 0) {
213d84f4f99SDavid Howells 		abort_creds(new);
21427d63798SAndi Kleen 		return ret != -EEXIST ? ret : 0;
2151da177e4SLinus Torvalds 	}
2161da177e4SLinus Torvalds 
217d84f4f99SDavid Howells 	return commit_creds(new);
2181da177e4SLinus Torvalds }
2191da177e4SLinus Torvalds 
2201da177e4SLinus Torvalds /*
221973c9f4fSDavid Howells  * Install a session keyring directly to a credentials struct.
2221da177e4SLinus Torvalds  */
223685bfd2cSOleg Nesterov int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
2241da177e4SLinus Torvalds {
2257e047ef5SDavid Howells 	unsigned long flags;
2261da177e4SLinus Torvalds 	struct key *old;
2271a26feb9SDavid Howells 
2281a26feb9SDavid Howells 	might_sleep();
2291da177e4SLinus Torvalds 
2301da177e4SLinus Torvalds 	/* create an empty session keyring */
2311da177e4SLinus Torvalds 	if (!keyring) {
2327e047ef5SDavid Howells 		flags = KEY_ALLOC_QUOTA_OVERRUN;
233d84f4f99SDavid Howells 		if (cred->tgcred->session_keyring)
2347e047ef5SDavid Howells 			flags = KEY_ALLOC_IN_QUOTA;
2357e047ef5SDavid Howells 
236d84f4f99SDavid Howells 		keyring = keyring_alloc("_ses", cred->uid, cred->gid,
237d84f4f99SDavid Howells 					cred, flags, NULL);
2381a26feb9SDavid Howells 		if (IS_ERR(keyring))
2391a26feb9SDavid Howells 			return PTR_ERR(keyring);
240d84f4f99SDavid Howells 	} else {
2411da177e4SLinus Torvalds 		atomic_inc(&keyring->usage);
2421da177e4SLinus Torvalds 	}
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	/* install the keyring */
245d84f4f99SDavid Howells 	spin_lock_irq(&cred->tgcred->lock);
246d84f4f99SDavid Howells 	old = cred->tgcred->session_keyring;
247d84f4f99SDavid Howells 	rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
248d84f4f99SDavid Howells 	spin_unlock_irq(&cred->tgcred->lock);
2491da177e4SLinus Torvalds 
2501a26feb9SDavid Howells 	/* we're using RCU on the pointer, but there's no point synchronising
2511a26feb9SDavid Howells 	 * on it if it didn't previously point to anything */
2521a26feb9SDavid Howells 	if (old) {
253b2b18660SPaul E. McKenney 		synchronize_rcu();
2541da177e4SLinus Torvalds 		key_put(old);
2551a26feb9SDavid Howells 	}
2561a26feb9SDavid Howells 
2571a26feb9SDavid Howells 	return 0;
258d84f4f99SDavid Howells }
2591da177e4SLinus Torvalds 
2601da177e4SLinus Torvalds /*
261973c9f4fSDavid Howells  * Install a session keyring, discarding the old one.  If a keyring is not
262973c9f4fSDavid Howells  * supplied, an empty one is invented.
2631da177e4SLinus Torvalds  */
264d84f4f99SDavid Howells static int install_session_keyring(struct key *keyring)
2651da177e4SLinus Torvalds {
266d84f4f99SDavid Howells 	struct cred *new;
267d84f4f99SDavid Howells 	int ret;
2681da177e4SLinus Torvalds 
269d84f4f99SDavid Howells 	new = prepare_creds();
270d84f4f99SDavid Howells 	if (!new)
271d84f4f99SDavid Howells 		return -ENOMEM;
272b5f545c8SDavid Howells 
27399599537SDavid Howells 	ret = install_session_keyring_to_cred(new, keyring);
274d84f4f99SDavid Howells 	if (ret < 0) {
275d84f4f99SDavid Howells 		abort_creds(new);
276d84f4f99SDavid Howells 		return ret;
277d84f4f99SDavid Howells 	}
278b5f545c8SDavid Howells 
279d84f4f99SDavid Howells 	return commit_creds(new);
280d84f4f99SDavid Howells }
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds /*
283973c9f4fSDavid Howells  * Handle the fsuid changing.
2841da177e4SLinus Torvalds  */
2851da177e4SLinus Torvalds void key_fsuid_changed(struct task_struct *tsk)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds 	/* update the ownership of the thread keyring */
288b6dff3ecSDavid Howells 	BUG_ON(!tsk->cred);
289b6dff3ecSDavid Howells 	if (tsk->cred->thread_keyring) {
290b6dff3ecSDavid Howells 		down_write(&tsk->cred->thread_keyring->sem);
291b6dff3ecSDavid Howells 		tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
292b6dff3ecSDavid Howells 		up_write(&tsk->cred->thread_keyring->sem);
2931da177e4SLinus Torvalds 	}
294a8b17ed0SDavid Howells }
2951da177e4SLinus Torvalds 
2961da177e4SLinus Torvalds /*
297973c9f4fSDavid Howells  * Handle the fsgid changing.
2981da177e4SLinus Torvalds  */
2991da177e4SLinus Torvalds void key_fsgid_changed(struct task_struct *tsk)
3001da177e4SLinus Torvalds {
3011da177e4SLinus Torvalds 	/* update the ownership of the thread keyring */
302b6dff3ecSDavid Howells 	BUG_ON(!tsk->cred);
303b6dff3ecSDavid Howells 	if (tsk->cred->thread_keyring) {
304b6dff3ecSDavid Howells 		down_write(&tsk->cred->thread_keyring->sem);
305b6dff3ecSDavid Howells 		tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
306b6dff3ecSDavid Howells 		up_write(&tsk->cred->thread_keyring->sem);
3071da177e4SLinus Torvalds 	}
308a8b17ed0SDavid Howells }
3091da177e4SLinus Torvalds 
3101da177e4SLinus Torvalds /*
311973c9f4fSDavid Howells  * Search the process keyrings attached to the supplied cred for the first
312973c9f4fSDavid Howells  * matching key.
313973c9f4fSDavid Howells  *
314973c9f4fSDavid Howells  * The search criteria are the type and the match function.  The description is
315973c9f4fSDavid Howells  * given to the match function as a parameter, but doesn't otherwise influence
316973c9f4fSDavid Howells  * the search.  Typically the match function will compare the description
317973c9f4fSDavid Howells  * parameter to the key's description.
318973c9f4fSDavid Howells  *
319973c9f4fSDavid Howells  * This can only search keyrings that grant Search permission to the supplied
320973c9f4fSDavid Howells  * credentials.  Keyrings linked to searched keyrings will also be searched if
321973c9f4fSDavid Howells  * they grant Search permission too.  Keys can only be found if they grant
322973c9f4fSDavid Howells  * Search permission to the credentials.
323973c9f4fSDavid Howells  *
324973c9f4fSDavid Howells  * Returns a pointer to the key with the key usage count incremented if
325973c9f4fSDavid Howells  * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only
326973c9f4fSDavid Howells  * matched negative keys.
327973c9f4fSDavid Howells  *
328973c9f4fSDavid Howells  * In the case of a successful return, the possession attribute is set on the
329973c9f4fSDavid Howells  * returned key reference.
3301da177e4SLinus Torvalds  */
331927942aaSDavid Howells key_ref_t search_my_process_keyrings(struct key_type *type,
3321da177e4SLinus Torvalds 				     const void *description,
3333e30148cSDavid Howells 				     key_match_func_t match,
33478b7280cSDavid Howells 				     bool no_state_check,
335d84f4f99SDavid Howells 				     const struct cred *cred)
3361da177e4SLinus Torvalds {
337b5f545c8SDavid Howells 	key_ref_t key_ref, ret, err;
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
3401da177e4SLinus Torvalds 	 * searchable, but we failed to find a key or we found a negative key;
3411da177e4SLinus Torvalds 	 * otherwise we want to return a sample error (probably -EACCES) if
3421da177e4SLinus Torvalds 	 * none of the keyrings were searchable
3431da177e4SLinus Torvalds 	 *
3441da177e4SLinus Torvalds 	 * in terms of priority: success > -ENOKEY > -EAGAIN > other error
3451da177e4SLinus Torvalds 	 */
346664cceb0SDavid Howells 	key_ref = NULL;
3471da177e4SLinus Torvalds 	ret = NULL;
3481da177e4SLinus Torvalds 	err = ERR_PTR(-EAGAIN);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	/* search the thread keyring first */
351c69e8d9cSDavid Howells 	if (cred->thread_keyring) {
352664cceb0SDavid Howells 		key_ref = keyring_search_aux(
353c69e8d9cSDavid Howells 			make_key_ref(cred->thread_keyring, 1),
35478b7280cSDavid Howells 			cred, type, description, match, no_state_check);
355664cceb0SDavid Howells 		if (!IS_ERR(key_ref))
3561da177e4SLinus Torvalds 			goto found;
3571da177e4SLinus Torvalds 
358664cceb0SDavid Howells 		switch (PTR_ERR(key_ref)) {
3591da177e4SLinus Torvalds 		case -EAGAIN: /* no key */
3601da177e4SLinus Torvalds 			if (ret)
3611da177e4SLinus Torvalds 				break;
3621da177e4SLinus Torvalds 		case -ENOKEY: /* negative key */
363664cceb0SDavid Howells 			ret = key_ref;
3641da177e4SLinus Torvalds 			break;
3651da177e4SLinus Torvalds 		default:
366664cceb0SDavid Howells 			err = key_ref;
3671da177e4SLinus Torvalds 			break;
3681da177e4SLinus Torvalds 		}
3691da177e4SLinus Torvalds 	}
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	/* search the process keyring second */
372bb952bb9SDavid Howells 	if (cred->tgcred->process_keyring) {
373664cceb0SDavid Howells 		key_ref = keyring_search_aux(
374bb952bb9SDavid Howells 			make_key_ref(cred->tgcred->process_keyring, 1),
37578b7280cSDavid Howells 			cred, type, description, match, no_state_check);
376664cceb0SDavid Howells 		if (!IS_ERR(key_ref))
3771da177e4SLinus Torvalds 			goto found;
3781da177e4SLinus Torvalds 
379664cceb0SDavid Howells 		switch (PTR_ERR(key_ref)) {
3801da177e4SLinus Torvalds 		case -EAGAIN: /* no key */
3811da177e4SLinus Torvalds 			if (ret)
3821da177e4SLinus Torvalds 				break;
3831da177e4SLinus Torvalds 		case -ENOKEY: /* negative key */
384664cceb0SDavid Howells 			ret = key_ref;
3851da177e4SLinus Torvalds 			break;
3861da177e4SLinus Torvalds 		default:
387664cceb0SDavid Howells 			err = key_ref;
3881da177e4SLinus Torvalds 			break;
3891da177e4SLinus Torvalds 		}
3901da177e4SLinus Torvalds 	}
3911da177e4SLinus Torvalds 
3923e30148cSDavid Howells 	/* search the session keyring */
393bb952bb9SDavid Howells 	if (cred->tgcred->session_keyring) {
3948589b4e0SDavid Howells 		rcu_read_lock();
395664cceb0SDavid Howells 		key_ref = keyring_search_aux(
396664cceb0SDavid Howells 			make_key_ref(rcu_dereference(
397bb952bb9SDavid Howells 					     cred->tgcred->session_keyring),
398664cceb0SDavid Howells 				     1),
39978b7280cSDavid Howells 			cred, type, description, match, no_state_check);
4008589b4e0SDavid Howells 		rcu_read_unlock();
4011da177e4SLinus Torvalds 
402664cceb0SDavid Howells 		if (!IS_ERR(key_ref))
4031da177e4SLinus Torvalds 			goto found;
4041da177e4SLinus Torvalds 
405664cceb0SDavid Howells 		switch (PTR_ERR(key_ref)) {
4061da177e4SLinus Torvalds 		case -EAGAIN: /* no key */
4071da177e4SLinus Torvalds 			if (ret)
4081da177e4SLinus Torvalds 				break;
4091da177e4SLinus Torvalds 		case -ENOKEY: /* negative key */
410664cceb0SDavid Howells 			ret = key_ref;
4111da177e4SLinus Torvalds 			break;
4121da177e4SLinus Torvalds 		default:
413664cceb0SDavid Howells 			err = key_ref;
4141da177e4SLinus Torvalds 			break;
4151da177e4SLinus Torvalds 		}
4163e30148cSDavid Howells 	}
4173e30148cSDavid Howells 	/* or search the user-session keyring */
418c69e8d9cSDavid Howells 	else if (cred->user->session_keyring) {
419664cceb0SDavid Howells 		key_ref = keyring_search_aux(
420c69e8d9cSDavid Howells 			make_key_ref(cred->user->session_keyring, 1),
42178b7280cSDavid Howells 			cred, type, description, match, no_state_check);
422664cceb0SDavid Howells 		if (!IS_ERR(key_ref))
4233e30148cSDavid Howells 			goto found;
4243e30148cSDavid Howells 
425664cceb0SDavid Howells 		switch (PTR_ERR(key_ref)) {
4263e30148cSDavid Howells 		case -EAGAIN: /* no key */
4273e30148cSDavid Howells 			if (ret)
4283e30148cSDavid Howells 				break;
4293e30148cSDavid Howells 		case -ENOKEY: /* negative key */
430664cceb0SDavid Howells 			ret = key_ref;
4313e30148cSDavid Howells 			break;
4323e30148cSDavid Howells 		default:
433664cceb0SDavid Howells 			err = key_ref;
4343e30148cSDavid Howells 			break;
4353e30148cSDavid Howells 		}
4363e30148cSDavid Howells 	}
4373e30148cSDavid Howells 
438927942aaSDavid Howells 	/* no key - decide on the error we're going to go for */
439927942aaSDavid Howells 	key_ref = ret ? ret : err;
440927942aaSDavid Howells 
441927942aaSDavid Howells found:
442927942aaSDavid Howells 	return key_ref;
443927942aaSDavid Howells }
444927942aaSDavid Howells 
445927942aaSDavid Howells /*
446973c9f4fSDavid Howells  * Search the process keyrings attached to the supplied cred for the first
447973c9f4fSDavid Howells  * matching key in the manner of search_my_process_keyrings(), but also search
448973c9f4fSDavid Howells  * the keys attached to the assumed authorisation key using its credentials if
449973c9f4fSDavid Howells  * one is available.
450973c9f4fSDavid Howells  *
451973c9f4fSDavid Howells  * Return same as search_my_process_keyrings().
452927942aaSDavid Howells  */
453927942aaSDavid Howells key_ref_t search_process_keyrings(struct key_type *type,
454927942aaSDavid Howells 				  const void *description,
455927942aaSDavid Howells 				  key_match_func_t match,
456927942aaSDavid Howells 				  const struct cred *cred)
457927942aaSDavid Howells {
458927942aaSDavid Howells 	struct request_key_auth *rka;
459927942aaSDavid Howells 	key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;
460927942aaSDavid Howells 
461927942aaSDavid Howells 	might_sleep();
462927942aaSDavid Howells 
46378b7280cSDavid Howells 	key_ref = search_my_process_keyrings(type, description, match,
46478b7280cSDavid Howells 					     false, cred);
465927942aaSDavid Howells 	if (!IS_ERR(key_ref))
466927942aaSDavid Howells 		goto found;
467927942aaSDavid Howells 	err = key_ref;
468927942aaSDavid Howells 
469b5f545c8SDavid Howells 	/* if this process has an instantiation authorisation key, then we also
470b5f545c8SDavid Howells 	 * search the keyrings of the process mentioned there
471b5f545c8SDavid Howells 	 * - we don't permit access to request_key auth keys via this method
472b5f545c8SDavid Howells 	 */
473c69e8d9cSDavid Howells 	if (cred->request_key_auth &&
474d84f4f99SDavid Howells 	    cred == current_cred() &&
47504c567d9SDavid Howells 	    type != &key_type_request_key_auth
476b5f545c8SDavid Howells 	    ) {
47704c567d9SDavid Howells 		/* defend against the auth key being revoked */
478c69e8d9cSDavid Howells 		down_read(&cred->request_key_auth->sem);
47904c567d9SDavid Howells 
480c69e8d9cSDavid Howells 		if (key_validate(cred->request_key_auth) == 0) {
481c69e8d9cSDavid Howells 			rka = cred->request_key_auth->payload.data;
4823e30148cSDavid Howells 
48304c567d9SDavid Howells 			key_ref = search_process_keyrings(type, description,
484d84f4f99SDavid Howells 							  match, rka->cred);
48504c567d9SDavid Howells 
486c69e8d9cSDavid Howells 			up_read(&cred->request_key_auth->sem);
487b5f545c8SDavid Howells 
488b5f545c8SDavid Howells 			if (!IS_ERR(key_ref))
489b5f545c8SDavid Howells 				goto found;
490b5f545c8SDavid Howells 
491b5f545c8SDavid Howells 			ret = key_ref;
49204c567d9SDavid Howells 		} else {
493c69e8d9cSDavid Howells 			up_read(&cred->request_key_auth->sem);
49404c567d9SDavid Howells 		}
495b5f545c8SDavid Howells 	}
496b5f545c8SDavid Howells 
4971da177e4SLinus Torvalds 	/* no key - decide on the error we're going to go for */
498927942aaSDavid Howells 	if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY))
499927942aaSDavid Howells 		key_ref = ERR_PTR(-ENOKEY);
500927942aaSDavid Howells 	else if (err == ERR_PTR(-EACCES))
501927942aaSDavid Howells 		key_ref = ret;
502927942aaSDavid Howells 	else
503927942aaSDavid Howells 		key_ref = err;
5041da177e4SLinus Torvalds 
5051da177e4SLinus Torvalds found:
506664cceb0SDavid Howells 	return key_ref;
507a8b17ed0SDavid Howells }
5081da177e4SLinus Torvalds 
5091da177e4SLinus Torvalds /*
510973c9f4fSDavid Howells  * See if the key we're looking at is the target key.
511664cceb0SDavid Howells  */
512927942aaSDavid Howells int lookup_user_key_possessed(const struct key *key, const void *target)
513664cceb0SDavid Howells {
514664cceb0SDavid Howells 	return key == target;
515a8b17ed0SDavid Howells }
516664cceb0SDavid Howells 
517664cceb0SDavid Howells /*
518973c9f4fSDavid Howells  * Look up a key ID given us by userspace with a given permissions mask to get
519973c9f4fSDavid Howells  * the key it refers to.
520973c9f4fSDavid Howells  *
521973c9f4fSDavid Howells  * Flags can be passed to request that special keyrings be created if referred
522973c9f4fSDavid Howells  * to directly, to permit partially constructed keys to be found and to skip
523973c9f4fSDavid Howells  * validity and permission checks on the found key.
524973c9f4fSDavid Howells  *
525973c9f4fSDavid Howells  * Returns a pointer to the key with an incremented usage count if successful;
526973c9f4fSDavid Howells  * -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond
527973c9f4fSDavid Howells  * to a key or the best found key was a negative key; -EKEYREVOKED or
528973c9f4fSDavid Howells  * -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the
529973c9f4fSDavid Howells  * found key doesn't grant the requested permit or the LSM denied access to it;
530973c9f4fSDavid Howells  * or -ENOMEM if a special keyring couldn't be created.
531973c9f4fSDavid Howells  *
532973c9f4fSDavid Howells  * In the case of a successful return, the possession attribute is set on the
533973c9f4fSDavid Howells  * returned key reference.
5341da177e4SLinus Torvalds  */
5355593122eSDavid Howells key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
5368bbf4976SDavid Howells 			  key_perm_t perm)
5371da177e4SLinus Torvalds {
5388bbf4976SDavid Howells 	struct request_key_auth *rka;
539d84f4f99SDavid Howells 	const struct cred *cred;
5401da177e4SLinus Torvalds 	struct key *key;
541b6dff3ecSDavid Howells 	key_ref_t key_ref, skey_ref;
5421da177e4SLinus Torvalds 	int ret;
5431da177e4SLinus Torvalds 
544bb952bb9SDavid Howells try_again:
545bb952bb9SDavid Howells 	cred = get_current_cred();
546664cceb0SDavid Howells 	key_ref = ERR_PTR(-ENOKEY);
5471da177e4SLinus Torvalds 
5481da177e4SLinus Torvalds 	switch (id) {
5491da177e4SLinus Torvalds 	case KEY_SPEC_THREAD_KEYRING:
550b6dff3ecSDavid Howells 		if (!cred->thread_keyring) {
5515593122eSDavid Howells 			if (!(lflags & KEY_LOOKUP_CREATE))
5521da177e4SLinus Torvalds 				goto error;
5531da177e4SLinus Torvalds 
5548bbf4976SDavid Howells 			ret = install_thread_keyring();
5551da177e4SLinus Torvalds 			if (ret < 0) {
5564d09ec0fSDan Carpenter 				key_ref = ERR_PTR(ret);
5571da177e4SLinus Torvalds 				goto error;
5581da177e4SLinus Torvalds 			}
559bb952bb9SDavid Howells 			goto reget_creds;
5601da177e4SLinus Torvalds 		}
5611da177e4SLinus Torvalds 
562b6dff3ecSDavid Howells 		key = cred->thread_keyring;
5631da177e4SLinus Torvalds 		atomic_inc(&key->usage);
564664cceb0SDavid Howells 		key_ref = make_key_ref(key, 1);
5651da177e4SLinus Torvalds 		break;
5661da177e4SLinus Torvalds 
5671da177e4SLinus Torvalds 	case KEY_SPEC_PROCESS_KEYRING:
568bb952bb9SDavid Howells 		if (!cred->tgcred->process_keyring) {
5695593122eSDavid Howells 			if (!(lflags & KEY_LOOKUP_CREATE))
5701da177e4SLinus Torvalds 				goto error;
5711da177e4SLinus Torvalds 
5728bbf4976SDavid Howells 			ret = install_process_keyring();
5731da177e4SLinus Torvalds 			if (ret < 0) {
5744d09ec0fSDan Carpenter 				key_ref = ERR_PTR(ret);
5751da177e4SLinus Torvalds 				goto error;
5761da177e4SLinus Torvalds 			}
577bb952bb9SDavid Howells 			goto reget_creds;
5781da177e4SLinus Torvalds 		}
5791da177e4SLinus Torvalds 
580bb952bb9SDavid Howells 		key = cred->tgcred->process_keyring;
5811da177e4SLinus Torvalds 		atomic_inc(&key->usage);
582664cceb0SDavid Howells 		key_ref = make_key_ref(key, 1);
5831da177e4SLinus Torvalds 		break;
5841da177e4SLinus Torvalds 
5851da177e4SLinus Torvalds 	case KEY_SPEC_SESSION_KEYRING:
586bb952bb9SDavid Howells 		if (!cred->tgcred->session_keyring) {
5871da177e4SLinus Torvalds 			/* always install a session keyring upon access if one
5881da177e4SLinus Torvalds 			 * doesn't exist yet */
5898bbf4976SDavid Howells 			ret = install_user_keyrings();
59069664cf1SDavid Howells 			if (ret < 0)
59169664cf1SDavid Howells 				goto error;
5923ecf1b4fSDavid Howells 			if (lflags & KEY_LOOKUP_CREATE)
5933ecf1b4fSDavid Howells 				ret = join_session_keyring(NULL);
5943ecf1b4fSDavid Howells 			else
595b6dff3ecSDavid Howells 				ret = install_session_keyring(
596b6dff3ecSDavid Howells 					cred->user->session_keyring);
597d84f4f99SDavid Howells 
5981da177e4SLinus Torvalds 			if (ret < 0)
5991da177e4SLinus Torvalds 				goto error;
600bb952bb9SDavid Howells 			goto reget_creds;
6013ecf1b4fSDavid Howells 		} else if (cred->tgcred->session_keyring ==
6023ecf1b4fSDavid Howells 			   cred->user->session_keyring &&
6033ecf1b4fSDavid Howells 			   lflags & KEY_LOOKUP_CREATE) {
6043ecf1b4fSDavid Howells 			ret = join_session_keyring(NULL);
6053ecf1b4fSDavid Howells 			if (ret < 0)
6063ecf1b4fSDavid Howells 				goto error;
6073ecf1b4fSDavid Howells 			goto reget_creds;
6081da177e4SLinus Torvalds 		}
6091da177e4SLinus Torvalds 
6103e30148cSDavid Howells 		rcu_read_lock();
611bb952bb9SDavid Howells 		key = rcu_dereference(cred->tgcred->session_keyring);
6121da177e4SLinus Torvalds 		atomic_inc(&key->usage);
6133e30148cSDavid Howells 		rcu_read_unlock();
614664cceb0SDavid Howells 		key_ref = make_key_ref(key, 1);
6151da177e4SLinus Torvalds 		break;
6161da177e4SLinus Torvalds 
6171da177e4SLinus Torvalds 	case KEY_SPEC_USER_KEYRING:
618b6dff3ecSDavid Howells 		if (!cred->user->uid_keyring) {
6198bbf4976SDavid Howells 			ret = install_user_keyrings();
62069664cf1SDavid Howells 			if (ret < 0)
62169664cf1SDavid Howells 				goto error;
62269664cf1SDavid Howells 		}
62369664cf1SDavid Howells 
624b6dff3ecSDavid Howells 		key = cred->user->uid_keyring;
6251da177e4SLinus Torvalds 		atomic_inc(&key->usage);
626664cceb0SDavid Howells 		key_ref = make_key_ref(key, 1);
6271da177e4SLinus Torvalds 		break;
6281da177e4SLinus Torvalds 
6291da177e4SLinus Torvalds 	case KEY_SPEC_USER_SESSION_KEYRING:
630b6dff3ecSDavid Howells 		if (!cred->user->session_keyring) {
6318bbf4976SDavid Howells 			ret = install_user_keyrings();
63269664cf1SDavid Howells 			if (ret < 0)
63369664cf1SDavid Howells 				goto error;
63469664cf1SDavid Howells 		}
63569664cf1SDavid Howells 
636b6dff3ecSDavid Howells 		key = cred->user->session_keyring;
6371da177e4SLinus Torvalds 		atomic_inc(&key->usage);
638664cceb0SDavid Howells 		key_ref = make_key_ref(key, 1);
6391da177e4SLinus Torvalds 		break;
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	case KEY_SPEC_GROUP_KEYRING:
6421da177e4SLinus Torvalds 		/* group keyrings are not yet supported */
6434d09ec0fSDan Carpenter 		key_ref = ERR_PTR(-EINVAL);
6441da177e4SLinus Torvalds 		goto error;
6451da177e4SLinus Torvalds 
646b5f545c8SDavid Howells 	case KEY_SPEC_REQKEY_AUTH_KEY:
647b6dff3ecSDavid Howells 		key = cred->request_key_auth;
648b5f545c8SDavid Howells 		if (!key)
649b5f545c8SDavid Howells 			goto error;
650b5f545c8SDavid Howells 
651b5f545c8SDavid Howells 		atomic_inc(&key->usage);
652b5f545c8SDavid Howells 		key_ref = make_key_ref(key, 1);
653b5f545c8SDavid Howells 		break;
654b5f545c8SDavid Howells 
6558bbf4976SDavid Howells 	case KEY_SPEC_REQUESTOR_KEYRING:
656b6dff3ecSDavid Howells 		if (!cred->request_key_auth)
6578bbf4976SDavid Howells 			goto error;
6588bbf4976SDavid Howells 
659b6dff3ecSDavid Howells 		down_read(&cred->request_key_auth->sem);
660f67dabbdSDan Carpenter 		if (test_bit(KEY_FLAG_REVOKED,
661f67dabbdSDan Carpenter 			     &cred->request_key_auth->flags)) {
6628bbf4976SDavid Howells 			key_ref = ERR_PTR(-EKEYREVOKED);
6638bbf4976SDavid Howells 			key = NULL;
6648bbf4976SDavid Howells 		} else {
665b6dff3ecSDavid Howells 			rka = cred->request_key_auth->payload.data;
6668bbf4976SDavid Howells 			key = rka->dest_keyring;
6678bbf4976SDavid Howells 			atomic_inc(&key->usage);
6688bbf4976SDavid Howells 		}
669b6dff3ecSDavid Howells 		up_read(&cred->request_key_auth->sem);
6708bbf4976SDavid Howells 		if (!key)
6718bbf4976SDavid Howells 			goto error;
6728bbf4976SDavid Howells 		key_ref = make_key_ref(key, 1);
6738bbf4976SDavid Howells 		break;
6748bbf4976SDavid Howells 
6751da177e4SLinus Torvalds 	default:
676664cceb0SDavid Howells 		key_ref = ERR_PTR(-EINVAL);
6771da177e4SLinus Torvalds 		if (id < 1)
6781da177e4SLinus Torvalds 			goto error;
6791da177e4SLinus Torvalds 
6801da177e4SLinus Torvalds 		key = key_lookup(id);
681664cceb0SDavid Howells 		if (IS_ERR(key)) {
682e231c2eeSDavid Howells 			key_ref = ERR_CAST(key);
6831da177e4SLinus Torvalds 			goto error;
684664cceb0SDavid Howells 		}
685664cceb0SDavid Howells 
686664cceb0SDavid Howells 		key_ref = make_key_ref(key, 0);
687664cceb0SDavid Howells 
688664cceb0SDavid Howells 		/* check to see if we possess the key */
689664cceb0SDavid Howells 		skey_ref = search_process_keyrings(key->type, key,
690664cceb0SDavid Howells 						   lookup_user_key_possessed,
691d84f4f99SDavid Howells 						   cred);
692664cceb0SDavid Howells 
693664cceb0SDavid Howells 		if (!IS_ERR(skey_ref)) {
694664cceb0SDavid Howells 			key_put(key);
695664cceb0SDavid Howells 			key_ref = skey_ref;
696664cceb0SDavid Howells 		}
697664cceb0SDavid Howells 
6981da177e4SLinus Torvalds 		break;
6991da177e4SLinus Torvalds 	}
7001da177e4SLinus Torvalds 
7015593122eSDavid Howells 	/* unlink does not use the nominated key in any way, so can skip all
7025593122eSDavid Howells 	 * the permission checks as it is only concerned with the keyring */
7035593122eSDavid Howells 	if (lflags & KEY_LOOKUP_FOR_UNLINK) {
7045593122eSDavid Howells 		ret = 0;
7055593122eSDavid Howells 		goto error;
7065593122eSDavid Howells 	}
7075593122eSDavid Howells 
7085593122eSDavid Howells 	if (!(lflags & KEY_LOOKUP_PARTIAL)) {
70976181c13SDavid Howells 		ret = wait_for_key_construction(key, true);
71076181c13SDavid Howells 		switch (ret) {
71176181c13SDavid Howells 		case -ERESTARTSYS:
71276181c13SDavid Howells 			goto invalid_key;
71376181c13SDavid Howells 		default:
71476181c13SDavid Howells 			if (perm)
71576181c13SDavid Howells 				goto invalid_key;
71676181c13SDavid Howells 		case 0:
71776181c13SDavid Howells 			break;
71876181c13SDavid Howells 		}
71976181c13SDavid Howells 	} else if (perm) {
7201da177e4SLinus Torvalds 		ret = key_validate(key);
7211da177e4SLinus Torvalds 		if (ret < 0)
7221da177e4SLinus Torvalds 			goto invalid_key;
7231da177e4SLinus Torvalds 	}
7241da177e4SLinus Torvalds 
7251da177e4SLinus Torvalds 	ret = -EIO;
7265593122eSDavid Howells 	if (!(lflags & KEY_LOOKUP_PARTIAL) &&
7275593122eSDavid Howells 	    !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
7281da177e4SLinus Torvalds 		goto invalid_key;
7291da177e4SLinus Torvalds 
7303e30148cSDavid Howells 	/* check the permissions */
731d84f4f99SDavid Howells 	ret = key_task_permission(key_ref, cred, perm);
73229db9190SDavid Howells 	if (ret < 0)
7331da177e4SLinus Torvalds 		goto invalid_key;
7341da177e4SLinus Torvalds 
73531d5a79dSDavid Howells 	key->last_used_at = current_kernel_time().tv_sec;
73631d5a79dSDavid Howells 
7371da177e4SLinus Torvalds error:
738bb952bb9SDavid Howells 	put_cred(cred);
739664cceb0SDavid Howells 	return key_ref;
7401da177e4SLinus Torvalds 
7411da177e4SLinus Torvalds invalid_key:
742664cceb0SDavid Howells 	key_ref_put(key_ref);
743664cceb0SDavid Howells 	key_ref = ERR_PTR(ret);
7441da177e4SLinus Torvalds 	goto error;
7451da177e4SLinus Torvalds 
746bb952bb9SDavid Howells 	/* if we attempted to install a keyring, then it may have caused new
747bb952bb9SDavid Howells 	 * creds to be installed */
748bb952bb9SDavid Howells reget_creds:
749bb952bb9SDavid Howells 	put_cred(cred);
750bb952bb9SDavid Howells 	goto try_again;
751a8b17ed0SDavid Howells }
752bb952bb9SDavid Howells 
7531da177e4SLinus Torvalds /*
754973c9f4fSDavid Howells  * Join the named keyring as the session keyring if possible else attempt to
755973c9f4fSDavid Howells  * create a new one of that name and join that.
756973c9f4fSDavid Howells  *
757973c9f4fSDavid Howells  * If the name is NULL, an empty anonymous keyring will be installed as the
758973c9f4fSDavid Howells  * session keyring.
759973c9f4fSDavid Howells  *
760973c9f4fSDavid Howells  * Named session keyrings are joined with a semaphore held to prevent the
761973c9f4fSDavid Howells  * keyrings from going away whilst the attempt is made to going them and also
762973c9f4fSDavid Howells  * to prevent a race in creating compatible session keyrings.
7631da177e4SLinus Torvalds  */
7641da177e4SLinus Torvalds long join_session_keyring(const char *name)
7651da177e4SLinus Torvalds {
766d84f4f99SDavid Howells 	const struct cred *old;
767d84f4f99SDavid Howells 	struct cred *new;
7681da177e4SLinus Torvalds 	struct key *keyring;
769d84f4f99SDavid Howells 	long ret, serial;
770d84f4f99SDavid Howells 
771d84f4f99SDavid Howells 	/* only permit this if there's a single thread in the thread group -
772d84f4f99SDavid Howells 	 * this avoids us having to adjust the creds on all threads and risking
773d84f4f99SDavid Howells 	 * ENOMEM */
7745bb459bbSOleg Nesterov 	if (!current_is_single_threaded())
775d84f4f99SDavid Howells 		return -EMLINK;
776d84f4f99SDavid Howells 
777d84f4f99SDavid Howells 	new = prepare_creds();
778d84f4f99SDavid Howells 	if (!new)
779d84f4f99SDavid Howells 		return -ENOMEM;
780d84f4f99SDavid Howells 	old = current_cred();
7811da177e4SLinus Torvalds 
7821da177e4SLinus Torvalds 	/* if no name is provided, install an anonymous keyring */
7831da177e4SLinus Torvalds 	if (!name) {
784d84f4f99SDavid Howells 		ret = install_session_keyring_to_cred(new, NULL);
7851da177e4SLinus Torvalds 		if (ret < 0)
7861da177e4SLinus Torvalds 			goto error;
7871da177e4SLinus Torvalds 
788d84f4f99SDavid Howells 		serial = new->tgcred->session_keyring->serial;
789d84f4f99SDavid Howells 		ret = commit_creds(new);
790d84f4f99SDavid Howells 		if (ret == 0)
791d84f4f99SDavid Howells 			ret = serial;
792d84f4f99SDavid Howells 		goto okay;
7931da177e4SLinus Torvalds 	}
7941da177e4SLinus Torvalds 
7951da177e4SLinus Torvalds 	/* allow the user to join or create a named keyring */
796bb003079SIngo Molnar 	mutex_lock(&key_session_mutex);
7971da177e4SLinus Torvalds 
7981da177e4SLinus Torvalds 	/* look for an existing keyring of this name */
79969664cf1SDavid Howells 	keyring = find_keyring_by_name(name, false);
8001da177e4SLinus Torvalds 	if (PTR_ERR(keyring) == -ENOKEY) {
8011da177e4SLinus Torvalds 		/* not found - try and create a new one */
802d84f4f99SDavid Howells 		keyring = keyring_alloc(name, old->uid, old->gid, old,
8037e047ef5SDavid Howells 					KEY_ALLOC_IN_QUOTA, NULL);
8041da177e4SLinus Torvalds 		if (IS_ERR(keyring)) {
8051da177e4SLinus Torvalds 			ret = PTR_ERR(keyring);
806bcf945d3SDavid Howells 			goto error2;
8071da177e4SLinus Torvalds 		}
808d84f4f99SDavid Howells 	} else if (IS_ERR(keyring)) {
8091da177e4SLinus Torvalds 		ret = PTR_ERR(keyring);
8101da177e4SLinus Torvalds 		goto error2;
8111da177e4SLinus Torvalds 	}
8121da177e4SLinus Torvalds 
8131da177e4SLinus Torvalds 	/* we've got a keyring - now to install it */
814d84f4f99SDavid Howells 	ret = install_session_keyring_to_cred(new, keyring);
8151da177e4SLinus Torvalds 	if (ret < 0)
8161da177e4SLinus Torvalds 		goto error2;
8171da177e4SLinus Torvalds 
818d84f4f99SDavid Howells 	commit_creds(new);
819d84f4f99SDavid Howells 	mutex_unlock(&key_session_mutex);
820d84f4f99SDavid Howells 
8211da177e4SLinus Torvalds 	ret = keyring->serial;
8221da177e4SLinus Torvalds 	key_put(keyring);
823d84f4f99SDavid Howells okay:
824d84f4f99SDavid Howells 	return ret;
8251da177e4SLinus Torvalds 
8261da177e4SLinus Torvalds error2:
827bb003079SIngo Molnar 	mutex_unlock(&key_session_mutex);
8281da177e4SLinus Torvalds error:
829d84f4f99SDavid Howells 	abort_creds(new);
8301da177e4SLinus Torvalds 	return ret;
831d84f4f99SDavid Howells }
832ee18d64cSDavid Howells 
833ee18d64cSDavid Howells /*
834973c9f4fSDavid Howells  * Replace a process's session keyring on behalf of one of its children when
835973c9f4fSDavid Howells  * the target  process is about to resume userspace execution.
836ee18d64cSDavid Howells  */
837*67d12145SAl Viro void key_change_session_keyring(struct callback_head *twork)
838ee18d64cSDavid Howells {
839413cd3d9SOleg Nesterov 	const struct cred *old = current_cred();
840*67d12145SAl Viro 	struct cred *new = container_of(twork, struct cred, rcu);
841ee18d64cSDavid Howells 
842413cd3d9SOleg Nesterov 	if (unlikely(current->flags & PF_EXITING)) {
843413cd3d9SOleg Nesterov 		put_cred(new);
844ee18d64cSDavid Howells 		return;
845413cd3d9SOleg Nesterov 	}
846ee18d64cSDavid Howells 
847ee18d64cSDavid Howells 	new->  uid	= old->  uid;
848ee18d64cSDavid Howells 	new-> euid	= old-> euid;
849ee18d64cSDavid Howells 	new-> suid	= old-> suid;
850ee18d64cSDavid Howells 	new->fsuid	= old->fsuid;
851ee18d64cSDavid Howells 	new->  gid	= old->  gid;
852ee18d64cSDavid Howells 	new-> egid	= old-> egid;
853ee18d64cSDavid Howells 	new-> sgid	= old-> sgid;
854ee18d64cSDavid Howells 	new->fsgid	= old->fsgid;
855ee18d64cSDavid Howells 	new->user	= get_uid(old->user);
8560093ccb6SEric W. Biederman 	new->user_ns	= get_user_ns(new->user_ns);
857ee18d64cSDavid Howells 	new->group_info	= get_group_info(old->group_info);
858ee18d64cSDavid Howells 
859ee18d64cSDavid Howells 	new->securebits	= old->securebits;
860ee18d64cSDavid Howells 	new->cap_inheritable	= old->cap_inheritable;
861ee18d64cSDavid Howells 	new->cap_permitted	= old->cap_permitted;
862ee18d64cSDavid Howells 	new->cap_effective	= old->cap_effective;
863ee18d64cSDavid Howells 	new->cap_bset		= old->cap_bset;
864ee18d64cSDavid Howells 
865ee18d64cSDavid Howells 	new->jit_keyring	= old->jit_keyring;
866ee18d64cSDavid Howells 	new->thread_keyring	= key_get(old->thread_keyring);
867ee18d64cSDavid Howells 	new->tgcred->tgid	= old->tgcred->tgid;
868ee18d64cSDavid Howells 	new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);
869ee18d64cSDavid Howells 
870ee18d64cSDavid Howells 	security_transfer_creds(new, old);
871ee18d64cSDavid Howells 
872ee18d64cSDavid Howells 	commit_creds(new);
873ee18d64cSDavid Howells }
874