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
38 #include <kern/task.h>
39 #include <kern/thread.h>
40 #include <mach/thread_act.h>
41 #include <mach/mach_types.h>
42
43 #include <libkern/libkern.h>
44 #include <IOKit/IOBSD.h>
45
46 extern kern_return_t bank_get_bank_ledger_thread_group_and_persona(void *voucher,
47 void *bankledger, void **banktg, uint32_t *persona_id);
48
49 static int
kpersona_copyin(user_addr_t infop,struct kpersona_info * kinfo)50 kpersona_copyin(user_addr_t infop, struct kpersona_info *kinfo)
51 {
52 uint32_t info_v = 0;
53 int error;
54
55 error = copyin(infop, &info_v, sizeof(info_v));
56 if (error) {
57 return error;
58 }
59
60 /* only support a single version of the struct for now */
61 if (info_v != PERSONA_INFO_V1) {
62 return EINVAL;
63 }
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
73 static int
kpersona_copyout(struct kpersona_info * kinfo,user_addr_t infop)74 kpersona_copyout(struct kpersona_info *kinfo, user_addr_t infop)
75 {
76 uint32_t info_v;
77 int error;
78
79 error = copyin(infop, &info_v, sizeof(info_v));
80 if (error) {
81 return error;
82 }
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 */
86 if (info_v != PERSONA_INFO_V1) {
87 return EINVAL;
88 }
89
90 error = copyout(kinfo, infop, sizeof(*kinfo));
91 return error;
92 }
93
94
95 static int
kpersona_alloc_syscall(user_addr_t infop,user_addr_t idp,user_addr_t path)96 kpersona_alloc_syscall(user_addr_t infop, user_addr_t idp, user_addr_t path)
97 {
98 int error;
99 struct kpersona_info kinfo;
100 struct persona *persona = NULL;
101 uid_t id = PERSONA_ID_NONE;
102 const char *login;
103 char *pna_path = NULL;
104
105 if (!IOCurrentTaskHasEntitlement(PERSONA_MGMT_ENTITLEMENT)) {
106 return EPERM;
107 }
108
109 error = kpersona_copyin(infop, &kinfo);
110 if (error) {
111 return error;
112 }
113
114 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
115 if (kinfo.persona_id != PERSONA_ID_NONE && kinfo.persona_id != (uid_t)0) {
116 id = kinfo.persona_id;
117 }
118
119 if (path) {
120 pna_path = zalloc_flags(ZV_NAMEI, Z_WAITOK | Z_ZERO);
121
122 size_t pathlen;
123 error = copyinstr(path, (void *)pna_path, MAXPATHLEN, &pathlen);
124 if (error) {
125 zfree(ZV_NAMEI, pna_path);
126 return error;
127 }
128 }
129
130 error = 0;
131 persona = persona_alloc(id, login, kinfo.persona_type, pna_path, &error);
132 if (!persona) {
133 if (pna_path != NULL) {
134 zfree(ZV_NAMEI, pna_path);
135 }
136 return error;
137 }
138
139 /* persona struct contains a reference to pna_path */
140 pna_path = NULL;
141
142 error = persona_init_begin(persona);
143 if (error) {
144 goto out_persona_err;
145 }
146
147 if (kinfo.persona_gid) {
148 error = persona_set_gid(persona, kinfo.persona_gid);
149 if (error) {
150 goto out_persona_err;
151 }
152 }
153
154 if (kinfo.persona_ngroups > 0) {
155 /* force gmuid 0 to *opt-out* of memberd */
156 if (kinfo.persona_gmuid == 0) {
157 kinfo.persona_gmuid = KAUTH_UID_NONE;
158 }
159
160 error = persona_set_groups(persona, kinfo.persona_groups,
161 kinfo.persona_ngroups,
162 kinfo.persona_gmuid);
163 if (error) {
164 goto out_persona_err;
165 }
166 }
167
168 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
169 if (error) {
170 goto out_persona_err;
171 }
172
173 kinfo.persona_id = persona->pna_id;
174 error = kpersona_copyout(&kinfo, infop);
175 if (error) {
176 goto out_persona_err;
177 }
178
179 error = persona_verify_and_set_uniqueness(persona);
180 if (error) {
181 goto out_persona_err;
182 }
183
184 persona_init_end(persona, error);
185
186 /*
187 * On success, we have a persona structure in the global list with a
188 * single reference count on it. The corresponding _dealloc() call
189 * will release this reference.
190 */
191 return error;
192
193 out_persona_err:
194 assert(error != 0);
195 persona_init_end(persona, error);
196
197 #if PERSONA_DEBUG
198 printf("%s: ERROR:%d\n", __func__, error);
199 #endif
200 if (persona) {
201 persona_put(persona);
202 }
203 return error;
204 }
205
206 static int
kpersona_dealloc_syscall(user_addr_t idp)207 kpersona_dealloc_syscall(user_addr_t idp)
208 {
209 int error = 0;
210 uid_t persona_id;
211 struct persona *persona;
212
213 if (!IOCurrentTaskHasEntitlement(PERSONA_MGMT_ENTITLEMENT)) {
214 return EPERM;
215 }
216
217 error = copyin(idp, &persona_id, sizeof(persona_id));
218 if (error) {
219 return error;
220 }
221
222 /* invalidate the persona (deny subsequent spawn/fork) */
223 persona = persona_lookup_and_invalidate(persona_id);
224
225 if (!persona) {
226 return ESRCH;
227 }
228
229 /* one reference from the _lookup() */
230 persona_put(persona);
231
232 /* one reference from the _alloc() */
233 persona_put(persona);
234
235 return error;
236 }
237
238 static int
kpersona_get_syscall(user_addr_t idp)239 kpersona_get_syscall(user_addr_t idp)
240 {
241 int error;
242 uid_t current_persona_id;
243 struct persona *persona;
244
245 current_persona_id = current_persona_get_id();
246
247 /* Make sure the persona is still valid */
248 persona = persona_lookup(current_persona_id);
249 if (!persona) {
250 return ESRCH;
251 }
252
253 error = copyout(&persona->pna_id, idp, sizeof(persona->pna_id));
254 persona_put(persona);
255
256 return error;
257 }
258
259 static int
kpersona_getpath_syscall(user_addr_t idp,user_addr_t path)260 kpersona_getpath_syscall(user_addr_t idp, user_addr_t path)
261 {
262 int error;
263 uid_t persona_id;
264 struct persona *persona;
265 size_t pathlen;
266 uid_t lookup_persona_id = PERSONA_ID_NONE;
267
268 if (!path) {
269 return EINVAL;
270 }
271
272 error = copyin(idp, &persona_id, sizeof(persona_id));
273 if (error) {
274 return error;
275 }
276
277 /* Get current thread's persona id to compare if the
278 * input persona_id matches the current persona id
279 */
280 lookup_persona_id = current_persona_get_id();
281
282 if (persona_id && persona_id != lookup_persona_id) {
283 if (!kauth_cred_issuser(kauth_cred_get()) &&
284 !IOCurrentTaskHasEntitlement(PERSONA_MGMT_ENTITLEMENT)) {
285 return EPERM;
286 }
287 lookup_persona_id = persona_id;
288 }
289
290 persona = persona_lookup(lookup_persona_id);
291 if (!persona) {
292 return ESRCH;
293 }
294
295 if (persona->pna_path) {
296 error = copyoutstr((void *)persona->pna_path, path, MAXPATHLEN, &pathlen);
297 }
298
299 persona_put(persona);
300
301 return error;
302 }
303
304 static int
kpersona_info_syscall(user_addr_t idp,user_addr_t infop)305 kpersona_info_syscall(user_addr_t idp, user_addr_t infop)
306 {
307 int error;
308 uid_t persona_id;
309 struct persona *persona;
310 struct kpersona_info kinfo;
311 uid_t lookup_persona_id = PERSONA_ID_NONE;
312
313 error = copyin(idp, &persona_id, sizeof(persona_id));
314 if (error) {
315 return error;
316 }
317
318 /* Get current thread's persona id to compare if the
319 * input persona_id matches the current persona id
320 */
321 lookup_persona_id = current_persona_get_id();
322
323 if (persona_id && persona_id != lookup_persona_id) {
324 if (!kauth_cred_issuser(kauth_cred_get()) &&
325 !IOCurrentTaskHasEntitlement(PERSONA_MGMT_ENTITLEMENT)) {
326 return EPERM;
327 }
328 lookup_persona_id = persona_id;
329 }
330
331 persona = persona_lookup(lookup_persona_id);
332 if (!persona) {
333 return ESRCH;
334 }
335
336 persona_dbg("FOUND: persona: id:%d, gid:%d, login:\"%s\"",
337 persona->pna_id, persona_get_gid(persona),
338 persona->pna_login);
339
340 memset(&kinfo, 0, sizeof(kinfo));
341 kinfo.persona_info_version = PERSONA_INFO_V1;
342 kinfo.persona_id = persona->pna_id;
343 kinfo.persona_type = persona->pna_type;
344 kinfo.persona_gid = persona_get_gid(persona);
345 size_t ngroups = 0;
346 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
347 kinfo.persona_ngroups = (uint32_t)ngroups;
348 kinfo.persona_gmuid = persona_get_gmuid(persona);
349
350 /*
351 * NULL termination is assured b/c persona_name is
352 * exactly MAXLOGNAME + 1 bytes (and has been memset to 0)
353 */
354 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
355
356 persona_put(persona);
357
358 error = kpersona_copyout(&kinfo, infop);
359
360 return error;
361 }
362
363 static int
kpersona_pidinfo_syscall(user_addr_t idp,user_addr_t infop)364 kpersona_pidinfo_syscall(user_addr_t idp, user_addr_t infop)
365 {
366 int error;
367 pid_t pid;
368 struct persona *persona;
369 struct kpersona_info kinfo;
370
371 error = copyin(idp, &pid, sizeof(pid));
372 if (error) {
373 return error;
374 }
375
376 if (!kauth_cred_issuser(kauth_cred_get())
377 && (pid != proc_getpid(current_proc()))) {
378 return EPERM;
379 }
380
381 persona = persona_proc_get(pid);
382 if (!persona) {
383 return ESRCH;
384 }
385
386 memset(&kinfo, 0, sizeof(kinfo));
387 kinfo.persona_info_version = PERSONA_INFO_V1;
388 kinfo.persona_id = persona->pna_id;
389 kinfo.persona_type = persona->pna_type;
390 kinfo.persona_gid = persona_get_gid(persona);
391 size_t ngroups = 0;
392 persona_get_groups(persona, &ngroups, kinfo.persona_groups, NGROUPS);
393 kinfo.persona_ngroups = (uint32_t)ngroups;
394 kinfo.persona_gmuid = persona_get_gmuid(persona);
395
396 strncpy(kinfo.persona_name, persona->pna_login, MAXLOGNAME);
397
398 persona_put(persona);
399
400 error = kpersona_copyout(&kinfo, infop);
401
402 return error;
403 }
404
405 static int
kpersona_find_syscall(user_addr_t infop,user_addr_t idp,user_addr_t idlenp)406 kpersona_find_syscall(user_addr_t infop, user_addr_t idp, user_addr_t idlenp)
407 {
408 int error;
409 struct kpersona_info kinfo;
410 const char *login;
411 size_t u_idlen, k_idlen = 0;
412 struct persona **persona = NULL;
413
414 error = copyin(idlenp, &u_idlen, sizeof(u_idlen));
415 if (error) {
416 return error;
417 }
418
419 if (u_idlen > g_max_personas) {
420 u_idlen = g_max_personas;
421 }
422
423 error = kpersona_copyin(infop, &kinfo);
424 if (error) {
425 goto out;
426 }
427
428 login = kinfo.persona_name[0] ? kinfo.persona_name : NULL;
429
430 if (u_idlen > 0) {
431 persona = kalloc_type(struct persona *, u_idlen, Z_WAITOK | Z_ZERO);
432 if (!persona) {
433 error = ENOMEM;
434 goto out;
435 }
436 }
437
438 k_idlen = u_idlen;
439 error = persona_find_all(login, kinfo.persona_id, (persona_type_t)kinfo.persona_type, persona, &k_idlen);
440 if (error) {
441 goto out;
442 }
443
444 /* copyout all the IDs of each persona we found */
445 for (size_t i = 0; i < k_idlen; i++) {
446 if (i >= u_idlen) {
447 break;
448 }
449 error = copyout(&persona[i]->pna_id,
450 idp + (i * sizeof(persona[i]->pna_id)),
451 sizeof(persona[i]->pna_id));
452 if (error) {
453 goto out;
454 }
455 }
456
457 out:
458 if (persona) {
459 for (size_t i = 0; i < u_idlen; i++) {
460 persona_put(persona[i]);
461 }
462 kfree_type(struct persona *, u_idlen, persona);
463 }
464
465 (void)copyout(&k_idlen, idlenp, sizeof(u_idlen));
466
467 return error;
468 }
469
470 /*
471 * Syscall entry point / demux.
472 */
473 int
persona(__unused proc_t p,struct persona_args * pargs,__unused int32_t * retval)474 persona(__unused proc_t p, struct persona_args *pargs, __unused int32_t *retval)
475 {
476 int error;
477 uint32_t op = pargs->operation;
478 /* uint32_t flags = pargs->flags; */
479 user_addr_t infop = pargs->info;
480 user_addr_t idp = pargs->id;
481 user_addr_t path = pargs->path;
482
483 switch (op) {
484 case PERSONA_OP_ALLOC:
485 error = kpersona_alloc_syscall(infop, idp, USER_ADDR_NULL);
486 break;
487 case PERSONA_OP_PALLOC:
488 error = kpersona_alloc_syscall(infop, idp, path);
489 break;
490 case PERSONA_OP_DEALLOC:
491 error = kpersona_dealloc_syscall(idp);
492 break;
493 case PERSONA_OP_GET:
494 error = kpersona_get_syscall(idp);
495 break;
496 case PERSONA_OP_GETPATH:
497 error = kpersona_getpath_syscall(idp, path);
498 break;
499 case PERSONA_OP_INFO:
500 error = kpersona_info_syscall(idp, infop);
501 break;
502 case PERSONA_OP_PIDINFO:
503 error = kpersona_pidinfo_syscall(idp, infop);
504 break;
505 case PERSONA_OP_FIND:
506 case PERSONA_OP_FIND_BY_TYPE:
507 error = kpersona_find_syscall(infop, idp, pargs->idlen);
508 break;
509 default:
510 error = ENOSYS;
511 break;
512 }
513
514 return error;
515 }
516