1/* 2 * Copyright (c) 2000-2012 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 29#include <sys/types.h> 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/proc_internal.h> 34#include <sys/sysctl.h> 35#include <sys/signal.h> 36#include <sys/signalvar.h> 37#include <sys/codesign.h> 38 39#include <sys/fcntl.h> 40#include <sys/file.h> 41#include <sys/kauth.h> 42#include <sys/mount.h> 43#include <sys/msg.h> 44#include <sys/proc.h> 45#include <sys/socketvar.h> 46#include <sys/vnode.h> 47#include <sys/vnode_internal.h> 48 49#include <sys/ubc.h> 50#include <sys/ubc_internal.h> 51 52#include <security/mac.h> 53#include <security/mac_policy.h> 54#include <security/mac_framework.h> 55 56#include <mach/mach_types.h> 57#include <mach/vm_map.h> 58#include <mach/mach_vm.h> 59 60#include <kern/kern_types.h> 61#include <kern/task.h> 62 63#include <vm/vm_map.h> 64#include <vm/vm_kern.h> 65 66#include <sys/kasl.h> 67#include <sys/syslog.h> 68 69#include <kern/assert.h> 70 71#include <pexpert/pexpert.h> 72 73#include <mach/shared_region.h> 74 75unsigned long cs_procs_killed = 0; 76unsigned long cs_procs_invalidated = 0; 77 78int cs_force_kill = 0; 79int cs_force_hard = 0; 80int cs_debug = 0; 81#if SECURE_KERNEL 82const int cs_enforcement_enable=1; 83#else 84#if CONFIG_ENFORCE_SIGNED_CODE 85int cs_enforcement_enable=1; 86#else 87int cs_enforcement_enable=0; 88#endif 89int cs_enforcement_panic=0; 90#endif 91int cs_all_vnodes = 0; 92 93static lck_grp_t *cs_lockgrp; 94static lck_rw_t * SigPUPLock; 95 96SYSCTL_INT(_vm, OID_AUTO, cs_force_kill, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_kill, 0, ""); 97SYSCTL_INT(_vm, OID_AUTO, cs_force_hard, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_force_hard, 0, ""); 98SYSCTL_INT(_vm, OID_AUTO, cs_debug, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_debug, 0, ""); 99 100SYSCTL_INT(_vm, OID_AUTO, cs_all_vnodes, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_all_vnodes, 0, ""); 101 102#if !SECURE_KERNEL 103SYSCTL_INT(_vm, OID_AUTO, cs_enforcement, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_enable, 0, ""); 104SYSCTL_INT(_vm, OID_AUTO, cs_enforcement_panic, CTLFLAG_RW | CTLFLAG_LOCKED, &cs_enforcement_panic, 0, ""); 105#endif 106 107int panic_on_cs_killed = 0; 108void 109cs_init(void) 110{ 111#if MACH_ASSERT 112 panic_on_cs_killed = 1; 113#endif 114 PE_parse_boot_argn("panic_on_cs_killed", &panic_on_cs_killed, 115 sizeof (panic_on_cs_killed)); 116#if !SECURE_KERNEL 117 int disable_cs_enforcement = 0; 118 PE_parse_boot_argn("cs_enforcement_disable", &disable_cs_enforcement, 119 sizeof (disable_cs_enforcement)); 120 if (disable_cs_enforcement) { 121 cs_enforcement_enable = 0; 122 } else { 123 int panic = 0; 124 PE_parse_boot_argn("cs_enforcement_panic", &panic, sizeof(panic)); 125 cs_enforcement_panic = (panic != 0); 126 } 127 128 PE_parse_boot_argn("cs_debug", &cs_debug, sizeof (cs_debug)); 129#endif 130 lck_grp_attr_t *attr = lck_grp_attr_alloc_init(); 131 cs_lockgrp = lck_grp_alloc_init("KERNCS", attr); 132 SigPUPLock = lck_rw_alloc_init(cs_lockgrp, NULL); 133} 134 135int 136cs_allow_invalid(struct proc *p) 137{ 138#if MACH_ASSERT 139 lck_mtx_assert(&p->p_mlock, LCK_MTX_ASSERT_NOTOWNED); 140#endif 141#if CONFIG_MACF && CONFIG_ENFORCE_SIGNED_CODE 142 /* There needs to be a MAC policy to implement this hook, or else the 143 * kill bits will be cleared here every time. If we have 144 * CONFIG_ENFORCE_SIGNED_CODE, we can assume there is a policy 145 * implementing the hook. 146 */ 147 if( 0 != mac_proc_check_run_cs_invalid(p)) { 148 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() " 149 "not allowed: pid %d\n", 150 p->p_pid); 151 return 0; 152 } 153 if(cs_debug) printf("CODE SIGNING: cs_allow_invalid() " 154 "allowed: pid %d\n", 155 p->p_pid); 156 proc_lock(p); 157 p->p_csflags &= ~(CS_KILL | CS_HARD); 158 proc_unlock(p); 159 vm_map_switch_protect(get_task_map(p->task), FALSE); 160#endif 161 return (p->p_csflags & (CS_KILL | CS_HARD)) == 0; 162} 163 164int 165cs_invalid_page( 166 addr64_t vaddr) 167{ 168 struct proc *p; 169 int send_kill = 0, retval = 0, verbose = cs_debug; 170 uint32_t csflags; 171 172 p = current_proc(); 173 174 /* 175 * XXX revisit locking when proc is no longer protected 176 * by the kernel funnel... 177 */ 178 179 if (verbose) 180 printf("CODE SIGNING: cs_invalid_page(0x%llx): p=%d[%s]\n", 181 vaddr, p->p_pid, p->p_comm); 182 183 proc_lock(p); 184 185 /* XXX for testing */ 186 if (cs_force_kill) 187 p->p_csflags |= CS_KILL; 188 if (cs_force_hard) 189 p->p_csflags |= CS_HARD; 190 191 /* CS_KILL triggers a kill signal, and no you can't have the page. Nothing else. */ 192 if (p->p_csflags & CS_KILL) { 193 if (panic_on_cs_killed && 194 vaddr >= SHARED_REGION_BASE && 195 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { 196 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): killing p=%p\n", (uint64_t) vaddr, p); 197 } 198 p->p_csflags |= CS_KILLED; 199 cs_procs_killed++; 200 send_kill = 1; 201 retval = 1; 202 } 203 204#if __x86_64__ 205 if (panic_on_cs_killed && 206 vaddr >= SHARED_REGION_BASE && 207 vaddr < SHARED_REGION_BASE + SHARED_REGION_SIZE) { 208 panic("<rdar://14393620> cs_invalid_page(va=0x%llx): cs error p=%p\n", (uint64_t) vaddr, p); 209 } 210#endif /* __x86_64__ */ 211 212 /* CS_HARD means fail the mapping operation so the process stays valid. */ 213 if (p->p_csflags & CS_HARD) { 214 retval = 1; 215 } else { 216 if (p->p_csflags & CS_VALID) { 217 p->p_csflags &= ~CS_VALID; 218 cs_procs_invalidated++; 219 verbose = 1; 220 } 221 } 222 csflags = p->p_csflags; 223 proc_unlock(p); 224 225 if (verbose) { 226 char pid_str[10]; 227 snprintf(pid_str, sizeof(pid_str), "%d", p->p_pid); 228 kern_asl_msg(LOG_NOTICE, "messagetracer", 229 5, 230 "com.apple.message.domain", "com.apple.kernel.cs.invalidate", 231 "com.apple.message.signature", send_kill ? "kill" : retval ? "deny" : "invalidate", 232 "com.apple.message.signature4", pid_str, 233 "com.apple.message.signature3", p->p_comm, 234 "com.apple.message.summarize", "YES", 235 NULL 236 ); 237 printf("CODE SIGNING: cs_invalid_page(0x%llx): " 238 "p=%d[%s] final status 0x%x, %s page%s\n", 239 vaddr, p->p_pid, p->p_comm, p->p_csflags, 240 retval ? "denying" : "allowing (remove VALID)", 241 send_kill ? " sending SIGKILL" : ""); 242 } 243 244 if (send_kill) 245 threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS); 246 247 248 return retval; 249} 250 251/* 252 * Assumes p (if passed in) is locked with proc_lock(). 253 */ 254 255int 256cs_enforcement(struct proc *p) 257{ 258 259 if (cs_enforcement_enable) 260 return 1; 261 262 if (p == NULL) 263 p = current_proc(); 264 265 if (p != NULL && (p->p_csflags & CS_ENFORCEMENT)) 266 return 1; 267 268 return 0; 269} 270 271static struct { 272 struct cscsr_functions *funcs; 273 vm_map_offset_t csr_map_base; 274 vm_map_size_t csr_map_size; 275 int inuse; 276 int disabled; 277} csr_state; 278 279SYSCTL_INT(_vm, OID_AUTO, sigpup_disable, CTLFLAG_RW | CTLFLAG_LOCKED, &csr_state.disabled, 0, ""); 280 281static int 282vnsize(vfs_context_t vfs, vnode_t vp, uint64_t *size) 283{ 284 struct vnode_attr va; 285 int error; 286 287 VATTR_INIT(&va); 288 VATTR_WANTED(&va, va_data_size); 289 290 error = vnode_getattr(vp, &va, vfs); 291 if (error) 292 return error; 293 *size = va.va_data_size; 294 return 0; 295} 296 297int 298sigpup_install(user_addr_t argsp) 299{ 300 struct sigpup_install_table args; 301 memory_object_control_t control; 302 kern_return_t result; 303 vfs_context_t vfs = NULL; 304 struct vnode_attr va; 305 vnode_t vp = NULL; 306 char *buf = NULL; 307 uint64_t size; 308 size_t len = 0; 309 int error = 0; 310 311 if (!cs_enforcement_enable || csr_state.funcs == NULL) 312 return ENOTSUP; 313 314 lck_rw_lock_exclusive(SigPUPLock); 315 316 if (kauth_cred_issuser(kauth_cred_get()) == 0) { 317 error = EPERM; 318 goto cleanup; 319 } 320 321 if (cs_debug > 10) 322 printf("sigpup install\n"); 323 324 if (csr_state.csr_map_base != 0 || csr_state.inuse) { 325 error = EPERM; 326 goto cleanup; 327 } 328 329 if (USER_ADDR_NULL == argsp) { 330 error = EINVAL; 331 goto cleanup; 332 } 333 if ((error = copyin(argsp, &args, sizeof(args))) != 0) 334 goto cleanup; 335 336 if (cs_debug > 10) 337 printf("sigpup install with args\n"); 338 339 MALLOC(buf, char *, MAXPATHLEN, M_TEMP, M_WAITOK); 340 if (buf == NULL) { 341 error = ENOMEM; 342 goto cleanup; 343 } 344 if ((error = copyinstr((user_addr_t)args.path, buf, MAXPATHLEN, &len)) != 0) 345 goto cleanup; 346 347 if ((vfs = vfs_context_create(NULL)) == NULL) { 348 error = ENOMEM; 349 goto cleanup; 350 } 351 352 if ((error = vnode_lookup(buf, VNODE_LOOKUP_NOFOLLOW, &vp, vfs)) != 0) 353 goto cleanup; 354 355 if (cs_debug > 10) 356 printf("sigpup found file: %s\n", buf); 357 358 /* make sure vnode is on the process's root volume */ 359 if (rootvnode->v_mount != vp->v_mount) { 360 if (cs_debug) printf("sigpup csr no on root volume\n"); 361 error = EPERM; 362 goto cleanup; 363 } 364 365 /* make sure vnode is owned by "root" */ 366 VATTR_INIT(&va); 367 VATTR_WANTED(&va, va_uid); 368 error = vnode_getattr(vp, &va, vfs); 369 if (error) 370 goto cleanup; 371 372 if (va.va_uid != 0) { 373 if (cs_debug) printf("sigpup: csr file not owned by root\n"); 374 error = EPERM; 375 goto cleanup; 376 } 377 378 error = vnsize(vfs, vp, &size); 379 if (error) 380 goto cleanup; 381 382 control = ubc_getobject(vp, 0); 383 if (control == MEMORY_OBJECT_CONTROL_NULL) { 384 error = EINVAL; 385 goto cleanup; 386 } 387 388 csr_state.csr_map_size = mach_vm_round_page(size); 389 390 if (cs_debug > 10) 391 printf("mmap!\n"); 392 393 result = vm_map_enter_mem_object_control(kernel_map, 394 &csr_state.csr_map_base, 395 csr_state.csr_map_size, 396 0, VM_FLAGS_ANYWHERE, 397 control, 0 /* file offset */, 398 0 /* cow */, 399 VM_PROT_READ, 400 VM_PROT_READ, 401 VM_INHERIT_DEFAULT); 402 if (result != KERN_SUCCESS) { 403 error = EINVAL; 404 goto cleanup; 405 } 406 407 error = csr_state.funcs->csr_validate_header((const uint8_t *)csr_state.csr_map_base, 408 csr_state.csr_map_size); 409 if (error) { 410 if (cs_debug > 10) 411 printf("sigpup header invalid, dropping mapping"); 412 sigpup_drop(); 413 goto cleanup; 414 } 415 416 if (cs_debug > 10) 417 printf("table loaded %ld bytes\n", (long)csr_state.csr_map_size); 418 419cleanup: 420 lck_rw_unlock_exclusive(SigPUPLock); 421 422 if (buf) 423 FREE(buf, M_TEMP); 424 if (vp) 425 (void)vnode_put(vp); 426 if (vfs) 427 (void)vfs_context_rele(vfs); 428 429 if (error) 430 printf("sigpup: load failed with error: %d\n", error); 431 432 433 return error; 434} 435 436int 437sigpup_drop(void) 438{ 439 440 if (kauth_cred_issuser(kauth_cred_get()) == 0) 441 return EPERM; 442 443 lck_rw_lock_exclusive(SigPUPLock); 444 445 if (csr_state.csr_map_base == 0 || csr_state.inuse) { 446 printf("failed to unload the sigpup database\n"); 447 lck_rw_unlock_exclusive(SigPUPLock); 448 return EINVAL; 449 } 450 451 if (cs_debug > 10) 452 printf("sigpup: unloading\n"); 453 454 (void)mach_vm_deallocate(kernel_map, 455 csr_state.csr_map_base, csr_state.csr_map_size); 456 457 csr_state.csr_map_base = 0; 458 csr_state.csr_map_size = 0; 459 460 lck_rw_unlock_exclusive(SigPUPLock); 461 462 return 0; 463} 464 465void sigpup_attach_vnode(vnode_t); /* XXX */ 466 467void 468sigpup_attach_vnode(vnode_t vp) 469{ 470 const void *csblob; 471 size_t cslen; 472 473 if (!cs_enforcement_enable || csr_state.funcs == NULL || csr_state.csr_map_base == 0 || csr_state.disabled) 474 return; 475 476 /* if the file is not on the root volumes or already been check, skip */ 477 if (vp->v_mount != rootvnode->v_mount || (vp->v_flag & VNOCS)) 478 return; 479 480 csblob = csr_state.funcs->csr_find_file_codedirectory(vp, (const uint8_t *)csr_state.csr_map_base, 481 (size_t)csr_state.csr_map_size, &cslen); 482 if (csblob) { 483 ubc_cs_sigpup_add(vp, (vm_address_t)csblob, (vm_size_t)cslen); 484 csr_state.inuse = 1; 485 } 486 vp->v_flag |= VNOCS; 487} 488 489void 490cs_register_cscsr(struct cscsr_functions *funcs) 491{ 492 if (csr_state.funcs || funcs->csr_version < CSCSR_VERSION) 493 return; 494 csr_state.funcs = funcs; 495} 496