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