1/*
2 * Copyright (c) 2005, 2010 Apple Computer, 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
29/*
30 * process policy syscall implementation
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/kernel.h>
36#include <sys/malloc.h>
37#include <sys/proc_internal.h>
38#include <sys/proc.h>
39#include <sys/kauth.h>
40#include <sys/unistd.h>
41#include <sys/buf.h>
42#include <sys/ioctl.h>
43#include <sys/vm.h>
44#include <sys/user.h>
45
46#include <security/audit/audit.h>
47
48#include <mach/machine.h>
49#include <mach/mach_types.h>
50#include <mach/vm_param.h>
51#include <kern/task.h>
52#include <kern/lock.h>
53#include <kern/kalloc.h>
54#include <kern/assert.h>
55#include <vm/vm_kern.h>
56#include <vm/vm_map.h>
57#include <mach/host_info.h>
58#include <mach/task_info.h>
59#include <mach/thread_info.h>
60#include <mach/vm_region.h>
61
62#include <sys/process_policy.h>
63#include <sys/proc_info.h>
64#include <sys/bsdtask_info.h>
65#include <sys/kdebug.h>
66#include <sys/sysproto.h>
67#include <sys/msgbuf.h>
68
69#include <machine/machine_routines.h>
70
71#include <kern/ipc_misc.h>
72#include <vm/vm_protos.h>
73
74static int handle_lowresource(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
75static int handle_resourceuse(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
76static int handle_apptype(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
77static int handle_boost(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
78
79extern kern_return_t task_suspend(task_t);
80extern kern_return_t task_resume(task_t);
81
82
83/***************************** process_policy ********************/
84
85/*
86 *int process_policy(int scope, int action, int policy, int policy_subtype,
87 *                   proc_policy_attribute_t * attrp, pid_t target_pid,
88 *                   uint64_t target_threadid)
89 *{ int process_policy(int scope, int action, int policy, int policy_subtype,
90 * user_addr_t attrp, pid_t target_pid, uint64_t target_threadid); }
91 */
92
93/* system call implementation */
94int
95process_policy(__unused struct proc *p, struct process_policy_args * uap, __unused int32_t *retval)
96{
97	int error = 0;
98	int scope = uap->scope;
99	int policy = uap->policy;
100	int action = uap->action;
101	int policy_subtype = uap->policy_subtype;
102	user_addr_t attrp = uap->attrp;
103	pid_t target_pid = uap->target_pid;
104	uint64_t target_threadid = uap->target_threadid;
105	proc_t target_proc = PROC_NULL;
106	proc_t curp = current_proc();
107	kauth_cred_t my_cred;
108
109	if ((scope != PROC_POLICY_SCOPE_PROCESS) && (scope != PROC_POLICY_SCOPE_THREAD)) {
110		return(EINVAL);
111	}
112
113	if (target_pid == 0 || target_pid == proc_selfpid())
114		target_proc = proc_self();
115	else
116		target_proc = proc_find(target_pid);
117
118	if (target_proc == PROC_NULL)
119		return(ESRCH);
120
121	my_cred = kauth_cred_get();
122
123	/*
124	 * Resoure starvation control can be used by unpriv resource owner but priv at the time of ownership claim. This is
125	 * checked in low resource handle routine. So bypass the checks here.
126	 */
127	if ((policy != PROC_POLICY_RESOURCE_STARVATION) &&
128		(policy != PROC_POLICY_APPTYPE) &&
129		(!kauth_cred_issuser(my_cred) && curp != p))
130	{
131		error = EPERM;
132		goto out;
133	}
134
135#if CONFIG_MACF
136	switch (policy) {
137		case PROC_POLICY_BOOST:
138		case PROC_POLICY_RESOURCE_USAGE:
139			/* These policies do their own appropriate mac checks */
140			break;
141		default:
142			error = mac_proc_check_sched(curp, target_proc);
143			if (error) goto out;
144			break;
145	}
146#endif /* CONFIG_MACF */
147
148	switch(policy) {
149		case PROC_POLICY_BACKGROUND:
150			error = ENOTSUP;
151			break;
152		case PROC_POLICY_HARDWARE_ACCESS:
153			error = ENOTSUP;
154			break;
155		case PROC_POLICY_RESOURCE_STARVATION:
156			error = handle_lowresource(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
157			break;
158		case PROC_POLICY_RESOURCE_USAGE:
159			error = handle_resourceuse(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
160			break;
161		case PROC_POLICY_APPTYPE:
162			error = handle_apptype(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
163			break;
164		case PROC_POLICY_BOOST:
165			error = handle_boost(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
166			break;
167		default:
168			error = EINVAL;
169			break;
170	}
171
172out:
173	proc_rele(target_proc);
174	return(error);
175}
176
177static int
178handle_lowresource(__unused int scope, int action, __unused int policy, int policy_subtype, __unused user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
179{
180	int error = 0;
181
182	switch(policy_subtype) {
183		case PROC_POLICY_RS_NONE:
184		case PROC_POLICY_RS_VIRTUALMEM:
185			break;
186		default:
187			return(EINVAL);
188	}
189
190	if (action == PROC_POLICY_ACTION_RESTORE)
191		error = proc_resetpcontrol(proc_pid(proc));
192	else
193		error = EINVAL;
194
195	return(error);
196}
197
198
199static int
200handle_resourceuse(__unused int scope, __unused int action, __unused int policy, int policy_subtype, user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
201{
202	proc_policy_cpuusage_attr_t	cpuattr;
203#if CONFIG_MACF
204	proc_t 				curp = current_proc();
205#endif
206	int				entitled = TRUE;
207	uint64_t			interval = -1ULL;
208	int				error = 0;
209
210	switch(policy_subtype) {
211		case PROC_POLICY_RUSAGE_NONE:
212		case PROC_POLICY_RUSAGE_WIREDMEM:
213		case PROC_POLICY_RUSAGE_VIRTMEM:
214		case PROC_POLICY_RUSAGE_DISK:
215		case PROC_POLICY_RUSAGE_NETWORK:
216		case PROC_POLICY_RUSAGE_POWER:
217			return(ENOTSUP);
218			break;
219		default:
220			return(EINVAL);
221		case PROC_POLICY_RUSAGE_CPU:
222			break;
223	}
224
225#if CONFIG_MACF
226	if (curp != proc) {
227		/* the cpumon entitlement manages messing with CPU limits on self */
228		error = mac_proc_check_sched(curp, proc);
229		if (error)
230			return error;
231	}
232
233	/*
234	 * Allow a process to change CPU usage monitor parameters, unless a MAC policy
235	 * overrides it with an entitlement check.
236	 */
237	entitled = (mac_proc_check_cpumon(curp) == 0) ? TRUE : FALSE;
238#endif
239
240	switch (action) {
241		uint8_t percentage;
242
243		case PROC_POLICY_ACTION_GET:
244			error = proc_get_task_ruse_cpu(proc->task, &cpuattr.ppattr_cpu_attr,
245                                        &percentage,
246                                        &cpuattr.ppattr_cpu_attr_interval,
247                                        &cpuattr.ppattr_cpu_attr_deadline);
248			if (error == 0) {
249				cpuattr.ppattr_cpu_percentage = percentage;
250				cpuattr.ppattr_cpu_attr_interval /= NSEC_PER_SEC;
251				error = copyout((proc_policy_cpuusage_attr_t *)&cpuattr, (user_addr_t)attrp, sizeof(proc_policy_cpuusage_attr_t));
252			}
253			break;
254
255		case PROC_POLICY_ACTION_APPLY:
256		case PROC_POLICY_ACTION_SET:
257			error = copyin((user_addr_t)attrp, (proc_policy_cpuusage_attr_t *)&cpuattr, sizeof(proc_policy_cpuusage_attr_t));
258			if (error != 0) {
259				return (error);
260			}
261
262			/*
263			 * The process_policy API uses seconds as the units for the interval,
264			 * but the mach task policy SPI uses nanoseconds. Do the conversion,
265			 * but preserve -1 as it has special meaning.
266			 */
267			if (cpuattr.ppattr_cpu_attr_interval != -1ULL) {
268				interval = cpuattr.ppattr_cpu_attr_interval * NSEC_PER_SEC;
269			} else {
270				interval = -1ULL;
271			}
272
273			error = proc_set_task_ruse_cpu(proc->task, cpuattr.ppattr_cpu_attr,
274					cpuattr.ppattr_cpu_percentage,
275					interval,
276					cpuattr.ppattr_cpu_attr_deadline,
277					entitled);
278			break;
279
280		case PROC_POLICY_ACTION_RESTORE:
281			error = proc_clear_task_ruse_cpu(proc->task, entitled);
282			break;
283
284		default:
285			error = EINVAL;
286			break;
287
288	}
289
290	return(error);
291}
292
293
294static int
295handle_apptype(         int scope,
296                        int action,
297               __unused int policy,
298                        int policy_subtype,
299               __unused user_addr_t attrp,
300                        proc_t target_proc,
301               __unused uint64_t target_threadid)
302{
303	int error = 0;
304
305	if (scope != PROC_POLICY_SCOPE_PROCESS)
306		return (EINVAL);
307
308	/* Temporary compatibility with old importance donation interface until libproc is moved to new boost calls */
309	switch (policy_subtype) {
310		case PROC_POLICY_IOS_DONATEIMP:
311			if (action != PROC_POLICY_ACTION_ENABLE)
312				return (EINVAL);
313			if (target_proc != current_proc())
314				return (EINVAL);
315
316			/* PROCESS ENABLE APPTYPE DONATEIMP */
317			task_importance_mark_donor(target_proc->task, TRUE);
318
319			return(0);
320
321		case PROC_POLICY_IOS_HOLDIMP:
322			if (action != PROC_POLICY_ACTION_ENABLE)
323				return (EINVAL);
324			if (target_proc != current_proc())
325				return (EINVAL);
326
327			/* PROCESS ENABLE APPTYPE HOLDIMP */
328			error = task_importance_hold_external_assertion(current_task(), 1);
329
330			return(error);
331
332		case PROC_POLICY_IOS_DROPIMP:
333			if (action != PROC_POLICY_ACTION_ENABLE)
334				return (EINVAL);
335			if (target_proc != current_proc())
336				return (EINVAL);
337
338			/* PROCESS ENABLE APPTYPE DROPIMP */
339			error = task_importance_drop_external_assertion(current_task(), 1);
340
341			return(error);
342
343		default:
344			/* continue to TAL handling */
345			break;
346	}
347
348	if (policy_subtype != PROC_POLICY_OSX_APPTYPE_TAL)
349		return (EINVAL);
350
351	/* need to be super user to do this */
352	if (kauth_cred_issuser(kauth_cred_get()) == 0)
353		return (EPERM);
354
355	if (proc_task_is_tal(target_proc->task) == FALSE)
356		return (EINVAL);
357
358	switch (action) {
359		case PROC_POLICY_ACTION_ENABLE:
360			/* PROCESS ENABLE APPTYPE TAL */
361			proc_set_task_policy(target_proc->task, THREAD_NULL,
362                                             TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
363                                             TASK_POLICY_ENABLE);
364			break;
365		case PROC_POLICY_ACTION_DISABLE:
366			/* PROCESS DISABLE APPTYPE TAL */
367			proc_set_task_policy(target_proc->task, THREAD_NULL,
368                                             TASK_POLICY_ATTRIBUTE, TASK_POLICY_TAL,
369                                             TASK_POLICY_DISABLE);
370			break;
371		default:
372			return (EINVAL);
373			break;
374	}
375
376	return(0);
377}
378
379static int
380handle_boost(int scope,
381             int action,
382    __unused int policy,
383             int policy_subtype,
384    __unused user_addr_t attrp,
385             proc_t target_proc,
386    __unused uint64_t target_threadid)
387{
388	int error = 0;
389
390	assert(policy == PROC_POLICY_BOOST);
391
392	if (scope != PROC_POLICY_SCOPE_PROCESS)
393		return (EINVAL);
394
395	if (target_proc != current_proc())
396		return (EINVAL);
397
398	switch(policy_subtype) {
399		case PROC_POLICY_IMP_IMPORTANT:
400			if (task_is_importance_receiver(target_proc->task) == FALSE)
401				return (EINVAL);
402
403			switch (action) {
404				case PROC_POLICY_ACTION_HOLD:
405					/* PROCESS HOLD BOOST IMPORTANT */
406					error = task_importance_hold_external_assertion(current_task(), 1);
407					break;
408				case PROC_POLICY_ACTION_DROP:
409					/* PROCESS DROP BOOST IMPORTANT */
410					error = task_importance_drop_external_assertion(current_task(), 1);
411					break;
412				default:
413					error = (EINVAL);
414					break;
415			}
416			break;
417
418		case PROC_POLICY_IMP_DONATION:
419#if CONFIG_MACF
420			error = mac_proc_check_sched(current_proc(), target_proc);
421			if (error) return error;
422#endif
423			switch (action) {
424				case PROC_POLICY_ACTION_SET:
425					/* PROCESS SET BOOST DONATION */
426					task_importance_mark_donor(target_proc->task, TRUE);
427					break;
428				default:
429					error = (EINVAL);
430					break;
431			}
432			break;
433
434		default:
435			error = (EINVAL);
436			break;
437	}
438
439	return(error);
440}
441
442
443/*
444 * KPI to determine if a pid is currently backgrounded.
445 * Returns ESRCH if pid cannot be found or has started exiting.
446 * Returns EINVAL if state is NULL.
447 * Sets *state to 1 if pid is backgrounded, and 0 otherwise.
448 */
449int
450proc_pidbackgrounded(pid_t pid, uint32_t* state)
451{
452	proc_t target_proc = PROC_NULL;
453
454	if (state == NULL)
455		return(EINVAL);
456
457	target_proc = proc_find(pid);
458
459	if (target_proc == PROC_NULL)
460		return(ESRCH);
461
462	if ( proc_get_effective_task_policy(target_proc->task, TASK_POLICY_DARWIN_BG) ) {
463		*state = 1;
464	} else {
465		*state = 0;
466	}
467
468	proc_rele(target_proc);
469	return (0);
470}
471
472int
473proc_apply_resource_actions(void * bsdinfo, __unused int type, int action)
474{
475	proc_t p = (proc_t)bsdinfo;
476
477	switch(action) {
478		case PROC_POLICY_RSRCACT_THROTTLE:
479			/* no need to do anything */
480			break;
481
482		case PROC_POLICY_RSRCACT_SUSPEND:
483			task_suspend(p->task);
484			break;
485
486		case PROC_POLICY_RSRCACT_TERMINATE:
487			psignal(p, SIGKILL);
488			break;
489
490		case PROC_POLICY_RSRCACT_NOTIFY_KQ:
491			/* not implemented */
492			break;
493
494		case PROC_POLICY_RSRCACT_NOTIFY_EXC:
495			panic("shouldn't be applying exception notification to process!");
496			break;
497	}
498
499	return(0);
500}
501
502
503int
504proc_restore_resource_actions(void * bsdinfo, __unused int type, int action)
505{
506	proc_t p = (proc_t)bsdinfo;
507
508	switch(action) {
509		case PROC_POLICY_RSRCACT_THROTTLE:
510		case PROC_POLICY_RSRCACT_TERMINATE:
511		case PROC_POLICY_RSRCACT_NOTIFY_KQ:
512		case PROC_POLICY_RSRCACT_NOTIFY_EXC:
513			/* no need to do anything */
514			break;
515
516		case PROC_POLICY_RSRCACT_SUSPEND:
517			task_resume(p->task);
518			break;
519
520	}
521
522	return(0);
523}
524
525