xref: /linux-6.15/kernel/cred.c (revision a51a1d6b)
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 ([email protected])
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 */
39d7700842SElena Reshetova static struct group_info init_groups = { .usage = REFCOUNT_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 
74f8fa5d76SJens Axboe 	if (atomic_long_read(&cred->usage) != 0)
75f8fa5d76SJens Axboe 		panic("CRED: put_cred_rcu() sees %p with usage %ld\n",
76f8fa5d76SJens 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 {
100ae191417SJens Axboe 	kdebug("__put_cred(%p{%ld})", cred,
101ae191417SJens Axboe 	       atomic_long_read(&cred->usage));
102e0e81739SDavid Howells 
103f8fa5d76SJens 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 {
11941e84562SMateusz Guzik 	struct cred *real_cred, *cred;
120e0e81739SDavid Howells 
121ae191417SJens Axboe 	kdebug("exit_creds(%u,%p,%p,{%ld})", tsk->pid, tsk->real_cred, tsk->cred,
122ae191417SJens Axboe 	       atomic_long_read(&tsk->cred->usage));
123e0e81739SDavid Howells 
12441e84562SMateusz Guzik 	real_cred = (struct cred *) tsk->real_cred;
125e0e81739SDavid Howells 	tsk->real_cred = NULL;
126e0e81739SDavid Howells 
127e0e81739SDavid Howells 	cred = (struct cred *) tsk->cred;
128e0e81739SDavid Howells 	tsk->cred = NULL;
12941e84562SMateusz Guzik 
13041e84562SMateusz Guzik 	if (real_cred == cred) {
13141e84562SMateusz Guzik 		put_cred_many(cred, 2);
13241e84562SMateusz Guzik 	} else {
13341e84562SMateusz Guzik 		put_cred(real_cred);
134e0e81739SDavid Howells 		put_cred(cred);
13541e84562SMateusz Guzik 	}
1367743c48eSDavid Howells 
1377743c48eSDavid Howells #ifdef CONFIG_KEYS_REQUEST_CACHE
1388379bb84SDavid Howells 	key_put(tsk->cached_requested_key);
1398379bb84SDavid Howells 	tsk->cached_requested_key = NULL;
1407743c48eSDavid Howells #endif
141ee18d64cSDavid Howells }
142ee18d64cSDavid Howells 
143de09a977SDavid Howells /**
144de09a977SDavid Howells  * get_task_cred - Get another task's objective credentials
145de09a977SDavid Howells  * @task: The task to query
146de09a977SDavid Howells  *
147de09a977SDavid Howells  * Get the objective credentials of a task, pinning them so that they can't go
148de09a977SDavid Howells  * away.  Accessing a task's credentials directly is not permitted.
149de09a977SDavid Howells  *
150de09a977SDavid Howells  * The caller must also make sure task doesn't get deleted, either by holding a
151de09a977SDavid Howells  * ref on task or by holding tasklist_lock to prevent it from being unlinked.
152de09a977SDavid Howells  */
get_task_cred(struct task_struct * task)153de09a977SDavid Howells const struct cred *get_task_cred(struct task_struct *task)
154de09a977SDavid Howells {
155de09a977SDavid Howells 	const struct cred *cred;
156de09a977SDavid Howells 
157de09a977SDavid Howells 	rcu_read_lock();
158de09a977SDavid Howells 
159de09a977SDavid Howells 	do {
160de09a977SDavid Howells 		cred = __task_cred((task));
161de09a977SDavid Howells 		BUG_ON(!cred);
16297d0fb23SNeilBrown 	} while (!get_cred_rcu(cred));
163de09a977SDavid Howells 
164de09a977SDavid Howells 	rcu_read_unlock();
165de09a977SDavid Howells 	return cred;
166de09a977SDavid Howells }
167a6d8e763SNeilBrown EXPORT_SYMBOL(get_task_cred);
168de09a977SDavid Howells 
169ee18d64cSDavid Howells /*
170ee18d64cSDavid Howells  * Allocate blank credentials, such that the credentials can be filled in at a
171ee18d64cSDavid Howells  * later date without risk of ENOMEM.
172ee18d64cSDavid Howells  */
cred_alloc_blank(void)173ee18d64cSDavid Howells struct cred *cred_alloc_blank(void)
174ee18d64cSDavid Howells {
175ee18d64cSDavid Howells 	struct cred *new;
176ee18d64cSDavid Howells 
177ee18d64cSDavid Howells 	new = kmem_cache_zalloc(cred_jar, GFP_KERNEL);
178ee18d64cSDavid Howells 	if (!new)
179ee18d64cSDavid Howells 		return NULL;
180ee18d64cSDavid Howells 
181f8fa5d76SJens Axboe 	atomic_long_set(&new->usage, 1);
18284029fd0SShakeel Butt 	if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0)
183ee18d64cSDavid Howells 		goto error;
184ee18d64cSDavid Howells 
185ee18d64cSDavid Howells 	return new;
186ee18d64cSDavid Howells 
187ee18d64cSDavid Howells error:
188ee18d64cSDavid Howells 	abort_creds(new);
189ee18d64cSDavid Howells 	return NULL;
190e0e81739SDavid Howells }
191e0e81739SDavid Howells 
192d84f4f99SDavid Howells /**
193d84f4f99SDavid Howells  * prepare_creds - Prepare a new set of credentials for modification
194d84f4f99SDavid Howells  *
195d84f4f99SDavid Howells  * Prepare a new set of task credentials for modification.  A task's creds
196d84f4f99SDavid Howells  * shouldn't generally be modified directly, therefore this function is used to
197d84f4f99SDavid Howells  * prepare a new copy, which the caller then modifies and then commits by
198d84f4f99SDavid Howells  * calling commit_creds().
199d84f4f99SDavid Howells  *
2003b11a1deSDavid Howells  * Preparation involves making a copy of the objective creds for modification.
2013b11a1deSDavid Howells  *
202d84f4f99SDavid Howells  * Returns a pointer to the new creds-to-be if successful, NULL otherwise.
203d84f4f99SDavid Howells  *
204d84f4f99SDavid Howells  * Call commit_creds() or abort_creds() to clean up.
205f1752eecSDavid Howells  */
prepare_creds(void)206d84f4f99SDavid Howells struct cred *prepare_creds(void)
207f1752eecSDavid Howells {
208d84f4f99SDavid Howells 	struct task_struct *task = current;
209d84f4f99SDavid Howells 	const struct cred *old;
210d84f4f99SDavid Howells 	struct cred *new;
211f1752eecSDavid Howells 
212d84f4f99SDavid Howells 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
213d84f4f99SDavid Howells 	if (!new)
214d84f4f99SDavid Howells 		return NULL;
215d84f4f99SDavid Howells 
216e0e81739SDavid Howells 	kdebug("prepare_creds() alloc %p", new);
217e0e81739SDavid Howells 
218d84f4f99SDavid Howells 	old = task->cred;
219d84f4f99SDavid Howells 	memcpy(new, old, sizeof(struct cred));
220d84f4f99SDavid Howells 
221d7852fbdSLinus Torvalds 	new->non_rcu = 0;
222f8fa5d76SJens Axboe 	atomic_long_set(&new->usage, 1);
223d84f4f99SDavid Howells 	get_group_info(new->group_info);
224d84f4f99SDavid Howells 	get_uid(new->user);
2250093ccb6SEric W. Biederman 	get_user_ns(new->user_ns);
226f1752eecSDavid Howells 
227bb952bb9SDavid Howells #ifdef CONFIG_KEYS
2283a50597dSDavid Howells 	key_get(new->session_keyring);
2293a50597dSDavid Howells 	key_get(new->process_keyring);
230d84f4f99SDavid Howells 	key_get(new->thread_keyring);
231d84f4f99SDavid Howells 	key_get(new->request_key_auth);
232bb952bb9SDavid Howells #endif
233bb952bb9SDavid Howells 
234f1752eecSDavid Howells #ifdef CONFIG_SECURITY
235d84f4f99SDavid Howells 	new->security = NULL;
236f1752eecSDavid Howells #endif
237f1752eecSDavid Howells 
238905ae01cSAlexey Gladkov 	new->ucounts = get_ucounts(new->ucounts);
239905ae01cSAlexey Gladkov 	if (!new->ucounts)
240905ae01cSAlexey Gladkov 		goto error;
241905ae01cSAlexey Gladkov 
242bbb6d0f3SAlexey Gladkov 	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
243bbb6d0f3SAlexey Gladkov 		goto error;
244bbb6d0f3SAlexey Gladkov 
245d84f4f99SDavid Howells 	return new;
246d84f4f99SDavid Howells 
247d84f4f99SDavid Howells error:
248d84f4f99SDavid Howells 	abort_creds(new);
249d84f4f99SDavid Howells 	return NULL;
250d84f4f99SDavid Howells }
251d84f4f99SDavid Howells EXPORT_SYMBOL(prepare_creds);
252d84f4f99SDavid Howells 
253d84f4f99SDavid Howells /*
254a6f76f23SDavid Howells  * Prepare credentials for current to perform an execve()
2559b1bf12dSKOSAKI Motohiro  * - The caller must hold ->cred_guard_mutex
256a6f76f23SDavid Howells  */
prepare_exec_creds(void)257a6f76f23SDavid Howells struct cred *prepare_exec_creds(void)
258a6f76f23SDavid Howells {
259a6f76f23SDavid Howells 	struct cred *new;
260a6f76f23SDavid Howells 
261a6f76f23SDavid Howells 	new = prepare_creds();
2623a50597dSDavid Howells 	if (!new)
263a6f76f23SDavid Howells 		return new;
264a6f76f23SDavid Howells 
265a6f76f23SDavid Howells #ifdef CONFIG_KEYS
266a6f76f23SDavid Howells 	/* newly exec'd tasks don't get a thread keyring */
267a6f76f23SDavid Howells 	key_put(new->thread_keyring);
268a6f76f23SDavid Howells 	new->thread_keyring = NULL;
269a6f76f23SDavid Howells 
270a6f76f23SDavid Howells 	/* inherit the session keyring; new process keyring */
2713a50597dSDavid Howells 	key_put(new->process_keyring);
2723a50597dSDavid Howells 	new->process_keyring = NULL;
273a6f76f23SDavid Howells #endif
274a6f76f23SDavid Howells 
27587b047d2SEric W. Biederman 	new->suid = new->fsuid = new->euid;
27687b047d2SEric W. Biederman 	new->sgid = new->fsgid = new->egid;
27787b047d2SEric W. Biederman 
278a6f76f23SDavid Howells 	return new;
279a6f76f23SDavid Howells }
280a6f76f23SDavid Howells 
281a6f76f23SDavid Howells /*
282d84f4f99SDavid Howells  * Copy credentials for the new process created by fork()
283d84f4f99SDavid Howells  *
284d84f4f99SDavid Howells  * We share if we can, but under some circumstances we have to generate a new
285d84f4f99SDavid Howells  * set.
2863b11a1deSDavid Howells  *
2873b11a1deSDavid Howells  * The new process gets the current process's subjective credentials as its
2883b11a1deSDavid Howells  * objective and subjective credentials
289d84f4f99SDavid Howells  */
copy_creds(struct task_struct * p,unsigned long clone_flags)290d84f4f99SDavid Howells int copy_creds(struct task_struct *p, unsigned long clone_flags)
291d84f4f99SDavid Howells {
292d84f4f99SDavid Howells 	struct cred *new;
29318b6e041SSerge Hallyn 	int ret;
294f1752eecSDavid Howells 
2957743c48eSDavid Howells #ifdef CONFIG_KEYS_REQUEST_CACHE
2967743c48eSDavid Howells 	p->cached_requested_key = NULL;
2977743c48eSDavid Howells #endif
2987743c48eSDavid Howells 
299d84f4f99SDavid Howells 	if (
300d84f4f99SDavid Howells #ifdef CONFIG_KEYS
301d84f4f99SDavid Howells 		!p->cred->thread_keyring &&
302d84f4f99SDavid Howells #endif
303d84f4f99SDavid Howells 		clone_flags & CLONE_THREAD
304d84f4f99SDavid Howells 	    ) {
30541e84562SMateusz Guzik 		p->real_cred = get_cred_many(p->cred, 2);
306ae191417SJens Axboe 		kdebug("share_creds(%p{%ld})",
307ae191417SJens Axboe 		       p->cred, atomic_long_read(&p->cred->usage));
30821d1c5e3SAlexey Gladkov 		inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
309f1752eecSDavid Howells 		return 0;
310f1752eecSDavid Howells 	}
311d84f4f99SDavid Howells 
312d84f4f99SDavid Howells 	new = prepare_creds();
313d84f4f99SDavid Howells 	if (!new)
314d84f4f99SDavid Howells 		return -ENOMEM;
315d84f4f99SDavid Howells 
31618b6e041SSerge Hallyn 	if (clone_flags & CLONE_NEWUSER) {
31718b6e041SSerge Hallyn 		ret = create_user_ns(new);
31818b6e041SSerge Hallyn 		if (ret < 0)
31918b6e041SSerge Hallyn 			goto error_put;
3205e6b8a50SYang Yingliang 		ret = set_cred_ucounts(new);
3215e6b8a50SYang Yingliang 		if (ret < 0)
322905ae01cSAlexey Gladkov 			goto error_put;
32318b6e041SSerge Hallyn 	}
32418b6e041SSerge Hallyn 
325d84f4f99SDavid Howells #ifdef CONFIG_KEYS
326d84f4f99SDavid Howells 	/* new threads get their own thread keyrings if their parent already
327d84f4f99SDavid Howells 	 * had one */
328d84f4f99SDavid Howells 	if (new->thread_keyring) {
329d84f4f99SDavid Howells 		key_put(new->thread_keyring);
330d84f4f99SDavid Howells 		new->thread_keyring = NULL;
331d84f4f99SDavid Howells 		if (clone_flags & CLONE_THREAD)
332d84f4f99SDavid Howells 			install_thread_keyring_to_cred(new);
333d84f4f99SDavid Howells 	}
334d84f4f99SDavid Howells 
3353a50597dSDavid Howells 	/* The process keyring is only shared between the threads in a process;
3363a50597dSDavid Howells 	 * anything outside of those threads doesn't inherit.
3373a50597dSDavid Howells 	 */
338d84f4f99SDavid Howells 	if (!(clone_flags & CLONE_THREAD)) {
3393a50597dSDavid Howells 		key_put(new->process_keyring);
3403a50597dSDavid Howells 		new->process_keyring = NULL;
341d84f4f99SDavid Howells 	}
342d84f4f99SDavid Howells #endif
343d84f4f99SDavid Howells 
3443b11a1deSDavid Howells 	p->cred = p->real_cred = get_cred(new);
34521d1c5e3SAlexey Gladkov 	inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1);
346d84f4f99SDavid Howells 	return 0;
34718b6e041SSerge Hallyn 
34818b6e041SSerge Hallyn error_put:
34918b6e041SSerge Hallyn 	put_cred(new);
35018b6e041SSerge Hallyn 	return ret;
351d84f4f99SDavid Howells }
352d84f4f99SDavid Howells 
cred_cap_issubset(const struct cred * set,const struct cred * subset)353aa6d054eSEric W. Biederman static bool cred_cap_issubset(const struct cred *set, const struct cred *subset)
354aa6d054eSEric W. Biederman {
355aa6d054eSEric W. Biederman 	const struct user_namespace *set_ns = set->user_ns;
356aa6d054eSEric W. Biederman 	const struct user_namespace *subset_ns = subset->user_ns;
357aa6d054eSEric W. Biederman 
358aa6d054eSEric W. Biederman 	/* If the two credentials are in the same user namespace see if
359aa6d054eSEric W. Biederman 	 * the capabilities of subset are a subset of set.
360aa6d054eSEric W. Biederman 	 */
361aa6d054eSEric W. Biederman 	if (set_ns == subset_ns)
362aa6d054eSEric W. Biederman 		return cap_issubset(subset->cap_permitted, set->cap_permitted);
363aa6d054eSEric W. Biederman 
364aa6d054eSEric W. Biederman 	/* The credentials are in a different user namespaces
365aa6d054eSEric W. Biederman 	 * therefore one is a subset of the other only if a set is an
366aa6d054eSEric W. Biederman 	 * ancestor of subset and set->euid is owner of subset or one
367aa6d054eSEric W. Biederman 	 * of subsets ancestors.
368aa6d054eSEric W. Biederman 	 */
369aa6d054eSEric W. Biederman 	for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) {
370aa6d054eSEric W. Biederman 		if ((set_ns == subset_ns->parent)  &&
371aa6d054eSEric W. Biederman 		    uid_eq(subset_ns->owner, set->euid))
372aa6d054eSEric W. Biederman 			return true;
373aa6d054eSEric W. Biederman 	}
374aa6d054eSEric W. Biederman 
375aa6d054eSEric W. Biederman 	return false;
376aa6d054eSEric W. Biederman }
377aa6d054eSEric W. Biederman 
378d84f4f99SDavid Howells /**
379d84f4f99SDavid Howells  * commit_creds - Install new credentials upon the current task
380d84f4f99SDavid Howells  * @new: The credentials to be assigned
381d84f4f99SDavid Howells  *
382d84f4f99SDavid Howells  * Install a new set of credentials to the current task, using RCU to replace
3833b11a1deSDavid Howells  * the old set.  Both the objective and the subjective credentials pointers are
3843b11a1deSDavid Howells  * updated.  This function may not be called if the subjective credentials are
3853b11a1deSDavid Howells  * in an overridden state.
386d84f4f99SDavid Howells  *
387d84f4f99SDavid Howells  * This function eats the caller's reference to the new credentials.
388d84f4f99SDavid Howells  *
389d84f4f99SDavid Howells  * Always returns 0 thus allowing this function to be tail-called at the end
390d84f4f99SDavid Howells  * of, say, sys_setgid().
391d84f4f99SDavid Howells  */
commit_creds(struct cred * new)392d84f4f99SDavid Howells int commit_creds(struct cred *new)
393d84f4f99SDavid Howells {
394d84f4f99SDavid Howells 	struct task_struct *task = current;
395e0e81739SDavid Howells 	const struct cred *old = task->real_cred;
396d84f4f99SDavid Howells 
397ae191417SJens Axboe 	kdebug("commit_creds(%p{%ld})", new,
398ae191417SJens Axboe 	       atomic_long_read(&new->usage));
399e0e81739SDavid Howells 
400e0e81739SDavid Howells 	BUG_ON(task->cred != old);
401f8fa5d76SJens Axboe 	BUG_ON(atomic_long_read(&new->usage) < 1);
402d84f4f99SDavid Howells 
4033b11a1deSDavid Howells 	get_cred(new); /* we will require a ref for the subj creds too */
4043b11a1deSDavid Howells 
405d84f4f99SDavid Howells 	/* dumpability changes */
406078de5f7SEric W. Biederman 	if (!uid_eq(old->euid, new->euid) ||
407078de5f7SEric W. Biederman 	    !gid_eq(old->egid, new->egid) ||
408078de5f7SEric W. Biederman 	    !uid_eq(old->fsuid, new->fsuid) ||
409078de5f7SEric W. Biederman 	    !gid_eq(old->fsgid, new->fsgid) ||
410aa6d054eSEric W. Biederman 	    !cred_cap_issubset(old, new)) {
411b9456371SDavid Howells 		if (task->mm)
412d84f4f99SDavid Howells 			set_dumpable(task->mm, suid_dumpable);
413d84f4f99SDavid Howells 		task->pdeath_signal = 0;
414f6581f5bSJann Horn 		/*
415f6581f5bSJann Horn 		 * If a task drops privileges and becomes nondumpable,
416f6581f5bSJann Horn 		 * the dumpability change must become visible before
417f6581f5bSJann Horn 		 * the credential change; otherwise, a __ptrace_may_access()
418f6581f5bSJann Horn 		 * racing with this change may be able to attach to a task it
419f6581f5bSJann Horn 		 * shouldn't be able to attach to (as if the task had dropped
420f6581f5bSJann Horn 		 * privileges without becoming nondumpable).
421f6581f5bSJann Horn 		 * Pairs with a read barrier in __ptrace_may_access().
422f6581f5bSJann Horn 		 */
423d84f4f99SDavid Howells 		smp_wmb();
424d84f4f99SDavid Howells 	}
425d84f4f99SDavid Howells 
426d84f4f99SDavid Howells 	/* alter the thread keyring */
427078de5f7SEric W. Biederman 	if (!uid_eq(new->fsuid, old->fsuid))
4282e21865fSDavid Howells 		key_fsuid_changed(new);
429078de5f7SEric W. Biederman 	if (!gid_eq(new->fsgid, old->fsgid))
4302e21865fSDavid Howells 		key_fsgid_changed(new);
431d84f4f99SDavid Howells 
432d84f4f99SDavid Howells 	/* do it
43372fa5997SVasiliy Kulikov 	 * RLIMIT_NPROC limits on user->processes have already been checked
43472fa5997SVasiliy Kulikov 	 * in set_user().
435d84f4f99SDavid Howells 	 */
43621d1c5e3SAlexey Gladkov 	if (new->user != old->user || new->user_ns != old->user_ns)
43721d1c5e3SAlexey Gladkov 		inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1);
4383b11a1deSDavid Howells 	rcu_assign_pointer(task->real_cred, new);
439d84f4f99SDavid Howells 	rcu_assign_pointer(task->cred, new);
440629715adSEric W. Biederman 	if (new->user != old->user || new->user_ns != old->user_ns)
44121d1c5e3SAlexey Gladkov 		dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1);
442d84f4f99SDavid Howells 
443d84f4f99SDavid Howells 	/* send notifications */
444078de5f7SEric W. Biederman 	if (!uid_eq(new->uid,   old->uid)  ||
445078de5f7SEric W. Biederman 	    !uid_eq(new->euid,  old->euid) ||
446078de5f7SEric W. Biederman 	    !uid_eq(new->suid,  old->suid) ||
447078de5f7SEric W. Biederman 	    !uid_eq(new->fsuid, old->fsuid))
448d84f4f99SDavid Howells 		proc_id_connector(task, PROC_EVENT_UID);
449d84f4f99SDavid Howells 
450078de5f7SEric W. Biederman 	if (!gid_eq(new->gid,   old->gid)  ||
451078de5f7SEric W. Biederman 	    !gid_eq(new->egid,  old->egid) ||
452078de5f7SEric W. Biederman 	    !gid_eq(new->sgid,  old->sgid) ||
453078de5f7SEric W. Biederman 	    !gid_eq(new->fsgid, old->fsgid))
454d84f4f99SDavid Howells 		proc_id_connector(task, PROC_EVENT_GID);
455d84f4f99SDavid Howells 
4563b11a1deSDavid Howells 	/* release the old obj and subj refs both */
45741e84562SMateusz Guzik 	put_cred_many(old, 2);
458d84f4f99SDavid Howells 	return 0;
459d84f4f99SDavid Howells }
460d84f4f99SDavid Howells EXPORT_SYMBOL(commit_creds);
461d84f4f99SDavid Howells 
462d84f4f99SDavid Howells /**
463d84f4f99SDavid Howells  * abort_creds - Discard a set of credentials and unlock the current task
464d84f4f99SDavid Howells  * @new: The credentials that were going to be applied
465d84f4f99SDavid Howells  *
466d84f4f99SDavid Howells  * Discard a set of credentials that were under construction and unlock the
467d84f4f99SDavid Howells  * current task.
468d84f4f99SDavid Howells  */
abort_creds(struct cred * new)469d84f4f99SDavid Howells void abort_creds(struct cred *new)
470d84f4f99SDavid Howells {
471ae191417SJens Axboe 	kdebug("abort_creds(%p{%ld})", new,
472ae191417SJens Axboe 	       atomic_long_read(&new->usage));
473e0e81739SDavid Howells 
474f8fa5d76SJens Axboe 	BUG_ON(atomic_long_read(&new->usage) < 1);
475d84f4f99SDavid Howells 	put_cred(new);
476d84f4f99SDavid Howells }
477d84f4f99SDavid Howells EXPORT_SYMBOL(abort_creds);
478d84f4f99SDavid Howells 
479d84f4f99SDavid Howells /**
480d89b22d4SNeilBrown  * cred_fscmp - Compare two credentials with respect to filesystem access.
481d89b22d4SNeilBrown  * @a: The first credential
482d89b22d4SNeilBrown  * @b: The second credential
483d89b22d4SNeilBrown  *
484d89b22d4SNeilBrown  * cred_cmp() will return zero if both credentials have the same
485d89b22d4SNeilBrown  * fsuid, fsgid, and supplementary groups.  That is, if they will both
486d89b22d4SNeilBrown  * provide the same access to files based on mode/uid/gid.
487d89b22d4SNeilBrown  * If the credentials are different, then either -1 or 1 will
488d89b22d4SNeilBrown  * be returned depending on whether @a comes before or after @b
489d89b22d4SNeilBrown  * respectively in an arbitrary, but stable, ordering of credentials.
490d89b22d4SNeilBrown  *
491d89b22d4SNeilBrown  * Return: -1, 0, or 1 depending on comparison
492d89b22d4SNeilBrown  */
cred_fscmp(const struct cred * a,const struct cred * b)493d89b22d4SNeilBrown int cred_fscmp(const struct cred *a, const struct cred *b)
494d89b22d4SNeilBrown {
495d89b22d4SNeilBrown 	struct group_info *ga, *gb;
496d89b22d4SNeilBrown 	int g;
497d89b22d4SNeilBrown 
498d89b22d4SNeilBrown 	if (a == b)
499d89b22d4SNeilBrown 		return 0;
500d89b22d4SNeilBrown 	if (uid_lt(a->fsuid, b->fsuid))
501d89b22d4SNeilBrown 		return -1;
502d89b22d4SNeilBrown 	if (uid_gt(a->fsuid, b->fsuid))
503d89b22d4SNeilBrown 		return 1;
504d89b22d4SNeilBrown 
505d89b22d4SNeilBrown 	if (gid_lt(a->fsgid, b->fsgid))
506d89b22d4SNeilBrown 		return -1;
507d89b22d4SNeilBrown 	if (gid_gt(a->fsgid, b->fsgid))
508d89b22d4SNeilBrown 		return 1;
509d89b22d4SNeilBrown 
510d89b22d4SNeilBrown 	ga = a->group_info;
511d89b22d4SNeilBrown 	gb = b->group_info;
512d89b22d4SNeilBrown 	if (ga == gb)
513d89b22d4SNeilBrown 		return 0;
514d89b22d4SNeilBrown 	if (ga == NULL)
515d89b22d4SNeilBrown 		return -1;
516d89b22d4SNeilBrown 	if (gb == NULL)
517d89b22d4SNeilBrown 		return 1;
518d89b22d4SNeilBrown 	if (ga->ngroups < gb->ngroups)
519d89b22d4SNeilBrown 		return -1;
520d89b22d4SNeilBrown 	if (ga->ngroups > gb->ngroups)
521d89b22d4SNeilBrown 		return 1;
522d89b22d4SNeilBrown 
523d89b22d4SNeilBrown 	for (g = 0; g < ga->ngroups; g++) {
524d89b22d4SNeilBrown 		if (gid_lt(ga->gid[g], gb->gid[g]))
525d89b22d4SNeilBrown 			return -1;
526d89b22d4SNeilBrown 		if (gid_gt(ga->gid[g], gb->gid[g]))
527d89b22d4SNeilBrown 			return 1;
528d89b22d4SNeilBrown 	}
529d89b22d4SNeilBrown 	return 0;
530d89b22d4SNeilBrown }
531d89b22d4SNeilBrown EXPORT_SYMBOL(cred_fscmp);
532d89b22d4SNeilBrown 
set_cred_ucounts(struct cred * new)533905ae01cSAlexey Gladkov int set_cred_ucounts(struct cred *new)
534905ae01cSAlexey Gladkov {
53534dc2fd6SEric W. Biederman 	struct ucounts *new_ucounts, *old_ucounts = new->ucounts;
536905ae01cSAlexey Gladkov 
537905ae01cSAlexey Gladkov 	/*
538905ae01cSAlexey Gladkov 	 * This optimization is needed because alloc_ucounts() uses locks
539905ae01cSAlexey Gladkov 	 * for table lookups.
540905ae01cSAlexey Gladkov 	 */
541a55d0729SEric W. Biederman 	if (old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->uid))
542905ae01cSAlexey Gladkov 		return 0;
543905ae01cSAlexey Gladkov 
544a55d0729SEric W. Biederman 	if (!(new_ucounts = alloc_ucounts(new->user_ns, new->uid)))
545905ae01cSAlexey Gladkov 		return -EAGAIN;
546905ae01cSAlexey Gladkov 
54734dc2fd6SEric W. Biederman 	new->ucounts = new_ucounts;
548905ae01cSAlexey Gladkov 	put_ucounts(old_ucounts);
549905ae01cSAlexey Gladkov 
550905ae01cSAlexey Gladkov 	return 0;
551905ae01cSAlexey Gladkov }
552905ae01cSAlexey Gladkov 
553d84f4f99SDavid Howells /*
554d84f4f99SDavid Howells  * initialise the credentials stuff
555d84f4f99SDavid Howells  */
cred_init(void)556d84f4f99SDavid Howells void __init cred_init(void)
557d84f4f99SDavid Howells {
558d84f4f99SDavid Howells 	/* allocate a slab in which we can store credentials */
559*edc66702SKunwu Chan 	cred_jar = KMEM_CACHE(cred,
560*edc66702SKunwu Chan 			      SLAB_HWCACHE_ALIGN | SLAB_PANIC | SLAB_ACCOUNT);
561d84f4f99SDavid Howells }
5623a3b7ce9SDavid Howells 
5633a3b7ce9SDavid Howells /**
5643a3b7ce9SDavid Howells  * prepare_kernel_cred - Prepare a set of credentials for a kernel service
5653a3b7ce9SDavid Howells  * @daemon: A userspace daemon to be used as a reference
5663a3b7ce9SDavid Howells  *
5673a3b7ce9SDavid Howells  * Prepare a set of credentials for a kernel service.  This can then be used to
5683a3b7ce9SDavid Howells  * override a task's own credentials so that work can be done on behalf of that
5693a3b7ce9SDavid Howells  * task that requires a different subjective context.
5703a3b7ce9SDavid Howells  *
5715a17f040SKees Cook  * @daemon is used to provide a base cred, with the security data derived from
5725a17f040SKees Cook  * that; if this is "&init_task", they'll be set to 0, no groups, full
5735a17f040SKees Cook  * capabilities, and no keys.
5743a3b7ce9SDavid Howells  *
5753a3b7ce9SDavid Howells  * The caller may change these controls afterwards if desired.
5763a3b7ce9SDavid Howells  *
5773a3b7ce9SDavid Howells  * Returns the new credentials or NULL if out of memory.
5783a3b7ce9SDavid Howells  */
prepare_kernel_cred(struct task_struct * daemon)5793a3b7ce9SDavid Howells struct cred *prepare_kernel_cred(struct task_struct *daemon)
5803a3b7ce9SDavid Howells {
5813a3b7ce9SDavid Howells 	const struct cred *old;
5823a3b7ce9SDavid Howells 	struct cred *new;
5833a3b7ce9SDavid Howells 
5845a17f040SKees Cook 	if (WARN_ON_ONCE(!daemon))
5855a17f040SKees Cook 		return NULL;
5865a17f040SKees Cook 
5873a3b7ce9SDavid Howells 	new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
5883a3b7ce9SDavid Howells 	if (!new)
5893a3b7ce9SDavid Howells 		return NULL;
5903a3b7ce9SDavid Howells 
591e0e81739SDavid Howells 	kdebug("prepare_kernel_cred() alloc %p", new);
592e0e81739SDavid Howells 
5933a3b7ce9SDavid Howells 	old = get_task_cred(daemon);
594e0e81739SDavid Howells 
59543529c97SDavid Howells 	*new = *old;
596d7852fbdSLinus Torvalds 	new->non_rcu = 0;
597f8fa5d76SJens Axboe 	atomic_long_set(&new->usage, 1);
5983a3b7ce9SDavid Howells 	get_uid(new->user);
5990093ccb6SEric W. Biederman 	get_user_ns(new->user_ns);
6003a3b7ce9SDavid Howells 	get_group_info(new->group_info);
6013a3b7ce9SDavid Howells 
6023a3b7ce9SDavid Howells #ifdef CONFIG_KEYS
6033a50597dSDavid Howells 	new->session_keyring = NULL;
6043a50597dSDavid Howells 	new->process_keyring = NULL;
6053a3b7ce9SDavid Howells 	new->thread_keyring = NULL;
6063a50597dSDavid Howells 	new->request_key_auth = NULL;
6073a3b7ce9SDavid Howells 	new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
6083a3b7ce9SDavid Howells #endif
6093a3b7ce9SDavid Howells 
6103a3b7ce9SDavid Howells #ifdef CONFIG_SECURITY
6113a3b7ce9SDavid Howells 	new->security = NULL;
6123a3b7ce9SDavid Howells #endif
613905ae01cSAlexey Gladkov 	new->ucounts = get_ucounts(new->ucounts);
614905ae01cSAlexey Gladkov 	if (!new->ucounts)
615905ae01cSAlexey Gladkov 		goto error;
616905ae01cSAlexey Gladkov 
617bbb6d0f3SAlexey Gladkov 	if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0)
618bbb6d0f3SAlexey Gladkov 		goto error;
619bbb6d0f3SAlexey Gladkov 
6203a3b7ce9SDavid Howells 	put_cred(old);
6213a3b7ce9SDavid Howells 	return new;
6223a3b7ce9SDavid Howells 
6233a3b7ce9SDavid Howells error:
6243a3b7ce9SDavid Howells 	put_cred(new);
6250de33681SDavid Howells 	put_cred(old);
6263a3b7ce9SDavid Howells 	return NULL;
6273a3b7ce9SDavid Howells }
6283a3b7ce9SDavid Howells EXPORT_SYMBOL(prepare_kernel_cred);
6293a3b7ce9SDavid Howells 
6303a3b7ce9SDavid Howells /**
6313a3b7ce9SDavid Howells  * set_security_override - Set the security ID in a set of credentials
6323a3b7ce9SDavid Howells  * @new: The credentials to alter
6333a3b7ce9SDavid Howells  * @secid: The LSM security ID to set
6343a3b7ce9SDavid Howells  *
6353a3b7ce9SDavid Howells  * Set the LSM security ID in a set of credentials so that the subjective
6363a3b7ce9SDavid Howells  * security is overridden when an alternative set of credentials is used.
6373a3b7ce9SDavid Howells  */
set_security_override(struct cred * new,u32 secid)6383a3b7ce9SDavid Howells int set_security_override(struct cred *new, u32 secid)
6393a3b7ce9SDavid Howells {
6403a3b7ce9SDavid Howells 	return security_kernel_act_as(new, secid);
6413a3b7ce9SDavid Howells }
6423a3b7ce9SDavid Howells EXPORT_SYMBOL(set_security_override);
6433a3b7ce9SDavid Howells 
6443a3b7ce9SDavid Howells /**
6453a3b7ce9SDavid Howells  * set_security_override_from_ctx - Set the security ID in a set of credentials
6463a3b7ce9SDavid Howells  * @new: The credentials to alter
6473a3b7ce9SDavid Howells  * @secctx: The LSM security context to generate the security ID from.
6483a3b7ce9SDavid Howells  *
6493a3b7ce9SDavid Howells  * Set the LSM security ID in a set of credentials so that the subjective
6503a3b7ce9SDavid Howells  * security is overridden when an alternative set of credentials is used.  The
6513a3b7ce9SDavid Howells  * security ID is specified in string form as a security context to be
6523a3b7ce9SDavid Howells  * interpreted by the LSM.
6533a3b7ce9SDavid Howells  */
set_security_override_from_ctx(struct cred * new,const char * secctx)6543a3b7ce9SDavid Howells int set_security_override_from_ctx(struct cred *new, const char *secctx)
6553a3b7ce9SDavid Howells {
6563a3b7ce9SDavid Howells 	u32 secid;
6573a3b7ce9SDavid Howells 	int ret;
6583a3b7ce9SDavid Howells 
6593a3b7ce9SDavid Howells 	ret = security_secctx_to_secid(secctx, strlen(secctx), &secid);
6603a3b7ce9SDavid Howells 	if (ret < 0)
6613a3b7ce9SDavid Howells 		return ret;
6623a3b7ce9SDavid Howells 
6633a3b7ce9SDavid Howells 	return set_security_override(new, secid);
6643a3b7ce9SDavid Howells }
6653a3b7ce9SDavid Howells EXPORT_SYMBOL(set_security_override_from_ctx);
6663a3b7ce9SDavid Howells 
6673a3b7ce9SDavid Howells /**
6683a3b7ce9SDavid Howells  * set_create_files_as - Set the LSM file create context in a set of credentials
6693a3b7ce9SDavid Howells  * @new: The credentials to alter
6703a3b7ce9SDavid Howells  * @inode: The inode to take the context from
6713a3b7ce9SDavid Howells  *
6723a3b7ce9SDavid Howells  * Change the LSM file creation context in a set of credentials to be the same
6733a3b7ce9SDavid Howells  * as the object context of the specified inode, so that the new inodes have
6743a3b7ce9SDavid Howells  * the same MAC context as that inode.
6753a3b7ce9SDavid Howells  */
set_create_files_as(struct cred * new,struct inode * inode)6763a3b7ce9SDavid Howells int set_create_files_as(struct cred *new, struct inode *inode)
6773a3b7ce9SDavid Howells {
6785f65e5caSSeth Forshee 	if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid))
6795f65e5caSSeth Forshee 		return -EINVAL;
6803a3b7ce9SDavid Howells 	new->fsuid = inode->i_uid;
6813a3b7ce9SDavid Howells 	new->fsgid = inode->i_gid;
6823a3b7ce9SDavid Howells 	return security_kernel_create_files_as(new, inode);
6833a3b7ce9SDavid Howells }
6843a3b7ce9SDavid Howells EXPORT_SYMBOL(set_create_files_as);
685