xref: /openbmc/linux/kernel/cred.c (revision 207f135d)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2af777cd1SKees Cook /* Task credentials management - see Documentation/security/credentials.rst
3f1752eecSDavid Howells  *
4f1752eecSDavid Howells  * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved.
5f1752eecSDavid Howells  * Written by David Howells (dhowells@redhat.com)
6f1752eecSDavid Howells  */
74099451aStiozhang 
84099451aStiozhang #define pr_fmt(fmt) "CRED: " fmt
94099451aStiozhang 
109984de1aSPaul Gortmaker #include <linux/export.h>
11f1752eecSDavid Howells #include <linux/cred.h>
125a0e3ad6STejun Heo #include <linux/slab.h>
13f1752eecSDavid Howells #include <linux/sched.h>
14f7ccbae4SIngo Molnar #include <linux/sched/coredump.h>
15f1752eecSDavid Howells #include <linux/key.h>
16f1752eecSDavid Howells #include <linux/keyctl.h>
17f1752eecSDavid Howells #include <linux/init_task.h>
18f1752eecSDavid Howells #include <linux/security.h>
1940401530SAl Viro #include <linux/binfmts.h>
20d84f4f99SDavid Howells #include <linux/cn_proc.h>
21d89b22d4SNeilBrown #include <linux/uidgid.h>
22d84f4f99SDavid Howells 
23e0e81739SDavid Howells #if 0
24e0e81739SDavid Howells #define kdebug(FMT, ...)						\
2552aa8536SJoe Perches 	printk("[%-5.5s%5u] " FMT "\n",					\
2652aa8536SJoe Perches 	       current->comm, current->pid, ##__VA_ARGS__)
27e0e81739SDavid Howells #else
28e0e81739SDavid Howells #define kdebug(FMT, ...)						\
2952aa8536SJoe Perches do {									\
3052aa8536SJoe Perches 	if (0)								\
3152aa8536SJoe Perches 		no_printk("[%-5.5s%5u] " FMT "\n",			\
3252aa8536SJoe Perches 			  current->comm, current->pid, ##__VA_ARGS__);	\
3352aa8536SJoe Perches } while (0)
34e0e81739SDavid Howells #endif
35e0e81739SDavid Howells 
36d84f4f99SDavid Howells static struct kmem_cache *cred_jar;
37f1752eecSDavid Howells 
382813893fSIulia Manda /* init to 2 - one for init_task, one to ensure it is never freed */
3932c93976SRasmus Villemoes static struct group_info init_groups = { .usage = ATOMIC_INIT(2) };
402813893fSIulia Manda 
41f1752eecSDavid Howells /*
42f1752eecSDavid Howells  * The initial credentials for the initial task
43f1752eecSDavid Howells  */
44f1752eecSDavid Howells struct cred init_cred = {
453b11a1deSDavid Howells 	.usage			= ATOMIC_INIT(4),
46078de5f7SEric W. Biederman 	.uid			= GLOBAL_ROOT_UID,
47078de5f7SEric W. Biederman 	.gid			= GLOBAL_ROOT_GID,
48078de5f7SEric W. Biederman 	.suid			= GLOBAL_ROOT_UID,
49078de5f7SEric W. Biederman 	.sgid			= GLOBAL_ROOT_GID,
50078de5f7SEric W. Biederman 	.euid			= GLOBAL_ROOT_UID,
51078de5f7SEric W. Biederman 	.egid			= GLOBAL_ROOT_GID,
52078de5f7SEric W. Biederman 	.fsuid			= GLOBAL_ROOT_UID,
53078de5f7SEric W. Biederman 	.fsgid			= GLOBAL_ROOT_GID,
54f1752eecSDavid Howells 	.securebits		= SECUREBITS_DEFAULT,
55a3232d2fSEric Paris 	.cap_inheritable	= CAP_EMPTY_SET,
56f1752eecSDavid Howells 	.cap_permitted		= CAP_FULL_SET,
57a3232d2fSEric Paris 	.cap_effective		= CAP_FULL_SET,
58a3232d2fSEric Paris 	.cap_bset		= CAP_FULL_SET,
59f1752eecSDavid Howells 	.user			= INIT_USER,
6047a150edSSerge E. Hallyn 	.user_ns		= &init_user_ns,
61f1752eecSDavid Howells 	.group_info		= &init_groups,
62905ae01cSAlexey Gladkov 	.ucounts		= &init_ucounts,
63f1752eecSDavid Howells };
64f1752eecSDavid Howells 
65f1752eecSDavid Howells /*
66f1752eecSDavid Howells  * The RCU callback to actually dispose of a set of credentials
67f1752eecSDavid Howells  */
put_cred_rcu(struct rcu_head * rcu)68f1752eecSDavid Howells static void put_cred_rcu(struct rcu_head *rcu)
69f1752eecSDavid Howells {
70f1752eecSDavid Howells 	struct cred *cred = container_of(rcu, struct cred, rcu);
71f1752eecSDavid Howells 
72e0e81739SDavid Howells 	kdebug("put_cred_rcu(%p)", cred);
73e0e81739SDavid Howells 
74f6a7ce5aSJens Axboe 	if (atomic_long_read(&cred->usage) != 0)
75f6a7ce5aSJens Axboe 		panic("CRED: put_cred_rcu() sees %p with usage %ld\n",
76f6a7ce5aSJens Axboe 		      cred, atomic_long_read(&cred->usage));
77f1752eecSDavid Howells 
78d84f4f99SDavid Howells 	security_cred_free(cred);
793a50597dSDavid Howells 	key_put(cred->session_keyring);
803a50597dSDavid Howells 	key_put(cred->process_keyring);
81f1752eecSDavid Howells 	key_put(cred->thread_keyring);
82f1752eecSDavid Howells 	key_put(cred->request_key_auth);
834a5d6ba1SDavid Howells 	if (cred->group_info)
84f1752eecSDavid Howells 		put_group_info(cred->group_info);
85f1752eecSDavid Howells 	free_uid(cred->user);
86905ae01cSAlexey Gladkov 	if (cred->ucounts)
87905ae01cSAlexey Gladkov 		put_ucounts(cred->ucounts);
880093ccb6SEric W. Biederman 	put_user_ns(cred->user_ns);
89d84f4f99SDavid Howells 	kmem_cache_free(cred_jar, cred);
90f1752eecSDavid Howells }
91f1752eecSDavid Howells 
92f1752eecSDavid Howells /**
93f1752eecSDavid Howells  * __put_cred - Destroy a set of credentials
94d84f4f99SDavid Howells  * @cred: The record to release
95f1752eecSDavid Howells  *
96f1752eecSDavid Howells  * Destroy a set of credentials on which no references remain.
97f1752eecSDavid Howells  */
__put_cred(struct cred * cred)98f1752eecSDavid Howells void __put_cred(struct cred *cred)
99f1752eecSDavid Howells {
100*207f135dSJens Axboe 	kdebug("__put_cred(%p{%ld})", cred,
101*207f135dSJens Axboe 	       atomic_long_read(&cred->usage));
102e0e81739SDavid Howells 
103f6a7ce5aSJens Axboe 	BUG_ON(atomic_long_read(&cred->usage) != 0);
104e0e81739SDavid Howells 	BUG_ON(cred == current->cred);
105e0e81739SDavid Howells 	BUG_ON(cred == current->real_cred);
106d84f4f99SDavid Howells 
107d7852fbdSLinus Torvalds 	if (cred->non_rcu)
108d7852fbdSLinus Torvalds 		put_cred_rcu(&cred->rcu);
109d7852fbdSLinus Torvalds 	else
110f1752eecSDavid Howells 		call_rcu(&cred->rcu, put_cred_rcu);
111f1752eecSDavid Howells }
112f1752eecSDavid Howells EXPORT_SYMBOL(__put_cred);
113f1752eecSDavid Howells 
114e0e81739SDavid Howells /*
115e0e81739SDavid Howells  * Clean up a task's credentials when it exits
116e0e81739SDavid Howells  */
exit_creds(struct task_struct * tsk)117e0e81739SDavid Howells void exit_creds(struct task_struct *tsk)
118e0e81739SDavid Howells {
119e0e81739SDavid Howells 	struct cred *cred;
120e0e81739SDavid Howells 
121*207f135dSJens Axboe 	kdebug("exit_creds(%u,%p,%p,{%ld})", tsk->pid, tsk->real_cred, tsk->cred,
122*207f135dSJens Axboe 	       atomic_long_read(&tsk->cred->usage));
123e0e81739SDavid Howells 
124e0e81739SDavid Howells 	cred = (struct cred *) tsk->real_cred;
125e0e81739SDavid Howells 	tsk->real_cred = NULL;
126e0e81739SDavid Howells 	put_cred(cred);
127e0e81739SDavid Howells 
128e0e81739SDavid Howells 	cred = (struct cred *) tsk->cred;
129e0e81739SDavid Howells 	tsk->cred = NULL;
130e0e81739SDavid Howells 	put_cred(cred);
1317743c48eSDavid Howells 
1327743c48eSDavid Howells #ifdef CONFIG_KEYS_REQUEST_CACHE
1338379bb84SDavid Howells 	key_put(tsk->cached_requested_key);
1348379bb84SDavid Howells 	tsk->cached_requested_key = NULL;
1357743c48eSDavid Howells #endif
136ee18d64cSDavid Howells }
137ee18d64cSDavid Howells 
138de09a977SDavid Howells /**
139de09a977SDavid Howells  * get_task_cred - Get another task's objective credentials
140de09a977SDavid Howells  * @task: The task to query
141de09a977SDavid Howells  *
142de09a977SDavid Howells  * Get the objective credentials of a task, pinning them so that they can't go
143de09a977SDavid Howells  * away.  Accessing a task's credentials directly is not permitted.
144de09a977SDavid Howells  *
145de09a977SDavid Howells  * The caller must also make sure task doesn't get deleted, either by holding a
146de09a977SDavid Howells  * ref on task or by holding tasklist_lock to prevent it from being unlinked.
147de09a977SDavid Howells  */
get_task_cred(struct task_struct * task)148de09a977SDavid Howells const struct cred *get_task_cred(struct task_struct *task)
149de09a977SDavid Howells {
150de09a977SDavid Howells 	const struct cred *cred;
151de09a977SDavid Howells 
152de09a977SDavid Howells 	rcu_read_lock();
153de09a977SDavid Howells 
154de09a977SDavid Howells 	do {
155de09a977SDavid Howells 		cred = __task_cred((task));
156de09a977SDavid Howells 		BUG_ON(!cred);
15797d0fb23SNeilBrown 	} while (!get_cred_rcu(cred));
158de09a977SDavid Howells 
159de09a977SDavid Howells 	rcu_read_unlock();
160de09a977SDavid Howells 	return cred;
161de09a977SDavid Howells }
162a6d8e763SNeilBrown EXPORT_SYMBOL(get_task_cred);
163de09a977SDavid Howells 
164ee18d64cSDavid Howells /*
165ee18d64cSDavid Howells  * Allocate blank credentials, such that the credentials can be filled in at a
166ee18d64cSDavid Howells  * later date without risk of ENOMEM.
167ee18d64cSDavid Howells  */
cred_alloc_blank(void)168ee18d64cSDavid Howells struct cred *cred_alloc_blank(void)
169ee18d64cSDavid Howells {
170ee18d64cSDavid Howells 	struct cred *new;
171ee18d64cSDavid Howells 
172ee18d64cSDavid Howells 	new = kmem_cache_zalloc(cred_jar, GFP_KERNEL);
173ee18d64cSDavid Howells 	if (!new)
174ee18d64cSDavid Howells 		return NULL;
175ee18d64cSDavid Howells 
176f6a7ce5aSJens Axboe 	atomic_long_set(&new->usage, 1);
17784029fd0SShakeel Butt 	if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0)
178ee18d64cSDavid Howells 		goto error;
179ee18d64cSDavid Howells 
180ee18d64cSDavid Howells 	return new;
181ee18d64cSDavid Howells 
182ee18d64cSDavid Howells error:
183ee18d64cSDavid Howells 	abort_creds(new);
184ee18d64cSDavid Howells 	return NULL;
185e0e81739SDavid Howells }
186e0e81739SDavid Howells 
187d84f4f99SDavid Howells /**
188d84f4f99SDavid Howells  * prepare_creds - Prepare a new set of credentials for modification
189d84f4f99SDavid Howells  *
190d84f4f99SDavid Howells  * Prepare a new set of task credentials for modification.  A task's creds
191d84f4f99SDavid Howells  * shouldn't generally be modified directly, therefore this function is used to
192d84f4f99SDavid Howells  * prepare a new copy, which the caller then modifies and then commits by
193d84f4f99SDavid Howells  * calling commit_creds().
194d84f4f99SDavid Howells  *
1953b11a1deSDavid Howells  * Preparation involves making a copy of the objective creds for modification.
1963b11a1deSDavid Howells  *
197d84f4f99SDavid Howells  * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
198d84f4f99SDavid Howells  *
199d84f4f99SDavid Howells  * Call commit_creds() or abort_creds() to clean up.
200f1752eecSDavid Howells  */
prepare_creds(void)201d84f4f99SDavid Howells struct cred *prepare_creds(void)
202f1752eecSDavid Howells {
203d84f4f99SDavid Howells 	struct task_struct *task = current;
204d84f4f99SDavid Howells 	const struct cred *old;
205d84f4f99SDavid Howells 	struct cred *new;
206f1752eecSDavid Howells 
207d84f4f99SDavid Howells 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
208d84f4f99SDavid Howells 	if (!new)
209d84f4f99SDavid Howells 		return NULL;
210d84f4f99SDavid Howells 
211e0e81739SDavid Howells 	kdebug("prepare_creds() alloc %p", new);
212e0e81739SDavid Howells 
213d84f4f99SDavid Howells 	old = task->cred;
214d84f4f99SDavid Howells 	memcpy(new, old, sizeof(struct cred));
215d84f4f99SDavid Howells 
216d7852fbdSLinus Torvalds 	new->non_rcu = 0;
217f6a7ce5aSJens Axboe 	atomic_long_set(&new->usage, 1);
218d84f4f99SDavid Howells 	get_group_info(new->group_info);
219d84f4f99SDavid Howells 	get_uid(new->user);
2200093ccb6SEric W. Biederman 	get_user_ns(new->user_ns);
221f1752eecSDavid Howells 
222bb952bb9SDavid Howells #ifdef CONFIG_KEYS
2233a50597dSDavid Howells 	key_get(new->session_keyring);
2243a50597dSDavid Howells 	key_get(new->process_keyring);
225d84f4f99SDavid Howells 	key_get(new->thread_keyring);
226d84f4f99SDavid Howells 	key_get(new->request_key_auth);
227bb952bb9SDavid Howells #endif
228bb952bb9SDavid Howells 
229f1752eecSDavid Howells #ifdef CONFIG_SECURITY
230d84f4f99SDavid Howells 	new->security = NULL;
231f1752eecSDavid Howells #endif
232f1752eecSDavid Howells 
233905ae01cSAlexey Gladkov 	new->ucounts = get_ucounts(new->ucounts);
234905ae01cSAlexey Gladkov 	if (!new->ucounts)
235905ae01cSAlexey Gladkov 		goto error;
236905ae01cSAlexey Gladkov 
237bbb6d0f3SAlexey Gladkov 	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
238bbb6d0f3SAlexey Gladkov 		goto error;
239bbb6d0f3SAlexey Gladkov 
240d84f4f99SDavid Howells 	return new;
241d84f4f99SDavid Howells 
242d84f4f99SDavid Howells error:
243d84f4f99SDavid Howells 	abort_creds(new);
244d84f4f99SDavid Howells 	return NULL;
245d84f4f99SDavid Howells }
246d84f4f99SDavid Howells EXPORT_SYMBOL(prepare_creds);
247d84f4f99SDavid Howells 
248d84f4f99SDavid Howells /*
249a6f76f23SDavid Howells  * Prepare credentials for current to perform an execve()
2509b1bf12dSKOSAKI Motohiro  * - The caller must hold ->cred_guard_mutex
251a6f76f23SDavid Howells  */
prepare_exec_creds(void)252a6f76f23SDavid Howells struct cred *prepare_exec_creds(void)
253a6f76f23SDavid Howells {
254a6f76f23SDavid Howells 	struct cred *new;
255a6f76f23SDavid Howells 
256a6f76f23SDavid Howells 	new = prepare_creds();
2573a50597dSDavid Howells 	if (!new)
258a6f76f23SDavid Howells 		return new;
259a6f76f23SDavid Howells 
260a6f76f23SDavid Howells #ifdef CONFIG_KEYS
261a6f76f23SDavid Howells 	/* newly exec'd tasks don't get a thread keyring */
262a6f76f23SDavid Howells 	key_put(new->thread_keyring);
263a6f76f23SDavid Howells 	new->thread_keyring = NULL;
264a6f76f23SDavid Howells 
265a6f76f23SDavid Howells 	/* inherit the session keyring; new process keyring */
2663a50597dSDavid Howells 	key_put(new->process_keyring);
2673a50597dSDavid Howells 	new->process_keyring = NULL;
268a6f76f23SDavid Howells #endif
269a6f76f23SDavid Howells 
27087b047d2SEric W. Biederman 	new->suid = new->fsuid = new->euid;
27187b047d2SEric W. Biederman 	new->sgid = new->fsgid = new->egid;
27287b047d2SEric W. Biederman 
273a6f76f23SDavid Howells 	return new;
274a6f76f23SDavid Howells }
275a6f76f23SDavid Howells 
276a6f76f23SDavid Howells /*
277d84f4f99SDavid Howells  * Copy credentials for the new process created by fork()
278d84f4f99SDavid Howells  *
279d84f4f99SDavid Howells  * We share if we can, but under some circumstances we have to generate a new
280d84f4f99SDavid Howells  * set.
2813b11a1deSDavid Howells  *
2823b11a1deSDavid Howells  * The new process gets the current process's subjective credentials as its
2833b11a1deSDavid Howells  * objective and subjective credentials
284d84f4f99SDavid Howells  */
copy_creds(struct task_struct * p,unsigned long clone_flags)285d84f4f99SDavid Howells int copy_creds(struct task_struct *p, unsigned long clone_flags)
286d84f4f99SDavid Howells {
287d84f4f99SDavid Howells 	struct cred *new;
28818b6e041SSerge Hallyn 	int ret;
289f1752eecSDavid Howells 
2907743c48eSDavid Howells #ifdef CONFIG_KEYS_REQUEST_CACHE
2917743c48eSDavid Howells 	p->cached_requested_key = NULL;
2927743c48eSDavid Howells #endif
2937743c48eSDavid Howells 
294d84f4f99SDavid Howells 	if (
295d84f4f99SDavid Howells #ifdef CONFIG_KEYS
296d84f4f99SDavid Howells 		!p->cred->thread_keyring &&
297d84f4f99SDavid Howells #endif
298d84f4f99SDavid Howells 		clone_flags & CLONE_THREAD
299d84f4f99SDavid Howells 	    ) {
3003b11a1deSDavid Howells 		p->real_cred = get_cred(p->cred);
301d84f4f99SDavid Howells 		get_cred(p->cred);
302*207f135dSJens Axboe 		kdebug("share_creds(%p{%ld})",
303*207f135dSJens Axboe 		       p->cred, atomic_long_read(&p->cred->usage));
30421d1c5e3SAlexey Gladkov 		inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
305f1752eecSDavid Howells 		return 0;
306f1752eecSDavid Howells 	}
307d84f4f99SDavid Howells 
308d84f4f99SDavid Howells 	new = prepare_creds();
309d84f4f99SDavid Howells 	if (!new)
310d84f4f99SDavid Howells 		return -ENOMEM;
311d84f4f99SDavid Howells 
31218b6e041SSerge Hallyn 	if (clone_flags & CLONE_NEWUSER) {
31318b6e041SSerge Hallyn 		ret = create_user_ns(new);
31418b6e041SSerge Hallyn 		if (ret < 0)
31518b6e041SSerge Hallyn 			goto error_put;
3165e6b8a50SYang Yingliang 		ret = set_cred_ucounts(new);
3175e6b8a50SYang Yingliang 		if (ret < 0)
318905ae01cSAlexey Gladkov 			goto error_put;
31918b6e041SSerge Hallyn 	}
32018b6e041SSerge Hallyn 
321d84f4f99SDavid Howells #ifdef CONFIG_KEYS
322d84f4f99SDavid Howells 	/* new threads get their own thread keyrings if their parent already
323d84f4f99SDavid Howells 	 * had one */
324d84f4f99SDavid Howells 	if (new->thread_keyring) {
325d84f4f99SDavid Howells 		key_put(new->thread_keyring);
326d84f4f99SDavid Howells 		new->thread_keyring = NULL;
327d84f4f99SDavid Howells 		if (clone_flags & CLONE_THREAD)
328d84f4f99SDavid Howells 			install_thread_keyring_to_cred(new);
329d84f4f99SDavid Howells 	}
330d84f4f99SDavid Howells 
3313a50597dSDavid Howells 	/* The process keyring is only shared between the threads in a process;
3323a50597dSDavid Howells 	 * anything outside of those threads doesn't inherit.
3333a50597dSDavid Howells 	 */
334d84f4f99SDavid Howells 	if (!(clone_flags & CLONE_THREAD)) {
3353a50597dSDavid Howells 		key_put(new->process_keyring);
3363a50597dSDavid Howells 		new->process_keyring = NULL;
337d84f4f99SDavid Howells 	}
338d84f4f99SDavid Howells #endif
339d84f4f99SDavid Howells 
3403b11a1deSDavid Howells 	p->cred = p->real_cred = get_cred(new);
34121d1c5e3SAlexey Gladkov 	inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
342d84f4f99SDavid Howells 	return 0;
34318b6e041SSerge Hallyn 
34418b6e041SSerge Hallyn error_put:
34518b6e041SSerge Hallyn 	put_cred(new);
34618b6e041SSerge Hallyn 	return ret;
347d84f4f99SDavid Howells }
348d84f4f99SDavid Howells 
cred_cap_issubset(const struct cred * set,const struct cred * subset)349aa6d054eSEric W. Biederman static bool cred_cap_issubset(const struct cred *set, const struct cred *subset)
350aa6d054eSEric W. Biederman {
351aa6d054eSEric W. Biederman 	const struct user_namespace *set_ns = set->user_ns;
352aa6d054eSEric W. Biederman 	const struct user_namespace *subset_ns = subset->user_ns;
353aa6d054eSEric W. Biederman 
354aa6d054eSEric W. Biederman 	/* If the two credentials are in the same user namespace see if
355aa6d054eSEric W. Biederman 	 * the capabilities of subset are a subset of set.
356aa6d054eSEric W. Biederman 	 */
357aa6d054eSEric W. Biederman 	if (set_ns == subset_ns)
358aa6d054eSEric W. Biederman 		return cap_issubset(subset->cap_permitted, set->cap_permitted);
359aa6d054eSEric W. Biederman 
360aa6d054eSEric W. Biederman 	/* The credentials are in a different user namespaces
361aa6d054eSEric W. Biederman 	 * therefore one is a subset of the other only if a set is an
362aa6d054eSEric W. Biederman 	 * ancestor of subset and set->euid is owner of subset or one
363aa6d054eSEric W. Biederman 	 * of subsets ancestors.
364aa6d054eSEric W. Biederman 	 */
365aa6d054eSEric W. Biederman 	for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) {
366aa6d054eSEric W. Biederman 		if ((set_ns == subset_ns->parent)  &&
367aa6d054eSEric W. Biederman 		    uid_eq(subset_ns->owner, set->euid))
368aa6d054eSEric W. Biederman 			return true;
369aa6d054eSEric W. Biederman 	}
370aa6d054eSEric W. Biederman 
371aa6d054eSEric W. Biederman 	return false;
372aa6d054eSEric W. Biederman }
373aa6d054eSEric W. Biederman 
374d84f4f99SDavid Howells /**
375d84f4f99SDavid Howells  * commit_creds - Install new credentials upon the current task
376d84f4f99SDavid Howells  * @new: The credentials to be assigned
377d84f4f99SDavid Howells  *
378d84f4f99SDavid Howells  * Install a new set of credentials to the current task, using RCU to replace
3793b11a1deSDavid Howells  * the old set.  Both the objective and the subjective credentials pointers are
3803b11a1deSDavid Howells  * updated.  This function may not be called if the subjective credentials are
3813b11a1deSDavid Howells  * in an overridden state.
382d84f4f99SDavid Howells  *
383d84f4f99SDavid Howells  * This function eats the caller's reference to the new credentials.
384d84f4f99SDavid Howells  *
385d84f4f99SDavid Howells  * Always returns 0 thus allowing this function to be tail-called at the end
386d84f4f99SDavid Howells  * of, say, sys_setgid().
387d84f4f99SDavid Howells  */
commit_creds(struct cred * new)388d84f4f99SDavid Howells int commit_creds(struct cred *new)
389d84f4f99SDavid Howells {
390d84f4f99SDavid Howells 	struct task_struct *task = current;
391e0e81739SDavid Howells 	const struct cred *old = task->real_cred;
392d84f4f99SDavid Howells 
393*207f135dSJens Axboe 	kdebug("commit_creds(%p{%ld})", new,
394*207f135dSJens Axboe 	       atomic_long_read(&new->usage));
395e0e81739SDavid Howells 
396e0e81739SDavid Howells 	BUG_ON(task->cred != old);
397f6a7ce5aSJens Axboe 	BUG_ON(atomic_long_read(&new->usage) < 1);
398d84f4f99SDavid Howells 
3993b11a1deSDavid Howells 	get_cred(new); /* we will require a ref for the subj creds too */
4003b11a1deSDavid Howells 
401d84f4f99SDavid Howells 	/* dumpability changes */
402078de5f7SEric W. Biederman 	if (!uid_eq(old->euid, new->euid) ||
403078de5f7SEric W. Biederman 	    !gid_eq(old->egid, new->egid) ||
404078de5f7SEric W. Biederman 	    !uid_eq(old->fsuid, new->fsuid) ||
405078de5f7SEric W. Biederman 	    !gid_eq(old->fsgid, new->fsgid) ||
406aa6d054eSEric W. Biederman 	    !cred_cap_issubset(old, new)) {
407b9456371SDavid Howells 		if (task->mm)
408d84f4f99SDavid Howells 			set_dumpable(task->mm, suid_dumpable);
409d84f4f99SDavid Howells 		task->pdeath_signal = 0;
410f6581f5bSJann Horn 		/*
411f6581f5bSJann Horn 		 * If a task drops privileges and becomes nondumpable,
412f6581f5bSJann Horn 		 * the dumpability change must become visible before
413f6581f5bSJann Horn 		 * the credential change; otherwise, a __ptrace_may_access()
414f6581f5bSJann Horn 		 * racing with this change may be able to attach to a task it
415f6581f5bSJann Horn 		 * shouldn't be able to attach to (as if the task had dropped
416f6581f5bSJann Horn 		 * privileges without becoming nondumpable).
417f6581f5bSJann Horn 		 * Pairs with a read barrier in __ptrace_may_access().
418f6581f5bSJann Horn 		 */
419d84f4f99SDavid Howells 		smp_wmb();
420d84f4f99SDavid Howells 	}
421d84f4f99SDavid Howells 
422d84f4f99SDavid Howells 	/* alter the thread keyring */
423078de5f7SEric W. Biederman 	if (!uid_eq(new->fsuid, old->fsuid))
4242e21865fSDavid Howells 		key_fsuid_changed(new);
425078de5f7SEric W. Biederman 	if (!gid_eq(new->fsgid, old->fsgid))
4262e21865fSDavid Howells 		key_fsgid_changed(new);
427d84f4f99SDavid Howells 
428d84f4f99SDavid Howells 	/* do it
42972fa5997SVasiliy Kulikov 	 * RLIMIT_NPROC limits on user->processes have already been checked
43072fa5997SVasiliy Kulikov 	 * in set_user().
431d84f4f99SDavid Howells 	 */
43221d1c5e3SAlexey Gladkov 	if (new->user != old->user || new->user_ns != old->user_ns)
43321d1c5e3SAlexey Gladkov 		inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1);
4343b11a1deSDavid Howells 	rcu_assign_pointer(task->real_cred, new);
435d84f4f99SDavid Howells 	rcu_assign_pointer(task->cred, new);
436629715adSEric W. Biederman 	if (new->user != old->user || new->user_ns != old->user_ns)
43721d1c5e3SAlexey Gladkov 		dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1);
438d84f4f99SDavid Howells 
439d84f4f99SDavid Howells 	/* send notifications */
440078de5f7SEric W. Biederman 	if (!uid_eq(new->uid,   old->uid)  ||
441078de5f7SEric W. Biederman 	    !uid_eq(new->euid,  old->euid) ||
442078de5f7SEric W. Biederman 	    !uid_eq(new->suid,  old->suid) ||
443078de5f7SEric W. Biederman 	    !uid_eq(new->fsuid, old->fsuid))
444d84f4f99SDavid Howells 		proc_id_connector(task, PROC_EVENT_UID);
445d84f4f99SDavid Howells 
446078de5f7SEric W. Biederman 	if (!gid_eq(new->gid,   old->gid)  ||
447078de5f7SEric W. Biederman 	    !gid_eq(new->egid,  old->egid) ||
448078de5f7SEric W. Biederman 	    !gid_eq(new->sgid,  old->sgid) ||
449078de5f7SEric W. Biederman 	    !gid_eq(new->fsgid, old->fsgid))
450d84f4f99SDavid Howells 		proc_id_connector(task, PROC_EVENT_GID);
451d84f4f99SDavid Howells 
4523b11a1deSDavid Howells 	/* release the old obj and subj refs both */
4533b11a1deSDavid Howells 	put_cred(old);
454d84f4f99SDavid Howells 	put_cred(old);
455d84f4f99SDavid Howells 	return 0;
456d84f4f99SDavid Howells }
457d84f4f99SDavid Howells EXPORT_SYMBOL(commit_creds);
458d84f4f99SDavid Howells 
459d84f4f99SDavid Howells /**
460d84f4f99SDavid Howells  * abort_creds - Discard a set of credentials and unlock the current task
461d84f4f99SDavid Howells  * @new: The credentials that were going to be applied
462d84f4f99SDavid Howells  *
463d84f4f99SDavid Howells  * Discard a set of credentials that were under construction and unlock the
464d84f4f99SDavid Howells  * current task.
465d84f4f99SDavid Howells  */
abort_creds(struct cred * new)466d84f4f99SDavid Howells void abort_creds(struct cred *new)
467d84f4f99SDavid Howells {
468*207f135dSJens Axboe 	kdebug("abort_creds(%p{%ld})", new,
469*207f135dSJens Axboe 	       atomic_long_read(&new->usage));
470e0e81739SDavid Howells 
471f6a7ce5aSJens Axboe 	BUG_ON(atomic_long_read(&new->usage) < 1);
472d84f4f99SDavid Howells 	put_cred(new);
473d84f4f99SDavid Howells }
474d84f4f99SDavid Howells EXPORT_SYMBOL(abort_creds);
475d84f4f99SDavid Howells 
476d84f4f99SDavid Howells /**
4773b11a1deSDavid Howells  * override_creds - Override the current process's subjective credentials
478d84f4f99SDavid Howells  * @new: The credentials to be assigned
479d84f4f99SDavid Howells  *
4803b11a1deSDavid Howells  * Install a set of temporary override subjective credentials on the current
4813b11a1deSDavid Howells  * process, returning the old set for later reversion.
482d84f4f99SDavid Howells  */
override_creds(const struct cred * new)483d84f4f99SDavid Howells const struct cred *override_creds(const struct cred *new)
484d84f4f99SDavid Howells {
485d84f4f99SDavid Howells 	const struct cred *old = current->cred;
486d84f4f99SDavid Howells 
487*207f135dSJens Axboe 	kdebug("override_creds(%p{%ld})", new,
488*207f135dSJens Axboe 	       atomic_long_read(&new->usage));
489d7852fbdSLinus Torvalds 
490d7852fbdSLinus Torvalds 	/*
491d7852fbdSLinus Torvalds 	 * NOTE! This uses 'get_new_cred()' rather than 'get_cred()'.
492d7852fbdSLinus Torvalds 	 *
493d7852fbdSLinus Torvalds 	 * That means that we do not clear the 'non_rcu' flag, since
494d7852fbdSLinus Torvalds 	 * we are only installing the cred into the thread-synchronous
495d7852fbdSLinus Torvalds 	 * '->cred' pointer, not the '->real_cred' pointer that is
496d7852fbdSLinus Torvalds 	 * visible to other threads under RCU.
497d7852fbdSLinus Torvalds 	 */
498d7852fbdSLinus Torvalds 	get_new_cred((struct cred *)new);
499e0e81739SDavid Howells 	rcu_assign_pointer(current->cred, new);
500e0e81739SDavid Howells 
501*207f135dSJens Axboe 	kdebug("override_creds() = %p{%ld}", old,
502*207f135dSJens Axboe 	       atomic_long_read(&old->usage));
503d84f4f99SDavid Howells 	return old;
504d84f4f99SDavid Howells }
505d84f4f99SDavid Howells EXPORT_SYMBOL(override_creds);
506d84f4f99SDavid Howells 
507d84f4f99SDavid Howells /**
5083b11a1deSDavid Howells  * revert_creds - Revert a temporary subjective credentials override
509d84f4f99SDavid Howells  * @old: The credentials to be restored
510d84f4f99SDavid Howells  *
5113b11a1deSDavid Howells  * Revert a temporary set of override subjective credentials to an old set,
5123b11a1deSDavid Howells  * discarding the override set.
513d84f4f99SDavid Howells  */
revert_creds(const struct cred * old)514d84f4f99SDavid Howells void revert_creds(const struct cred *old)
515d84f4f99SDavid Howells {
516d84f4f99SDavid Howells 	const struct cred *override = current->cred;
517d84f4f99SDavid Howells 
518*207f135dSJens Axboe 	kdebug("revert_creds(%p{%ld})", old,
519*207f135dSJens Axboe 	       atomic_long_read(&old->usage));
520e0e81739SDavid Howells 
521d84f4f99SDavid Howells 	rcu_assign_pointer(current->cred, old);
522d84f4f99SDavid Howells 	put_cred(override);
523d84f4f99SDavid Howells }
524d84f4f99SDavid Howells EXPORT_SYMBOL(revert_creds);
525d84f4f99SDavid Howells 
526d89b22d4SNeilBrown /**
527d89b22d4SNeilBrown  * cred_fscmp - Compare two credentials with respect to filesystem access.
528d89b22d4SNeilBrown  * @a: The first credential
529d89b22d4SNeilBrown  * @b: The second credential
530d89b22d4SNeilBrown  *
531d89b22d4SNeilBrown  * cred_cmp() will return zero if both credentials have the same
532d89b22d4SNeilBrown  * fsuid, fsgid, and supplementary groups.  That is, if they will both
533d89b22d4SNeilBrown  * provide the same access to files based on mode/uid/gid.
534d89b22d4SNeilBrown  * If the credentials are different, then either -1 or 1 will
535d89b22d4SNeilBrown  * be returned depending on whether @a comes before or after @b
536d89b22d4SNeilBrown  * respectively in an arbitrary, but stable, ordering of credentials.
537d89b22d4SNeilBrown  *
538d89b22d4SNeilBrown  * Return: -1, 0, or 1 depending on comparison
539d89b22d4SNeilBrown  */
cred_fscmp(const struct cred * a,const struct cred * b)540d89b22d4SNeilBrown int cred_fscmp(const struct cred *a, const struct cred *b)
541d89b22d4SNeilBrown {
542d89b22d4SNeilBrown 	struct group_info *ga, *gb;
543d89b22d4SNeilBrown 	int g;
544d89b22d4SNeilBrown 
545d89b22d4SNeilBrown 	if (a == b)
546d89b22d4SNeilBrown 		return 0;
547d89b22d4SNeilBrown 	if (uid_lt(a->fsuid, b->fsuid))
548d89b22d4SNeilBrown 		return -1;
549d89b22d4SNeilBrown 	if (uid_gt(a->fsuid, b->fsuid))
550d89b22d4SNeilBrown 		return 1;
551d89b22d4SNeilBrown 
552d89b22d4SNeilBrown 	if (gid_lt(a->fsgid, b->fsgid))
553d89b22d4SNeilBrown 		return -1;
554d89b22d4SNeilBrown 	if (gid_gt(a->fsgid, b->fsgid))
555d89b22d4SNeilBrown 		return 1;
556d89b22d4SNeilBrown 
557d89b22d4SNeilBrown 	ga = a->group_info;
558d89b22d4SNeilBrown 	gb = b->group_info;
559d89b22d4SNeilBrown 	if (ga == gb)
560d89b22d4SNeilBrown 		return 0;
561d89b22d4SNeilBrown 	if (ga == NULL)
562d89b22d4SNeilBrown 		return -1;
563d89b22d4SNeilBrown 	if (gb == NULL)
564d89b22d4SNeilBrown 		return 1;
565d89b22d4SNeilBrown 	if (ga->ngroups < gb->ngroups)
566d89b22d4SNeilBrown 		return -1;
567d89b22d4SNeilBrown 	if (ga->ngroups > gb->ngroups)
568d89b22d4SNeilBrown 		return 1;
569d89b22d4SNeilBrown 
570d89b22d4SNeilBrown 	for (g = 0; g < ga->ngroups; g++) {
571d89b22d4SNeilBrown 		if (gid_lt(ga->gid[g], gb->gid[g]))
572d89b22d4SNeilBrown 			return -1;
573d89b22d4SNeilBrown 		if (gid_gt(ga->gid[g], gb->gid[g]))
574d89b22d4SNeilBrown 			return 1;
575d89b22d4SNeilBrown 	}
576d89b22d4SNeilBrown 	return 0;
577d89b22d4SNeilBrown }
578d89b22d4SNeilBrown EXPORT_SYMBOL(cred_fscmp);
579d89b22d4SNeilBrown 
set_cred_ucounts(struct cred * new)580905ae01cSAlexey Gladkov int set_cred_ucounts(struct cred *new)
581905ae01cSAlexey Gladkov {
58234dc2fd6SEric W. Biederman 	struct ucounts *new_ucounts, *old_ucounts = new->ucounts;
583905ae01cSAlexey Gladkov 
584905ae01cSAlexey Gladkov 	/*
585905ae01cSAlexey Gladkov 	 * This optimization is needed because alloc_ucounts() uses locks
586905ae01cSAlexey Gladkov 	 * for table lookups.
587905ae01cSAlexey Gladkov 	 */
588a55d0729SEric W. Biederman 	if (old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->uid))
589905ae01cSAlexey Gladkov 		return 0;
590905ae01cSAlexey Gladkov 
591a55d0729SEric W. Biederman 	if (!(new_ucounts = alloc_ucounts(new->user_ns, new->uid)))
592905ae01cSAlexey Gladkov 		return -EAGAIN;
593905ae01cSAlexey Gladkov 
59434dc2fd6SEric W. Biederman 	new->ucounts = new_ucounts;
595905ae01cSAlexey Gladkov 	put_ucounts(old_ucounts);
596905ae01cSAlexey Gladkov 
597905ae01cSAlexey Gladkov 	return 0;
598905ae01cSAlexey Gladkov }
599905ae01cSAlexey Gladkov 
600d84f4f99SDavid Howells /*
601d84f4f99SDavid Howells  * initialise the credentials stuff
602d84f4f99SDavid Howells  */
cred_init(void)603d84f4f99SDavid Howells void __init cred_init(void)
604d84f4f99SDavid Howells {
605d84f4f99SDavid Howells 	/* allocate a slab in which we can store credentials */
6065d097056SVladimir Davydov 	cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), 0,
6075d097056SVladimir Davydov 			SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL);
608d84f4f99SDavid Howells }
6093a3b7ce9SDavid Howells 
6103a3b7ce9SDavid Howells /**
6113a3b7ce9SDavid Howells  * prepare_kernel_cred - Prepare a set of credentials for a kernel service
6123a3b7ce9SDavid Howells  * @daemon: A userspace daemon to be used as a reference
6133a3b7ce9SDavid Howells  *
6143a3b7ce9SDavid Howells  * Prepare a set of credentials for a kernel service.  This can then be used to
6153a3b7ce9SDavid Howells  * override a task's own credentials so that work can be done on behalf of that
6163a3b7ce9SDavid Howells  * task that requires a different subjective context.
6173a3b7ce9SDavid Howells  *
6185a17f040SKees Cook  * @daemon is used to provide a base cred, with the security data derived from
6195a17f040SKees Cook  * that; if this is "&init_task", they'll be set to 0, no groups, full
6205a17f040SKees Cook  * capabilities, and no keys.
6213a3b7ce9SDavid Howells  *
6223a3b7ce9SDavid Howells  * The caller may change these controls afterwards if desired.
6233a3b7ce9SDavid Howells  *
6243a3b7ce9SDavid Howells  * Returns the new credentials or NULL if out of memory.
6253a3b7ce9SDavid Howells  */
prepare_kernel_cred(struct task_struct * daemon)6263a3b7ce9SDavid Howells struct cred *prepare_kernel_cred(struct task_struct *daemon)
6273a3b7ce9SDavid Howells {
6283a3b7ce9SDavid Howells 	const struct cred *old;
6293a3b7ce9SDavid Howells 	struct cred *new;
6303a3b7ce9SDavid Howells 
6315a17f040SKees Cook 	if (WARN_ON_ONCE(!daemon))
6325a17f040SKees Cook 		return NULL;
6335a17f040SKees Cook 
6343a3b7ce9SDavid Howells 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
6353a3b7ce9SDavid Howells 	if (!new)
6363a3b7ce9SDavid Howells 		return NULL;
6373a3b7ce9SDavid Howells 
638e0e81739SDavid Howells 	kdebug("prepare_kernel_cred() alloc %p", new);
639e0e81739SDavid Howells 
6403a3b7ce9SDavid Howells 	old = get_task_cred(daemon);
641e0e81739SDavid Howells 
64243529c97SDavid Howells 	*new = *old;
643d7852fbdSLinus Torvalds 	new->non_rcu = 0;
644f6a7ce5aSJens Axboe 	atomic_long_set(&new->usage, 1);
6453a3b7ce9SDavid Howells 	get_uid(new->user);
6460093ccb6SEric W. Biederman 	get_user_ns(new->user_ns);
6473a3b7ce9SDavid Howells 	get_group_info(new->group_info);
6483a3b7ce9SDavid Howells 
6493a3b7ce9SDavid Howells #ifdef CONFIG_KEYS
6503a50597dSDavid Howells 	new->session_keyring = NULL;
6513a50597dSDavid Howells 	new->process_keyring = NULL;
6523a3b7ce9SDavid Howells 	new->thread_keyring = NULL;
6533a50597dSDavid Howells 	new->request_key_auth = NULL;
6543a3b7ce9SDavid Howells 	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
6553a3b7ce9SDavid Howells #endif
6563a3b7ce9SDavid Howells 
6573a3b7ce9SDavid Howells #ifdef CONFIG_SECURITY
6583a3b7ce9SDavid Howells 	new->security = NULL;
6593a3b7ce9SDavid Howells #endif
660905ae01cSAlexey Gladkov 	new->ucounts = get_ucounts(new->ucounts);
661905ae01cSAlexey Gladkov 	if (!new->ucounts)
662905ae01cSAlexey Gladkov 		goto error;
663905ae01cSAlexey Gladkov 
664bbb6d0f3SAlexey Gladkov 	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
665bbb6d0f3SAlexey Gladkov 		goto error;
666bbb6d0f3SAlexey Gladkov 
6673a3b7ce9SDavid Howells 	put_cred(old);
6683a3b7ce9SDavid Howells 	return new;
6693a3b7ce9SDavid Howells 
6703a3b7ce9SDavid Howells error:
6713a3b7ce9SDavid Howells 	put_cred(new);
6720de33681SDavid Howells 	put_cred(old);
6733a3b7ce9SDavid Howells 	return NULL;
6743a3b7ce9SDavid Howells }
6753a3b7ce9SDavid Howells EXPORT_SYMBOL(prepare_kernel_cred);
6763a3b7ce9SDavid Howells 
6773a3b7ce9SDavid Howells /**
6783a3b7ce9SDavid Howells  * set_security_override - Set the security ID in a set of credentials
6793a3b7ce9SDavid Howells  * @new: The credentials to alter
6803a3b7ce9SDavid Howells  * @secid: The LSM security ID to set
6813a3b7ce9SDavid Howells  *
6823a3b7ce9SDavid Howells  * Set the LSM security ID in a set of credentials so that the subjective
6833a3b7ce9SDavid Howells  * security is overridden when an alternative set of credentials is used.
6843a3b7ce9SDavid Howells  */
set_security_override(struct cred * new,u32 secid)6853a3b7ce9SDavid Howells int set_security_override(struct cred *new, u32 secid)
6863a3b7ce9SDavid Howells {
6873a3b7ce9SDavid Howells 	return security_kernel_act_as(new, secid);
6883a3b7ce9SDavid Howells }
6893a3b7ce9SDavid Howells EXPORT_SYMBOL(set_security_override);
6903a3b7ce9SDavid Howells 
6913a3b7ce9SDavid Howells /**
6923a3b7ce9SDavid Howells  * set_security_override_from_ctx - Set the security ID in a set of credentials
6933a3b7ce9SDavid Howells  * @new: The credentials to alter
6943a3b7ce9SDavid Howells  * @secctx: The LSM security context to generate the security ID from.
6953a3b7ce9SDavid Howells  *
6963a3b7ce9SDavid Howells  * Set the LSM security ID in a set of credentials so that the subjective
6973a3b7ce9SDavid Howells  * security is overridden when an alternative set of credentials is used.  The
6983a3b7ce9SDavid Howells  * security ID is specified in string form as a security context to be
6993a3b7ce9SDavid Howells  * interpreted by the LSM.
7003a3b7ce9SDavid Howells  */
set_security_override_from_ctx(struct cred * new,const char * secctx)7013a3b7ce9SDavid Howells int set_security_override_from_ctx(struct cred *new, const char *secctx)
7023a3b7ce9SDavid Howells {
7033a3b7ce9SDavid Howells 	u32 secid;
7043a3b7ce9SDavid Howells 	int ret;
7053a3b7ce9SDavid Howells 
7063a3b7ce9SDavid Howells 	ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
7073a3b7ce9SDavid Howells 	if (ret < 0)
7083a3b7ce9SDavid Howells 		return ret;
7093a3b7ce9SDavid Howells 
7103a3b7ce9SDavid Howells 	return set_security_override(new, secid);
7113a3b7ce9SDavid Howells }
7123a3b7ce9SDavid Howells EXPORT_SYMBOL(set_security_override_from_ctx);
7133a3b7ce9SDavid Howells 
7143a3b7ce9SDavid Howells /**
7153a3b7ce9SDavid Howells  * set_create_files_as - Set the LSM file create context in a set of credentials
7163a3b7ce9SDavid Howells  * @new: The credentials to alter
7173a3b7ce9SDavid Howells  * @inode: The inode to take the context from
7183a3b7ce9SDavid Howells  *
7193a3b7ce9SDavid Howells  * Change the LSM file creation context in a set of credentials to be the same
7203a3b7ce9SDavid Howells  * as the object context of the specified inode, so that the new inodes have
7213a3b7ce9SDavid Howells  * the same MAC context as that inode.
7223a3b7ce9SDavid Howells  */
set_create_files_as(struct cred * new,struct inode * inode)7233a3b7ce9SDavid Howells int set_create_files_as(struct cred *new, struct inode *inode)
7243a3b7ce9SDavid Howells {
7255f65e5caSSeth Forshee 	if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid))
7265f65e5caSSeth Forshee 		return -EINVAL;
7273a3b7ce9SDavid Howells 	new->fsuid = inode->i_uid;
7283a3b7ce9SDavid Howells 	new->fsgid = inode->i_gid;
7293a3b7ce9SDavid Howells 	return security_kernel_create_files_as(new, inode);
7303a3b7ce9SDavid Howells }
7313a3b7ce9SDavid Howells EXPORT_SYMBOL(set_create_files_as);
732