]> git.saurik.com Git - apple/xnu.git/blame_incremental - bsd/kern/kern_persona.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / kern / kern_persona.c
... / ...
CommitLineData
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#include <sys/kernel.h>
29#include <sys/kernel_types.h>
30#include <sys/persona.h>
31#include <pexpert/pexpert.h>
32
33#if CONFIG_PERSONAS
34#include <machine/atomic.h>
35
36#include <kern/assert.h>
37#include <kern/simple_lock.h>
38#include <kern/task.h>
39#include <kern/zalloc.h>
40#include <mach/thread_act.h>
41#include <kern/thread.h>
42
43#include <sys/param.h>
44#include <sys/proc_internal.h>
45#include <sys/kauth.h>
46#include <sys/proc_info.h>
47#include <sys/resourcevar.h>
48
49#include <os/log.h>
50#define pna_err(fmt, ...) \
51 os_log_error(OS_LOG_DEFAULT, "ERROR: " fmt, ## __VA_ARGS__)
52
53#define MAX_PERSONAS 512
54
55#define TEMP_PERSONA_ID 499
56
57#define FIRST_PERSONA_ID 501
58#define PERSONA_ID_STEP 10
59
60#define PERSONA_ALLOC_TOKEN (0x7a0000ae)
61#define PERSONA_INIT_TOKEN (0x7500005e)
62#define PERSONA_MAGIC (0x0aa55aa0)
63#define persona_initialized(p) ((p)->pna_valid == PERSONA_MAGIC || (p)->pna_valid == PERSONA_INIT_TOKEN)
64#define persona_valid(p) ((p)->pna_valid == PERSONA_MAGIC)
65#define persona_mkinvalid(p) ((p)->pna_valid = ~(PERSONA_MAGIC))
66
67static LIST_HEAD(personalist, persona) all_personas;
68static uint32_t g_total_personas;
69const uint32_t g_max_personas = MAX_PERSONAS;
70struct persona *system_persona = NULL;
71struct persona *proxy_system_persona = NULL;
72#if !defined(XNU_TARGET_OS_OSX)
73int unique_persona = 1;
74#else
75int unique_persona = 0;
76#endif
77
78static uid_t g_next_persona_id;
79
80LCK_GRP_DECLARE(persona_lck_grp, "personas");
81LCK_MTX_DECLARE(all_personas_lock, &persona_lck_grp);
82
83os_refgrp_decl(static, persona_refgrp, "persona", NULL);
84
85static ZONE_DECLARE(persona_zone, "personas", sizeof(struct persona), ZC_ZFREE_CLEARMEM);
86
87kauth_cred_t g_default_persona_cred;
88extern struct auditinfo_addr * const audit_default_aia_p;
89
90#define lock_personas() lck_mtx_lock(&all_personas_lock)
91#define unlock_personas() lck_mtx_unlock(&all_personas_lock)
92
93extern void mach_kauth_cred_uthread_update(void);
94
95extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher,
96 void *bankledger, void **banktg, uint32_t *persona_id);
97void
98ipc_voucher_release(void *voucher);
99
100void
101personas_bootstrap(void)
102{
103 struct posix_cred pcred;
104 int unique_persona_bootarg;
105
106 persona_dbg("Initializing persona subsystem");
107 LIST_INIT(&all_personas);
108 g_total_personas = 0;
109
110 g_next_persona_id = FIRST_PERSONA_ID;
111
112 /*
113 * setup the default credentials that a persona temporarily
114 * inherits (to work around kauth APIs)
115 */
116 bzero(&pcred, sizeof(pcred));
117 pcred.cr_uid = pcred.cr_ruid = pcred.cr_svuid = TEMP_PERSONA_ID;
118 pcred.cr_rgid = pcred.cr_svgid = TEMP_PERSONA_ID;
119 pcred.cr_groups[0] = TEMP_PERSONA_ID;
120 pcred.cr_ngroups = 1;
121 pcred.cr_flags = CRF_NOMEMBERD;
122 pcred.cr_gmuid = KAUTH_UID_NONE;
123
124 g_default_persona_cred = posix_cred_create(&pcred);
125 if (!g_default_persona_cred) {
126 panic("couldn't create default persona credentials!");
127 }
128#if CONFIG_AUDIT
129 /* posix_cred_create() sets this value to NULL */
130 g_default_persona_cred->cr_audit.as_aia_p = audit_default_aia_p;
131#endif
132 if (PE_parse_boot_argn("unique_persona", &unique_persona_bootarg, sizeof(unique_persona_bootarg))) {
133 unique_persona = !!unique_persona_bootarg;
134 }
135}
136
137struct persona *
138persona_alloc(uid_t id, const char *login, persona_type_t type, char *path, int *error)
139{
140 struct persona *persona;
141 int err = 0;
142
143 if (!login) {
144 pna_err("Must provide a login name for a new persona!");
145 if (error) {
146 *error = EINVAL;
147 }
148 return NULL;
149 }
150
151 if (type <= PERSONA_INVALID || type > PERSONA_TYPE_MAX) {
152 pna_err("Invalid type: %d", type);
153 if (error) {
154 *error = EINVAL;
155 }
156 return NULL;
157 }
158
159 persona = (struct persona *)zalloc(persona_zone);
160 if (!persona) {
161 if (error) {
162 *error = ENOMEM;
163 }
164 return NULL;
165 }
166
167 bzero(persona, sizeof(*persona));
168
169 if (os_atomic_inc(&g_total_personas, relaxed) > MAX_PERSONAS) {
170 /* too many personas! */
171 pna_err("too many active personas!");
172 err = EBUSY;
173 goto out_error;
174 }
175
176 strncpy(persona->pna_login, login, sizeof(persona->pna_login) - 1);
177 persona_dbg("Starting persona allocation for: '%s'", persona->pna_login);
178
179 LIST_INIT(&persona->pna_members);
180 lck_mtx_init(&persona->pna_lock, &persona_lck_grp, LCK_ATTR_NULL);
181 os_ref_init(&persona->pna_refcount, &persona_refgrp);
182
183 /*
184 * Setup initial (temporary) kauth_cred structure
185 * We need to do this here because all kauth calls require
186 * an existing cred structure.
187 */
188 persona->pna_cred = kauth_cred_create(g_default_persona_cred);
189 if (!persona->pna_cred) {
190 pna_err("could not copy initial credentials!");
191 err = EIO;
192 goto out_error;
193 }
194
195 persona->pna_type = type;
196 persona->pna_id = id;
197 persona->pna_valid = PERSONA_ALLOC_TOKEN;
198 persona->pna_path = path;
199
200 /*
201 * NOTE: this persona has not been fully initialized. A subsequent
202 * call to persona_init_begin() followed by persona_init_end() will make
203 * the persona visible to the rest of the system.
204 */
205 if (error) {
206 *error = 0;
207 }
208 return persona;
209
210out_error:
211 os_atomic_dec(&g_total_personas, relaxed);
212 zfree(persona_zone, persona);
213 if (error) {
214 *error = err;
215 }
216 return NULL;
217}
218
219/**
220 * persona_init_begin
221 *
222 * This function begins initialization of a persona. It first acquires the
223 * global persona list lock via lock_personas(), then selects an appropriate
224 * persona ID and sets up the persona's credentials. This function *must* be
225 * followed by a call to persona_init_end() which will mark the persona
226 * structure as valid
227 *
228 * Conditions:
229 * persona has been allocated via persona_alloc()
230 * nothing locked
231 *
232 * Returns:
233 * global persona list is locked (even on error)
234 */
235int
236persona_init_begin(struct persona *persona)
237{
238 struct persona *tmp;
239 int err = 0;
240 kauth_cred_t tmp_cred;
241 gid_t new_group;
242 uid_t id;
243
244 if (!persona || (persona->pna_valid != PERSONA_ALLOC_TOKEN)) {
245 return EINVAL;
246 }
247
248 id = persona->pna_id;
249
250 lock_personas();
251try_again:
252 if (id == PERSONA_ID_NONE) {
253 persona->pna_id = g_next_persona_id;
254 }
255
256 persona_dbg("Beginning Initialization of %d:%d (%s)...", id, persona->pna_id, persona->pna_login);
257
258 err = 0;
259 LIST_FOREACH(tmp, &all_personas, pna_list) {
260 persona_lock(tmp);
261 if (id == PERSONA_ID_NONE && tmp->pna_id == persona->pna_id) {
262 persona_unlock(tmp);
263 /*
264 * someone else manually claimed this ID, and we're
265 * trying to allocate an ID for the caller: try again
266 */
267 g_next_persona_id += PERSONA_ID_STEP;
268 goto try_again;
269 }
270 if (strncmp(tmp->pna_login, persona->pna_login, sizeof(tmp->pna_login)) == 0 ||
271 tmp->pna_id == persona->pna_id) {
272 persona_unlock(tmp);
273 /*
274 * Disallow use of identical login names and re-use
275 * of previously allocated persona IDs
276 */
277 err = EEXIST;
278 break;
279 }
280 persona_unlock(tmp);
281 }
282 if (err) {
283 goto out;
284 }
285
286 /* ensure the cred has proper UID/GID defaults */
287 kauth_cred_ref(persona->pna_cred);
288 tmp_cred = kauth_cred_setuidgid(persona->pna_cred,
289 persona->pna_id,
290 persona->pna_id);
291 kauth_cred_unref(&persona->pna_cred);
292 if (tmp_cred != persona->pna_cred) {
293 persona->pna_cred = tmp_cred;
294 }
295
296 if (!persona->pna_cred) {
297 err = EACCES;
298 goto out;
299 }
300
301 /* it should be a member of exactly 1 group (equal to its UID) */
302 new_group = (gid_t)persona->pna_id;
303
304 kauth_cred_ref(persona->pna_cred);
305 /* opt _out_ of memberd as a default */
306 tmp_cred = kauth_cred_setgroups(persona->pna_cred,
307 &new_group, 1, KAUTH_UID_NONE);
308 kauth_cred_unref(&persona->pna_cred);
309 if (tmp_cred != persona->pna_cred) {
310 persona->pna_cred = tmp_cred;
311 }
312
313 if (!persona->pna_cred) {
314 err = EACCES;
315 goto out;
316 }
317
318 /* if the kernel supplied the persona ID, increment for next time */
319 if (id == PERSONA_ID_NONE) {
320 g_next_persona_id += PERSONA_ID_STEP;
321 }
322
323 persona->pna_valid = PERSONA_INIT_TOKEN;
324
325out:
326 if (err != 0) {
327 persona_dbg("ERROR:%d while initializing %d:%d (%s)...", err, id, persona->pna_id, persona->pna_login);
328 /*
329 * mark the persona with an error so that persona_init_end()
330 * will *not* add it to the global list.
331 */
332 persona->pna_id = PERSONA_ID_NONE;
333 }
334
335 /*
336 * leave the global persona list locked: it will be
337 * unlocked in a call to persona_init_end()
338 */
339 return err;
340}
341
342/**
343 * persona_init_end
344 *
345 * This function finalizes the persona initialization by marking it valid and
346 * adding it to the global list of personas. After unlocking the global list,
347 * the persona will be visible to the reset of the system. The function will
348 * only mark the persona valid if the input parameter 'error' is 0.
349 *
350 * Conditions:
351 * persona is initialized via persona_init_begin()
352 * global persona list is locked via lock_personas()
353 *
354 * Returns:
355 * global persona list is unlocked
356 */
357void
358persona_init_end(struct persona *persona, int error)
359{
360 if (persona == NULL) {
361 return;
362 }
363
364 /*
365 * If the pna_valid member is set to the INIT_TOKEN value, then it has
366 * successfully gone through persona_init_begin(), and we can mark it
367 * valid and make it visible to the rest of the system. However, if
368 * there was an error either during initialization or otherwise, we
369 * need to decrement the global count of personas because this one
370 * will be disposed-of by the callers invocation of persona_put().
371 */
372 if (error != 0 || persona->pna_valid == PERSONA_ALLOC_TOKEN) {
373 persona_dbg("ERROR:%d after initialization of %d (%s)", error, persona->pna_id, persona->pna_login);
374 /* remove this persona from the global count */
375 os_atomic_dec(&g_total_personas, relaxed);
376 } else if (error == 0 &&
377 persona->pna_valid == PERSONA_INIT_TOKEN) {
378 persona->pna_valid = PERSONA_MAGIC;
379 LIST_INSERT_HEAD(&all_personas, persona, pna_list);
380 persona_dbg("Initialization of %d (%s) Complete.", persona->pna_id, persona->pna_login);
381 }
382
383 unlock_personas();
384}
385
386/**
387 * persona_verify_and_set_uniqueness
388 *
389 * This function checks the persona, if the one being spawned is of type
390 * PERSONA_SYSTEM or PERSONA_SYSTEM_PROXY, is unique.
391 *
392 * Conditions:
393 * global persona list is locked on entry and return.
394 *
395 * Returns:
396 * EEXIST: if persona is system/system-proxy and is not unique.
397 * 0: Otherwise.
398 */
399int
400persona_verify_and_set_uniqueness(struct persona *persona)
401{
402 if (persona == NULL) {
403 return EINVAL;
404 }
405
406 if (!unique_persona) {
407 return 0;
408 }
409
410 if (persona->pna_type == PERSONA_SYSTEM) {
411 if (system_persona != NULL) {
412 return EEXIST;
413 }
414 system_persona = persona;
415 return 0;
416 }
417
418 if (persona->pna_type == PERSONA_SYSTEM_PROXY) {
419 if (proxy_system_persona != NULL) {
420 return EEXIST;
421 }
422 proxy_system_persona = persona;
423 return 0;
424 }
425 return 0;
426}
427
428/**
429 * persona_is_unique
430 *
431 * This function checks if the persona spawned is unique.
432 *
433 * Returns:
434 * TRUE: if unique.
435 * FALSE: otherwise.
436 */
437boolean_t
438persona_is_unique(struct persona *persona)
439{
440 if (persona == NULL) {
441 return FALSE;
442 }
443
444 if (!unique_persona) {
445 return FALSE;
446 }
447
448 if (persona->pna_type == PERSONA_SYSTEM ||
449 persona->pna_type == PERSONA_SYSTEM_PROXY) {
450 return TRUE;
451 }
452
453 return FALSE;
454}
455
456static struct persona *
457persona_get_locked(struct persona *persona)
458{
459 os_ref_retain_locked(&persona->pna_refcount);
460 return persona;
461}
462
463struct persona *
464persona_get(struct persona *persona)
465{
466 struct persona *ret;
467 if (!persona) {
468 return NULL;
469 }
470 persona_lock(persona);
471 ret = persona_get_locked(persona);
472 persona_unlock(persona);
473
474 return ret;
475}
476
477void
478persona_put(struct persona *persona)
479{
480 int destroy = 0;
481
482 if (!persona) {
483 return;
484 }
485
486 persona_lock(persona);
487 if (os_ref_release_locked(&persona->pna_refcount) == 0) {
488 destroy = 1;
489 }
490 persona_unlock(persona);
491
492 if (!destroy) {
493 return;
494 }
495
496 persona_dbg("Destroying persona %s", persona_desc(persona, 0));
497
498 /* release our credential reference */
499 if (persona->pna_cred) {
500 kauth_cred_unref(&persona->pna_cred);
501 }
502
503 /* remove it from the global list and decrement the count */
504 lock_personas();
505 persona_lock(persona);
506 if (persona_valid(persona)) {
507 LIST_REMOVE(persona, pna_list);
508 if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) {
509 panic("persona count underflow!\n");
510 }
511 persona_mkinvalid(persona);
512 }
513 if (persona->pna_path != NULL) {
514 zfree(ZV_NAMEI, persona->pna_path);
515 }
516 persona_unlock(persona);
517 unlock_personas();
518
519 assert(LIST_EMPTY(&persona->pna_members));
520 memset(persona, 0, sizeof(*persona));
521 zfree(persona_zone, persona);
522}
523
524uid_t
525persona_get_id(struct persona *persona)
526{
527 if (persona) {
528 return persona->pna_id;
529 }
530 return PERSONA_ID_NONE;
531}
532
533struct persona *
534persona_lookup(uid_t id)
535{
536 struct persona *persona, *tmp;
537
538 persona = NULL;
539
540 /*
541 * simple, linear lookup for now: there shouldn't be too many
542 * of these in memory at any given time.
543 */
544 lock_personas();
545 LIST_FOREACH(tmp, &all_personas, pna_list) {
546 persona_lock(tmp);
547 if (tmp->pna_id == id && persona_valid(tmp)) {
548 persona = persona_get_locked(tmp);
549 persona_unlock(tmp);
550 break;
551 }
552 persona_unlock(tmp);
553 }
554 unlock_personas();
555
556 return persona;
557}
558
559struct persona *
560persona_lookup_and_invalidate(uid_t id)
561{
562 struct persona *persona, *entry, *tmp;
563
564 persona = NULL;
565
566 lock_personas();
567 LIST_FOREACH_SAFE(entry, &all_personas, pna_list, tmp) {
568 persona_lock(entry);
569 if (entry->pna_id == id) {
570 if (persona_valid(entry) && !persona_is_unique(entry)) {
571 persona = persona_get_locked(entry);
572 assert(persona != NULL);
573 LIST_REMOVE(persona, pna_list);
574 if (os_atomic_dec_orig(&g_total_personas, relaxed) == 0) {
575 panic("persona ref count underflow!\n");
576 }
577 persona_mkinvalid(persona);
578 }
579 persona_unlock(entry);
580 break;
581 }
582 persona_unlock(entry);
583 }
584 unlock_personas();
585
586 return persona;
587}
588
589int
590persona_find_by_type(persona_type_t persona_type, struct persona **persona, size_t *plen)
591{
592 return persona_find_all(NULL, PERSONA_ID_NONE, persona_type, persona, plen);
593}
594
595int
596persona_find(const char *login, uid_t uid,
597 struct persona **persona, size_t *plen)
598{
599 return persona_find_all(login, uid, PERSONA_INVALID, persona, plen);
600}
601
602int
603persona_find_all(const char *login, uid_t uid, persona_type_t persona_type,
604 struct persona **persona, size_t *plen)
605{
606 struct persona *tmp;
607 int match = 0;
608 size_t found = 0;
609
610 if (login) {
611 match++;
612 }
613 if (uid != PERSONA_ID_NONE) {
614 match++;
615 }
616 if ((persona_type > PERSONA_INVALID) && (persona_type <= PERSONA_TYPE_MAX)) {
617 match++;
618 } else if (persona_type != PERSONA_INVALID) {
619 return EINVAL;
620 }
621
622 if (match == 0) {
623 return EINVAL;
624 }
625
626 persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
627 match, login, uid);
628
629 lock_personas();
630 LIST_FOREACH(tmp, &all_personas, pna_list) {
631 int m = 0;
632 persona_lock(tmp);
633 if (login && strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0) {
634 m++;
635 }
636 if (uid != PERSONA_ID_NONE && uid == tmp->pna_id) {
637 m++;
638 }
639 if (persona_type != PERSONA_INVALID && persona_type == tmp->pna_type) {
640 m++;
641 }
642 if (m == match) {
643 if (persona && *plen > found) {
644 persona[found] = persona_get_locked(tmp);
645 }
646 found++;
647 }
648#ifdef PERSONA_DEBUG
649 if (m > 0) {
650 persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
651 tmp->pna_id, m, match, (int)found, (int)*plen);
652 }
653#endif
654 persona_unlock(tmp);
655 }
656 unlock_personas();
657
658 *plen = found;
659 if (!found) {
660 return ESRCH;
661 }
662 return 0;
663}
664
665struct persona *
666persona_proc_get(pid_t pid)
667{
668 struct persona *persona;
669 proc_t p = proc_find(pid);
670
671 if (!p) {
672 return NULL;
673 }
674
675 proc_lock(p);
676 persona = persona_get(p->p_persona);
677 proc_unlock(p);
678
679 proc_rele(p);
680
681 return persona;
682}
683
684struct persona *
685current_persona_get(void)
686{
687 struct persona *persona = NULL;
688 uid_t current_persona_id = PERSONA_ID_NONE;
689 ipc_voucher_t voucher;
690
691 thread_get_mach_voucher(current_thread(), 0, &voucher);
692 /* returns a voucher ref */
693 if (voucher != IPC_VOUCHER_NULL) {
694 /*
695 * If the voucher doesn't contain a bank attribute, it uses
696 * the default bank task value to determine the persona id
697 * which is the same as the proc's persona id
698 */
699 bank_get_bank_ledger_thread_group_and_persona(voucher, NULL,
700 NULL, &current_persona_id);
701 ipc_voucher_release(voucher);
702 persona = persona_lookup(current_persona_id);
703 } else {
704 /* Fallback - get the proc's persona */
705 proc_t p = current_proc();
706 proc_lock(p);
707 persona = persona_get(p->p_persona);
708 proc_unlock(p);
709 }
710 return persona;
711}
712
713/**
714 * inherit a persona from parent to child
715 */
716int
717persona_proc_inherit(proc_t child, proc_t parent)
718{
719 if (child->p_persona != NULL) {
720 persona_dbg("proc_inherit: child already in persona: %s",
721 persona_desc(child->p_persona, 0));
722 return -1;
723 }
724
725 /* no persona to inherit */
726 if (parent->p_persona == NULL) {
727 return 0;
728 }
729
730 return persona_proc_adopt(child, parent->p_persona, parent->p_ucred);
731}
732
733int
734persona_proc_adopt_id(proc_t p, uid_t id, kauth_cred_t auth_override)
735{
736 int ret;
737 struct persona *persona;
738
739 persona = persona_lookup(id);
740 if (!persona) {
741 return ESRCH;
742 }
743
744 ret = persona_proc_adopt(p, persona, auth_override);
745
746 /* put the reference from the lookup() */
747 persona_put(persona);
748
749 return ret;
750}
751
752
753typedef enum e_persona_reset_op {
754 PROC_REMOVE_PERSONA = 1,
755 PROC_RESET_OLD_PERSONA = 2,
756} persona_reset_op_t;
757
758/*
759 * internal cleanup routine for proc_set_cred_internal
760 *
761 */
762static struct persona *
763proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
764 struct persona *old_persona,
765 struct persona *new_persona)
766{
767#if (DEVELOPMENT || DEBUG)
768 persona_lock_assert_held(new_persona);
769#endif
770
771 switch (op) {
772 case PROC_REMOVE_PERSONA:
773 old_persona = p->p_persona;
774 OS_FALLTHROUGH;
775 case PROC_RESET_OLD_PERSONA:
776 break;
777 default:
778 /* invalid arguments */
779 return NULL;
780 }
781
782 /* unlock the new persona (locked on entry) */
783 persona_unlock(new_persona);
784 /* lock the old persona and the process */
785 assert(old_persona != NULL);
786 persona_lock(old_persona);
787 proc_lock(p);
788
789 switch (op) {
790 case PROC_REMOVE_PERSONA:
791 LIST_REMOVE(p, p_persona_list);
792 p->p_persona = NULL;
793 break;
794 case PROC_RESET_OLD_PERSONA:
795 p->p_persona = old_persona;
796 LIST_INSERT_HEAD(&old_persona->pna_members, p, p_persona_list);
797 break;
798 }
799
800 proc_unlock(p);
801 persona_unlock(old_persona);
802
803 /* re-lock the new persona */
804 persona_lock(new_persona);
805 return old_persona;
806}
807
808/*
809 * Assumes persona is locked.
810 * On success, takes a reference to 'persona' and returns the
811 * previous persona the process had adopted. The caller is
812 * responsible to release the reference.
813 */
814static struct persona *
815proc_set_cred_internal(proc_t p, struct persona *persona,
816 kauth_cred_t auth_override, int *rlim_error)
817{
818 struct persona *old_persona = NULL;
819 kauth_cred_t my_cred, my_new_cred;
820 uid_t old_uid, new_uid;
821 size_t count;
822 rlim_t nproc = proc_limitgetcur(p, RLIMIT_NPROC, TRUE);
823
824 /*
825 * This operation must be done under the proc trans lock
826 * by the thread which took the trans lock!
827 */
828 assert(((p->p_lflag & P_LINTRANSIT) == P_LINTRANSIT) &&
829 p->p_transholder == current_thread());
830 assert(persona != NULL);
831
832 /* no work to do if we "re-adopt" the same persona */
833 if (p->p_persona == persona) {
834 return NULL;
835 }
836
837 /*
838 * If p is in a persona, then we need to remove 'p' from the list of
839 * processes in that persona. To do this, we need to drop the lock
840 * held on the incoming (new) persona and lock the old one.
841 */
842 if (p->p_persona) {
843 old_persona = proc_reset_persona_internal(p, PROC_REMOVE_PERSONA,
844 NULL, persona);
845 }
846
847 if (auth_override) {
848 my_new_cred = auth_override;
849 } else {
850 my_new_cred = persona->pna_cred;
851 }
852
853 if (!my_new_cred) {
854 panic("NULL credentials (persona:%p)", persona);
855 }
856
857 *rlim_error = 0;
858
859 kauth_cred_ref(my_new_cred);
860
861 new_uid = persona->pna_id;
862
863 /*
864 * Check to see if we will hit a proc rlimit by moving the process
865 * into the persona. If so, we'll bail early before actually moving
866 * the process or changing its credentials.
867 */
868 if (new_uid != 0 &&
869 (rlim_t)chgproccnt(new_uid, 0) > nproc) {
870 pna_err("PID:%d hit proc rlimit in new persona(%d): %s",
871 p->p_pid, new_uid, persona_desc(persona, 1));
872 *rlim_error = EACCES;
873 if (old_persona) {
874 (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA,
875 old_persona, persona);
876 }
877 kauth_cred_unref(&my_new_cred);
878 return NULL;
879 }
880
881 /*
882 * Set the new credentials on the proc
883 */
884set_proc_cred:
885 my_cred = kauth_cred_proc_ref(p);
886 persona_dbg("proc_adopt PID:%d, %s -> %s",
887 p->p_pid,
888 persona_desc(old_persona, 1),
889 persona_desc(persona, 1));
890
891 old_uid = kauth_cred_getruid(my_cred);
892
893 if (my_cred != my_new_cred) {
894 kauth_cred_t old_cred = my_cred;
895
896 proc_ucred_lock(p);
897 /*
898 * We need to protect against a race where another thread
899 * also changed the credential after we took our
900 * reference. If p_ucred has changed then we should
901 * restart this again with the new cred.
902 */
903 if (p->p_ucred != my_cred) {
904 proc_ucred_unlock(p);
905 kauth_cred_unref(&my_cred);
906 /* try again */
907 goto set_proc_cred;
908 }
909
910 /* update the credential and take a ref for the proc */
911 kauth_cred_ref(my_new_cred);
912 p->p_ucred = my_new_cred;
913
914 /* update cred on proc (and current thread) */
915 mach_kauth_cred_uthread_update();
916 PROC_UPDATE_CREDS_ONPROC(p);
917
918 /* drop the proc's old ref on the credential */
919 kauth_cred_unref(&old_cred);
920 proc_ucred_unlock(p);
921 }
922
923 /* drop this function's reference to the old cred */
924 kauth_cred_unref(&my_cred);
925
926 /*
927 * Update the proc count.
928 * If the UIDs are the same, then there is no work to do.
929 */
930 if (old_persona) {
931 old_uid = old_persona->pna_id;
932 }
933
934 if (new_uid != old_uid) {
935 count = chgproccnt(old_uid, -1);
936 persona_dbg("Decrement %s:%d proc_count to: %lu",
937 old_persona ? "Persona" : "UID", old_uid, count);
938
939 /*
940 * Increment the proc count on the UID associated with
941 * the new persona. Enforce the resource limit just
942 * as in fork1()
943 */
944 count = chgproccnt(new_uid, 1);
945 persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %lu",
946 new_uid, kauth_cred_getuid(my_new_cred), count);
947 }
948
949 OSBitOrAtomic(P_ADOPTPERSONA, &p->p_flag);
950
951 proc_lock(p);
952 p->p_persona = persona_get_locked(persona);
953 LIST_INSERT_HEAD(&persona->pna_members, p, p_persona_list);
954 proc_unlock(p);
955
956 kauth_cred_unref(&my_new_cred);
957
958 return old_persona;
959}
960
961int
962persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override)
963{
964 int error;
965 struct persona *old_persona;
966
967 if (!persona) {
968 return EINVAL;
969 }
970
971 persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
972 persona->pna_id, persona_desc(persona, 0));
973
974 persona_lock(persona);
975 if (!persona->pna_cred || !persona_valid(persona)) {
976 persona_dbg("Invalid persona (%s): NULL credentials!", persona_desc(persona, 1));
977 persona_unlock(persona);
978 return EINVAL;
979 }
980
981 /* the persona credentials can no longer be adjusted */
982 persona->pna_cred_locked = 1;
983
984 /*
985 * assume the persona: this may drop and re-acquire the persona lock!
986 */
987 error = 0;
988 old_persona = proc_set_cred_internal(p, persona, auth_override, &error);
989
990 /* join the process group associated with the persona */
991 if (persona->pna_pgid) {
992 uid_t uid = kauth_cred_getuid(persona->pna_cred);
993 persona_dbg(" PID:%d, pgid:%d%s",
994 p->p_pid, persona->pna_pgid,
995 persona->pna_pgid == uid ? ", new_session" : ".");
996 enterpgrp(p, persona->pna_pgid, persona->pna_pgid == uid);
997 }
998
999 /* Only Multiuser Mode needs to update the session login name to the persona name */
1000#if (TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
1001 uint32_t multiuser_flags = COMM_PAGE_READ(uint32_t, MULTIUSER_CONFIG);
1002 /* set the login name of the session */
1003 if (multiuser_flags) {
1004 struct session * sessp = proc_session(p);
1005 if (sessp != SESSION_NULL) {
1006 session_lock(sessp);
1007 bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME);
1008 session_unlock(sessp);
1009 session_rele(sessp);
1010 }
1011 }
1012#endif
1013 persona_unlock(persona);
1014
1015 set_security_token(p);
1016
1017 /*
1018 * Drop the reference to the old persona.
1019 */
1020 if (old_persona) {
1021 persona_put(old_persona);
1022 }
1023
1024 persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
1025 return error;
1026}
1027
1028int
1029persona_proc_drop(proc_t p)
1030{
1031 struct persona *persona = NULL;
1032
1033 persona_dbg("PID:%d, %s -> <none>", p->p_pid, persona_desc(p->p_persona, 0));
1034
1035 /*
1036 * There are really no other credentials for us to assume,
1037 * so we'll just continue running with the credentials
1038 * we got from the persona.
1039 */
1040
1041 /*
1042 * the locks must be taken in reverse order here, so
1043 * we have to be careful not to cause deadlock
1044 */
1045try_again:
1046 proc_lock(p);
1047 if (p->p_persona) {
1048 uid_t puid, ruid;
1049 if (!persona_try_lock(p->p_persona)) {
1050 proc_unlock(p);
1051 mutex_pause(0); /* back-off time */
1052 goto try_again;
1053 }
1054 persona = p->p_persona;
1055 LIST_REMOVE(p, p_persona_list);
1056 p->p_persona = NULL;
1057
1058 ruid = kauth_cred_getruid(p->p_ucred);
1059 puid = kauth_cred_getuid(persona->pna_cred);
1060 proc_unlock(p);
1061 (void)chgproccnt(ruid, 1);
1062 (void)chgproccnt(puid, -1);
1063 } else {
1064 proc_unlock(p);
1065 }
1066
1067 /*
1068 * if the proc had a persona, then it is still locked here
1069 * (preserving proper lock ordering)
1070 */
1071
1072 if (persona) {
1073 persona_unlock(persona);
1074 persona_put(persona);
1075 }
1076
1077 return 0;
1078}
1079
1080int
1081persona_get_type(struct persona *persona)
1082{
1083 int type;
1084
1085 if (!persona) {
1086 return PERSONA_INVALID;
1087 }
1088
1089 persona_lock(persona);
1090 if (!persona_valid(persona)) {
1091 persona_unlock(persona);
1092 return PERSONA_INVALID;
1093 }
1094 type = persona->pna_type;
1095 persona_unlock(persona);
1096
1097 return type;
1098}
1099
1100int
1101persona_set_cred(struct persona *persona, kauth_cred_t cred)
1102{
1103 int ret = 0;
1104 kauth_cred_t my_cred;
1105 if (!persona || !cred) {
1106 return EINVAL;
1107 }
1108
1109 persona_lock(persona);
1110 if (!persona_initialized(persona)) {
1111 ret = EINVAL;
1112 goto out_unlock;
1113 }
1114 if (persona->pna_cred_locked) {
1115 ret = EPERM;
1116 goto out_unlock;
1117 }
1118
1119 /* create a new cred from the passed-in cred */
1120 my_cred = kauth_cred_create(cred);
1121
1122 /* ensure that the UID matches the persona ID */
1123 my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
1124 persona->pna_id, persona->pna_id,
1125 KAUTH_UID_NONE);
1126
1127 /* TODO: clear the saved GID?! */
1128
1129 /* replace the persona's cred with the new one */
1130 if (persona->pna_cred) {
1131 kauth_cred_unref(&persona->pna_cred);
1132 }
1133 persona->pna_cred = my_cred;
1134
1135out_unlock:
1136 persona_unlock(persona);
1137 return ret;
1138}
1139
1140int
1141persona_set_cred_from_proc(struct persona *persona, proc_t proc)
1142{
1143 int ret = 0;
1144 kauth_cred_t parent_cred, my_cred;
1145 if (!persona || !proc) {
1146 return EINVAL;
1147 }
1148
1149 persona_lock(persona);
1150 if (!persona_initialized(persona)) {
1151 ret = EINVAL;
1152 goto out_unlock;
1153 }
1154 if (persona->pna_cred_locked) {
1155 ret = EPERM;
1156 goto out_unlock;
1157 }
1158
1159 parent_cred = kauth_cred_proc_ref(proc);
1160
1161 /* TODO: clear the saved UID/GID! */
1162
1163 /* create a new cred from the proc's cred */
1164 my_cred = kauth_cred_create(parent_cred);
1165
1166 /* ensure that the UID matches the persona ID */
1167 my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
1168 persona->pna_id, persona->pna_id,
1169 KAUTH_UID_NONE);
1170
1171 /* replace the persona's cred with the new one */
1172 if (persona->pna_cred) {
1173 kauth_cred_unref(&persona->pna_cred);
1174 }
1175 persona->pna_cred = my_cred;
1176
1177 kauth_cred_unref(&parent_cred);
1178
1179out_unlock:
1180 persona_unlock(persona);
1181 return ret;
1182}
1183
1184kauth_cred_t
1185persona_get_cred(struct persona *persona)
1186{
1187 kauth_cred_t cred = NULL;
1188
1189 if (!persona) {
1190 return NULL;
1191 }
1192
1193 persona_lock(persona);
1194 if (!persona_valid(persona)) {
1195 goto out_unlock;
1196 }
1197
1198 if (persona->pna_cred) {
1199 kauth_cred_ref(persona->pna_cred);
1200 cred = persona->pna_cred;
1201 }
1202
1203out_unlock:
1204 persona_unlock(persona);
1205
1206 return cred;
1207}
1208
1209uid_t
1210persona_get_uid(struct persona *persona)
1211{
1212 uid_t uid = UID_MAX;
1213
1214 if (!persona || !persona->pna_cred) {
1215 return UID_MAX;
1216 }
1217
1218 persona_lock(persona);
1219 if (persona_valid(persona)) {
1220 uid = kauth_cred_getuid(persona->pna_cred);
1221 assert(uid == persona->pna_id);
1222 }
1223 persona_unlock(persona);
1224
1225 return uid;
1226}
1227
1228int
1229persona_set_gid(struct persona *persona, gid_t gid)
1230{
1231 int ret = 0;
1232 kauth_cred_t my_cred, new_cred;
1233
1234 if (!persona || !persona->pna_cred) {
1235 return EINVAL;
1236 }
1237
1238 persona_lock(persona);
1239 if (!persona_initialized(persona)) {
1240 ret = EINVAL;
1241 goto out_unlock;
1242 }
1243 if (persona->pna_cred_locked) {
1244 ret = EPERM;
1245 goto out_unlock;
1246 }
1247
1248 my_cred = persona->pna_cred;
1249 kauth_cred_ref(my_cred);
1250 new_cred = kauth_cred_setresgid(my_cred, gid, gid, gid);
1251 if (new_cred != my_cred) {
1252 persona->pna_cred = new_cred;
1253 }
1254 kauth_cred_unref(&my_cred);
1255
1256out_unlock:
1257 persona_unlock(persona);
1258 return ret;
1259}
1260
1261gid_t
1262persona_get_gid(struct persona *persona)
1263{
1264 gid_t gid = GID_MAX;
1265
1266 if (!persona || !persona->pna_cred) {
1267 return GID_MAX;
1268 }
1269
1270 persona_lock(persona);
1271 if (persona_valid(persona)) {
1272 gid = kauth_cred_getgid(persona->pna_cred);
1273 }
1274 persona_unlock(persona);
1275
1276 return gid;
1277}
1278
1279int
1280persona_set_groups(struct persona *persona, gid_t *groups, size_t ngroups, uid_t gmuid)
1281{
1282 int ret = 0;
1283 kauth_cred_t my_cred, new_cred;
1284
1285 if (!persona || !persona->pna_cred) {
1286 return EINVAL;
1287 }
1288 if (ngroups > NGROUPS_MAX) {
1289 return EINVAL;
1290 }
1291
1292 persona_lock(persona);
1293 if (!persona_initialized(persona)) {
1294 ret = EINVAL;
1295 goto out_unlock;
1296 }
1297 if (persona->pna_cred_locked) {
1298 ret = EPERM;
1299 goto out_unlock;
1300 }
1301
1302 my_cred = persona->pna_cred;
1303 kauth_cred_ref(my_cred);
1304 new_cred = kauth_cred_setgroups(my_cred, groups, ngroups, gmuid);
1305 if (new_cred != my_cred) {
1306 persona->pna_cred = new_cred;
1307 }
1308 kauth_cred_unref(&my_cred);
1309
1310out_unlock:
1311 persona_unlock(persona);
1312 return ret;
1313}
1314
1315int
1316persona_get_groups(struct persona *persona, size_t *ngroups, gid_t *groups, size_t groups_sz)
1317{
1318 int ret = EINVAL;
1319 if (!persona || !persona->pna_cred || !groups || !ngroups || groups_sz > NGROUPS) {
1320 return EINVAL;
1321 }
1322
1323 *ngroups = groups_sz;
1324
1325 persona_lock(persona);
1326 if (persona_valid(persona)) {
1327 size_t kauth_ngroups = groups_sz;
1328 kauth_cred_getgroups(persona->pna_cred, groups, &kauth_ngroups);
1329 *ngroups = (uint32_t)kauth_ngroups;
1330 ret = 0;
1331 }
1332 persona_unlock(persona);
1333
1334 return ret;
1335}
1336
1337uid_t
1338persona_get_gmuid(struct persona *persona)
1339{
1340 uid_t gmuid = KAUTH_UID_NONE;
1341
1342 if (!persona || !persona->pna_cred) {
1343 return gmuid;
1344 }
1345
1346 persona_lock(persona);
1347 if (!persona_valid(persona)) {
1348 goto out_unlock;
1349 }
1350
1351 posix_cred_t pcred = posix_cred_get(persona->pna_cred);
1352 gmuid = pcred->cr_gmuid;
1353
1354out_unlock:
1355 persona_unlock(persona);
1356 return gmuid;
1357}
1358
1359int
1360persona_get_login(struct persona *persona, char login[MAXLOGNAME + 1])
1361{
1362 int ret = EINVAL;
1363 if (!persona || !persona->pna_cred) {
1364 return EINVAL;
1365 }
1366
1367 persona_lock(persona);
1368 if (!persona_valid(persona)) {
1369 goto out_unlock;
1370 }
1371
1372 strlcpy(login, persona->pna_login, MAXLOGNAME);
1373 ret = 0;
1374
1375out_unlock:
1376 persona_unlock(persona);
1377 return ret;
1378}
1379
1380#else /* !CONFIG_PERSONAS */
1381
1382/*
1383 * symbol exports for kext compatibility
1384 */
1385
1386struct persona *system_persona = NULL;
1387struct persona *proxy_system_persona = NULL;
1388int unique_persona = 0;
1389
1390uid_t
1391persona_get_id(__unused struct persona *persona)
1392{
1393 return PERSONA_ID_NONE;
1394}
1395
1396int
1397persona_get_type(__unused struct persona *persona)
1398{
1399 return PERSONA_INVALID;
1400}
1401
1402kauth_cred_t
1403persona_get_cred(__unused struct persona *persona)
1404{
1405 return NULL;
1406}
1407
1408struct persona *
1409persona_lookup(__unused uid_t id)
1410{
1411 return NULL;
1412}
1413
1414int
1415persona_find(__unused const char *login,
1416 __unused uid_t uid,
1417 __unused struct persona **persona,
1418 __unused size_t *plen)
1419{
1420 return ENOTSUP;
1421}
1422
1423int
1424persona_find_by_type(__unused int persona_type,
1425 __unused struct persona **persona,
1426 __unused size_t *plen)
1427{
1428 return ENOTSUP;
1429}
1430
1431struct persona *
1432persona_proc_get(__unused pid_t pid)
1433{
1434 return NULL;
1435}
1436
1437struct persona *
1438current_persona_get(void)
1439{
1440 return NULL;
1441}
1442
1443struct persona *
1444persona_get(struct persona *persona)
1445{
1446 return persona;
1447}
1448
1449void
1450persona_put(__unused struct persona *persona)
1451{
1452 return;
1453}
1454#endif