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