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