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/kauth.h>
39#include <sys/unistd.h>
40#include <sys/buf.h>
41#include <sys/ioctl.h>
42#include <sys/vm.h>
43#include <sys/user.h>
44
45#include <security/audit/audit.h>
46
47#include <mach/machine.h>
48#include <mach/mach_types.h>
49#include <mach/vm_param.h>
50#include <kern/task.h>
51#include <kern/lock.h>
52#include <kern/kalloc.h>
53#include <kern/assert.h>
54#include <vm/vm_kern.h>
55#include <vm/vm_map.h>
56#include <mach/host_info.h>
57#include <mach/task_info.h>
58#include <mach/thread_info.h>
59#include <mach/vm_region.h>
60
61#include <sys/process_policy.h>
62#include <sys/proc_info.h>
63#include <sys/bsdtask_info.h>
64#include <sys/kdebug.h>
65#include <sys/sysproto.h>
66#include <sys/msgbuf.h>
67
68#include <machine/machine_routines.h>
69
70#include <kern/ipc_misc.h>
71#include <vm/vm_protos.h>
72#if CONFIG_EMBEDDED
73#include <security/mac.h>
74#include <sys/kern_memorystatus.h>
75#endif /* CONFIG_EMBEDDED */
76
77static int handle_background(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
78static int handle_hwaccess(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
79static int handle_lowresrouce(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
80static int handle_resourceuse(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
81static int handle_apptype(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
82
83extern kern_return_t task_suspend(task_t);
84extern kern_return_t task_resume(task_t);
85
86#if CONFIG_EMBEDDED
87static int handle_applifecycle(int scope, int action, int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid);
88#endif /* CONFIG_EMBEDDED */
89
90
91/***************************** process_policy ********************/
92
93/*
94 *int process_policy(int scope, int action, int policy, int policy_subtype,
95 *                   proc_policy_attribute_t * attrp, pid_t target_pid,
96 *                   uint64_t target_threadid)
97 *{ int process_policy(int scope, int action, int policy, int policy_subtype,
98 * user_addr_t attrp, pid_t target_pid, uint64_t target_threadid); }
99 */
100
101/* system call implementaion */
102int
103process_policy(__unused struct proc *p, struct process_policy_args * uap, __unused int32_t *retval)
104{
105	int error = 0;
106	int scope = uap->scope;
107	int policy = uap->policy;
108	int action = uap->action;
109	int policy_subtype = uap->policy_subtype;
110	user_addr_t attrp = uap->attrp;
111	pid_t target_pid = uap->target_pid;
112	uint64_t target_threadid = uap->target_threadid;
113	proc_t target_proc = PROC_NULL;
114	proc_t curp = current_proc();
115	kauth_cred_t my_cred;
116#if CONFIG_EMBEDDED
117	kauth_cred_t target_cred;
118#endif
119
120	if ((scope != PROC_POLICY_SCOPE_PROCESS) && (scope != PROC_POLICY_SCOPE_THREAD)) {
121		return(EINVAL);
122	}
123	target_proc = proc_find(target_pid);
124	if (target_proc == PROC_NULL)  {
125		return(ESRCH);
126	}
127
128	my_cred = kauth_cred_get();
129
130#if CONFIG_EMBEDDED
131	target_cred = kauth_cred_proc_ref(target_proc);
132
133	if (!kauth_cred_issuser(my_cred) && kauth_cred_getruid(my_cred) &&
134	    kauth_cred_getuid(my_cred) != kauth_cred_getuid(target_cred) &&
135	    kauth_cred_getruid(my_cred) != kauth_cred_getuid(target_cred))
136#else
137	/*
138	 * Resoure starvation control can be used by unpriv resource owner but priv at the time of ownership claim. This is
139	 * checked in low resource handle routine. So bypass the checks here.
140	 */
141	if ((policy != PROC_POLICY_RESOURCE_STARVATION) &&
142		(policy != PROC_POLICY_APPTYPE) &&
143		(!kauth_cred_issuser(my_cred) && curp != p))
144#endif
145	{
146		error = EPERM;
147		goto out;
148	}
149
150#if CONFIG_MACF
151#if CONFIG_EMBEDDED
152	/* Lifecycle management will invoke approp macf checks */
153	if (policy != PROC_POLICY_APP_LIFECYCLE) {
154#endif /* CONFIG_EMBEDDED */
155		error = mac_proc_check_sched(curp, target_proc);
156		if (error)
157			goto out;
158#if CONFIG_EMBEDDED
159	}
160#endif /* CONFIG_EMBEDDED */
161#endif /* CONFIG_MACF */
162
163
164	switch(policy) {
165		case PROC_POLICY_BACKGROUND:
166			error = handle_background(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
167			break;
168		case PROC_POLICY_HARDWARE_ACCESS:
169			error = handle_hwaccess(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
170			break;
171		case PROC_POLICY_RESOURCE_STARVATION:
172			error = handle_lowresrouce(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
173			break;
174		case PROC_POLICY_RESOURCE_USAGE:
175			error = handle_resourceuse(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
176			break;
177#if CONFIG_EMBEDDED
178		case PROC_POLICY_APP_LIFECYCLE:
179			error = handle_applifecycle(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
180			break;
181#endif /* CONFIG_EMBEDDED */
182		case PROC_POLICY_APPTYPE:
183			error = handle_apptype(scope, action, policy, policy_subtype, attrp, target_proc, target_threadid);
184			break;
185		default:
186			error = EINVAL;
187			break;
188	}
189
190out:
191	proc_rele(target_proc);
192#if CONFIG_EMBEDDED
193        kauth_cred_unref(&target_cred);
194#endif
195	return(error);
196}
197
198
199/* darwin background handling code */
200static int
201handle_background(int scope, int action, __unused int policy, __unused int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid)
202{
203	int intval, error = 0;
204
205
206	switch (action) {
207		case PROC_POLICY_ACTION_GET:
208			if (scope == PROC_POLICY_SCOPE_PROCESS) {
209				intval = proc_get_task_bg_policy(proc->task);
210			} else {
211				/* thread scope */
212				intval = proc_get_thread_bg_policy(proc->task, target_threadid);
213			}
214			error = copyout((int *)&intval, (user_addr_t)attrp, sizeof(int));
215			break;
216
217		case PROC_POLICY_ACTION_SET:
218			error = copyin((user_addr_t)attrp, (int *)&intval, sizeof(int));
219			if (error != 0)
220				goto out;
221			if (intval > PROC_POLICY_BG_ALL) {
222				error = EINVAL;
223				goto out;
224			}
225			if (scope == PROC_POLICY_SCOPE_PROCESS) {
226				error = proc_set_bgtaskpolicy(proc->task, intval);
227			} else {
228				/* thread scope */
229				error = proc_set_bgthreadpolicy(proc->task, target_threadid, intval);
230			}
231			break;
232
233		case PROC_POLICY_ACTION_ADD:
234			error = copyin((user_addr_t)attrp, (int *)&intval, sizeof(int));
235			if (error != 0)
236				goto out;
237			if (intval > PROC_POLICY_BG_ALL) {
238				error = EINVAL;
239				goto out;
240			}
241			if (scope == PROC_POLICY_SCOPE_PROCESS) {
242				error = proc_add_bgtaskpolicy(proc->task, intval);
243			} else {
244				/* thread scope */
245				error = proc_add_bgthreadpolicy(proc->task, target_threadid, intval);
246			}
247			break;
248
249		case PROC_POLICY_ACTION_REMOVE:
250			error = copyin((user_addr_t)attrp, (int *)&intval, sizeof(int));
251			if (error != 0)
252				goto out;
253			if (intval > PROC_POLICY_BG_ALL) {
254				error = EINVAL;
255				goto out;
256			}
257			if (scope == PROC_POLICY_SCOPE_PROCESS) {
258				error = proc_remove_bgtaskpolicy(proc->task, intval);
259			} else {
260				/* thread scope */
261				error = proc_remove_bgthreadpolicy(proc->task, target_threadid, intval);
262			}
263			break;
264
265		case PROC_POLICY_ACTION_APPLY:
266			if (scope == PROC_POLICY_SCOPE_PROCESS) {
267				error = proc_apply_bgtaskpolicy(proc->task);
268			} else {
269				/* thread scope */
270				error = proc_apply_bgthreadpolicy(proc->task, target_threadid);
271			}
272			break;
273
274		case PROC_POLICY_ACTION_RESTORE:
275			if (scope == PROC_POLICY_SCOPE_PROCESS) {
276				error = proc_restore_bgtaskpolicy(proc->task);
277			} else {
278				/* thread scope */
279				error = proc_restore_bgthreadpolicy(proc->task, target_threadid);
280			}
281			break;
282
283		case PROC_POLICY_ACTION_DENYINHERIT:
284			error = proc_denyinherit_policy(proc->task);
285			break;
286
287		case PROC_POLICY_ACTION_DENYSELFSET:
288			error = proc_denyselfset_policy(proc->task);
289			break;
290
291		default:
292			return(EINVAL);
293	}
294
295out:
296	return(error);
297}
298
299static int
300handle_hwaccess(__unused int scope, __unused int action, __unused int policy, int policy_subtype, __unused user_addr_t attrp, __unused proc_t proc, __unused uint64_t target_threadid)
301{
302	switch(policy_subtype) {
303		case PROC_POLICY_HWACCESS_NONE:
304		case PROC_POLICY_HWACCESS_DISK:
305		case PROC_POLICY_HWACCESS_GPU:
306		case PROC_POLICY_HWACCESS_NETWORK:
307		case PROC_POLICY_HWACCESS_CPU:
308			break;
309		default:
310			return(EINVAL);
311	}
312	return(0);
313}
314
315static int
316handle_lowresrouce(__unused int scope, int action, __unused int policy, int policy_subtype, __unused user_addr_t attrp, proc_t proc, __unused uint64_t target_threadid)
317{
318	int error = 0;
319
320	switch(policy_subtype) {
321		case PROC_POLICY_RS_NONE:
322		case PROC_POLICY_RS_VIRTUALMEM:
323			break;
324		default:
325			return(EINVAL);
326	}
327
328	if (action == PROC_POLICY_ACTION_RESTORE)
329		error = proc_resetpcontrol(proc_pid(proc));
330	else
331		error = EINVAL;
332
333	return(error);
334}
335
336
337static int
338handle_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)
339{
340	proc_policy_cpuusage_attr_t cpuattr;
341	int error = 0;
342
343	switch(policy_subtype) {
344		case PROC_POLICY_RUSAGE_NONE:
345		case PROC_POLICY_RUSAGE_WIREDMEM:
346		case PROC_POLICY_RUSAGE_VIRTMEM:
347		case PROC_POLICY_RUSAGE_DISK:
348		case PROC_POLICY_RUSAGE_NETWORK:
349		case PROC_POLICY_RUSAGE_POWER:
350			return(ENOTSUP);
351			break;
352		default:
353			return(EINVAL);
354		case PROC_POLICY_RUSAGE_CPU:
355			break;
356	}
357
358	switch (action) {
359		case PROC_POLICY_ACTION_GET:
360			error = proc_get_task_ruse_cpu(proc->task, &cpuattr.ppattr_cpu_attr,
361                                        &cpuattr.ppattr_cpu_percentage,
362                                        &cpuattr.ppattr_cpu_attr_interval,
363                                        &cpuattr.ppattr_cpu_attr_deadline);
364			if (error == 0)
365				error = copyout((proc_policy_cpuusage_attr_t *)&cpuattr, (user_addr_t)attrp, sizeof(proc_policy_cpuusage_attr_t));
366			break;
367
368		case PROC_POLICY_ACTION_APPLY:
369		case PROC_POLICY_ACTION_SET:
370			error = copyin((user_addr_t)attrp, (proc_policy_cpuusage_attr_t *)&cpuattr, sizeof(proc_policy_cpuusage_attr_t));
371
372			if (error == 0) {
373			error = proc_set_task_ruse_cpu(proc->task, cpuattr.ppattr_cpu_attr,
374					cpuattr.ppattr_cpu_percentage,
375					cpuattr.ppattr_cpu_attr_interval,
376					cpuattr.ppattr_cpu_attr_deadline);
377			}
378			break;
379
380		case PROC_POLICY_ACTION_RESTORE:
381			error = proc_clear_task_ruse_cpu(proc->task);
382			break;
383
384		default:
385			error = EINVAL;
386			break;
387
388	}
389
390	return(error);
391}
392
393#if CONFIG_EMBEDDED
394static int
395handle_applifecycle(__unused int scope, int action, __unused int policy, int policy_subtype, user_addr_t attrp, proc_t proc, uint64_t target_threadid)
396{
397
398	int error = 0;
399	int state = 0, oldstate = 0;
400	int noteval = 0;
401
402
403
404	switch(policy_subtype) {
405		case PROC_POLICY_APPLIFE_NONE:
406			error = 0;
407			break;
408
409		case PROC_POLICY_APPLIFE_STATE:
410#if CONFIG_MACF
411			error = mac_proc_check_sched(current_proc(), proc);
412			if (error)
413				goto out;
414#endif
415			switch (action) {
416				case PROC_POLICY_ACTION_GET :
417					state = proc_lf_getappstate(proc->task);
418					error = copyout((int *)&state, (user_addr_t)attrp, sizeof(int));
419					break;
420				case PROC_POLICY_ACTION_APPLY :
421				case PROC_POLICY_ACTION_SET :
422					error = copyin((user_addr_t)attrp, (int  *)&state, sizeof(int));
423					if ((error == 0) && (state != TASK_APPSTATE_NONE)) {
424						oldstate = proc_lf_getappstate(proc->task);
425						error = proc_lf_setappstate(proc->task, state);
426						if (error == 0) {
427							switch (state) {
428								case TASK_APPSTATE_ACTIVE:
429									noteval = NOTE_APPACTIVE;
430									break;
431								case TASK_APPSTATE_BACKGROUND:
432									noteval = NOTE_APPBACKGROUND;
433									break;
434								case TASK_APPSTATE_NONUI:
435									noteval = NOTE_APPNONUI;
436									break;
437								case TASK_APPSTATE_INACTIVE:
438									noteval = NOTE_APPINACTIVE;
439									break;
440							}
441
442							proc_lock(proc);
443							proc_knote(proc, noteval);
444							proc_unlock(proc);
445						}
446					}
447					break;
448
449				default:
450					error = EINVAL;
451					break;
452			}
453			break;
454
455		case PROC_POLICY_APPLIFE_DEVSTATUS:
456#if CONFIG_MACF
457			/* ToDo - this should be a generic check, since we could potentially hang other behaviours here. */
458			error = mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_HIBERNATE);
459			if (error) {
460				error = EPERM;
461				goto out;
462			}
463#endif
464			if (action == PROC_POLICY_ACTION_APPLY) {
465				/* Used as a freeze hint */
466				memorystatus_on_inactivity(-1);
467
468				/* in future use devicestatus for pid_socketshutdown() */
469				error = 0;
470			 } else {
471				error = EINVAL;
472			}
473			break;
474
475		case PROC_POLICY_APPLIFE_PIDBIND:
476#if CONFIG_MACF
477			error = mac_proc_check_suspend_resume(current_proc(), MAC_PROC_CHECK_PIDBIND);
478			if (error) {
479				error = EPERM;
480				goto out;
481			}
482#endif
483			error = copyin((user_addr_t)attrp, (int  *)&state, sizeof(int));
484			if (error != 0)
485				goto out;
486			if (action == PROC_POLICY_ACTION_APPLY) {
487				/* bind the thread in target_thread in current process to target_proc */
488				error = proc_lf_pidbind(current_task(), target_threadid, proc->task, state);
489			 } else
490				error = EINVAL;
491			break;
492		default:
493			error = EINVAL;
494			break;
495	}
496
497out:
498	return(error);
499}
500#endif /* CONFIG_EMBEDDED */
501
502
503static int
504handle_apptype(__unused int scope, int action, __unused int policy, int policy_subtype, __unused user_addr_t attrp, proc_t target_proc, __unused uint64_t target_threadid)
505{
506	int error = 0;
507
508	switch(policy_subtype) {
509#if !CONFIG_EMBEDDED
510		case PROC_POLICY_OSX_APPTYPE_TAL:
511			/* need to be super user to do this */
512			if (kauth_cred_issuser(kauth_cred_get()) == 0) {
513				error = EPERM;
514				goto out;
515			}
516			break;
517		case PROC_POLICY_OSX_APPTYPE_DASHCLIENT:
518			/* no special priv needed */
519			break;
520#endif /* !CONFIG_EMBEDDED */
521		case PROC_POLICY_OSX_APPTYPE_NONE:
522#if CONFIG_EMBEDDED
523		case PROC_POLICY_IOS_RESV1_APPTYPE:
524		case PROC_POLICY_IOS_APPLE_DAEMON:
525		case PROC_POLICY_IOS_APPTYPE:
526		case PROC_POLICY_IOS_NONUITYPE:
527#endif /* CONFIG_EMBEDDED */
528			return(ENOTSUP);
529			break;
530		default:
531			return(EINVAL);
532	}
533
534	switch (action) {
535		case PROC_POLICY_ACTION_ENABLE:
536			/* reapply the app foreground/background policy */
537			error = proc_enable_task_apptype(target_proc->task, policy_subtype);
538			break;
539		case PROC_POLICY_ACTION_DISABLE:
540			/* remove the app foreground/background policy */
541			error = proc_disable_task_apptype(target_proc->task, policy_subtype);
542			break;
543		default:
544			error = EINVAL;
545			break;
546	}
547
548#if !CONFIG_EMBEDDED
549out:
550#endif /* !CONFIG_EMBEDDED */
551	return(error);
552}
553
554
555int
556proc_apply_resource_actions(void * bsdinfo, int type, int action)
557{
558	proc_t p = (proc_t)bsdinfo;
559
560	switch(action) {
561		case PROC_POLICY_RSRCACT_THROTTLE:
562			/* no need to do anything */
563			break;
564
565		case PROC_POLICY_RSRCACT_SUSPEND:
566			task_suspend(p->task);
567			break;
568
569		case PROC_POLICY_RSRCACT_TERMINATE:
570			psignal(p, SIGKILL);
571			break;
572
573		case PROC_POLICY_RSRCACT_NOTIFY_KQ:
574			proc_lock(p);
575			proc_knote(p, NOTE_RESOURCEEND | (type & 0xff));
576			proc_unlock(p);
577			break;
578
579		case PROC_POLICY_RSRCACT_NOTIFY_EXC:
580			panic("shouldn't be applying exception notification to process!");
581			break;
582	}
583
584	return(0);
585}
586
587
588int
589proc_restore_resource_actions(void * bsdinfo, __unused int type, int action)
590{
591	proc_t p = (proc_t)bsdinfo;
592
593	switch(action) {
594		case PROC_POLICY_RSRCACT_THROTTLE:
595		case PROC_POLICY_RSRCACT_TERMINATE:
596		case PROC_POLICY_RSRCACT_NOTIFY_KQ:
597		case PROC_POLICY_RSRCACT_NOTIFY_EXC:
598			/* no need to do anything */
599			break;
600
601		case PROC_POLICY_RSRCACT_SUSPEND:
602			task_resume(p->task);
603			break;
604
605	}
606
607	return(0);
608}
609
610