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