1/*
2 * Copyright (c) 2015-2020 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/kernel.h>
30#include <sys/commpage.h>
31#include <sys/kernel_types.h>
32#include <sys/persona.h>
33#include <pexpert/pexpert.h>
34#include <machine/cpu_capabilities.h>
35
36#if CONFIG_PERSONAS
37#include <machine/atomic.h>
38
39#include <kern/assert.h>
40#include <kern/simple_lock.h>
41#include <kern/task.h>
42#include <kern/zalloc.h>
43#include <mach/thread_act.h>
44#include <kern/thread.h>
45
46#include <sys/param.h>
47#include <sys/proc_internal.h>
48#include <sys/kauth.h>
49#include <sys/proc_info.h>
50#include <sys/resourcevar.h>
51
52#include <security/audit/audit.h>
53
54#include <os/log.h>
55#define pna_err(fmt, ...) \
56 os_log_error(OS_LOG_DEFAULT, "ERROR: " fmt, ## __VA_ARGS__)
57
58#define MAX_PERSONAS 512
59
60#define TEMP_PERSONA_ID 499
61
62#define FIRST_PERSONA_ID 501
63#define PERSONA_ID_STEP 10
64
65#define PERSONA_ALLOC_TOKEN (0x7a0000ae)
66#define PERSONA_INIT_TOKEN (0x7500005e)
67#define PERSONA_MAGIC (0x0aa55aa0)
68#define persona_initialized(p) ((p)->pna_valid == PERSONA_MAGIC || (p)->pna_valid == PERSONA_INIT_TOKEN)
69#define persona_valid(p) ((p)->pna_valid == PERSONA_MAGIC)
70#define persona_mkinvalid(p) ((p)->pna_valid = ~(PERSONA_MAGIC))
71
72static LIST_HEAD(personalist, persona) all_personas = LIST_HEAD_INITIALIZER(all_personas);
73static uint32_t g_total_personas;
74const uint32_t g_max_personas = MAX_PERSONAS;
75static uid_t g_next_persona_id = FIRST_PERSONA_ID;
76
77LCK_GRP_DECLARE(persona_lck_grp, "personas");
78LCK_MTX_DECLARE(all_personas_lock, &persona_lck_grp);
79
80os_refgrp_decl(static, persona_refgrp, "persona", NULL);
81
82static KALLOC_TYPE_DEFINE(persona_zone, struct persona, KT_DEFAULT);
83
84#define lock_personas() lck_mtx_lock(&all_personas_lock)
85#define unlock_personas() lck_mtx_unlock(&all_personas_lock)
86
87extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher,
88 void *bankledger, void **banktg, uint32_t *persona_id);
89void
90ipc_voucher_release(void *voucher);
91
92struct persona *
93persona_alloc(uid_t id, const char *login, persona_type_t type, char *path, uid_t uid, int *error)
94{
95 struct persona *persona;
96 int err = 0;
97
98 if (!login) {
99 pna_err("Must provide a login name for a new persona!");
100 if (error) {
101 *error = EINVAL;
102 }
103 return NULL;
104 }
105
106 if (type <= PERSONA_INVALID || type > PERSONA_TYPE_MAX) {
107 pna_err("Invalid type: %d", type);
108 if (error) {
109 *error = EINVAL;
110 }
111 return NULL;
112 }
113
114 persona = zalloc_flags(persona_zone, Z_WAITOK | Z_ZERO | Z_NOFAIL);
115
116 if (os_atomic_inc(&g_total_personas, relaxed) > MAX_PERSONAS) {
117 /* too many personas! */
118 pna_err("too many active personas!");
119 err = EBUSY;
120 goto out_error;
121 }
122
123 strncpy(persona->pna_login, login, sizeof(persona->pna_login) - 1);
124 persona_dbg("Starting persona allocation for: '%s'", persona->pna_login);
125
126 LIST_INIT(&persona->pna_members);
127 lck_mtx_init(lck: &persona->pna_lock, grp: &persona_lck_grp, LCK_ATTR_NULL);
128 os_ref_init(&persona->pna_refcount, &persona_refgrp);
129
130 persona->pna_type = type;
131 persona->pna_id = id;
132 persona->pna_valid = PERSONA_ALLOC_TOKEN;
133 persona->pna_path = path;
134 persona->pna_uid = uid;
135
136 /*
137 * NOTE: this persona has not been fully initialized. A subsequent
138 * call to persona_init_begin() followed by persona_init_end() will make
139 * the persona visible to the rest of the system.
140 */
141 if (error) {
142 *error = 0;
143 }
144 return persona;
145
146out_error:
147 os_atomic_dec(&g_total_personas, relaxed);
148 zfree(persona_zone, persona);
149 if (error) {
150 *error = err;
151 }
152 return NULL;
153}
154
155/**
156 * persona_init_begin
157 *
158 * This function begins initialization of a persona. It first acquires the
159 * global persona list lock via lock_personas(), then selects an appropriate
160 * persona ID and sets up the persona's credentials. This function *must* be
161 * followed by a call to persona_init_end() which will mark the persona
162 * structure as valid
163 *
164 * Conditions:
165 * persona has been allocated via persona_alloc()
166 * nothing locked
167 *
168 * Returns:
169 * global persona list is locked (even on error)
170 */
171int
172persona_init_begin(struct persona *persona)
173{
174 struct persona *tmp;
175 int err = 0;
176 uid_t id;
177
178 if (!persona || (persona->pna_valid != PERSONA_ALLOC_TOKEN)) {
179 return EINVAL;
180 }
181
182 id = persona->pna_id;
183
184 lock_personas();
185try_again:
186 if (id == PERSONA_ID_NONE) {
187 persona->pna_id = g_next_persona_id;
188 }
189
190 persona_dbg("Beginning Initialization of %d:%d (%s)...", id, persona->pna_id, persona->pna_login);
191
192 err = 0;
193 LIST_FOREACH(tmp, &all_personas, pna_list) {
194 persona_lock(tmp);
195 if (id == PERSONA_ID_NONE && tmp->pna_id == persona->pna_id) {
196 persona_unlock(tmp);
197 /*
198 * someone else manually claimed this ID, and we're
199 * trying to allocate an ID for the caller: try again
200 */
201 g_next_persona_id += PERSONA_ID_STEP;
202 goto try_again;
203 }
204 if (strncmp(s1: tmp->pna_login, s2: persona->pna_login, n: sizeof(tmp->pna_login)) == 0 ||
205 tmp->pna_id == persona->pna_id) {
206 persona_unlock(tmp);
207 /*
208 * Disallow use of identical login names and re-use
209 * of previously allocated persona IDs
210 */
211 err = EEXIST;
212 break;
213 }
214 persona_unlock(tmp);
215 }
216 if (err) {
217 goto out;
218 }
219
220 /* if the kernel supplied the persona ID, increment for next time */
221 if (id == PERSONA_ID_NONE) {
222 g_next_persona_id += PERSONA_ID_STEP;
223 }
224
225 persona->pna_valid = PERSONA_INIT_TOKEN;
226
227out:
228 if (err != 0) {
229 persona_dbg("ERROR:%d while initializing %d:%d (%s)...", err, id, persona->pna_id, persona->pna_login);
230 /*
231 * mark the persona with an error so that persona_init_end()
232 * will *not* add it to the global list.
233 */
234 persona->pna_id = PERSONA_ID_NONE;
235 }
236
237 /*
238 * leave the global persona list locked: it will be
239 * unlocked in a call to persona_init_end()
240 */
241 return err;
242}
243
244/**
245 * persona_init_end
246 *
247 * This function finalizes the persona initialization by marking it valid and
248 * adding it to the global list of personas. After unlocking the global list,
249 * the persona will be visible to the reset of the system. The function will
250 * only mark the persona valid if the input parameter 'error' is 0.
251 *
252 * Conditions:
253 * persona is initialized via persona_init_begin()
254 * global persona list is locked via lock_personas()
255 *
256 * Returns:
257 * global persona list is unlocked
258 */
259void
260persona_init_end(struct persona *persona, int error)
261{
262 if (persona == NULL) {
263 return;
264 }
265
266 /*
267 * If the pna_valid member is set to the INIT_TOKEN value, then it has
268 * successfully gone through persona_init_begin(), and we can mark it
269 * valid and make it visible to the rest of the system. However, if
270 * there was an error either during initialization or otherwise, we
271 * need to decrement the global count of personas because this one
272 * will be disposed-of by the callers invocation of persona_put().
273 */
274 if (error != 0 || persona->pna_valid == PERSONA_ALLOC_TOKEN) {
275 persona_dbg("ERROR:%d after initialization of %d (%s)", error, persona->pna_id, persona->pna_login);
276 /* remove this persona from the global count */
277 os_atomic_dec(&g_total_personas, relaxed);
278 } else if (error == 0 &&
279 persona->pna_valid == PERSONA_INIT_TOKEN) {
280 persona->pna_valid = PERSONA_MAGIC;
281 LIST_INSERT_HEAD(&all_personas, persona, pna_list);
282 persona_dbg("Initialization of %d (%s) Complete.", persona->pna_id, persona->pna_login);
283 }
284
285 unlock_personas();
286}
287
288static struct persona *
289persona_get_locked(struct persona *persona)
290{
291 os_ref_retain_locked(rc: &persona->pna_refcount);
292 return persona;
293}
294
295struct persona *
296persona_get(struct persona *persona)
297{
298 struct persona *ret;
299 if (!persona) {
300 return NULL;
301 }
302 persona_lock(persona);
303 ret = persona_get_locked(persona);
304 persona_unlock(persona);
305
306 return ret;
307}
308
309struct persona *
310proc_persona_get(proc_t p)
311{
312 proc_lock(p);
313 struct persona *persona = persona_get(persona: p->p_persona);
314 proc_unlock(p);
315
316 return persona;
317}
318
319static void
320persona_put_and_unlock(struct persona *persona)
321{
322 int destroy = 0;
323
324 if (os_ref_release_locked(rc: &persona->pna_refcount) == 0) {
325 destroy = 1;
326 }
327 persona_unlock(persona);
328
329 if (!destroy) {
330 return;
331 }
332
333 persona_dbg("Destroying persona %s", persona_desc(persona, 0));
334
335 /* remove it from the global list and decrement the count */
336 lock_personas();
337 persona_lock(persona);
338 if (persona_valid(persona)) {
339 LIST_REMOVE(persona, pna_list);
340 if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) {
341 panic("persona count underflow!");
342 }
343 persona_mkinvalid(persona);
344 }
345 if (persona->pna_path != NULL) {
346 zfree(ZV_NAMEI, persona->pna_path);
347 }
348 persona_unlock(persona);
349 unlock_personas();
350
351 assert(LIST_EMPTY(&persona->pna_members));
352 memset(s: persona, c: 0, n: sizeof(*persona));
353 zfree(persona_zone, persona);
354}
355
356void
357persona_put(struct persona *persona)
358{
359 if (persona) {
360 persona_lock(persona);
361 persona_put_and_unlock(persona);
362 }
363}
364
365uid_t
366persona_get_id(struct persona *persona)
367{
368 if (persona) {
369 return persona->pna_id;
370 }
371 return PERSONA_ID_NONE;
372}
373
374struct persona *
375persona_lookup(uid_t id)
376{
377 struct persona *persona, *tmp;
378
379 persona = NULL;
380
381 /*
382 * simple, linear lookup for now: there shouldn't be too many
383 * of these in memory at any given time.
384 */
385 lock_personas();
386 LIST_FOREACH(tmp, &all_personas, pna_list) {
387 persona_lock(tmp);
388 if (tmp->pna_id == id && persona_valid(tmp)) {
389 persona = persona_get_locked(persona: tmp);
390 persona_unlock(tmp);
391 break;
392 }
393 persona_unlock(tmp);
394 }
395 unlock_personas();
396
397 return persona;
398}
399
400struct persona *
401persona_lookup_and_invalidate(uid_t id)
402{
403 struct persona *persona, *entry, *tmp;
404
405 persona = NULL;
406
407 lock_personas();
408 LIST_FOREACH_SAFE(entry, &all_personas, pna_list, tmp) {
409 persona_lock(entry);
410 if (entry->pna_id == id) {
411 if (persona_valid(entry)) {
412 persona = persona_get_locked(persona: entry);
413 assert(persona != NULL);
414 LIST_REMOVE(persona, pna_list);
415 if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) {
416 panic("persona ref count underflow!");
417 }
418 persona_mkinvalid(persona);
419 }
420 persona_unlock(entry);
421 break;
422 }
423 persona_unlock(entry);
424 }
425 unlock_personas();
426
427 return persona;
428}
429
430int
431persona_find_by_type(persona_type_t persona_type, struct persona **persona, size_t *plen)
432{
433 return persona_find_all(NULL, PERSONA_ID_NONE, persona_type, persona, plen);
434}
435
436int
437persona_find(const char *login, uid_t uid,
438 struct persona **persona, size_t *plen)
439{
440 return persona_find_all(login, uid, persona_type: PERSONA_INVALID, persona, plen);
441}
442
443int
444persona_find_all(const char *login, uid_t uid, persona_type_t persona_type,
445 struct persona **persona, size_t *plen)
446{
447 struct persona *tmp;
448 int match = 0;
449 size_t found = 0;
450
451 if (login) {
452 match++;
453 }
454 if (uid != PERSONA_ID_NONE) {
455 match++;
456 }
457 if ((persona_type > PERSONA_INVALID) && (persona_type <= PERSONA_TYPE_MAX)) {
458 match++;
459 } else if (persona_type != PERSONA_INVALID) {
460 return EINVAL;
461 }
462
463 if (match == 0) {
464 return EINVAL;
465 }
466
467 persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
468 match, login, uid);
469
470 lock_personas();
471 LIST_FOREACH(tmp, &all_personas, pna_list) {
472 int m = 0;
473 persona_lock(tmp);
474 if (login && strncmp(s1: tmp->pna_login, s2: login, n: sizeof(tmp->pna_login)) == 0) {
475 m++;
476 }
477 if (uid != PERSONA_ID_NONE && uid == tmp->pna_id) {
478 m++;
479 }
480 if (persona_type != PERSONA_INVALID && persona_type == tmp->pna_type) {
481 m++;
482 }
483 if (m == match) {
484 if (persona && *plen > found) {
485 persona[found] = persona_get_locked(persona: tmp);
486 }
487 found++;
488 }
489#ifdef PERSONA_DEBUG
490 if (m > 0) {
491 persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
492 tmp->pna_id, m, match, (int)found, (int)*plen);
493 }
494#endif
495 persona_unlock(tmp);
496 }
497 unlock_personas();
498
499 *plen = found;
500 if (!found) {
501 return ESRCH;
502 }
503 return 0;
504}
505
506struct persona *
507persona_proc_get(pid_t pid)
508{
509 proc_t p = proc_find(pid);
510 if (!p) {
511 return NULL;
512 }
513
514 struct persona *persona = proc_persona_get(p);
515
516 proc_rele(p);
517
518 return persona;
519}
520
521uid_t
522current_persona_get_id(void)
523{
524 uid_t current_persona_id = PERSONA_ID_NONE;
525 ipc_voucher_t voucher;
526
527 thread_get_mach_voucher(thr_act: current_thread(), which: 0, voucher: &voucher);
528 /* returns a voucher ref */
529 if (voucher != IPC_VOUCHER_NULL) {
530 /*
531 * If the voucher doesn't contain a bank attribute, it uses
532 * the default bank task value to determine the persona id
533 * which is the same as the proc's persona id
534 */
535 bank_get_bank_ledger_thread_group_and_persona(voucher, NULL,
536 NULL, persona_id: &current_persona_id);
537 ipc_voucher_release(voucher);
538 } else {
539 /* Fallback - get the proc's persona */
540 current_persona_id = proc_persona_id(current_proc());
541 }
542 return current_persona_id;
543}
544
545struct persona *
546current_persona_get(void)
547{
548 struct persona *persona = NULL;
549 uid_t current_persona_id = PERSONA_ID_NONE;
550
551 current_persona_id = current_persona_get_id();
552 persona = persona_lookup(id: current_persona_id);
553 return persona;
554}
555
556typedef enum e_persona_reset_op {
557 PROC_REMOVE_PERSONA = 1,
558 PROC_RESET_OLD_PERSONA = 2,
559} persona_reset_op_t;
560
561/*
562 * internal cleanup routine for proc_set_persona_internal
563 *
564 */
565static struct persona *
566proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
567 struct persona *old_persona,
568 struct persona *new_persona)
569{
570#if (DEVELOPMENT || DEBUG)
571 persona_lock_assert_held(new_persona);
572#endif
573
574 switch (op) {
575 case PROC_REMOVE_PERSONA:
576 old_persona = p->p_persona;
577 OS_FALLTHROUGH;
578 case PROC_RESET_OLD_PERSONA:
579 break;
580 default:
581 /* invalid arguments */
582 return NULL;
583 }
584
585 /* unlock the new persona (locked on entry) */
586 persona_unlock(new_persona);
587 /* lock the old persona and the process */
588 assert(old_persona != NULL);
589 persona_lock(old_persona);
590 proc_lock(p);
591
592 switch (op) {
593 case PROC_REMOVE_PERSONA:
594 LIST_REMOVE(p, p_persona_list);
595 p->p_persona = NULL;
596 break;
597 case PROC_RESET_OLD_PERSONA:
598 p->p_persona = old_persona;
599 LIST_INSERT_HEAD(&old_persona->pna_members, p, p_persona_list);
600 break;
601 }
602
603 proc_unlock(p);
604 persona_unlock(old_persona);
605
606 /* re-lock the new persona */
607 persona_lock(new_persona);
608 return old_persona;
609}
610
611/*
612 * Assumes persona is locked.
613 * On success, takes a reference to 'persona' and returns the
614 * previous persona the process had adopted. The caller is
615 * responsible to release the reference.
616 */
617static struct persona *
618proc_set_persona_internal(
619 proc_t p,
620 struct persona *persona,
621 kauth_cred_derive_t derive_fn,
622 int *rlim_error)
623{
624 struct persona *old_persona = NULL;
625 uid_t old_uid, new_uid;
626 size_t count;
627 rlim_t nproc = proc_limitgetcur(p, RLIMIT_NPROC);
628
629 /*
630 * This operation must be done under the proc trans lock
631 * by the thread which took the trans lock!
632 */
633 assert(((p->p_lflag & P_LINTRANSIT) == P_LINTRANSIT) &&
634 p->p_transholder == current_thread());
635 assert(persona != NULL);
636
637 /* no work to do if we "re-adopt" the same persona */
638 if (p->p_persona == persona) {
639 return NULL;
640 }
641
642 /*
643 * If p is in a persona, then we need to remove 'p' from the list of
644 * processes in that persona. To do this, we need to drop the lock
645 * held on the incoming (new) persona and lock the old one.
646 */
647 if (p->p_persona) {
648 old_persona = proc_reset_persona_internal(p, op: PROC_REMOVE_PERSONA,
649 NULL, new_persona: persona);
650 }
651
652 /*
653 * Check to see if we will hit a proc rlimit by moving the process
654 * into the persona. If so, we'll bail early before actually moving
655 * the process or changing its credentials.
656 */
657 new_uid = persona->pna_id;
658
659 if (new_uid != 0 &&
660 (rlim_t)chgproccnt(uid: new_uid, diff: 0) > nproc) {
661 pna_err("PID:%d hit proc rlimit in new persona(%d): %s",
662 proc_getpid(p), new_uid, persona_desc(persona, 1));
663
664 *rlim_error = EACCES;
665
666 if (old_persona) {
667 (void)proc_reset_persona_internal(p, op: PROC_RESET_OLD_PERSONA,
668 old_persona, new_persona: persona);
669 }
670
671 return NULL;
672 }
673
674 *rlim_error = 0;
675
676 if (old_persona) {
677 old_uid = old_persona->pna_id;
678 } else {
679 /* proc_ucred_unsafe() is OK because p is a fork/exec/... child */
680 old_uid = kauth_cred_getruid(cred: proc_ucred_unsafe(p));
681 }
682
683 if (derive_fn) {
684 kauth_cred_proc_update(p, action: PROC_SETTOKEN_SETUGID, fn: derive_fn);
685 }
686
687 if (new_uid != old_uid) {
688 count = chgproccnt(uid: old_uid, diff: -1);
689 persona_dbg("Decrement %s:%d proc_count to: %lu",
690 old_persona ? "Persona" : "UID", old_uid, count);
691
692 /*
693 * Increment the proc count on the UID associated with
694 * the new persona. Enforce the resource limit just
695 * as in fork1()
696 */
697 count = chgproccnt(uid: new_uid, diff: 1);
698 persona_dbg("Increment Persona:%d proc_count to: %lu",
699 new_uid, count);
700 }
701
702 OSBitOrAtomic(P_ADOPTPERSONA, &p->p_flag);
703
704 proc_lock(p);
705 p->p_persona = persona_get_locked(persona);
706 LIST_INSERT_HEAD(&persona->pna_members, p, p_persona_list);
707 proc_unlock(p);
708
709 return old_persona;
710}
711
712/* only called during fork or exec: child's ucred is stable */
713int
714persona_proc_adopt(
715 proc_t p,
716 struct persona *persona, /* consumed */
717 kauth_cred_derive_t derive_fn)
718{
719 int error;
720 struct persona *old_persona;
721
722 if (!persona) {
723 return EINVAL;
724 }
725
726 persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
727 persona->pna_id, persona_desc(persona, 0));
728
729 persona_lock(persona);
730 if (!persona_valid(persona)) {
731 persona_dbg("Invalid persona (%s)!", persona_desc(persona, 1));
732 persona_put_and_unlock(persona);
733 return EINVAL;
734 }
735
736 /*
737 * assume the persona: this may drop and re-acquire the persona lock!
738 */
739 error = 0;
740 old_persona = proc_set_persona_internal(p, persona, derive_fn, rlim_error: &error);
741
742 /* Only Multiuser Mode needs to update the session login name to the persona name */
743#if XNU_TARGET_OS_IOS
744 uint32_t multiuser_flags = COMM_PAGE_READ(uint32_t, MULTIUSER_CONFIG);
745 /* set the login name of the session */
746 if (multiuser_flags & kIsMultiUserDevice) {
747 struct pgrp *pg;
748 struct session *sessp;
749
750 if ((pg = proc_pgrp(p, &sessp)) != PGRP_NULL) {
751 session_lock(sessp);
752 bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME);
753 session_unlock(sessp);
754 pgrp_rele(pg);
755 }
756 }
757#endif
758 persona_put_and_unlock(persona);
759
760 /*
761 * Drop the reference to the old persona.
762 */
763 if (old_persona) {
764 persona_put(persona: old_persona);
765 }
766
767 persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
768 return error;
769}
770
771int
772persona_proc_drop(proc_t p)
773{
774 struct persona *persona = NULL;
775
776 persona_dbg("PID:%d, %s -> <none>", proc_getpid(p), persona_desc(p->p_persona, 0));
777
778 /*
779 * There are really no other credentials for us to assume,
780 * so we'll just continue running with the credentials
781 * we got from the persona.
782 */
783
784 /*
785 * the locks must be taken in reverse order here, so
786 * we have to be careful not to cause deadlock
787 */
788try_again:
789 proc_lock(p);
790 if (p->p_persona) {
791 uid_t puid, ruid;
792 if (!persona_try_lock(p->p_persona)) {
793 proc_unlock(p);
794 mutex_pause(0); /* back-off time */
795 goto try_again;
796 }
797 persona = p->p_persona;
798 LIST_REMOVE(p, p_persona_list);
799 p->p_persona = NULL;
800
801 smr_proc_task_enter();
802 ruid = kauth_cred_getruid(cred: proc_ucred_smr(p));
803 smr_proc_task_leave();
804
805 puid = persona->pna_id;
806 proc_unlock(p);
807 (void)chgproccnt(uid: ruid, diff: 1);
808 (void)chgproccnt(uid: puid, diff: -1);
809 } else {
810 proc_unlock(p);
811 }
812
813 /*
814 * if the proc had a persona, then it is still locked here
815 * (preserving proper lock ordering)
816 */
817
818 if (persona) {
819 persona_unlock(persona);
820 persona_put(persona);
821 }
822
823 return 0;
824}
825
826int
827persona_get_type(struct persona *persona)
828{
829 int type;
830
831 if (!persona) {
832 return PERSONA_INVALID;
833 }
834
835 persona_lock(persona);
836 if (!persona_valid(persona)) {
837 persona_unlock(persona);
838 return PERSONA_INVALID;
839 }
840 type = persona->pna_type;
841 persona_unlock(persona);
842
843 return type;
844}
845
846uid_t
847persona_get_uid(struct persona *persona)
848{
849 uid_t uid = KAUTH_UID_NONE;
850
851 if (!persona) {
852 return KAUTH_UID_NONE;
853 }
854
855 persona_lock(persona);
856 if (persona_valid(persona)) {
857 uid = persona->pna_uid;
858 }
859 persona_unlock(persona);
860
861 return uid;
862}
863
864boolean_t
865persona_is_adoption_allowed(struct persona *persona)
866{
867 if (!persona) {
868 return FALSE;
869 }
870 int type = persona->pna_type;
871 return type == PERSONA_SYSTEM || type == PERSONA_SYSTEM_PROXY;
872}
873
874#else /* !CONFIG_PERSONAS */
875
876/*
877 * symbol exports for kext compatibility
878 */
879
880uid_t
881persona_get_id(__unused struct persona *persona)
882{
883 return PERSONA_ID_NONE;
884}
885
886uid_t
887persona_get_uid(__unused struct persona *persona)
888{
889 return KAUTH_UID_NONE;
890}
891
892int
893persona_get_type(__unused struct persona *persona)
894{
895 return PERSONA_INVALID;
896}
897
898struct persona *
899persona_lookup(__unused uid_t id)
900{
901 return NULL;
902}
903
904int
905persona_find(__unused const char *login,
906 __unused uid_t uid,
907 __unused struct persona **persona,
908 __unused size_t *plen)
909{
910 return ENOTSUP;
911}
912
913int
914persona_find_by_type(__unused int persona_type,
915 __unused struct persona **persona,
916 __unused size_t *plen)
917{
918 return ENOTSUP;
919}
920
921struct persona *
922persona_proc_get(__unused pid_t pid)
923{
924 return NULL;
925}
926
927uid_t
928current_persona_get_id(void)
929{
930 return PERSONA_ID_NONE;
931}
932
933struct persona *
934current_persona_get(void)
935{
936 return NULL;
937}
938
939struct persona *
940persona_get(struct persona *persona)
941{
942 return persona;
943}
944
945struct persona *
946proc_persona_get(__unused proc_t p)
947{
948 return NULL;
949}
950
951void
952persona_put(__unused struct persona *persona)
953{
954 return;
955}
956
957boolean_t
958persona_is_adoption_allowed(__unused struct persona *persona)
959{
960 return FALSE;
961}
962#endif
963