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