]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/sys_persona.c
xnu-6153.81.5.tar.gz
[apple/xnu.git] / bsd / kern / sys_persona.c
CommitLineData
490019cf
A
1/*
2 * Copyright (c) 2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <sys/param.h>
29#include <sys/kernel.h>
30#include <sys/kernel_types.h>
31#include <sys/sysproto.h>
32
33#include <sys/kauth.h>
34#include <sys/malloc.h>
35#include <sys/persona.h>
36#include <sys/proc.h>
37
cb323159
A
38#include <kern/task.h>
39#include <kern/thread.h>
40#include <mach/thread_act.h>
41#include <mach/mach_types.h>
42
490019cf 43#include <libkern/libkern.h>
cb323159
A
44#include <IOKit/IOBSD.h>
45
46extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher,
47 void *bankledger, void **banktg, uint32_t *persona_id);
490019cf 48
0a7de745
A
49static int
50kpersona_copyin(user_addr_t infop, struct kpersona_info *kinfo)
490019cf
A
51{
52 uint32_t info_v = 0;
53 int error;
54
55 error = copyin(infop, &info_v, sizeof(info_v));
0a7de745 56 if (error) {
490019cf 57 return error;
0a7de745 58 }
490019cf
A
59
60 /* only support a single version of the struct for now */
0a7de745 61 if (info_v != PERSONA_INFO_V1) {
490019cf 62 return EINVAL;
0a7de745 63 }
490019cf
A
64
65 error = copyin(infop, kinfo, sizeof(*kinfo));
66
67 /* enforce NULL termination on strings */
68 kinfo->persona_name[MAXLOGNAME] = 0;
69
70 return error;
71}
72
0a7de745
A
73static int
74kpersona_copyout(struct kpersona_info *kinfo, user_addr_t infop)
490019cf
A
75{
76 uint32_t info_v;
77 int error;
78
79 error = copyin(infop, &info_v, sizeof(info_v));
0a7de745 80 if (error) {
490019cf 81 return error;
0a7de745 82 }
490019cf
A
83
84 /* only support a single version of the struct for now */
85 /* TODO: in the future compare info_v to kinfo->persona_info_version */
0a7de745 86 if (info_v != PERSONA_INFO_V1) {
490019cf 87 return EINVAL;
0a7de745 88 }
490019cf
A
89
90 error = copyout(kinfo, infop, sizeof(*kinfo));
91 return error;
92}
93
94
0a7de745 95static int
cb323159 96kpersona_alloc_syscall(user_addr_t infop, user_addr_t idp, user_addr_t path)
490019cf
A
97{
98 int error;
99 struct kpersona_info kinfo;
cb323159 100 struct persona *persona = NULL;
490019cf
A
101 uid_t id = PERSONA_ID_NONE;
102 const char *login;
cb323159 103 char *pna_path = NULL;
490019cf 104
cb323159 105 if (!IOTaskHasEntitlement(current_task(), PERSONA_MGMT_ENTITLEMENT)) {
490019cf 106 return EPERM;
0a7de745 107 }
490019cf
A
108
109 error = kpersona_copyin(infop, &kinfo);
0a7de745 110 if (error) {
490019cf 111 return error;
0a7de745 112 }
490019cf
A
113
114 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
0a7de745 115 if (kinfo.persona_id != PERSONA_ID_NONE && kinfo.persona_id != (uid_t)0) {
490019cf 116 id = kinfo.persona_id;
0a7de745 117 }
490019cf 118
cb323159
A
119 if (path) {
120 MALLOC_ZONE(pna_path, caddr_t, MAXPATHLEN, M_NAMEI, M_WAITOK | M_ZERO);
121 if (pna_path == NULL) {
122 return ENOMEM;
123 }
124 size_t pathlen;
125 error = copyinstr(path, (void *)pna_path, MAXPATHLEN, &pathlen);
126 if (error) {
127 FREE_ZONE(pna_path, MAXPATHLEN, M_NAMEI);
128 return error;
129 }
130 }
131
490019cf 132 error = 0;
cb323159 133 persona = persona_alloc(id, login, kinfo.persona_type, pna_path, &error);
0a7de745 134 if (!persona) {
cb323159
A
135 if (pna_path != NULL) {
136 FREE_ZONE(pna_path, MAXPATHLEN, M_NAMEI);
137 }
490019cf 138 return error;
0a7de745 139 }
490019cf 140
cb323159
A
141 /* persona struct contains a reference to pna_path */
142 pna_path = NULL;
143
d9a64523
A
144 error = persona_init_begin(persona);
145 if (error) {
146 goto out_persona_err;
147 }
148
490019cf
A
149 if (kinfo.persona_gid) {
150 error = persona_set_gid(persona, kinfo.persona_gid);
0a7de745 151 if (error) {
d9a64523 152 goto out_persona_err;
0a7de745 153 }
490019cf
A
154 }
155
156 if (kinfo.persona_ngroups > 0) {
157 /* force gmuid 0 to *opt-out* of memberd */
0a7de745 158 if (kinfo.persona_gmuid == 0) {
490019cf 159 kinfo.persona_gmuid = KAUTH_UID_NONE;
0a7de745 160 }
490019cf
A
161
162 error = persona_set_groups(persona, kinfo.persona_groups,
0a7de745
A
163 kinfo.persona_ngroups,
164 kinfo.persona_gmuid);
165 if (error) {
d9a64523 166 goto out_persona_err;
0a7de745 167 }
490019cf
A
168 }
169
170 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
d9a64523
A
171 if (error) {
172 goto out_persona_err;
173 }
174
175 kinfo.persona_id = persona->pna_id;
490019cf 176 error = kpersona_copyout(&kinfo, infop);
d9a64523
A
177 if (error) {
178 goto out_persona_err;
179 }
180
cb323159
A
181 error = persona_verify_and_set_uniqueness(persona);
182 if (error) {
183 goto out_persona_err;
184 }
185
d9a64523 186 persona_init_end(persona, error);
490019cf
A
187
188 /*
189 * On success, we have a persona structure in the global list with a
190 * single reference count on it. The corresponding _dealloc() call
191 * will release this reference.
192 */
193 return error;
194
d9a64523
A
195out_persona_err:
196 assert(error != 0);
197 persona_init_end(persona, error);
198
199#if PERSONA_DEBUG
490019cf 200 printf("%s: ERROR:%d\n", __func__, error);
d9a64523 201#endif
0a7de745 202 if (persona) {
490019cf 203 persona_put(persona);
0a7de745 204 }
490019cf
A
205 return error;
206}
207
0a7de745
A
208static int
209kpersona_dealloc_syscall(user_addr_t idp)
490019cf 210{
a39ff7e2 211 int error = 0;
490019cf
A
212 uid_t persona_id;
213 struct persona *persona;
214
cb323159 215 if (!IOTaskHasEntitlement(current_task(), PERSONA_MGMT_ENTITLEMENT)) {
490019cf 216 return EPERM;
0a7de745 217 }
490019cf
A
218
219 error = copyin(idp, &persona_id, sizeof(persona_id));
0a7de745 220 if (error) {
490019cf 221 return error;
0a7de745 222 }
490019cf 223
a39ff7e2
A
224 /* invalidate the persona (deny subsequent spawn/fork) */
225 persona = persona_lookup_and_invalidate(persona_id);
226
0a7de745 227 if (!persona) {
490019cf 228 return ESRCH;
0a7de745 229 }
490019cf 230
490019cf
A
231 /* one reference from the _lookup() */
232 persona_put(persona);
233
234 /* one reference from the _alloc() */
a39ff7e2 235 persona_put(persona);
490019cf
A
236
237 return error;
238}
239
0a7de745
A
240static int
241kpersona_get_syscall(user_addr_t idp)
490019cf
A
242{
243 int error;
cb323159
A
244 struct persona *persona;
245
246 persona = current_persona_get();
490019cf 247
0a7de745 248 if (!persona) {
490019cf 249 return ESRCH;
0a7de745 250 }
490019cf
A
251
252 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
253 persona_put(persona);
254
255 return error;
256}
257
cb323159
A
258static int
259kpersona_getpath_syscall(user_addr_t idp, user_addr_t path)
260{
261 int error;
262 uid_t persona_id;
263 struct persona *persona;
264 size_t pathlen;
265 uid_t current_persona_id = PERSONA_ID_NONE;
266
267 if (!path) {
268 return EINVAL;
269 }
270
271 error = copyin(idp, &persona_id, sizeof(persona_id));
272 if (error) {
273 return error;
274 }
275
276 /* Get current thread's persona id to compare if the
277 * input persona_id matches the current persona id
278 */
279 persona = current_persona_get();
280 if (persona) {
281 current_persona_id = persona->pna_id;
282 }
283
284 if (persona_id && persona_id != current_persona_id) {
285 /* Release the reference on the current persona id's persona */
286 persona_put(persona);
287 if (!kauth_cred_issuser(kauth_cred_get()) &&
288 !IOTaskHasEntitlement(current_task(), PERSONA_MGMT_ENTITLEMENT)) {
289 return EPERM;
290 }
291 persona = persona_lookup(persona_id);
292 }
293
294 if (!persona) {
295 return ESRCH;
296 }
297
298 if (persona->pna_path) {
299 error = copyoutstr((void *)persona->pna_path, path, MAXPATHLEN, &pathlen);
300 }
301
302 persona_put(persona);
303
304 return error;
305}
306
0a7de745
A
307static int
308kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
490019cf
A
309{
310 int error;
cb323159 311 uid_t current_persona_id = PERSONA_ID_NONE;
490019cf
A
312 uid_t persona_id;
313 struct persona *persona;
314 struct kpersona_info kinfo;
315
316 error = copyin(idp, &persona_id, sizeof(persona_id));
0a7de745 317 if (error) {
490019cf 318 return error;
0a7de745 319 }
490019cf 320
cb323159
A
321 /* Get current thread's persona id to compare if the
322 * input persona_id matches the current persona id
490019cf 323 */
cb323159
A
324 persona = current_persona_get();
325 if (persona) {
326 current_persona_id = persona->pna_id;
327 }
328
329 if (persona_id && persona_id != current_persona_id) {
330 /* Release the reference on the current persona id's persona */
331 persona_put(persona);
332 if (!kauth_cred_issuser(kauth_cred_get()) &&
333 !IOTaskHasEntitlement(current_task(), PERSONA_MGMT_ENTITLEMENT)) {
334 return EPERM;
335 }
336 persona = persona_lookup(persona_id);
337 }
490019cf 338
0a7de745 339 if (!persona) {
490019cf 340 return ESRCH;
0a7de745 341 }
490019cf 342
d9a64523 343 persona_dbg("FOUND: persona: id:%d, gid:%d, login:\"%s\"",
0a7de745
A
344 persona->pna_id, persona_get_gid(persona),
345 persona->pna_login);
490019cf
A
346
347 memset(&kinfo, 0, sizeof(kinfo));
348 kinfo.persona_info_version = PERSONA_INFO_V1;
349 kinfo.persona_id = persona->pna_id;
350 kinfo.persona_type = persona->pna_type;
351 kinfo.persona_gid = persona_get_gid(persona);
527f9951 352 unsigned ngroups = 0;
490019cf
A
353 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
354 kinfo.persona_ngroups = ngroups;
355 kinfo.persona_gmuid = persona_get_gmuid(persona);
356
357 /*
358 * NULL termination is assured b/c persona_name is
359 * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
360 */
361 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
362
363 persona_put(persona);
364
365 error = kpersona_copyout(&kinfo, infop);
366
367 return error;
368}
369
0a7de745
A
370static int
371kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
490019cf
A
372{
373 int error;
374 pid_t pid;
375 struct persona *persona;
376 struct kpersona_info kinfo;
377
378 error = copyin(idp, &pid, sizeof(pid));
0a7de745 379 if (error) {
490019cf 380 return error;
0a7de745 381 }
490019cf
A
382
383 if (!kauth_cred_issuser(kauth_cred_get())
0a7de745 384 && (pid != current_proc()->p_pid)) {
490019cf 385 return EPERM;
0a7de745 386 }
490019cf
A
387
388 persona = persona_proc_get(pid);
0a7de745 389 if (!persona) {
490019cf 390 return ESRCH;
0a7de745 391 }
490019cf
A
392
393 memset(&kinfo, 0, sizeof(kinfo));
394 kinfo.persona_info_version = PERSONA_INFO_V1;
395 kinfo.persona_id = persona->pna_id;
396 kinfo.persona_type = persona->pna_type;
397 kinfo.persona_gid = persona_get_gid(persona);
527f9951 398 unsigned ngroups = 0;
490019cf
A
399 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
400 kinfo.persona_ngroups = ngroups;
401 kinfo.persona_gmuid = persona_get_gmuid(persona);
402
403 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
404
405 persona_put(persona);
406
407 error = kpersona_copyout(&kinfo, infop);
408
409 return error;
410}
411
0a7de745
A
412static int
413kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
490019cf
A
414{
415 int error;
416 struct kpersona_info kinfo;
417 const char *login;
418 size_t u_idlen, k_idlen = 0;
419 struct persona **persona = NULL;
420
421 error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
0a7de745 422 if (error) {
490019cf 423 return error;
0a7de745 424 }
490019cf 425
0a7de745 426 if (u_idlen > g_max_personas) {
490019cf 427 u_idlen = g_max_personas;
0a7de745 428 }
490019cf
A
429
430 error = kpersona_copyin(infop, &kinfo);
0a7de745 431 if (error) {
490019cf 432 goto out;
0a7de745 433 }
490019cf
A
434
435 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
436
437 if (u_idlen > 0) {
438 MALLOC(persona, struct persona **, sizeof(*persona) * u_idlen,
0a7de745 439 M_TEMP, M_WAITOK | M_ZERO);
490019cf
A
440 if (!persona) {
441 error = ENOMEM;
442 goto out;
443 }
444 }
445
446 k_idlen = u_idlen;
cb323159 447 error = persona_find_all(login, kinfo.persona_id, kinfo.persona_type, persona, &k_idlen);
0a7de745 448 if (error) {
490019cf 449 goto out;
0a7de745 450 }
490019cf
A
451
452 /* copyout all the IDs of each persona we found */
453 for (size_t i = 0; i < k_idlen; i++) {
0a7de745 454 if (i >= u_idlen) {
490019cf 455 break;
0a7de745 456 }
490019cf 457 error = copyout(&persona[i]->pna_id,
0a7de745
A
458 idp + (i * sizeof(persona[i]->pna_id)),
459 sizeof(persona[i]->pna_id));
460 if (error) {
490019cf 461 goto out;
0a7de745 462 }
490019cf
A
463 }
464
465out:
466 if (persona) {
0a7de745 467 for (size_t i = 0; i < u_idlen; i++) {
490019cf 468 persona_put(persona[i]);
0a7de745 469 }
490019cf
A
470 FREE(persona, M_TEMP);
471 }
472
473 (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
474
475 return error;
476}
477
490019cf
A
478/*
479 * Syscall entry point / demux.
480 */
0a7de745
A
481int
482persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
490019cf
A
483{
484 int error;
485 uint32_t op = pargs->operation;
486 /* uint32_t flags = pargs->flags; */
487 user_addr_t infop = pargs->info;
488 user_addr_t idp = pargs->id;
cb323159 489 user_addr_t path = pargs->path;
490019cf
A
490
491 switch (op) {
492 case PERSONA_OP_ALLOC:
cb323159
A
493 error = kpersona_alloc_syscall(infop, idp, USER_ADDR_NULL);
494 break;
495 case PERSONA_OP_PALLOC:
496 error = kpersona_alloc_syscall(infop, idp, path);
490019cf
A
497 break;
498 case PERSONA_OP_DEALLOC:
499 error = kpersona_dealloc_syscall(idp);
500 break;
501 case PERSONA_OP_GET:
502 error = kpersona_get_syscall(idp);
503 break;
cb323159
A
504 case PERSONA_OP_GETPATH:
505 error = kpersona_getpath_syscall(idp, path);
506 break;
490019cf
A
507 case PERSONA_OP_INFO:
508 error = kpersona_info_syscall(idp, infop);
509 break;
510 case PERSONA_OP_PIDINFO:
511 error = kpersona_pidinfo_syscall(idp, infop);
512 break;
513 case PERSONA_OP_FIND:
cb323159 514 case PERSONA_OP_FIND_BY_TYPE:
490019cf
A
515 error = kpersona_find_syscall(infop, idp, pargs->idlen);
516 break;
517 default:
518 error = ENOSYS;
519 break;
520 }
521
522 return error;
523}