]> git.saurik.com Git - apple/xnu.git/blame - bsd/kern/kern_cs.c
xnu-2422.115.4.tar.gz
[apple/xnu.git] / bsd / kern / kern_cs.c
CommitLineData
39236c6e
A
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
15129b1c
A
73#include <mach/shared_region.h>
74
39236c6e
A
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
15129b1c 107int panic_on_cs_killed = 0;
39236c6e
A
108void
109cs_init(void)
110{
15129b1c
A
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));
39236c6e
A
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) {
15129b1c
A
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 }
39236c6e
A
198 p->p_csflags |= CS_KILLED;
199 cs_procs_killed++;
200 send_kill = 1;
201 retval = 1;
202 }
203
15129b1c
A
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
39236c6e
A
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): "
15129b1c 238 "p=%d[%s] final status 0x%x, %s page%s\n",
39236c6e 239 vaddr, p->p_pid, p->p_comm, p->p_csflags,
15129b1c 240 retval ? "denying" : "allowing (remove VALID)",
39236c6e
A
241 send_kill ? " sending SIGKILL" : "");
242 }
243
244 if (send_kill)
15129b1c 245 threadsignal(current_thread(), SIGKILL, EXC_BAD_ACCESS);
39236c6e
A
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}