]>
Commit | Line | Data |
---|---|---|
b4d0d230 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
af777cd1 | 2 | /* Task credentials management - see Documentation/security/credentials.rst |
f1752eec DH |
3 | * |
4 | * Copyright (C) 2008 Red Hat, Inc. All Rights Reserved. | |
5 | * Written by David Howells ([email protected]) | |
f1752eec | 6 | */ |
4099451a | 7 | |
8 | #define pr_fmt(fmt) "CRED: " fmt | |
9 | ||
9984de1a | 10 | #include <linux/export.h> |
f1752eec | 11 | #include <linux/cred.h> |
5a0e3ad6 | 12 | #include <linux/slab.h> |
f1752eec | 13 | #include <linux/sched.h> |
f7ccbae4 | 14 | #include <linux/sched/coredump.h> |
f1752eec DH |
15 | #include <linux/key.h> |
16 | #include <linux/keyctl.h> | |
17 | #include <linux/init_task.h> | |
18 | #include <linux/security.h> | |
40401530 | 19 | #include <linux/binfmts.h> |
d84f4f99 | 20 | #include <linux/cn_proc.h> |
d89b22d4 | 21 | #include <linux/uidgid.h> |
d84f4f99 | 22 | |
e0e81739 | 23 | #if 0 |
52aa8536 JP |
24 | #define kdebug(FMT, ...) \ |
25 | printk("[%-5.5s%5u] " FMT "\n", \ | |
26 | current->comm, current->pid, ##__VA_ARGS__) | |
e0e81739 | 27 | #else |
52aa8536 JP |
28 | #define kdebug(FMT, ...) \ |
29 | do { \ | |
30 | if (0) \ | |
31 | no_printk("[%-5.5s%5u] " FMT "\n", \ | |
32 | current->comm, current->pid, ##__VA_ARGS__); \ | |
33 | } while (0) | |
e0e81739 DH |
34 | #endif |
35 | ||
d84f4f99 | 36 | static struct kmem_cache *cred_jar; |
f1752eec | 37 | |
2813893f | 38 | /* init to 2 - one for init_task, one to ensure it is never freed */ |
d7700842 | 39 | static struct group_info init_groups = { .usage = REFCOUNT_INIT(2) }; |
2813893f | 40 | |
f1752eec DH |
41 | /* |
42 | * The initial credentials for the initial task | |
43 | */ | |
44 | struct cred init_cred = { | |
3b11a1de | 45 | .usage = ATOMIC_INIT(4), |
078de5f7 EB |
46 | .uid = GLOBAL_ROOT_UID, |
47 | .gid = GLOBAL_ROOT_GID, | |
48 | .suid = GLOBAL_ROOT_UID, | |
49 | .sgid = GLOBAL_ROOT_GID, | |
50 | .euid = GLOBAL_ROOT_UID, | |
51 | .egid = GLOBAL_ROOT_GID, | |
52 | .fsuid = GLOBAL_ROOT_UID, | |
53 | .fsgid = GLOBAL_ROOT_GID, | |
f1752eec | 54 | .securebits = SECUREBITS_DEFAULT, |
a3232d2f | 55 | .cap_inheritable = CAP_EMPTY_SET, |
f1752eec | 56 | .cap_permitted = CAP_FULL_SET, |
a3232d2f EP |
57 | .cap_effective = CAP_FULL_SET, |
58 | .cap_bset = CAP_FULL_SET, | |
f1752eec | 59 | .user = INIT_USER, |
47a150ed | 60 | .user_ns = &init_user_ns, |
f1752eec | 61 | .group_info = &init_groups, |
905ae01c | 62 | .ucounts = &init_ucounts, |
f1752eec DH |
63 | }; |
64 | ||
65 | /* | |
66 | * The RCU callback to actually dispose of a set of credentials | |
67 | */ | |
68 | static void put_cred_rcu(struct rcu_head *rcu) | |
69 | { | |
70 | struct cred *cred = container_of(rcu, struct cred, rcu); | |
71 | ||
e0e81739 DH |
72 | kdebug("put_cred_rcu(%p)", cred); |
73 | ||
f8fa5d76 JA |
74 | if (atomic_long_read(&cred->usage) != 0) |
75 | panic("CRED: put_cred_rcu() sees %p with usage %ld\n", | |
76 | cred, atomic_long_read(&cred->usage)); | |
f1752eec | 77 | |
d84f4f99 | 78 | security_cred_free(cred); |
3a50597d DH |
79 | key_put(cred->session_keyring); |
80 | key_put(cred->process_keyring); | |
f1752eec DH |
81 | key_put(cred->thread_keyring); |
82 | key_put(cred->request_key_auth); | |
4a5d6ba1 DH |
83 | if (cred->group_info) |
84 | put_group_info(cred->group_info); | |
f1752eec | 85 | free_uid(cred->user); |
905ae01c AG |
86 | if (cred->ucounts) |
87 | put_ucounts(cred->ucounts); | |
0093ccb6 | 88 | put_user_ns(cred->user_ns); |
d84f4f99 | 89 | kmem_cache_free(cred_jar, cred); |
f1752eec DH |
90 | } |
91 | ||
92 | /** | |
93 | * __put_cred - Destroy a set of credentials | |
d84f4f99 | 94 | * @cred: The record to release |
f1752eec DH |
95 | * |
96 | * Destroy a set of credentials on which no references remain. | |
97 | */ | |
98 | void __put_cred(struct cred *cred) | |
99 | { | |
ae191417 JA |
100 | kdebug("__put_cred(%p{%ld})", cred, |
101 | atomic_long_read(&cred->usage)); | |
e0e81739 | 102 | |
f8fa5d76 | 103 | BUG_ON(atomic_long_read(&cred->usage) != 0); |
e0e81739 DH |
104 | BUG_ON(cred == current->cred); |
105 | BUG_ON(cred == current->real_cred); | |
d84f4f99 | 106 | |
d7852fbd LT |
107 | if (cred->non_rcu) |
108 | put_cred_rcu(&cred->rcu); | |
109 | else | |
110 | call_rcu(&cred->rcu, put_cred_rcu); | |
f1752eec DH |
111 | } |
112 | EXPORT_SYMBOL(__put_cred); | |
113 | ||
e0e81739 DH |
114 | /* |
115 | * Clean up a task's credentials when it exits | |
116 | */ | |
117 | void exit_creds(struct task_struct *tsk) | |
118 | { | |
41e84562 | 119 | struct cred *real_cred, *cred; |
e0e81739 | 120 | |
ae191417 JA |
121 | kdebug("exit_creds(%u,%p,%p,{%ld})", tsk->pid, tsk->real_cred, tsk->cred, |
122 | atomic_long_read(&tsk->cred->usage)); | |
e0e81739 | 123 | |
41e84562 | 124 | real_cred = (struct cred *) tsk->real_cred; |
e0e81739 | 125 | tsk->real_cred = NULL; |
e0e81739 DH |
126 | |
127 | cred = (struct cred *) tsk->cred; | |
128 | tsk->cred = NULL; | |
41e84562 | 129 | |
41e84562 | 130 | if (real_cred == cred) { |
41e84562 MG |
131 | put_cred_many(cred, 2); |
132 | } else { | |
41e84562 | 133 | put_cred(real_cred); |
41e84562 MG |
134 | put_cred(cred); |
135 | } | |
7743c48e DH |
136 | |
137 | #ifdef CONFIG_KEYS_REQUEST_CACHE | |
8379bb84 DH |
138 | key_put(tsk->cached_requested_key); |
139 | tsk->cached_requested_key = NULL; | |
7743c48e | 140 | #endif |
ee18d64c DH |
141 | } |
142 | ||
de09a977 DH |
143 | /** |
144 | * get_task_cred - Get another task's objective credentials | |
145 | * @task: The task to query | |
146 | * | |
147 | * Get the objective credentials of a task, pinning them so that they can't go | |
148 | * away. Accessing a task's credentials directly is not permitted. | |
149 | * | |
150 | * The caller must also make sure task doesn't get deleted, either by holding a | |
151 | * ref on task or by holding tasklist_lock to prevent it from being unlinked. | |
152 | */ | |
153 | const struct cred *get_task_cred(struct task_struct *task) | |
154 | { | |
155 | const struct cred *cred; | |
156 | ||
157 | rcu_read_lock(); | |
158 | ||
159 | do { | |
160 | cred = __task_cred((task)); | |
161 | BUG_ON(!cred); | |
97d0fb23 | 162 | } while (!get_cred_rcu(cred)); |
de09a977 DH |
163 | |
164 | rcu_read_unlock(); | |
165 | return cred; | |
166 | } | |
a6d8e763 | 167 | EXPORT_SYMBOL(get_task_cred); |
de09a977 | 168 | |
ee18d64c DH |
169 | /* |
170 | * Allocate blank credentials, such that the credentials can be filled in at a | |
171 | * later date without risk of ENOMEM. | |
172 | */ | |
173 | struct cred *cred_alloc_blank(void) | |
174 | { | |
175 | struct cred *new; | |
176 | ||
177 | new = kmem_cache_zalloc(cred_jar, GFP_KERNEL); | |
178 | if (!new) | |
179 | return NULL; | |
180 | ||
f8fa5d76 | 181 | atomic_long_set(&new->usage, 1); |
84029fd0 | 182 | if (security_cred_alloc_blank(new, GFP_KERNEL_ACCOUNT) < 0) |
ee18d64c DH |
183 | goto error; |
184 | ||
ee18d64c DH |
185 | return new; |
186 | ||
187 | error: | |
188 | abort_creds(new); | |
189 | return NULL; | |
e0e81739 DH |
190 | } |
191 | ||
d84f4f99 DH |
192 | /** |
193 | * prepare_creds - Prepare a new set of credentials for modification | |
194 | * | |
195 | * Prepare a new set of task credentials for modification. A task's creds | |
196 | * shouldn't generally be modified directly, therefore this function is used to | |
197 | * prepare a new copy, which the caller then modifies and then commits by | |
198 | * calling commit_creds(). | |
199 | * | |
3b11a1de DH |
200 | * Preparation involves making a copy of the objective creds for modification. |
201 | * | |
d84f4f99 DH |
202 | * Returns a pointer to the new creds-to-be if successful, NULL otherwise. |
203 | * | |
204 | * Call commit_creds() or abort_creds() to clean up. | |
205 | */ | |
206 | struct cred *prepare_creds(void) | |
207 | { | |
208 | struct task_struct *task = current; | |
209 | const struct cred *old; | |
210 | struct cred *new; | |
211 | ||
d84f4f99 DH |
212 | new = kmem_cache_alloc(cred_jar, GFP_KERNEL); |
213 | if (!new) | |
214 | return NULL; | |
215 | ||
e0e81739 DH |
216 | kdebug("prepare_creds() alloc %p", new); |
217 | ||
d84f4f99 DH |
218 | old = task->cred; |
219 | memcpy(new, old, sizeof(struct cred)); | |
220 | ||
d7852fbd | 221 | new->non_rcu = 0; |
f8fa5d76 | 222 | atomic_long_set(&new->usage, 1); |
d84f4f99 DH |
223 | get_group_info(new->group_info); |
224 | get_uid(new->user); | |
0093ccb6 | 225 | get_user_ns(new->user_ns); |
d84f4f99 DH |
226 | |
227 | #ifdef CONFIG_KEYS | |
3a50597d DH |
228 | key_get(new->session_keyring); |
229 | key_get(new->process_keyring); | |
d84f4f99 DH |
230 | key_get(new->thread_keyring); |
231 | key_get(new->request_key_auth); | |
d84f4f99 DH |
232 | #endif |
233 | ||
234 | #ifdef CONFIG_SECURITY | |
235 | new->security = NULL; | |
236 | #endif | |
237 | ||
905ae01c AG |
238 | new->ucounts = get_ucounts(new->ucounts); |
239 | if (!new->ucounts) | |
240 | goto error; | |
241 | ||
bbb6d0f3 AG |
242 | if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0) |
243 | goto error; | |
244 | ||
d84f4f99 DH |
245 | return new; |
246 | ||
247 | error: | |
248 | abort_creds(new); | |
249 | return NULL; | |
250 | } | |
251 | EXPORT_SYMBOL(prepare_creds); | |
252 | ||
a6f76f23 DH |
253 | /* |
254 | * Prepare credentials for current to perform an execve() | |
9b1bf12d | 255 | * - The caller must hold ->cred_guard_mutex |
a6f76f23 DH |
256 | */ |
257 | struct cred *prepare_exec_creds(void) | |
258 | { | |
a6f76f23 DH |
259 | struct cred *new; |
260 | ||
a6f76f23 | 261 | new = prepare_creds(); |
3a50597d | 262 | if (!new) |
a6f76f23 | 263 | return new; |
a6f76f23 DH |
264 | |
265 | #ifdef CONFIG_KEYS | |
266 | /* newly exec'd tasks don't get a thread keyring */ | |
267 | key_put(new->thread_keyring); | |
268 | new->thread_keyring = NULL; | |
269 | ||
a6f76f23 | 270 | /* inherit the session keyring; new process keyring */ |
3a50597d DH |
271 | key_put(new->process_keyring); |
272 | new->process_keyring = NULL; | |
a6f76f23 DH |
273 | #endif |
274 | ||
87b047d2 EB |
275 | new->suid = new->fsuid = new->euid; |
276 | new->sgid = new->fsgid = new->egid; | |
277 | ||
a6f76f23 DH |
278 | return new; |
279 | } | |
280 | ||
f1752eec DH |
281 | /* |
282 | * Copy credentials for the new process created by fork() | |
d84f4f99 DH |
283 | * |
284 | * We share if we can, but under some circumstances we have to generate a new | |
285 | * set. | |
3b11a1de DH |
286 | * |
287 | * The new process gets the current process's subjective credentials as its | |
288 | * objective and subjective credentials | |
f1752eec DH |
289 | */ |
290 | int copy_creds(struct task_struct *p, unsigned long clone_flags) | |
291 | { | |
d84f4f99 | 292 | struct cred *new; |
18b6e041 | 293 | int ret; |
d84f4f99 | 294 | |
7743c48e DH |
295 | #ifdef CONFIG_KEYS_REQUEST_CACHE |
296 | p->cached_requested_key = NULL; | |
297 | #endif | |
298 | ||
d84f4f99 DH |
299 | if ( |
300 | #ifdef CONFIG_KEYS | |
301 | !p->cred->thread_keyring && | |
302 | #endif | |
303 | clone_flags & CLONE_THREAD | |
304 | ) { | |
41e84562 | 305 | p->real_cred = get_cred_many(p->cred, 2); |
ae191417 JA |
306 | kdebug("share_creds(%p{%ld})", |
307 | p->cred, atomic_long_read(&p->cred->usage)); | |
21d1c5e3 | 308 | inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1); |
d84f4f99 DH |
309 | return 0; |
310 | } | |
311 | ||
312 | new = prepare_creds(); | |
313 | if (!new) | |
f1752eec DH |
314 | return -ENOMEM; |
315 | ||
18b6e041 SH |
316 | if (clone_flags & CLONE_NEWUSER) { |
317 | ret = create_user_ns(new); | |
318 | if (ret < 0) | |
319 | goto error_put; | |
5e6b8a50 YY |
320 | ret = set_cred_ucounts(new); |
321 | if (ret < 0) | |
905ae01c | 322 | goto error_put; |
18b6e041 SH |
323 | } |
324 | ||
bb952bb9 | 325 | #ifdef CONFIG_KEYS |
d84f4f99 DH |
326 | /* new threads get their own thread keyrings if their parent already |
327 | * had one */ | |
328 | if (new->thread_keyring) { | |
329 | key_put(new->thread_keyring); | |
330 | new->thread_keyring = NULL; | |
331 | if (clone_flags & CLONE_THREAD) | |
332 | install_thread_keyring_to_cred(new); | |
333 | } | |
334 | ||
3a50597d DH |
335 | /* The process keyring is only shared between the threads in a process; |
336 | * anything outside of those threads doesn't inherit. | |
337 | */ | |
d84f4f99 | 338 | if (!(clone_flags & CLONE_THREAD)) { |
3a50597d DH |
339 | key_put(new->process_keyring); |
340 | new->process_keyring = NULL; | |
bb952bb9 DH |
341 | } |
342 | #endif | |
343 | ||
3b11a1de | 344 | p->cred = p->real_cred = get_cred(new); |
21d1c5e3 | 345 | inc_rlimit_ucounts(task_ucounts(p), UCOUNT_RLIMIT_NPROC, 1); |
d84f4f99 | 346 | return 0; |
18b6e041 SH |
347 | |
348 | error_put: | |
349 | put_cred(new); | |
350 | return ret; | |
d84f4f99 | 351 | } |
f1752eec | 352 | |
aa6d054e EB |
353 | static bool cred_cap_issubset(const struct cred *set, const struct cred *subset) |
354 | { | |
355 | const struct user_namespace *set_ns = set->user_ns; | |
356 | const struct user_namespace *subset_ns = subset->user_ns; | |
357 | ||
358 | /* If the two credentials are in the same user namespace see if | |
359 | * the capabilities of subset are a subset of set. | |
360 | */ | |
361 | if (set_ns == subset_ns) | |
362 | return cap_issubset(subset->cap_permitted, set->cap_permitted); | |
363 | ||
364 | /* The credentials are in a different user namespaces | |
365 | * therefore one is a subset of the other only if a set is an | |
366 | * ancestor of subset and set->euid is owner of subset or one | |
367 | * of subsets ancestors. | |
368 | */ | |
369 | for (;subset_ns != &init_user_ns; subset_ns = subset_ns->parent) { | |
370 | if ((set_ns == subset_ns->parent) && | |
371 | uid_eq(subset_ns->owner, set->euid)) | |
372 | return true; | |
373 | } | |
374 | ||
375 | return false; | |
376 | } | |
377 | ||
d84f4f99 DH |
378 | /** |
379 | * commit_creds - Install new credentials upon the current task | |
380 | * @new: The credentials to be assigned | |
381 | * | |
382 | * Install a new set of credentials to the current task, using RCU to replace | |
3b11a1de DH |
383 | * the old set. Both the objective and the subjective credentials pointers are |
384 | * updated. This function may not be called if the subjective credentials are | |
385 | * in an overridden state. | |
d84f4f99 DH |
386 | * |
387 | * This function eats the caller's reference to the new credentials. | |
388 | * | |
389 | * Always returns 0 thus allowing this function to be tail-called at the end | |
390 | * of, say, sys_setgid(). | |
391 | */ | |
392 | int commit_creds(struct cred *new) | |
393 | { | |
394 | struct task_struct *task = current; | |
e0e81739 | 395 | const struct cred *old = task->real_cred; |
d84f4f99 | 396 | |
ae191417 JA |
397 | kdebug("commit_creds(%p{%ld})", new, |
398 | atomic_long_read(&new->usage)); | |
e0e81739 DH |
399 | |
400 | BUG_ON(task->cred != old); | |
f8fa5d76 | 401 | BUG_ON(atomic_long_read(&new->usage) < 1); |
d84f4f99 | 402 | |
3b11a1de DH |
403 | get_cred(new); /* we will require a ref for the subj creds too */ |
404 | ||
d84f4f99 | 405 | /* dumpability changes */ |
078de5f7 EB |
406 | if (!uid_eq(old->euid, new->euid) || |
407 | !gid_eq(old->egid, new->egid) || | |
408 | !uid_eq(old->fsuid, new->fsuid) || | |
409 | !gid_eq(old->fsgid, new->fsgid) || | |
aa6d054e | 410 | !cred_cap_issubset(old, new)) { |
b9456371 DH |
411 | if (task->mm) |
412 | set_dumpable(task->mm, suid_dumpable); | |
d84f4f99 | 413 | task->pdeath_signal = 0; |
f6581f5b JH |
414 | /* |
415 | * If a task drops privileges and becomes nondumpable, | |
416 | * the dumpability change must become visible before | |
417 | * the credential change; otherwise, a __ptrace_may_access() | |
418 | * racing with this change may be able to attach to a task it | |
419 | * shouldn't be able to attach to (as if the task had dropped | |
420 | * privileges without becoming nondumpable). | |
421 | * Pairs with a read barrier in __ptrace_may_access(). | |
422 | */ | |
d84f4f99 | 423 | smp_wmb(); |
f1752eec DH |
424 | } |
425 | ||
d84f4f99 | 426 | /* alter the thread keyring */ |
078de5f7 | 427 | if (!uid_eq(new->fsuid, old->fsuid)) |
2e21865f | 428 | key_fsuid_changed(new); |
078de5f7 | 429 | if (!gid_eq(new->fsgid, old->fsgid)) |
2e21865f | 430 | key_fsgid_changed(new); |
d84f4f99 DH |
431 | |
432 | /* do it | |
72fa5997 VK |
433 | * RLIMIT_NPROC limits on user->processes have already been checked |
434 | * in set_user(). | |
d84f4f99 | 435 | */ |
21d1c5e3 AG |
436 | if (new->user != old->user || new->user_ns != old->user_ns) |
437 | inc_rlimit_ucounts(new->ucounts, UCOUNT_RLIMIT_NPROC, 1); | |
3b11a1de | 438 | rcu_assign_pointer(task->real_cred, new); |
d84f4f99 | 439 | rcu_assign_pointer(task->cred, new); |
629715ad | 440 | if (new->user != old->user || new->user_ns != old->user_ns) |
21d1c5e3 | 441 | dec_rlimit_ucounts(old->ucounts, UCOUNT_RLIMIT_NPROC, 1); |
d84f4f99 | 442 | |
d84f4f99 | 443 | /* send notifications */ |
078de5f7 EB |
444 | if (!uid_eq(new->uid, old->uid) || |
445 | !uid_eq(new->euid, old->euid) || | |
446 | !uid_eq(new->suid, old->suid) || | |
447 | !uid_eq(new->fsuid, old->fsuid)) | |
d84f4f99 | 448 | proc_id_connector(task, PROC_EVENT_UID); |
f1752eec | 449 | |
078de5f7 EB |
450 | if (!gid_eq(new->gid, old->gid) || |
451 | !gid_eq(new->egid, old->egid) || | |
452 | !gid_eq(new->sgid, old->sgid) || | |
453 | !gid_eq(new->fsgid, old->fsgid)) | |
d84f4f99 | 454 | proc_id_connector(task, PROC_EVENT_GID); |
f1752eec | 455 | |
3b11a1de | 456 | /* release the old obj and subj refs both */ |
41e84562 | 457 | put_cred_many(old, 2); |
f1752eec DH |
458 | return 0; |
459 | } | |
d84f4f99 DH |
460 | EXPORT_SYMBOL(commit_creds); |
461 | ||
462 | /** | |
463 | * abort_creds - Discard a set of credentials and unlock the current task | |
464 | * @new: The credentials that were going to be applied | |
465 | * | |
466 | * Discard a set of credentials that were under construction and unlock the | |
467 | * current task. | |
468 | */ | |
469 | void abort_creds(struct cred *new) | |
470 | { | |
ae191417 JA |
471 | kdebug("abort_creds(%p{%ld})", new, |
472 | atomic_long_read(&new->usage)); | |
e0e81739 | 473 | |
f8fa5d76 | 474 | BUG_ON(atomic_long_read(&new->usage) < 1); |
d84f4f99 DH |
475 | put_cred(new); |
476 | } | |
477 | EXPORT_SYMBOL(abort_creds); | |
478 | ||
479 | /** | |
3b11a1de | 480 | * override_creds - Override the current process's subjective credentials |
d84f4f99 DH |
481 | * @new: The credentials to be assigned |
482 | * | |
3b11a1de DH |
483 | * Install a set of temporary override subjective credentials on the current |
484 | * process, returning the old set for later reversion. | |
d84f4f99 DH |
485 | */ |
486 | const struct cred *override_creds(const struct cred *new) | |
487 | { | |
488 | const struct cred *old = current->cred; | |
489 | ||
ae191417 JA |
490 | kdebug("override_creds(%p{%ld})", new, |
491 | atomic_long_read(&new->usage)); | |
d7852fbd LT |
492 | |
493 | /* | |
494 | * NOTE! This uses 'get_new_cred()' rather than 'get_cred()'. | |
495 | * | |
496 | * That means that we do not clear the 'non_rcu' flag, since | |
497 | * we are only installing the cred into the thread-synchronous | |
498 | * '->cred' pointer, not the '->real_cred' pointer that is | |
499 | * visible to other threads under RCU. | |
d7852fbd LT |
500 | */ |
501 | get_new_cred((struct cred *)new); | |
e0e81739 | 502 | rcu_assign_pointer(current->cred, new); |
e0e81739 | 503 | |
ae191417 JA |
504 | kdebug("override_creds() = %p{%ld}", old, |
505 | atomic_long_read(&old->usage)); | |
d84f4f99 DH |
506 | return old; |
507 | } | |
508 | EXPORT_SYMBOL(override_creds); | |
509 | ||
510 | /** | |
3b11a1de | 511 | * revert_creds - Revert a temporary subjective credentials override |
d84f4f99 DH |
512 | * @old: The credentials to be restored |
513 | * | |
3b11a1de DH |
514 | * Revert a temporary set of override subjective credentials to an old set, |
515 | * discarding the override set. | |
d84f4f99 DH |
516 | */ |
517 | void revert_creds(const struct cred *old) | |
518 | { | |
519 | const struct cred *override = current->cred; | |
520 | ||
ae191417 JA |
521 | kdebug("revert_creds(%p{%ld})", old, |
522 | atomic_long_read(&old->usage)); | |
e0e81739 | 523 | |
d84f4f99 DH |
524 | rcu_assign_pointer(current->cred, old); |
525 | put_cred(override); | |
526 | } | |
527 | EXPORT_SYMBOL(revert_creds); | |
528 | ||
d89b22d4 N |
529 | /** |
530 | * cred_fscmp - Compare two credentials with respect to filesystem access. | |
531 | * @a: The first credential | |
532 | * @b: The second credential | |
533 | * | |
534 | * cred_cmp() will return zero if both credentials have the same | |
535 | * fsuid, fsgid, and supplementary groups. That is, if they will both | |
536 | * provide the same access to files based on mode/uid/gid. | |
537 | * If the credentials are different, then either -1 or 1 will | |
538 | * be returned depending on whether @a comes before or after @b | |
539 | * respectively in an arbitrary, but stable, ordering of credentials. | |
540 | * | |
541 | * Return: -1, 0, or 1 depending on comparison | |
542 | */ | |
543 | int cred_fscmp(const struct cred *a, const struct cred *b) | |
544 | { | |
545 | struct group_info *ga, *gb; | |
546 | int g; | |
547 | ||
548 | if (a == b) | |
549 | return 0; | |
550 | if (uid_lt(a->fsuid, b->fsuid)) | |
551 | return -1; | |
552 | if (uid_gt(a->fsuid, b->fsuid)) | |
553 | return 1; | |
554 | ||
555 | if (gid_lt(a->fsgid, b->fsgid)) | |
556 | return -1; | |
557 | if (gid_gt(a->fsgid, b->fsgid)) | |
558 | return 1; | |
559 | ||
560 | ga = a->group_info; | |
561 | gb = b->group_info; | |
562 | if (ga == gb) | |
563 | return 0; | |
564 | if (ga == NULL) | |
565 | return -1; | |
566 | if (gb == NULL) | |
567 | return 1; | |
568 | if (ga->ngroups < gb->ngroups) | |
569 | return -1; | |
570 | if (ga->ngroups > gb->ngroups) | |
571 | return 1; | |
572 | ||
573 | for (g = 0; g < ga->ngroups; g++) { | |
574 | if (gid_lt(ga->gid[g], gb->gid[g])) | |
575 | return -1; | |
576 | if (gid_gt(ga->gid[g], gb->gid[g])) | |
577 | return 1; | |
578 | } | |
579 | return 0; | |
580 | } | |
581 | EXPORT_SYMBOL(cred_fscmp); | |
582 | ||
905ae01c AG |
583 | int set_cred_ucounts(struct cred *new) |
584 | { | |
34dc2fd6 | 585 | struct ucounts *new_ucounts, *old_ucounts = new->ucounts; |
905ae01c | 586 | |
905ae01c AG |
587 | /* |
588 | * This optimization is needed because alloc_ucounts() uses locks | |
589 | * for table lookups. | |
590 | */ | |
a55d0729 | 591 | if (old_ucounts->ns == new->user_ns && uid_eq(old_ucounts->uid, new->uid)) |
905ae01c AG |
592 | return 0; |
593 | ||
a55d0729 | 594 | if (!(new_ucounts = alloc_ucounts(new->user_ns, new->uid))) |
905ae01c AG |
595 | return -EAGAIN; |
596 | ||
34dc2fd6 | 597 | new->ucounts = new_ucounts; |
99c31f9f | 598 | put_ucounts(old_ucounts); |
905ae01c AG |
599 | |
600 | return 0; | |
601 | } | |
602 | ||
d84f4f99 DH |
603 | /* |
604 | * initialise the credentials stuff | |
605 | */ | |
606 | void __init cred_init(void) | |
607 | { | |
608 | /* allocate a slab in which we can store credentials */ | |
5d097056 VD |
609 | cred_jar = kmem_cache_create("cred_jar", sizeof(struct cred), 0, |
610 | SLAB_HWCACHE_ALIGN|SLAB_PANIC|SLAB_ACCOUNT, NULL); | |
d84f4f99 | 611 | } |
3a3b7ce9 DH |
612 | |
613 | /** | |
614 | * prepare_kernel_cred - Prepare a set of credentials for a kernel service | |
615 | * @daemon: A userspace daemon to be used as a reference | |
616 | * | |
617 | * Prepare a set of credentials for a kernel service. This can then be used to | |
618 | * override a task's own credentials so that work can be done on behalf of that | |
619 | * task that requires a different subjective context. | |
620 | * | |
5a17f040 KC |
621 | * @daemon is used to provide a base cred, with the security data derived from |
622 | * that; if this is "&init_task", they'll be set to 0, no groups, full | |
623 | * capabilities, and no keys. | |
3a3b7ce9 DH |
624 | * |
625 | * The caller may change these controls afterwards if desired. | |
626 | * | |
627 | * Returns the new credentials or NULL if out of memory. | |
3a3b7ce9 DH |
628 | */ |
629 | struct cred *prepare_kernel_cred(struct task_struct *daemon) | |
630 | { | |
631 | const struct cred *old; | |
632 | struct cred *new; | |
633 | ||
5a17f040 KC |
634 | if (WARN_ON_ONCE(!daemon)) |
635 | return NULL; | |
636 | ||
3a3b7ce9 DH |
637 | new = kmem_cache_alloc(cred_jar, GFP_KERNEL); |
638 | if (!new) | |
639 | return NULL; | |
640 | ||
e0e81739 DH |
641 | kdebug("prepare_kernel_cred() alloc %p", new); |
642 | ||
5a17f040 | 643 | old = get_task_cred(daemon); |
e0e81739 | 644 | |
43529c97 | 645 | *new = *old; |
d7852fbd | 646 | new->non_rcu = 0; |
f8fa5d76 | 647 | atomic_long_set(&new->usage, 1); |
3a3b7ce9 | 648 | get_uid(new->user); |
0093ccb6 | 649 | get_user_ns(new->user_ns); |
3a3b7ce9 DH |
650 | get_group_info(new->group_info); |
651 | ||
652 | #ifdef CONFIG_KEYS | |
3a50597d DH |
653 | new->session_keyring = NULL; |
654 | new->process_keyring = NULL; | |
3a3b7ce9 | 655 | new->thread_keyring = NULL; |
3a50597d | 656 | new->request_key_auth = NULL; |
3a3b7ce9 DH |
657 | new->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING; |
658 | #endif | |
659 | ||
660 | #ifdef CONFIG_SECURITY | |
661 | new->security = NULL; | |
662 | #endif | |
905ae01c AG |
663 | new->ucounts = get_ucounts(new->ucounts); |
664 | if (!new->ucounts) | |
665 | goto error; | |
666 | ||
bbb6d0f3 AG |
667 | if (security_prepare_creds(new, old, GFP_KERNEL_ACCOUNT) < 0) |
668 | goto error; | |
669 | ||
3a3b7ce9 DH |
670 | put_cred(old); |
671 | return new; | |
672 | ||
673 | error: | |
674 | put_cred(new); | |
0de33681 | 675 | put_cred(old); |
3a3b7ce9 DH |
676 | return NULL; |
677 | } | |
678 | EXPORT_SYMBOL(prepare_kernel_cred); | |
679 | ||
680 | /** | |
681 | * set_security_override - Set the security ID in a set of credentials | |
682 | * @new: The credentials to alter | |
683 | * @secid: The LSM security ID to set | |
684 | * | |
685 | * Set the LSM security ID in a set of credentials so that the subjective | |
686 | * security is overridden when an alternative set of credentials is used. | |
687 | */ | |
688 | int set_security_override(struct cred *new, u32 secid) | |
689 | { | |
690 | return security_kernel_act_as(new, secid); | |
691 | } | |
692 | EXPORT_SYMBOL(set_security_override); | |
693 | ||
694 | /** | |
695 | * set_security_override_from_ctx - Set the security ID in a set of credentials | |
696 | * @new: The credentials to alter | |
697 | * @secctx: The LSM security context to generate the security ID from. | |
698 | * | |
699 | * Set the LSM security ID in a set of credentials so that the subjective | |
700 | * security is overridden when an alternative set of credentials is used. The | |
701 | * security ID is specified in string form as a security context to be | |
702 | * interpreted by the LSM. | |
703 | */ | |
704 | int set_security_override_from_ctx(struct cred *new, const char *secctx) | |
705 | { | |
706 | u32 secid; | |
707 | int ret; | |
708 | ||
709 | ret = security_secctx_to_secid(secctx, strlen(secctx), &secid); | |
710 | if (ret < 0) | |
711 | return ret; | |
712 | ||
713 | return set_security_override(new, secid); | |
714 | } | |
715 | EXPORT_SYMBOL(set_security_override_from_ctx); | |
716 | ||
717 | /** | |
718 | * set_create_files_as - Set the LSM file create context in a set of credentials | |
719 | * @new: The credentials to alter | |
720 | * @inode: The inode to take the context from | |
721 | * | |
722 | * Change the LSM file creation context in a set of credentials to be the same | |
723 | * as the object context of the specified inode, so that the new inodes have | |
724 | * the same MAC context as that inode. | |
725 | */ | |
726 | int set_create_files_as(struct cred *new, struct inode *inode) | |
727 | { | |
5f65e5ca SF |
728 | if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid)) |
729 | return -EINVAL; | |
3a3b7ce9 DH |
730 | new->fsuid = inode->i_uid; |
731 | new->fsgid = inode->i_gid; | |
732 | return security_kernel_create_files_as(new, inode); | |
733 | } | |
734 | EXPORT_SYMBOL(set_create_files_as); |