]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_persona.c
xnu-3789.70.16.tar.gz
[apple/xnu.git] / bsd / kern / kern_persona.c
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
64 static LIST_HEAD(personalist, persona) all_personas;
65 static uint32_t g_total_personas;
66 uint32_t g_max_personas = MAX_PERSONAS;
67
68 struct persona *g_system_persona = NULL;
69
70 static uid_t g_next_persona_id;
71
72 lck_mtx_t all_personas_lock;
73 lck_attr_t *persona_lck_attr;
74 lck_grp_t *persona_lck_grp;
75 lck_grp_attr_t *persona_lck_grp_attr;
76
77 static zone_t persona_zone;
78
79 kauth_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
85 extern void mach_kauth_cred_uthread_update(void);
86
87 void 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
132 struct 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();
188 try_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
259 out_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
279 out_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
287 int persona_invalidate(struct persona *persona)
288 {
289 int error = 0;
290 if (!persona)
291 return EINVAL;
292
293 lock_personas();
294 persona_lock(persona);
295
296 if (!persona_valid(persona))
297 panic("Double-invalidation of persona %p", persona);
298
299 LIST_REMOVE(persona, pna_list);
300 if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
301 panic("persona ref count underflow!\n");
302 persona_mkinvalid(persona);
303
304 persona_unlock(persona);
305 unlock_personas();
306
307 return error;
308 }
309
310 static struct persona *persona_get_locked(struct persona *persona)
311 {
312 if (persona->pna_refcount) {
313 persona->pna_refcount++;
314 return persona;
315 }
316 return NULL;
317 }
318
319 struct persona *persona_get(struct persona *persona)
320 {
321 struct persona *ret;
322 if (!persona)
323 return NULL;
324 persona_lock(persona);
325 ret = persona_get_locked(persona);
326 persona_unlock(persona);
327
328 return ret;
329 }
330
331 void persona_put(struct persona *persona)
332 {
333 int destroy = 0;
334
335 if (!persona)
336 return;
337
338 persona_lock(persona);
339 if (persona->pna_refcount >= 0) {
340 if (--(persona->pna_refcount) == 0)
341 destroy = 1;
342 }
343 persona_unlock(persona);
344
345 if (!destroy)
346 return;
347
348 persona_dbg("Destroying persona %s", persona_desc(persona, 0));
349
350 /* release our credential reference */
351 if (persona->pna_cred)
352 kauth_cred_unref(&persona->pna_cred);
353
354 /* remove it from the global list and decrement the count */
355 lock_personas();
356 if (persona_valid(persona)) {
357 LIST_REMOVE(persona, pna_list);
358 if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX)
359 panic("persona count underflow!\n");
360 persona_mkinvalid(persona);
361 }
362 unlock_personas();
363
364 assert(LIST_EMPTY(&persona->pna_members));
365 memset(persona, 0, sizeof(*persona));
366 zfree(persona_zone, persona);
367 }
368
369 uid_t persona_get_id(struct persona *persona)
370 {
371 if (persona)
372 return persona->pna_id;
373 return PERSONA_ID_NONE;
374 }
375
376 struct persona *persona_lookup(uid_t id)
377 {
378 struct persona *persona, *tmp;
379
380 persona = NULL;
381
382 /*
383 * simple, linear lookup for now: there shouldn't be too many
384 * of these in memory at any given time.
385 */
386 lock_personas();
387 LIST_FOREACH(tmp, &all_personas, pna_list) {
388 persona_lock(tmp);
389 if (tmp->pna_id == id && persona_valid(tmp)) {
390 persona = persona_get_locked(tmp);
391 persona_unlock(tmp);
392 break;
393 }
394 persona_unlock(tmp);
395 }
396 unlock_personas();
397
398 return persona;
399 }
400
401 int persona_find(const char *login, uid_t uid,
402 struct persona **persona, size_t *plen)
403 {
404 struct persona *tmp;
405 int match = 0;
406 size_t found = 0;
407
408 if (login)
409 match++;
410 if (uid != PERSONA_ID_NONE)
411 match++;
412
413 if (match == 0)
414 return EINVAL;
415
416 persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
417 match, login, uid);
418
419 lock_personas();
420 LIST_FOREACH(tmp, &all_personas, pna_list) {
421 int m = 0;
422 persona_lock(tmp);
423 if (login && strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0)
424 m++;
425 if (uid != PERSONA_ID_NONE && uid == tmp->pna_id)
426 m++;
427 if (m == match) {
428 if (persona && *plen > found)
429 persona[found] = persona_get_locked(tmp);
430 found++;
431 }
432 #ifdef PERSONA_DEBUG
433 if (m > 0)
434 persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
435 tmp->pna_id, m, match, (int)found, (int)*plen);
436 #endif
437 persona_unlock(tmp);
438 }
439 unlock_personas();
440
441 *plen = found;
442 if (!found)
443 return ESRCH;
444 return 0;
445 }
446
447 struct persona *persona_proc_get(pid_t pid)
448 {
449 struct persona *persona;
450 proc_t p = proc_find(pid);
451
452 if (!p)
453 return NULL;
454
455 proc_lock(p);
456 persona = persona_get(p->p_persona);
457 proc_unlock(p);
458
459 proc_rele(p);
460
461 return persona;
462 }
463
464 struct persona *current_persona_get(void)
465 {
466 proc_t p = current_proc();
467 struct persona *persona;
468
469 proc_lock(p);
470 persona = persona_get(p->p_persona);
471 proc_unlock(p);
472
473 return persona;
474 }
475
476 /**
477 * inherit a persona from parent to child
478 */
479 int persona_proc_inherit(proc_t child, proc_t parent)
480 {
481 if (child->p_persona != NULL) {
482 persona_dbg("proc_inherit: child already in persona: %s",
483 persona_desc(child->p_persona, 0));
484 return -1;
485 }
486
487 /* no persona to inherit */
488 if (parent->p_persona == NULL)
489 return 0;
490
491 return persona_proc_adopt(child, parent->p_persona, parent->p_ucred);
492 }
493
494 int persona_proc_adopt_id(proc_t p, uid_t id, kauth_cred_t auth_override)
495 {
496 int ret;
497 struct persona *persona;
498
499 persona = persona_lookup(id);
500 if (!persona)
501 return ESRCH;
502
503 ret = persona_proc_adopt(p, persona, auth_override);
504
505 /* put the reference from the lookup() */
506 persona_put(persona);
507
508 return ret;
509 }
510
511
512 typedef enum e_persona_reset_op {
513 PROC_REMOVE_PERSONA = 1,
514 PROC_RESET_OLD_PERSONA = 2,
515 } persona_reset_op_t;
516
517 /*
518 * internal cleanup routine for proc_set_cred_internal
519 *
520 */
521 static struct persona *proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
522 struct persona *old_persona,
523 struct persona *new_persona)
524 {
525 #if (DEVELOPMENT || DEBUG)
526 persona_lock_assert_held(new_persona);
527 #endif
528
529 switch (op) {
530 case PROC_REMOVE_PERSONA:
531 old_persona = p->p_persona;
532 /* fall through */
533 case PROC_RESET_OLD_PERSONA:
534 break;
535 default:
536 /* invalid arguments */
537 return NULL;
538 }
539
540 /* unlock the new persona (locked on entry) */
541 persona_unlock(new_persona);
542 /* lock the old persona and the process */
543 persona_lock(old_persona);
544 proc_lock(p);
545
546 switch (op) {
547 case PROC_REMOVE_PERSONA:
548 LIST_REMOVE(p, p_persona_list);
549 p->p_persona = NULL;
550 break;
551 case PROC_RESET_OLD_PERSONA:
552 p->p_persona = old_persona;
553 LIST_INSERT_HEAD(&old_persona->pna_members, p, p_persona_list);
554 break;
555 }
556
557 proc_unlock(p);
558 persona_unlock(old_persona);
559
560 /* re-lock the new persona */
561 persona_lock(new_persona);
562 return old_persona;
563 }
564
565 /*
566 * Assumes persona is locked.
567 * On success, takes a reference to 'persona' and returns the
568 * previous persona the process had adopted. The caller is
569 * responsible to release the reference.
570 */
571 static struct persona *proc_set_cred_internal(proc_t p, struct persona *persona,
572 kauth_cred_t auth_override, int *rlim_error)
573 {
574 struct persona *old_persona = NULL;
575 kauth_cred_t my_cred, my_new_cred;
576 uid_t old_uid, new_uid;
577 int count;
578
579 /*
580 * This operation must be done under the proc trans lock
581 * by the thread which took the trans lock!
582 */
583 assert(((p->p_lflag & P_LINTRANSIT) == P_LINTRANSIT) &&
584 p->p_transholder == current_thread());
585 assert(persona != NULL);
586
587 /* no work to do if we "re-adopt" the same persona */
588 if (p->p_persona == persona)
589 return NULL;
590
591 /*
592 * If p is in a persona, then we need to remove 'p' from the list of
593 * processes in that persona. To do this, we need to drop the lock
594 * held on the incoming (new) persona and lock the old one.
595 */
596 if (p->p_persona) {
597 old_persona = proc_reset_persona_internal(p, PROC_REMOVE_PERSONA,
598 NULL, persona);
599 }
600
601 if (auth_override)
602 my_new_cred = auth_override;
603 else
604 my_new_cred = persona->pna_cred;
605
606 if (!my_new_cred)
607 panic("NULL credentials (persona:%p)", persona);
608
609 *rlim_error = 0;
610
611 kauth_cred_ref(my_new_cred);
612
613 new_uid = persona->pna_id;
614
615 /*
616 * Check to see if we will hit a proc rlimit by moving the process
617 * into the persona. If so, we'll bail early before actually moving
618 * the process or changing its credentials.
619 */
620 if (new_uid != 0 &&
621 (rlim_t)chgproccnt(new_uid, 0) > p->p_rlimit[RLIMIT_NPROC].rlim_cur) {
622 pna_err("PID:%d hit proc rlimit in new persona(%d): %s",
623 p->p_pid, new_uid, persona_desc(persona, 1));
624 *rlim_error = EACCES;
625 (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA,
626 old_persona, persona);
627 kauth_cred_unref(&my_new_cred);
628 return NULL;
629 }
630
631 /*
632 * Set the new credentials on the proc
633 */
634 set_proc_cred:
635 my_cred = kauth_cred_proc_ref(p);
636 persona_dbg("proc_adopt PID:%d, %s -> %s",
637 p->p_pid,
638 persona_desc(old_persona, 1),
639 persona_desc(persona, 1));
640
641 old_uid = kauth_cred_getruid(my_cred);
642
643 if (my_cred != my_new_cred) {
644 kauth_cred_t old_cred = my_cred;
645
646 proc_ucred_lock(p);
647 /*
648 * We need to protect against a race where another thread
649 * also changed the credential after we took our
650 * reference. If p_ucred has changed then we should
651 * restart this again with the new cred.
652 */
653 if (p->p_ucred != my_cred) {
654 proc_ucred_unlock(p);
655 kauth_cred_unref(&my_cred);
656 /* try again */
657 goto set_proc_cred;
658 }
659
660 /* update the credential and take a ref for the proc */
661 kauth_cred_ref(my_new_cred);
662 p->p_ucred = my_new_cred;
663
664 /* update cred on proc (and current thread) */
665 mach_kauth_cred_uthread_update();
666 PROC_UPDATE_CREDS_ONPROC(p);
667
668 /* drop the proc's old ref on the credential */
669 kauth_cred_unref(&old_cred);
670 proc_ucred_unlock(p);
671 }
672
673 /* drop this function's reference to the old cred */
674 kauth_cred_unref(&my_cred);
675
676 /*
677 * Update the proc count.
678 * If the UIDs are the same, then there is no work to do.
679 */
680 if (old_persona)
681 old_uid = old_persona->pna_id;
682
683 if (new_uid != old_uid) {
684 count = chgproccnt(old_uid, -1);
685 persona_dbg("Decrement %s:%d proc_count to: %d",
686 old_persona ? "Persona" : "UID", old_uid, count);
687
688 /*
689 * Increment the proc count on the UID associated with
690 * the new persona. Enforce the resource limit just
691 * as in fork1()
692 */
693 count = chgproccnt(new_uid, 1);
694 persona_dbg("Increment Persona:%d (UID:%d) proc_count to: %d",
695 new_uid, kauth_cred_getuid(my_new_cred), count);
696 }
697
698 OSBitOrAtomic(P_ADOPTPERSONA, &p->p_flag);
699
700 proc_lock(p);
701 p->p_persona = persona_get_locked(persona);
702 LIST_INSERT_HEAD(&persona->pna_members, p, p_persona_list);
703 proc_unlock(p);
704
705 kauth_cred_unref(&my_new_cred);
706
707 return old_persona;
708 }
709
710 int persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override)
711 {
712 int error;
713 struct persona *old_persona;
714 struct session * sessp;
715
716 if (!persona)
717 return EINVAL;
718
719 persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
720 persona->pna_id, persona_desc(persona, 0));
721
722 persona_lock(persona);
723 if (!persona->pna_cred || !persona_valid(persona)) {
724 persona_dbg("Invalid persona (%s): NULL credentials!", persona_desc(persona, 1));
725 persona_unlock(persona);
726 return EINVAL;
727 }
728
729 /* the persona credentials can no longer be adjusted */
730 persona->pna_cred_locked = 1;
731
732 /*
733 * assume the persona: this may drop and re-acquire the persona lock!
734 */
735 error = 0;
736 old_persona = proc_set_cred_internal(p, persona, auth_override, &error);
737
738 /* join the process group associated with the persona */
739 if (persona->pna_pgid) {
740 uid_t uid = kauth_cred_getuid(persona->pna_cred);
741 persona_dbg(" PID:%d, pgid:%d%s",
742 p->p_pid, persona->pna_pgid,
743 persona->pna_pgid == uid ? ", new_session" : ".");
744 enterpgrp(p, persona->pna_pgid, persona->pna_pgid == uid);
745 }
746
747 /* set the login name of the session */
748 sessp = proc_session(p);
749 if (sessp != SESSION_NULL) {
750 session_lock(sessp);
751 bcopy(persona->pna_login, sessp->s_login, MAXLOGNAME);
752 session_unlock(sessp);
753 session_rele(sessp);
754 }
755
756 persona_unlock(persona);
757
758 set_security_token(p);
759
760 /*
761 * Drop the reference to the old persona.
762 */
763 if (old_persona)
764 persona_put(old_persona);
765
766 persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
767 return error;
768 }
769
770 int persona_proc_drop(proc_t p)
771 {
772 struct persona *persona = NULL;
773
774 persona_dbg("PID:%d, %s -> <none>", p->p_pid, persona_desc(p->p_persona, 0));
775
776 /*
777 * There are really no other credentials for us to assume,
778 * so we'll just continue running with the credentials
779 * we got from the persona.
780 */
781
782 /*
783 * the locks must be taken in reverse order here, so
784 * we have to be careful not to cause deadlock
785 */
786 try_again:
787 proc_lock(p);
788 if (p->p_persona) {
789 uid_t puid, ruid;
790 if (!persona_try_lock(p->p_persona)) {
791 proc_unlock(p);
792 mutex_pause(0); /* back-off time */
793 goto try_again;
794 }
795 persona = p->p_persona;
796 LIST_REMOVE(p, p_persona_list);
797 p->p_persona = NULL;
798
799 ruid = kauth_cred_getruid(p->p_ucred);
800 puid = kauth_cred_getuid(persona->pna_cred);
801 proc_unlock(p);
802 (void)chgproccnt(ruid, 1);
803 (void)chgproccnt(puid, -1);
804 } else {
805 proc_unlock(p);
806 }
807
808 /*
809 * if the proc had a persona, then it is still locked here
810 * (preserving proper lock ordering)
811 */
812
813 if (persona) {
814 persona_unlock(persona);
815 persona_put(persona);
816 }
817
818 return 0;
819 }
820
821 int persona_get_type(struct persona *persona)
822 {
823 int type;
824
825 if (!persona)
826 return PERSONA_INVALID;
827
828 persona_lock(persona);
829 if (!persona_valid(persona)) {
830 persona_unlock(persona);
831 return PERSONA_INVALID;
832 }
833 type = persona->pna_type;
834 persona_unlock(persona);
835
836 return type;
837 }
838
839 int persona_set_cred(struct persona *persona, kauth_cred_t cred)
840 {
841 int ret = 0;
842 kauth_cred_t my_cred;
843 if (!persona || !cred)
844 return EINVAL;
845
846 persona_lock(persona);
847 if (!persona_valid(persona)) {
848 ret = EINVAL;
849 goto out_unlock;
850 }
851 if (persona->pna_cred_locked) {
852 ret = EPERM;
853 goto out_unlock;
854 }
855
856 /* create a new cred from the passed-in cred */
857 my_cred = kauth_cred_create(cred);
858
859 /* ensure that the UID matches the persona ID */
860 my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
861 persona->pna_id, persona->pna_id,
862 KAUTH_UID_NONE);
863
864 /* TODO: clear the saved GID?! */
865
866 /* replace the persona's cred with the new one */
867 if (persona->pna_cred)
868 kauth_cred_unref(&persona->pna_cred);
869 persona->pna_cred = my_cred;
870
871 out_unlock:
872 persona_unlock(persona);
873 return ret;
874 }
875
876 int persona_set_cred_from_proc(struct persona *persona, proc_t proc)
877 {
878 int ret = 0;
879 kauth_cred_t parent_cred, my_cred;
880 if (!persona || !proc)
881 return EINVAL;
882
883 persona_lock(persona);
884 if (!persona_valid(persona)) {
885 ret = EINVAL;
886 goto out_unlock;
887 }
888 if (persona->pna_cred_locked) {
889 ret = EPERM;
890 goto out_unlock;
891 }
892
893 parent_cred = kauth_cred_proc_ref(proc);
894
895 /* TODO: clear the saved UID/GID! */
896
897 /* create a new cred from the proc's cred */
898 my_cred = kauth_cred_create(parent_cred);
899
900 /* ensure that the UID matches the persona ID */
901 my_cred = kauth_cred_setresuid(my_cred, persona->pna_id,
902 persona->pna_id, persona->pna_id,
903 KAUTH_UID_NONE);
904
905 /* replace the persona's cred with the new one */
906 if (persona->pna_cred)
907 kauth_cred_unref(&persona->pna_cred);
908 persona->pna_cred = my_cred;
909
910 kauth_cred_unref(&parent_cred);
911
912 out_unlock:
913 persona_unlock(persona);
914 return ret;
915 }
916
917 kauth_cred_t persona_get_cred(struct persona *persona)
918 {
919 kauth_cred_t cred = NULL;
920
921 if (!persona)
922 return NULL;
923
924 persona_lock(persona);
925 if (!persona_valid(persona))
926 goto out_unlock;
927
928 if (persona->pna_cred) {
929 kauth_cred_ref(persona->pna_cred);
930 cred = persona->pna_cred;
931 }
932
933 out_unlock:
934 persona_unlock(persona);
935
936 return cred;
937 }
938
939 uid_t persona_get_uid(struct persona *persona)
940 {
941 uid_t uid = UID_MAX;
942
943 if (!persona || !persona->pna_cred)
944 return UID_MAX;
945
946 persona_lock(persona);
947 if (persona_valid(persona)) {
948 uid = kauth_cred_getuid(persona->pna_cred);
949 assert(uid == persona->pna_id);
950 }
951 persona_unlock(persona);
952
953 return uid;
954 }
955
956 int persona_set_gid(struct persona *persona, gid_t gid)
957 {
958 int ret = 0;
959 kauth_cred_t my_cred, new_cred;
960
961 if (!persona || !persona->pna_cred)
962 return EINVAL;
963
964 persona_lock(persona);
965 if (!persona_valid(persona)) {
966 ret = EINVAL;
967 goto out_unlock;
968 }
969 if (persona->pna_cred_locked) {
970 ret = EPERM;
971 goto out_unlock;
972 }
973
974 my_cred = persona->pna_cred;
975 kauth_cred_ref(my_cred);
976 new_cred = kauth_cred_setresgid(my_cred, gid, gid, gid);
977 if (new_cred != my_cred)
978 persona->pna_cred = new_cred;
979 kauth_cred_unref(&my_cred);
980
981 out_unlock:
982 persona_unlock(persona);
983 return ret;
984 }
985
986 gid_t persona_get_gid(struct persona *persona)
987 {
988 gid_t gid = GID_MAX;
989
990 if (!persona || !persona->pna_cred)
991 return GID_MAX;
992
993 persona_lock(persona);
994 if (persona_valid(persona))
995 gid = kauth_cred_getgid(persona->pna_cred);
996 persona_unlock(persona);
997
998 return gid;
999 }
1000
1001 int persona_set_groups(struct persona *persona, gid_t *groups, int ngroups, uid_t gmuid)
1002 {
1003 int ret = 0;
1004 kauth_cred_t my_cred, new_cred;
1005
1006 if (!persona || !persona->pna_cred)
1007 return EINVAL;
1008 if (ngroups > NGROUPS_MAX)
1009 return EINVAL;
1010
1011 persona_lock(persona);
1012 if (!persona_valid(persona)) {
1013 ret = EINVAL;
1014 goto out_unlock;
1015 }
1016 if (persona->pna_cred_locked) {
1017 ret = EPERM;
1018 goto out_unlock;
1019 }
1020
1021 my_cred = persona->pna_cred;
1022 kauth_cred_ref(my_cred);
1023 new_cred = kauth_cred_setgroups(my_cred, groups, ngroups, gmuid);
1024 if (new_cred != my_cred)
1025 persona->pna_cred = new_cred;
1026 kauth_cred_unref(&my_cred);
1027
1028 out_unlock:
1029 persona_unlock(persona);
1030 return ret;
1031 }
1032
1033 int persona_get_groups(struct persona *persona, int *ngroups, gid_t *groups, int groups_sz)
1034 {
1035 int ret = EINVAL;
1036 if (!persona || !persona->pna_cred || !groups || !ngroups)
1037 return EINVAL;
1038
1039 *ngroups = groups_sz;
1040
1041 persona_lock(persona);
1042 if (persona_valid(persona)) {
1043 kauth_cred_getgroups(persona->pna_cred, groups, ngroups);
1044 ret = 0;
1045 }
1046 persona_unlock(persona);
1047
1048 return ret;
1049 }
1050
1051 uid_t persona_get_gmuid(struct persona *persona)
1052 {
1053 uid_t gmuid = KAUTH_UID_NONE;
1054
1055 if (!persona || !persona->pna_cred)
1056 return gmuid;
1057
1058 persona_lock(persona);
1059 if (!persona_valid(persona))
1060 goto out_unlock;
1061
1062 posix_cred_t pcred = posix_cred_get(persona->pna_cred);
1063 gmuid = pcred->cr_gmuid;
1064
1065 out_unlock:
1066 persona_unlock(persona);
1067 return gmuid;
1068 }
1069
1070 int persona_get_login(struct persona *persona, char login[MAXLOGNAME+1])
1071 {
1072 int ret = EINVAL;
1073 if (!persona || !persona->pna_cred)
1074 return EINVAL;
1075
1076 persona_lock(persona);
1077 if (!persona_valid(persona))
1078 goto out_unlock;
1079
1080 strlcpy(login, persona->pna_login, MAXLOGNAME);
1081 ret = 0;
1082
1083 out_unlock:
1084 persona_unlock(persona);
1085 login[MAXLOGNAME] = 0;
1086
1087 return ret;
1088 }
1089
1090 #else /* !CONFIG_PERSONAS */
1091
1092 /*
1093 * symbol exports for kext compatibility
1094 */
1095
1096 uid_t persona_get_id(__unused struct persona *persona)
1097 {
1098 return PERSONA_ID_NONE;
1099 }
1100
1101 int persona_get_type(__unused struct persona *persona)
1102 {
1103 return PERSONA_INVALID;
1104 }
1105
1106 kauth_cred_t persona_get_cred(__unused struct persona *persona)
1107 {
1108 return NULL;
1109 }
1110
1111 struct persona *persona_lookup(__unused uid_t id)
1112 {
1113 return NULL;
1114 }
1115
1116 int persona_find(__unused const char *login,
1117 __unused uid_t uid,
1118 __unused struct persona **persona,
1119 __unused size_t *plen)
1120 {
1121 return ENOTSUP;
1122 }
1123
1124 struct persona *current_persona_get(void)
1125 {
1126 return NULL;
1127 }
1128
1129 struct persona *persona_get(struct persona *persona)
1130 {
1131 return persona;
1132 }
1133
1134 void persona_put(__unused struct persona *persona)
1135 {
1136 return;
1137 }
1138 #endif