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