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