xref: /xnu-10002.81.5/bsd/kern/kern_persona.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
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 = LIST_HEAD_INITIALIZER(all_personas);
73 static uint32_t g_total_personas;
74 const uint32_t g_max_personas = MAX_PERSONAS;
75 static uid_t g_next_persona_id = FIRST_PERSONA_ID;
76 
77 LCK_GRP_DECLARE(persona_lck_grp, "personas");
78 LCK_MTX_DECLARE(all_personas_lock, &persona_lck_grp);
79 
80 os_refgrp_decl(static, persona_refgrp, "persona", NULL);
81 
82 static 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 
87 extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher,
88     void *bankledger, void **banktg, uint32_t *persona_id);
89 void
90 ipc_voucher_release(void *voucher);
91 
92 struct persona *
persona_alloc(uid_t id,const char * login,persona_type_t type,char * path,uid_t uid,int * error)93 persona_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(&persona->pna_lock, &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 
146 out_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  */
171 int
persona_init_begin(struct persona * persona)172 persona_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();
185 try_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(tmp->pna_login, persona->pna_login, 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 
227 out:
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  */
259 void
persona_init_end(struct persona * persona,int error)260 persona_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 
288 static struct persona *
persona_get_locked(struct persona * persona)289 persona_get_locked(struct persona *persona)
290 {
291 	os_ref_retain_locked(&persona->pna_refcount);
292 	return persona;
293 }
294 
295 struct persona *
persona_get(struct persona * persona)296 persona_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 
309 struct persona *
proc_persona_get(proc_t p)310 proc_persona_get(proc_t p)
311 {
312 	proc_lock(p);
313 	struct persona *persona = persona_get(p->p_persona);
314 	proc_unlock(p);
315 
316 	return persona;
317 }
318 
319 static void
persona_put_and_unlock(struct persona * persona)320 persona_put_and_unlock(struct persona *persona)
321 {
322 	int destroy = 0;
323 
324 	if (os_ref_release_locked(&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(persona, 0, sizeof(*persona));
353 	zfree(persona_zone, persona);
354 }
355 
356 void
persona_put(struct persona * persona)357 persona_put(struct persona *persona)
358 {
359 	if (persona) {
360 		persona_lock(persona);
361 		persona_put_and_unlock(persona);
362 	}
363 }
364 
365 uid_t
persona_get_id(struct persona * persona)366 persona_get_id(struct persona *persona)
367 {
368 	if (persona) {
369 		return persona->pna_id;
370 	}
371 	return PERSONA_ID_NONE;
372 }
373 
374 struct persona *
persona_lookup(uid_t id)375 persona_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(tmp);
390 			persona_unlock(tmp);
391 			break;
392 		}
393 		persona_unlock(tmp);
394 	}
395 	unlock_personas();
396 
397 	return persona;
398 }
399 
400 struct persona *
persona_lookup_and_invalidate(uid_t id)401 persona_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(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 
430 int
persona_find_by_type(persona_type_t persona_type,struct persona ** persona,size_t * plen)431 persona_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 
436 int
persona_find(const char * login,uid_t uid,struct persona ** persona,size_t * plen)437 persona_find(const char *login, uid_t uid,
438     struct persona **persona, size_t *plen)
439 {
440 	return persona_find_all(login, uid, PERSONA_INVALID, persona, plen);
441 }
442 
443 int
persona_find_all(const char * login,uid_t uid,persona_type_t persona_type,struct persona ** persona,size_t * plen)444 persona_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(tmp->pna_login, login, 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(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 
506 struct persona *
persona_proc_get(pid_t pid)507 persona_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 
521 uid_t
current_persona_get_id(void)522 current_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(current_thread(), 0, &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, &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 
545 struct persona *
current_persona_get(void)546 current_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(current_persona_id);
553 	return persona;
554 }
555 
556 typedef 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  */
565 static struct persona *
proc_reset_persona_internal(proc_t p,persona_reset_op_t op,struct persona * old_persona,struct persona * new_persona)566 proc_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  */
617 static struct persona *
proc_set_persona_internal(proc_t p,struct persona * persona,kauth_cred_derive_t derive_fn,int * rlim_error)618 proc_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, PROC_REMOVE_PERSONA,
649 		    NULL, 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(new_uid, 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, PROC_RESET_OLD_PERSONA,
668 			    old_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(proc_ucred_unsafe(p));
681 	}
682 
683 	if (derive_fn) {
684 		kauth_cred_proc_update(p, PROC_SETTOKEN_SETUGID, derive_fn);
685 	}
686 
687 	if (new_uid != old_uid) {
688 		count = chgproccnt(old_uid, -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(new_uid, 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 */
713 int
persona_proc_adopt(proc_t p,struct persona * persona,kauth_cred_derive_t derive_fn)714 persona_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, &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(old_persona);
765 	}
766 
767 	persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
768 	return error;
769 }
770 
771 int
persona_proc_drop(proc_t p)772 persona_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 	 */
788 try_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(proc_ucred_smr(p));
803 		smr_proc_task_leave();
804 
805 		puid = persona->pna_id;
806 		proc_unlock(p);
807 		(void)chgproccnt(ruid, 1);
808 		(void)chgproccnt(puid, -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 
826 int
persona_get_type(struct persona * persona)827 persona_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 
846 uid_t
persona_get_uid(struct persona * persona)847 persona_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 
864 boolean_t
persona_is_adoption_allowed(struct persona * persona)865 persona_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 
880 uid_t
persona_get_id(__unused struct persona * persona)881 persona_get_id(__unused struct persona *persona)
882 {
883 	return PERSONA_ID_NONE;
884 }
885 
886 uid_t
persona_get_uid(__unused struct persona * persona)887 persona_get_uid(__unused struct persona *persona)
888 {
889 	return KAUTH_UID_NONE;
890 }
891 
892 int
persona_get_type(__unused struct persona * persona)893 persona_get_type(__unused struct persona *persona)
894 {
895 	return PERSONA_INVALID;
896 }
897 
898 struct persona *
persona_lookup(__unused uid_t id)899 persona_lookup(__unused uid_t id)
900 {
901 	return NULL;
902 }
903 
904 int
persona_find(__unused const char * login,__unused uid_t uid,__unused struct persona ** persona,__unused size_t * plen)905 persona_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 
913 int
persona_find_by_type(__unused int persona_type,__unused struct persona ** persona,__unused size_t * plen)914 persona_find_by_type(__unused int persona_type,
915     __unused struct persona **persona,
916     __unused size_t *plen)
917 {
918 	return ENOTSUP;
919 }
920 
921 struct persona *
persona_proc_get(__unused pid_t pid)922 persona_proc_get(__unused pid_t pid)
923 {
924 	return NULL;
925 }
926 
927 uid_t
current_persona_get_id(void)928 current_persona_get_id(void)
929 {
930 	return PERSONA_ID_NONE;
931 }
932 
933 struct persona *
current_persona_get(void)934 current_persona_get(void)
935 {
936 	return NULL;
937 }
938 
939 struct persona *
persona_get(struct persona * persona)940 persona_get(struct persona *persona)
941 {
942 	return persona;
943 }
944 
945 struct persona *
proc_persona_get(__unused proc_t p)946 proc_persona_get(__unused proc_t p)
947 {
948 	return NULL;
949 }
950 
951 void
persona_put(__unused struct persona * persona)952 persona_put(__unused struct persona *persona)
953 {
954 	return;
955 }
956 
957 boolean_t
persona_is_adoption_allowed(__unused struct persona * persona)958 persona_is_adoption_allowed(__unused struct persona *persona)
959 {
960 	return FALSE;
961 }
962 #endif
963