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