]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_persona.c
xnu-4903.270.47.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@
0a7de745 5 *
490019cf
A
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.
0a7de745 14 *
490019cf
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
490019cf
A
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.
0a7de745 25 *
490019cf
A
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
d9a64523 44#include <os/log.h>
490019cf 45#define pna_err(fmt, ...) \
d9a64523 46 os_log_error(OS_LOG_DEFAULT, "ERROR: " fmt, ## __VA_ARGS__)
490019cf
A
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
d9a64523
A
58#define PERSONA_ALLOC_TOKEN (0x7a0000ae)
59#define PERSONA_INIT_TOKEN (0x7500005e)
490019cf 60#define PERSONA_MAGIC (0x0aa55aa0)
d9a64523 61#define persona_initialized(p) ((p)->pna_valid == PERSONA_MAGIC || (p)->pna_valid == PERSONA_INIT_TOKEN)
490019cf
A
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
d9a64523
A
78os_refgrp_decl(static, persona_refgrp, "persona", NULL);
79
490019cf
A
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
0a7de745
A
90void
91personas_bootstrap(void)
490019cf
A
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();
490019cf
A
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),
0a7de745
A
109 MAX_PERSONAS * sizeof(struct persona),
110 MAX_PERSONAS, "personas");
490019cf
A
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);
0a7de745 126 if (!g_default_persona_cred) {
490019cf 127 panic("couldn't create default persona credentials!");
0a7de745 128 }
490019cf
A
129
130 g_system_persona = persona_alloc(PERSONA_SYSTEM_UID,
0a7de745
A
131 PERSONA_SYSTEM_LOGIN,
132 PERSONA_SYSTEM, NULL);
d9a64523
A
133 int err = persona_init_begin(g_system_persona);
134 assert(err == 0);
135
136 persona_init_end(g_system_persona, err);
137
490019cf
A
138 assert(g_system_persona != NULL);
139}
140
0a7de745
A
141struct persona *
142persona_alloc(uid_t id, const char *login, int type, int *error)
490019cf 143{
d9a64523 144 struct persona *persona;
490019cf 145 int err = 0;
490019cf
A
146
147 if (!login) {
148 pna_err("Must provide a login name for a new persona!");
0a7de745 149 if (error) {
490019cf 150 *error = EINVAL;
0a7de745 151 }
490019cf
A
152 return NULL;
153 }
154
155 if (type <= PERSONA_INVALID || type > PERSONA_TYPE_MAX) {
156 pna_err("Invalid type: %d", type);
0a7de745 157 if (error) {
490019cf 158 *error = EINVAL;
0a7de745 159 }
490019cf
A
160 return NULL;
161 }
162
163 persona = (struct persona *)zalloc(persona_zone);
164 if (!persona) {
0a7de745 165 if (error) {
490019cf 166 *error = ENOMEM;
0a7de745 167 }
490019cf
A
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
0a7de745 180 strncpy(persona->pna_login, login, sizeof(persona->pna_login) - 1);
d9a64523 181 persona_dbg("Starting persona allocation for: '%s'", persona->pna_login);
490019cf
A
182
183 LIST_INIT(&persona->pna_members);
184 lck_mtx_init(&persona->pna_lock, persona_lck_grp, persona_lck_attr);
d9a64523 185 os_ref_init(&persona->pna_refcount, &persona_refgrp);
490019cf
A
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
d9a64523
A
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
213out_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:
0a7de745
A
232 * persona has been allocated via persona_alloc()
233 * nothing locked
d9a64523
A
234 *
235 * Returns:
0a7de745 236 * global persona list is locked (even on error)
d9a64523 237 */
0a7de745
A
238int
239persona_init_begin(struct persona *persona)
d9a64523
A
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
490019cf
A
253 lock_personas();
254try_again:
0a7de745 255 if (id == PERSONA_ID_NONE) {
490019cf 256 persona->pna_id = g_next_persona_id;
0a7de745 257 }
490019cf 258
d9a64523 259 persona_dbg("Beginning Initialization of %d:%d (%s)...", id, persona->pna_id, persona->pna_login);
490019cf
A
260
261 err = 0;
262 LIST_FOREACH(tmp, &all_personas, pna_list) {
d9a64523
A
263 persona_lock(tmp);
264 if (id == PERSONA_ID_NONE && tmp->pna_id == persona->pna_id) {
265 persona_unlock(tmp);
490019cf
A
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 }
d9a64523
A
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);
490019cf
A
276 /*
277 * Disallow use of identical login names and re-use
278 * of previously allocated persona IDs
279 */
280 err = EEXIST;
281 break;
282 }
d9a64523 283 persona_unlock(tmp);
490019cf 284 }
0a7de745 285 if (err) {
d9a64523 286 goto out;
0a7de745 287 }
490019cf
A
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,
0a7de745
A
292 persona->pna_id,
293 persona->pna_id);
490019cf 294 kauth_cred_unref(&persona->pna_cred);
0a7de745 295 if (tmp_cred != persona->pna_cred) {
490019cf 296 persona->pna_cred = tmp_cred;
0a7de745 297 }
490019cf
A
298
299 if (!persona->pna_cred) {
300 err = EACCES;
d9a64523 301 goto out;
490019cf
A
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,
0a7de745 310 &new_group, 1, KAUTH_UID_NONE);
490019cf 311 kauth_cred_unref(&persona->pna_cred);
0a7de745 312 if (tmp_cred != persona->pna_cred) {
490019cf 313 persona->pna_cred = tmp_cred;
0a7de745 314 }
490019cf
A
315
316 if (!persona->pna_cred) {
317 err = EACCES;
d9a64523 318 goto out;
490019cf
A
319 }
320
490019cf 321 /* if the kernel supplied the persona ID, increment for next time */
0a7de745 322 if (id == PERSONA_ID_NONE) {
490019cf 323 g_next_persona_id += PERSONA_ID_STEP;
0a7de745 324 }
490019cf 325
d9a64523 326 persona->pna_valid = PERSONA_INIT_TOKEN;
490019cf 327
d9a64523
A
328out:
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;
490019cf
A
336 }
337
d9a64523
A
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}
490019cf 344
d9a64523
A
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:
0a7de745
A
354 * persona is initialized via persona_init_begin()
355 * global persona list is locked via lock_personas()
d9a64523
A
356 *
357 * Returns:
0a7de745 358 * global persona list is unlocked
d9a64523 359 */
0a7de745
A
360void
361persona_init_end(struct persona *persona, int error)
d9a64523
A
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 &&
0a7de745 380 persona->pna_valid == PERSONA_INIT_TOKEN) {
d9a64523
A
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();
490019cf
A
387}
388
0a7de745
A
389static struct persona *
390persona_get_locked(struct persona *persona)
490019cf 391{
d9a64523
A
392 os_ref_retain_locked(&persona->pna_refcount);
393 return persona;
490019cf
A
394}
395
0a7de745
A
396struct persona *
397persona_get(struct persona *persona)
490019cf
A
398{
399 struct persona *ret;
0a7de745 400 if (!persona) {
490019cf 401 return NULL;
0a7de745 402 }
490019cf
A
403 persona_lock(persona);
404 ret = persona_get_locked(persona);
405 persona_unlock(persona);
406
407 return ret;
408}
409
0a7de745
A
410void
411persona_put(struct persona *persona)
490019cf
A
412{
413 int destroy = 0;
414
0a7de745 415 if (!persona) {
490019cf 416 return;
0a7de745 417 }
490019cf
A
418
419 persona_lock(persona);
d9a64523
A
420 if (os_ref_release_locked(&persona->pna_refcount) == 0) {
421 destroy = 1;
490019cf
A
422 }
423 persona_unlock(persona);
424
0a7de745 425 if (!destroy) {
490019cf 426 return;
0a7de745 427 }
490019cf
A
428
429 persona_dbg("Destroying persona %s", persona_desc(persona, 0));
430
431 /* release our credential reference */
0a7de745 432 if (persona->pna_cred) {
490019cf 433 kauth_cred_unref(&persona->pna_cred);
0a7de745 434 }
490019cf
A
435
436 /* remove it from the global list and decrement the count */
437 lock_personas();
a39ff7e2 438 persona_lock(persona);
490019cf
A
439 if (persona_valid(persona)) {
440 LIST_REMOVE(persona, pna_list);
0a7de745 441 if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX) {
490019cf 442 panic("persona count underflow!\n");
0a7de745 443 }
490019cf
A
444 persona_mkinvalid(persona);
445 }
a39ff7e2 446 persona_unlock(persona);
490019cf
A
447 unlock_personas();
448
449 assert(LIST_EMPTY(&persona->pna_members));
450 memset(persona, 0, sizeof(*persona));
451 zfree(persona_zone, persona);
452}
453
0a7de745
A
454uid_t
455persona_get_id(struct persona *persona)
490019cf 456{
0a7de745 457 if (persona) {
490019cf 458 return persona->pna_id;
0a7de745 459 }
490019cf
A
460 return PERSONA_ID_NONE;
461}
462
0a7de745
A
463struct persona *
464persona_lookup(uid_t id)
490019cf
A
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
0a7de745
A
489struct persona *
490persona_lookup_and_invalidate(uid_t id)
a39ff7e2
A
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);
0a7de745 504 if (hw_atomic_add(&g_total_personas, -1) == UINT_MAX) {
a39ff7e2 505 panic("persona ref count underflow!\n");
0a7de745 506 }
a39ff7e2
A
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
0a7de745
A
519int
520persona_find(const char *login, uid_t uid,
521 struct persona **persona, size_t *plen)
490019cf
A
522{
523 struct persona *tmp;
524 int match = 0;
525 size_t found = 0;
526
0a7de745 527 if (login) {
490019cf 528 match++;
0a7de745
A
529 }
530 if (uid != PERSONA_ID_NONE) {
490019cf 531 match++;
0a7de745 532 }
490019cf 533
0a7de745 534 if (match == 0) {
490019cf 535 return EINVAL;
0a7de745 536 }
490019cf
A
537
538 persona_dbg("Searching with %d parameters (l:\"%s\", u:%d)",
0a7de745 539 match, login, uid);
490019cf
A
540
541 lock_personas();
542 LIST_FOREACH(tmp, &all_personas, pna_list) {
543 int m = 0;
544 persona_lock(tmp);
0a7de745 545 if (login && strncmp(tmp->pna_login, login, sizeof(tmp->pna_login)) == 0) {
490019cf 546 m++;
0a7de745
A
547 }
548 if (uid != PERSONA_ID_NONE && uid == tmp->pna_id) {
490019cf 549 m++;
0a7de745 550 }
490019cf 551 if (m == match) {
0a7de745 552 if (persona && *plen > found) {
490019cf 553 persona[found] = persona_get_locked(tmp);
0a7de745 554 }
490019cf
A
555 found++;
556 }
557#ifdef PERSONA_DEBUG
0a7de745 558 if (m > 0) {
490019cf 559 persona_dbg("ID:%d Matched %d/%d, found:%d, *plen:%d",
0a7de745
A
560 tmp->pna_id, m, match, (int)found, (int)*plen);
561 }
490019cf
A
562#endif
563 persona_unlock(tmp);
564 }
565 unlock_personas();
566
567 *plen = found;
0a7de745 568 if (!found) {
490019cf 569 return ESRCH;
0a7de745 570 }
490019cf
A
571 return 0;
572}
573
0a7de745
A
574struct persona *
575persona_proc_get(pid_t pid)
490019cf
A
576{
577 struct persona *persona;
578 proc_t p = proc_find(pid);
579
0a7de745 580 if (!p) {
490019cf 581 return NULL;
0a7de745 582 }
490019cf
A
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
0a7de745
A
593struct persona *
594current_persona_get(void)
490019cf
A
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 */
0a7de745
A
609int
610persona_proc_inherit(proc_t child, proc_t parent)
490019cf
A
611{
612 if (child->p_persona != NULL) {
613 persona_dbg("proc_inherit: child already in persona: %s",
0a7de745 614 persona_desc(child->p_persona, 0));
490019cf
A
615 return -1;
616 }
617
618 /* no persona to inherit */
0a7de745 619 if (parent->p_persona == NULL) {
490019cf 620 return 0;
0a7de745 621 }
490019cf
A
622
623 return persona_proc_adopt(child, parent->p_persona, parent->p_ucred);
624}
625
0a7de745
A
626int
627persona_proc_adopt_id(proc_t p, uid_t id, kauth_cred_t auth_override)
490019cf
A
628{
629 int ret;
630 struct persona *persona;
631
632 persona = persona_lookup(id);
0a7de745 633 if (!persona) {
490019cf 634 return ESRCH;
0a7de745 635 }
490019cf
A
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
646typedef 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 */
0a7de745
A
655static struct persona *
656proc_reset_persona_internal(proc_t p, persona_reset_op_t op,
657 struct persona *old_persona,
658 struct persona *new_persona)
490019cf
A
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;
0a7de745 667 /* fall through */
490019cf
A
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 */
0a7de745
A
706static struct persona *
707proc_set_cred_internal(proc_t p, struct persona *persona,
708 kauth_cred_t auth_override, int *rlim_error)
490019cf
A
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) &&
0a7de745 720 p->p_transholder == current_thread());
490019cf
A
721 assert(persona != NULL);
722
723 /* no work to do if we "re-adopt" the same persona */
0a7de745 724 if (p->p_persona == persona) {
490019cf 725 return NULL;
0a7de745 726 }
490019cf
A
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,
0a7de745 735 NULL, persona);
490019cf
A
736 }
737
0a7de745 738 if (auth_override) {
490019cf 739 my_new_cred = auth_override;
0a7de745 740 } else {
490019cf 741 my_new_cred = persona->pna_cred;
0a7de745 742 }
490019cf 743
0a7de745 744 if (!my_new_cred) {
490019cf 745 panic("NULL credentials (persona:%p)", persona);
0a7de745 746 }
490019cf
A
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",
0a7de745 762 p->p_pid, new_uid, persona_desc(persona, 1));
490019cf
A
763 *rlim_error = EACCES;
764 (void)proc_reset_persona_internal(p, PROC_RESET_OLD_PERSONA,
0a7de745 765 old_persona, persona);
490019cf
A
766 kauth_cred_unref(&my_new_cred);
767 return NULL;
768 }
769
770 /*
771 * Set the new credentials on the proc
772 */
773set_proc_cred:
774 my_cred = kauth_cred_proc_ref(p);
775 persona_dbg("proc_adopt PID:%d, %s -> %s",
0a7de745
A
776 p->p_pid,
777 persona_desc(old_persona, 1),
778 persona_desc(persona, 1));
490019cf
A
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 */
0a7de745 819 if (old_persona) {
490019cf 820 old_uid = old_persona->pna_id;
0a7de745 821 }
490019cf
A
822
823 if (new_uid != old_uid) {
824 count = chgproccnt(old_uid, -1);
825 persona_dbg("Decrement %s:%d proc_count to: %d",
0a7de745 826 old_persona ? "Persona" : "UID", old_uid, count);
490019cf
A
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",
0a7de745 835 new_uid, kauth_cred_getuid(my_new_cred), count);
490019cf
A
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
0a7de745
A
850int
851persona_proc_adopt(proc_t p, struct persona *persona, kauth_cred_t auth_override)
490019cf
A
852{
853 int error;
854 struct persona *old_persona;
855 struct session * sessp;
856
0a7de745 857 if (!persona) {
490019cf 858 return EINVAL;
0a7de745 859 }
490019cf
A
860
861 persona_dbg("%d adopting Persona %d (%s)", proc_pid(p),
0a7de745 862 persona->pna_id, persona_desc(persona, 0));
490019cf
A
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",
0a7de745
A
884 p->p_pid, persona->pna_pgid,
885 persona->pna_pgid == uid ? ", new_session" : ".");
490019cf
A
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 */
0a7de745 905 if (old_persona) {
490019cf 906 persona_put(old_persona);
0a7de745 907 }
490019cf
A
908
909 persona_dbg("%s", error == 0 ? "SUCCESS" : "FAILED");
910 return error;
911}
912
0a7de745
A
913int
914persona_proc_drop(proc_t p)
490019cf
A
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 */
930try_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
0a7de745
A
965int
966persona_get_type(struct persona *persona)
490019cf
A
967{
968 int type;
969
0a7de745 970 if (!persona) {
490019cf 971 return PERSONA_INVALID;
0a7de745 972 }
490019cf
A
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
0a7de745
A
985int
986persona_set_cred(struct persona *persona, kauth_cred_t cred)
490019cf
A
987{
988 int ret = 0;
989 kauth_cred_t my_cred;
0a7de745 990 if (!persona || !cred) {
490019cf 991 return EINVAL;
0a7de745 992 }
490019cf
A
993
994 persona_lock(persona);
d9a64523 995 if (!persona_initialized(persona)) {
490019cf
A
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,
0a7de745
A
1009 persona->pna_id, persona->pna_id,
1010 KAUTH_UID_NONE);
490019cf
A
1011
1012 /* TODO: clear the saved GID?! */
1013
1014 /* replace the persona's cred with the new one */
0a7de745 1015 if (persona->pna_cred) {
490019cf 1016 kauth_cred_unref(&persona->pna_cred);
0a7de745 1017 }
490019cf
A
1018 persona->pna_cred = my_cred;
1019
1020out_unlock:
1021 persona_unlock(persona);
1022 return ret;
1023}
1024
0a7de745
A
1025int
1026persona_set_cred_from_proc(struct persona *persona, proc_t proc)
490019cf
A
1027{
1028 int ret = 0;
1029 kauth_cred_t parent_cred, my_cred;
0a7de745 1030 if (!persona || !proc) {
490019cf 1031 return EINVAL;
0a7de745 1032 }
490019cf
A
1033
1034 persona_lock(persona);
d9a64523 1035 if (!persona_initialized(persona)) {
490019cf
A
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,
0a7de745
A
1053 persona->pna_id, persona->pna_id,
1054 KAUTH_UID_NONE);
490019cf
A
1055
1056 /* replace the persona's cred with the new one */
0a7de745 1057 if (persona->pna_cred) {
490019cf 1058 kauth_cred_unref(&persona->pna_cred);
0a7de745 1059 }
490019cf
A
1060 persona->pna_cred = my_cred;
1061
1062 kauth_cred_unref(&parent_cred);
1063
1064out_unlock:
1065 persona_unlock(persona);
1066 return ret;
1067}
1068
0a7de745
A
1069kauth_cred_t
1070persona_get_cred(struct persona *persona)
490019cf
A
1071{
1072 kauth_cred_t cred = NULL;
1073
0a7de745 1074 if (!persona) {
490019cf 1075 return NULL;
0a7de745 1076 }
490019cf
A
1077
1078 persona_lock(persona);
0a7de745 1079 if (!persona_valid(persona)) {
490019cf 1080 goto out_unlock;
0a7de745 1081 }
490019cf
A
1082
1083 if (persona->pna_cred) {
1084 kauth_cred_ref(persona->pna_cred);
1085 cred = persona->pna_cred;
1086 }
1087
1088out_unlock:
1089 persona_unlock(persona);
1090
1091 return cred;
1092}
1093
0a7de745
A
1094uid_t
1095persona_get_uid(struct persona *persona)
490019cf
A
1096{
1097 uid_t uid = UID_MAX;
1098
0a7de745 1099 if (!persona || !persona->pna_cred) {
490019cf 1100 return UID_MAX;
0a7de745 1101 }
490019cf
A
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
0a7de745
A
1113int
1114persona_set_gid(struct persona *persona, gid_t gid)
490019cf
A
1115{
1116 int ret = 0;
1117 kauth_cred_t my_cred, new_cred;
1118
0a7de745 1119 if (!persona || !persona->pna_cred) {
490019cf 1120 return EINVAL;
0a7de745 1121 }
490019cf
A
1122
1123 persona_lock(persona);
d9a64523 1124 if (!persona_initialized(persona)) {
490019cf
A
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);
0a7de745 1136 if (new_cred != my_cred) {
490019cf 1137 persona->pna_cred = new_cred;
0a7de745 1138 }
490019cf
A
1139 kauth_cred_unref(&my_cred);
1140
1141out_unlock:
1142 persona_unlock(persona);
1143 return ret;
1144}
1145
0a7de745
A
1146gid_t
1147persona_get_gid(struct persona *persona)
490019cf
A
1148{
1149 gid_t gid = GID_MAX;
1150
0a7de745 1151 if (!persona || !persona->pna_cred) {
490019cf 1152 return GID_MAX;
0a7de745 1153 }
490019cf
A
1154
1155 persona_lock(persona);
0a7de745 1156 if (persona_valid(persona)) {
490019cf 1157 gid = kauth_cred_getgid(persona->pna_cred);
0a7de745 1158 }
490019cf
A
1159 persona_unlock(persona);
1160
1161 return gid;
1162}
1163
0a7de745
A
1164int
1165persona_set_groups(struct persona *persona, gid_t *groups, unsigned ngroups, uid_t gmuid)
490019cf
A
1166{
1167 int ret = 0;
1168 kauth_cred_t my_cred, new_cred;
1169
0a7de745 1170 if (!persona || !persona->pna_cred) {
490019cf 1171 return EINVAL;
0a7de745
A
1172 }
1173 if (ngroups > NGROUPS_MAX) {
490019cf 1174 return EINVAL;
0a7de745 1175 }
490019cf
A
1176
1177 persona_lock(persona);
d9a64523 1178 if (!persona_initialized(persona)) {
490019cf
A
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);
527f9951 1189 new_cred = kauth_cred_setgroups(my_cred, groups, (int)ngroups, gmuid);
0a7de745 1190 if (new_cred != my_cred) {
490019cf 1191 persona->pna_cred = new_cred;
0a7de745 1192 }
490019cf
A
1193 kauth_cred_unref(&my_cred);
1194
1195out_unlock:
1196 persona_unlock(persona);
1197 return ret;
1198}
1199
0a7de745
A
1200int
1201persona_get_groups(struct persona *persona, unsigned *ngroups, gid_t *groups, unsigned groups_sz)
490019cf
A
1202{
1203 int ret = EINVAL;
0a7de745 1204 if (!persona || !persona->pna_cred || !groups || !ngroups || groups_sz > NGROUPS) {
490019cf 1205 return EINVAL;
0a7de745 1206 }
490019cf
A
1207
1208 *ngroups = groups_sz;
1209
1210 persona_lock(persona);
1211 if (persona_valid(persona)) {
527f9951
A
1212 int kauth_ngroups = (int)groups_sz;
1213 kauth_cred_getgroups(persona->pna_cred, groups, &kauth_ngroups);
1214 *ngroups = (unsigned)kauth_ngroups;
490019cf
A
1215 ret = 0;
1216 }
1217 persona_unlock(persona);
1218
1219 return ret;
1220}
1221
0a7de745
A
1222uid_t
1223persona_get_gmuid(struct persona *persona)
490019cf
A
1224{
1225 uid_t gmuid = KAUTH_UID_NONE;
1226
0a7de745 1227 if (!persona || !persona->pna_cred) {
490019cf 1228 return gmuid;
0a7de745 1229 }
490019cf
A
1230
1231 persona_lock(persona);
0a7de745 1232 if (!persona_valid(persona)) {
490019cf 1233 goto out_unlock;
0a7de745 1234 }
490019cf
A
1235
1236 posix_cred_t pcred = posix_cred_get(persona->pna_cred);
1237 gmuid = pcred->cr_gmuid;
1238
1239out_unlock:
1240 persona_unlock(persona);
1241 return gmuid;
1242}
1243
0a7de745
A
1244int
1245persona_get_login(struct persona *persona, char login[MAXLOGNAME + 1])
490019cf
A
1246{
1247 int ret = EINVAL;
0a7de745 1248 if (!persona || !persona->pna_cred) {
490019cf 1249 return EINVAL;
0a7de745 1250 }
490019cf
A
1251
1252 persona_lock(persona);
0a7de745 1253 if (!persona_valid(persona)) {
490019cf 1254 goto out_unlock;
0a7de745 1255 }
490019cf
A
1256
1257 strlcpy(login, persona->pna_login, MAXLOGNAME);
1258 ret = 0;
1259
1260out_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
0a7de745
A
1273uid_t
1274persona_get_id(__unused struct persona *persona)
490019cf
A
1275{
1276 return PERSONA_ID_NONE;
1277}
1278
0a7de745
A
1279int
1280persona_get_type(__unused struct persona *persona)
490019cf
A
1281{
1282 return PERSONA_INVALID;
1283}
1284
0a7de745
A
1285kauth_cred_t
1286persona_get_cred(__unused struct persona *persona)
490019cf
A
1287{
1288 return NULL;
1289}
1290
0a7de745
A
1291struct persona *
1292persona_lookup(__unused uid_t id)
490019cf
A
1293{
1294 return NULL;
1295}
1296
0a7de745
A
1297int
1298persona_find(__unused const char *login,
1299 __unused uid_t uid,
1300 __unused struct persona **persona,
1301 __unused size_t *plen)
490019cf
A
1302{
1303 return ENOTSUP;
1304}
1305
0a7de745
A
1306struct persona *
1307current_persona_get(void)
490019cf
A
1308{
1309 return NULL;
1310}
1311
0a7de745
A
1312struct persona *
1313persona_get(struct persona *persona)
490019cf
A
1314{
1315 return persona;
1316}
1317
0a7de745
A
1318void
1319persona_put(__unused struct persona *persona)
490019cf
A
1320{
1321 return;
1322}
1323#endif