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